medusajs / medusa

The world's most flexible commerce platform.
https://medusajs.com
MIT License
26.24k stars 2.67k forks source link

Unable to set a payment session (medusa.carts.setPaymentSession) with provider_id as 'stripe #6597

Closed dianaow closed 5 months ago

dianaow commented 9 months ago

Bug report

Describe the bug

Unable to set a payment session with the provider_id as 'stripe' after creating payment sessions. I'm certain Stripe has already been set up correctly on Medusa backend and admin, with PUBLIC_STRIPE_KEY and PRIVATE_STRIPE_KEY specified in .env file.

System information

Medusa version (including plugins): "1.20.2" Node.js version: v18.18.0

Steps to reproduce the behavior

Error occurs at medusa.carts.setPaymentSession

   let cart = await medusa.carts.createPaymentSessions(cartid)
      .then(async ({ cart }) => {

         // check if stripe is selected
         const isStripeAvailable = cart.payment_sessions?.some(
            (session: any) => (
            session.provider_id === "stripe"
            )
         )
         if (!isStripeAvailable) {
            return
         }

         // select stripe payment session
         const clientSecret = await medusa.carts.setPaymentSession(lcartid, {
            provider_id: "stripe",
         })
         .then(({ cart }) => cart.payment_session.data.client_secret)
         .catch((e) => console.log(e))

        return clientSecret
      })
      .catch((e) => console.log(e))

Expected behavior

A 200 response with a cart object returned

Screenshots

Medusa Admin Region settings: Stripe is checked Screenshot 2024-03-06 at 6 28 40 PM

POST request to create a payment session (I see the presence of stripe as a payment provider option)

Screenshot 2024-03-06 at 5 03 18 PM

POST request to set a payment session

Screenshot 2024-03-06 at 5 04 01 PM

Error message

Error: Request failed with status code 404

 {
  config: {
    transitional: {
      silentJSONParsing: true,
      forcedJSONParsing: true,
      clarifyTimeoutError: false
    },
    adapter: [Function: httpAdapter],
    transformRequest: [ [Function: transformRequest] ],
    transformResponse: [ [Function: transformResponse] ],
    timeout: 0,
    xsrfCookieName: 'XSRF-TOKEN',
    xsrfHeaderName: 'X-XSRF-TOKEN',
    maxContentLength: -1,
    maxBodyLength: -1,
    validateStatus: [Function: validateStatus],
    headers: {
      Accept: 'application/json',
      'Content-Type': 'application/json',
      'Idempotency-Key': 'd338b7c6-5a76-4564-b905-5ff372672ed7',
      'User-Agent': 'axios/0.24.0',
      'Content-Length': 24
    },
    baseURL: 'http://localhost:9000',
    raxConfig: {
      instance: [Function],
      retry: 3,
      backoffType: 'exponential',
      shouldRetry: [Function: shouldRetry],
      currentRetryAttempt: 0,
      retryDelay: 100,
      httpMethodsToRetry: [Array],
      noResponseRetries: 2,
      checkRetryAfter: true,
      maxRetryAfter: 300000,
      statusCodesToRetry: [Array]
    },
    method: 'post',
    withCredentials: true,
    url: '/store/carts/cart_01HR029TMR3KMY9YJFNCMBD31W/payment-session',
    json: true,
    data: '{"provider_id":"stripe"}'
  },
  request: <ref *1> ClientRequest {
    _events: [Object: null prototype] {
      abort: [Function (anonymous)],
      aborted: [Function (anonymous)],
      connect: [Function (anonymous)],
      error: [Function (anonymous)],
      socket: [Function (anonymous)],
      timeout: [Function (anonymous)],
      finish: [Function: requestOnFinish]
    },
    _eventsCount: 7,
    _maxListeners: undefined,
    outputData: [],
    outputSize: 0,
    writable: true,
    destroyed: false,
    _last: true,
    chunkedEncoding: false,
    shouldKeepAlive: false,
    maxRequestsOnConnectionReached: false,
    _defaultKeepAlive: true,
    useChunkedEncodingByDefault: true,
    sendDate: false,
    _removedConnection: false,
    _removedContLen: false,
    _removedTE: false,
    strictContentLength: false,
    _contentLength: 24,
    _hasBody: true,
    _trailer: '',
    finished: true,
    _headerSent: true,
    _closed: false,
    socket: Socket {
      connecting: false,
      _hadError: false,
      _parent: null,
      _host: 'localhost',
      _closeAfterHandlingError: false,
      _readableState: [ReadableState],
      _events: [Object: null prototype],
      _eventsCount: 7,
      _maxListeners: undefined,
      _writableState: [WritableState],
      allowHalfOpen: false,
      _sockname: null,
      _pendingData: null,
      _pendingEncoding: '',
      server: null,
      _server: null,
      parser: null,
      _httpMessage: [Circular *1],
      [Symbol(async_id_symbol)]: 21776,
      [Symbol(kHandle)]: [TCP],
      [Symbol(lastWriteQueueSize)]: 0,
      [Symbol(timeout)]: null,
      [Symbol(kBuffer)]: null,
      [Symbol(kBufferCb)]: null,
      [Symbol(kBufferGen)]: null,
      [Symbol(kCapture)]: false,
      [Symbol(kSetNoDelay)]: true,
      [Symbol(kSetKeepAlive)]: false,
      [Symbol(kSetKeepAliveInitialDelay)]: 0,
      [Symbol(kBytesRead)]: 0,
      [Symbol(kBytesWritten)]: 0
    },
    _header: 'POST /store/carts/cart_01HR029TMR3KMY9YJFNCMBD31W/payment-session HTTP/1.1\r\n' +
      'Accept: application/json\r\n' +
      'Content-Type: application/json\r\n' +
      'Idempotency-Key: d338b7c6-5a76-4564-b905-5ff372672ed7\r\n' +
      'User-Agent: axios/0.24.0\r\n' +
      'Content-Length: 24\r\n' +
      'Host: localhost:9000\r\n' +
      'Connection: close\r\n' +
      '\r\n',
    _keepAliveTimeout: 0,
    _onPendingData: [Function: nop],
    agent: Agent {
      _events: [Object: null prototype],
      _eventsCount: 2,
      _maxListeners: undefined,
      defaultPort: 80,
      protocol: 'http:',
      options: [Object: null prototype],
      requests: [Object: null prototype] {},
      sockets: [Object: null prototype],
      freeSockets: [Object: null prototype] {},
      keepAliveMsecs: 1000,
      keepAlive: false,
      maxSockets: Infinity,
      maxFreeSockets: 256,
      scheduling: 'lifo',
      maxTotalSockets: Infinity,
      totalSocketCount: 2,
      [Symbol(kCapture)]: false
    },
    socketPath: undefined,
    method: 'POST',
    maxHeaderSize: undefined,
    insecureHTTPParser: undefined,
    joinDuplicateHeaders: undefined,
    path: '/store/carts/cart_01HR029TMR3KMY9YJFNCMBD31W/payment-session',
    _ended: true,
    res: IncomingMessage {
      _readableState: [ReadableState],
      _events: [Object: null prototype],
      _eventsCount: 3,
      _maxListeners: undefined,
      socket: [Socket],
      httpVersionMajor: 1,
      httpVersionMinor: 1,
      httpVersion: '1.1',
      complete: true,
      rawHeaders: [Array],
      rawTrailers: [],
      joinDuplicateHeaders: undefined,
      aborted: false,
      upgrade: false,
      url: '',
      method: null,
      statusCode: 404,
      statusMessage: 'Not Found',
      client: [Socket],
      _consuming: true,
      _dumped: false,
      req: [Circular *1],
      responseUrl: 'http://localhost:9000/store/carts/cart_01HR029TMR3KMY9YJFNCMBD31W/payment-session',
      redirects: [],
      [Symbol(kCapture)]: false,
      [Symbol(kHeaders)]: [Object],
      [Symbol(kHeadersCount)]: 18,
      [Symbol(kTrailers)]: null,
      [Symbol(kTrailersCount)]: 0
    },
    aborted: false,
    timeoutCb: null,
    upgradeOrConnect: false,
    parser: null,
    maxHeadersCount: null,
    reusedSocket: false,
    host: 'localhost',
    protocol: 'http:',
    _redirectable: Writable {
      _writableState: [WritableState],
      _events: [Object: null prototype],
      _eventsCount: 2,
      _maxListeners: undefined,
      _options: [Object],
      _ended: true,
      _ending: true,
      _redirectCount: 0,
      _redirects: [],
      _requestBodyLength: 24,
      _requestBodyBuffers: [],
      _onNativeResponse: [Function (anonymous)],
      _currentRequest: [Circular *1],
      _currentUrl: 'http://localhost:9000/store/carts/cart_01HR029TMR3KMY9YJFNCMBD31W/payment-session',
      [Symbol(kCapture)]: false
    },
    [Symbol(kCapture)]: false,
    [Symbol(kBytesWritten)]: 0,
    [Symbol(kNeedDrain)]: false,
    [Symbol(corked)]: 0,
    [Symbol(kOutHeaders)]: [Object: null prototype] {
      accept: [Array],
      'content-type': [Array],
      'idempotency-key': [Array],
      'user-agent': [Array],
      'content-length': [Array],
      host: [Array]
    },
    [Symbol(errored)]: null,
    [Symbol(kHighWaterMark)]: 16384,
    [Symbol(kRejectNonStandardBodyWrites)]: false,
    [Symbol(kUniqueHeaders)]: null
  },
  response: {
    status: 404,
    statusText: 'Not Found',
    headers: {
      'x-powered-by': 'Express',
      vary: 'Origin',
      'access-control-allow-credentials': 'true',
      'content-type': 'application/json; charset=utf-8',
      'content-length': '82',
      etag: 'W/"52-Jq2d2nyt3uXU7m3XjjI72eh/Ejs"',
      'set-cookie': [Array],
      date: 'Wed, 06 Mar 2024 10:05:48 GMT',
      connection: 'close'
    },
    config: {
      transitional: [Object],
      adapter: [Function: httpAdapter],
      transformRequest: [Array],
      transformResponse: [Array],
      timeout: 0,
      xsrfCookieName: 'XSRF-TOKEN',
      xsrfHeaderName: 'X-XSRF-TOKEN',
      maxContentLength: -1,
      maxBodyLength: -1,
      validateStatus: [Function: validateStatus],
      headers: [Object],
      baseURL: 'http://localhost:9000',
      raxConfig: [Object],
      method: 'post',
      withCredentials: true,
      url: '/store/carts/cart_01HR029TMR3KMY9YJFNCMBD31W/payment-session',
      json: true,
      data: '{"provider_id":"stripe"}'
    },
    request: <ref *1> ClientRequest {
      _events: [Object: null prototype],
      _eventsCount: 7,
      _maxListeners: undefined,
      outputData: [],
      outputSize: 0,
      writable: true,
      destroyed: false,
      _last: true,
      chunkedEncoding: false,
      shouldKeepAlive: false,
      maxRequestsOnConnectionReached: false,
      _defaultKeepAlive: true,
      useChunkedEncodingByDefault: true,
      sendDate: false,
      _removedConnection: false,
      _removedContLen: false,
      _removedTE: false,
      strictContentLength: false,
      _contentLength: 24,
      _hasBody: true,
      _trailer: '',
      finished: true,
      _headerSent: true,
      _closed: false,
      socket: [Socket],
      _header: 'POST /store/carts/cart_01HR029TMR3KMY9YJFNCMBD31W/payment-session HTTP/1.1\r\n' +
        'Accept: application/json\r\n' +
        'Content-Type: application/json\r\n' +
        'Idempotency-Key: d338b7c6-5a76-4564-b905-5ff372672ed7\r\n' +
        'User-Agent: axios/0.24.0\r\n' +
        'Content-Length: 24\r\n' +
        'Host: localhost:9000\r\n' +
        'Connection: close\r\n' +
        '\r\n',
      _keepAliveTimeout: 0,
      _onPendingData: [Function: nop],
      agent: [Agent],
      socketPath: undefined,
      method: 'POST',
      maxHeaderSize: undefined,
      insecureHTTPParser: undefined,
      joinDuplicateHeaders: undefined,
      path: '/store/carts/cart_01HR029TMR3KMY9YJFNCMBD31W/payment-session',
      _ended: true,
      res: [IncomingMessage],
      aborted: false,
      timeoutCb: null,
      upgradeOrConnect: false,
      parser: null,
      maxHeadersCount: null,
      reusedSocket: false,
      host: 'localhost',
      protocol: 'http:',
      _redirectable: [Writable],
      [Symbol(kCapture)]: false,
      [Symbol(kBytesWritten)]: 0,
      [Symbol(kNeedDrain)]: false,
      [Symbol(corked)]: 0,
      [Symbol(kOutHeaders)]: [Object: null prototype],
      [Symbol(errored)]: null,
      [Symbol(kHighWaterMark)]: 16384,
      [Symbol(kRejectNonStandardBodyWrites)]: false,
      [Symbol(kUniqueHeaders)]: null
    },
    data: {
      type: 'not_found',
      message: 'Could not find a payment provider with id: stripe'
    }
  },
  isAxiosError: true,
  toJSON: [Function: toJSON]
}
dianaow commented 8 months ago

It is necessary to add a webhook_secret to the plugins array in medusa-config.js in the backend.

As quoted from the documentation: "As for the webhook_secret, it’s essential for your production environment. So, if you’re only using Stripe for development you can skip adding the value for this option at the moment."

I interpreted this to mean that if I were to attempt a checkout and payment flow with a Stripe account in test mode, I wouldn't need to add the webhook_secret. However, the opposite is true. To get a successful response from any Medusa Store APIs related to payment sessions, shipping methods and cart completion, a webhook_secret needs to be specified alongside correct public and secret Stripe API keys.

sradevski commented 5 months ago

Hey, thanks for the report! Since v2 brought a lot of architectural and API changes on the backend, we will be closing this ticket since it no longer applies to our new setup, or the issue has already been fixed. If you are still facing issues with v1, please open a new ticket and we will address it as soon as possible. Thanks! 🙏