Back to Medusa

{metadata.title}

www/apps/resources/app/commerce-modules/payment/payment-flow/page.mdx

2.14.26.5 KB
Original Source

import { CodeTabs, CodeTab } from "docs-ui"

export const metadata = { title: Accept Payment in Checkout Flow, }

{metadata.title}

In this guide, you'll learn how to implement it using workflows or the Payment Module.

Why Implement the Payment Flow?

Medusa already provides a built-in payment flow that allows you to accept payments from customers, which you can learn about in the Accept Payment Flow in Checkout guide.

You may need to implement a custom payment flow if you have a different use case, or you're using the Payment Module separately from the Medusa application.

This guide will help you understand how to implement a payment flow using the Payment Module's main service or workflows.

You can also follow this guide to get a general understanding of how the payment flow works in the Medusa application.


How to Implement the Accept Payment Flow?

<Note title="Tip">

For a guide on how to implement this flow in the storefront, check out this guide.

</Note> <Note>

It's highly recommended to use Medusa's workflows to implement this flow. Use the Payment Module's main service for more complex cases.

</Note>

1. Create a Payment Collection

A payment collection holds all details related to a resource’s payment operations. So, you start off by creating a payment collection.

In the Medusa application, you associate the payment collection with a cart, which is the resource that the customer is trying to pay for.

For example:

<CodeTabs group="workflow-service"> <CodeTab label="Using Workflow" value="workflow">
ts
import { createPaymentCollectionForCartWorkflow } from "@medusajs/medusa/core-flows"

// ...

await createPaymentCollectionForCartWorkflow(container)
  .run({
    input: {
      cart_id: "cart_123",
    },
  })
</CodeTab> <CodeTab label="Using Service" value="service">
ts
const paymentCollection =
  await paymentModuleService.createPaymentCollections({
    currency_code: "usd",
    amount: 5000,
  })
</CodeTab> </CodeTabs>

2. Show Payment Providers

Next, you'll show the customer the available payment providers to choose from.

In the Medusa application, you need to use Query to retrieve the available payment providers in a region.

<CodeTabs group="query-service"> <CodeTab label="Using Query" value="query">
ts
const query = container.resolve("query")

const { data: regionPaymentProviders } = await query.graph({
  entryPoint: "region_payment_provider",
  variables: {
    filters: {
      region_id: "reg_123",
    },
  },
  fields: ["payment_providers.*"],
})

const paymentProviders = regionPaymentProviders.map(
  (relation) => relation.payment_providers
)
</CodeTab> <CodeTab label="Using Service" value="service">
ts
const paymentProviders = await paymentModuleService.listPaymentProviders()
</CodeTab> </CodeTabs>

3. Create Payment Sessions

The payment collection has one or more payment sessions, each being a payment amount to be authorized by a payment provider.

So, once the customer selects a payment provider, create a payment session for the selected payment provider.

This will also initialize the payment session in the third-party payment provider.

For example:

<CodeTabs group="workflow-service"> <CodeTab label="Using Workflow" value="workflow">
ts
import { createPaymentSessionsWorkflow } from "@medusajs/medusa/core-flows"

// ...

const { result: paymentSession } = await createPaymentSessionsWorkflow(container)
  .run({
    input: {
      payment_collection_id: "paycol_123",
      provider_id: "pp_stripe_stripe",
    },
  })
</CodeTab> <CodeTab label="Using Service" value="service">
ts
const paymentSession =
  await paymentModuleService.createPaymentSession(
    paymentCollection.id,
    {
      provider_id: "pp_stripe_stripe",
      currency_code: "usd",
      amount: 5000,
      data: {
        // any necessary data for the
        // payment provider
      },
    }
  )
</CodeTab> </CodeTabs>

4. Authorize Payment Session

Once the customer places the order, you need to authorize the payment session with the third-party payment provider.

For example:

<CodeTabs group="workflow-service"> <CodeTab label="Using Step" value="workflow">
ts
import { authorizePaymentSessionStep } from "@medusajs/medusa/core-flows"

// ...

authorizePaymentSessionStep({
  id: "payses_123",
  context: {},
})
</CodeTab> <CodeTab label="Using Service" value="service">
ts
const payment = authorizePaymentSessionStep({
  id: "payses_123",
  context: {},
})
</CodeTab> </CodeTabs>

When the payment authorization is successful, a payment is created and returned.

Handling Additional Action

<Note>

If you used the authorizePaymentSessionStep, you don't need to implement this logic as it's implemented in the step.

</Note>

If the payment authorization isn’t successful, whether because it requires additional action or for another reason, the method updates the payment session with the new status and throws an error.

In that case, you can catch that error and, if the session's status property is requires_more, handle the additional action, then retry the authorization.

For example:

ts
try {
  const payment =
    await paymentModuleService.authorizePaymentSession(
      paymentSession.id,
      {}
    )
} catch (e) {
  // retrieve the payment session again
  const updatedPaymentSession = (
    await paymentModuleService.listPaymentSessions({
      id: [paymentSession.id],
    })
  )[0]

  if (updatedPaymentSession.status === "requires_more") {
    // TODO perform required action
    // TODO authorize payment again.
  }
}

5. Payment Flow Complete

The payment flow is complete once the payment session is authorized and the payment is created.

You can then:

<Note>

Some payment providers allow capturing the payment automatically once it’s authorized. In that case, you don’t need to do it manually.

</Note>