- Get Started
- Product
- Resources
- Tools & SDKs
- Framework
- Reference
- Get Started
- Product
- Resources
- Tools & SDKs
- Framework
- Reference
How to Create a Payment Provider
In this document, you’ll learn how to create a Payment Provider to be used with the Payment Module.
1. Create Module Provider Directory#
Start by creating a new directory for your module provider.
If you're creating the module provider in a Medusa application, create it under the src/modules
directory. For example, src/modules/my-payment
.
If you're creating the module provider in a plugin, create it under the src/providers
directory. For example, src/providers/my-payment
.
src/modules/my-payment
directory as an example.2. Create the Payment Provider Service#
Create the file src/modules/my-payment/service.ts
that holds the module's main service. It must extend the AbstractPaymentProvider
class imported from @medusajs/framework/utils
:
constructor#
The constructor allows you to access resources from the module's container using the first parameter, and the module's options using the second parameter.
Example
1import { AbstractPaymentProvider } from "@medusajs/framework/utils"2import { Logger } from "@medusajs/framework/types"3 4type Options = {5 apiKey: string6}7 8type InjectedDependencies = {9 logger: Logger10}11 12class MyPaymentProviderService extends AbstractPaymentProvider<Options> {13 protected logger_: Logger14 protected options_: Options15 // assuming you're initializing a client16 protected client17 18 constructor(19 container: InjectedDependencies,20 options: Options21 ) {22 super(container, options)23 24 this.logger_ = container.logger25 this.options_ = options26 27 // TODO initialize your client28 }29 // ...30}31 32export default MyPaymentProviderService
Type Parameters
TConfig
objectOptionalParameters
cradle
Record<string, unknown>identifier#
Each payment provider has a unique identifier defined in its class. The provider's ID
will be stored as pp_{identifier}_{id}
, where {id}
is the provider's id
property in the medusa-config.ts
.
Example
validateOptions#
This method validates the options of the provider set in medusa-config.ts
.
Implementing this method is optional. It's useful if your provider requires custom validation.
If the options aren't valid, throw an error.
Example
Parameters
options
Record<any, any>capturePayment#
This method is used to capture a payment. The payment is captured in one of the following scenarios:
- The authorizePayment method returns the status
captured
, which automatically executed this method after authorization. - The merchant requests to capture the payment after its associated payment session was authorized.
- A webhook event occurred that instructs the payment provider to capture the payment session. Learn more about handing webhook events in this guide.
In this method, use the third-party provider to capture the payment.
Example
1// other imports...2import {3 CapturePaymentInput,4 CapturePaymentOutput,5} from "@medusajs/framework/types"6 7class MyPaymentProviderService extends AbstractPaymentProvider<8 Options9> {10 async capturePayment(11 input: CapturePaymentInput12 ): Promise<CapturePaymentOutput> {13 const externalId = input.data?.id14 15 // assuming you have a client that captures the payment16 const newData = await this.client.capturePayment(externalId)17 return {data: newData}18 }19 // ...20}
Parameters
input
PaymentProviderInputdata
field should contain the data from the payment provider. when the payment was created.Returns
Promise
Promise<PaymentProviderOutput>The new data to store in the payment's data
property. Throws in case of an error.
Promise
Promise<PaymentProviderOutput>data
property. Throws in case of an error.authorizePayment#
This method authorizes a payment session. When authorized successfully, a payment is created by the Payment Module which can be later captured using the capturePayment method.
Refer to this guide to learn more about how this fits into the payment flow and how to handle required actions.
To automatically capture the payment after authorization, return the status captured
.
Example
1// other imports...2import {3 AuthorizePaymentInput,4 AuthorizePaymentOutput,5 PaymentSessionStatus6} from "@medusajs/framework/types"7 8class MyPaymentProviderService extends AbstractPaymentProvider<9 Options10> {11 async authorizePayment(12 input: AuthorizePaymentInput13 ): Promise<AuthorizePaymentOutput> {14 const externalId = input.data?.id15 16 // assuming you have a client that authorizes the payment17 const paymentData = await this.client.authorizePayment(externalId)18 19 return {20 data: paymentData,21 status: "authorized"22 }23 }24 25 // ...26}
Parameters
input
PaymentProviderInputdata
field should contain the data from the payment provider. when the payment was created.Returns
Promise
Promise<AuthorizePaymentOutput>The status of the authorization, along with the data
field about the payment. Throws in case of an error.
Promise
Promise<AuthorizePaymentOutput>data
field about the payment. Throws in case of an error.cancelPayment#
This method cancels a payment.
Example
1// other imports...2import {3 PaymentProviderError,4 PaymentProviderSessionResponse,5} from "@medusajs/framework/types"6 7class MyPaymentProviderService extends AbstractPaymentProvider<8 Options9> {10 async cancelPayment(11 input: CancelPaymentInput12 ): Promise<CancelPaymentOutput> {13 const externalId = input.data?.id14 15 // assuming you have a client that cancels the payment16 const paymentData = await this.client.cancelPayment(externalId)17 return { data: paymentData }18 }19 20 // ...21}
Parameters
input
PaymentProviderInputdata
field should contain the data from the payment provider. when the payment was created.Returns
Promise
Promise<PaymentProviderOutput>The new data to store in the payment's data
property, if any. Throws in case of an error.
Promise
Promise<PaymentProviderOutput>data
property, if any. Throws in case of an error.initiatePayment#
This method is used when a payment session is created. It can be used to initiate the payment in the third-party session, before authorizing or capturing the payment later.
Example
1// other imports...2import {3 InitiatePaymentInput,4 InitiatePaymentOutput,5} from "@medusajs/framework/types"6 7class MyPaymentProviderService extends AbstractPaymentProvider<8 Options9> {10 async initiatePayment(11 input: InitiatePaymentInput12 ): Promise<InitiatePaymentOutput> {13 const {14 amount,15 currency_code,16 context: customerDetails17 } = input18 19 // assuming you have a client that initializes the payment20 const response = await this.client.init(21 amount, currency_code, customerDetails22 )23 24 return {25 id: response.id26 data: response,27 }28 }29 30 // ...31}
Parameters
input
InitiatePaymentInputReturns
Promise
Promise<InitiatePaymentOutput>The new data to store in the payment's data
property. Throws in case of an error.
Promise
Promise<InitiatePaymentOutput>data
property. Throws in case of an error.deletePayment#
This method is used when a payment session is deleted, which can only happen if it isn't authorized, yet.
Use this to delete or cancel the payment in the third-party service.
Example
1// other imports...2import {3 DeletePaymentInput,4 DeletePaymentOutput,5} from "@medusajs/framework/types"6 7class MyPaymentProviderService extends AbstractPaymentProvider<8 Options9> {10 async deletePayment(11 input: DeletePaymentInput12 ): Promise<DeletePaymentOutput> {13 const externalId = input.data?.id14 15 // assuming you have a client that cancels the payment16 await this.client.cancelPayment(externalId)17 return {}18 }19 }20 21 // ...22}
Parameters
input
PaymentProviderInputdata
field should contain the data from the payment provider. when the payment was created.Returns
Promise
Promise<PaymentProviderOutput>The new data to store in the payment's data
property, if any. Throws in case of an error.
Promise
Promise<PaymentProviderOutput>data
property, if any. Throws in case of an error.getPaymentStatus#
This method gets the status of a payment session based on the status in the third-party integration.
Example
1// other imports...2import {3 GetPaymentStatusInput,4 GetPaymentStatusOutput,5 PaymentSessionStatus6} from "@medusajs/framework/types"7 8class MyPaymentProviderService extends AbstractPaymentProvider<9 Options10> {11 async getPaymentStatus(12 input: GetPaymentStatusInput13 ): Promise<GetPaymentStatusOutput> {14 const externalId = input.data?.id15 16 // assuming you have a client that retrieves the payment status17 const status = await this.client.getStatus(externalId)18 19 switch (status) {20 case "requires_capture":21 return {status: "authorized"}22 case "success":23 return {status: "captured"}24 case "canceled":25 return {status: "canceled"}26 default:27 return {status: "pending"}28 }29 }30 31 // ...32}
Parameters
input
PaymentProviderInputdata
field should contain the data from the payment provider. when the payment was created.Returns
Promise
Promise<GetPaymentStatusOutput>The payment session's status. It can also return additional data
from the payment provider.
Promise
Promise<GetPaymentStatusOutput>data
from the payment provider.refundPayment#
This method refunds an amount of a payment previously captured.
Example
1// other imports...2import {3 RefundPaymentInput,4 RefundPaymentOutput,5} from "@medusajs/framework/types"6 7class MyPaymentProviderService extends AbstractPaymentProvider<8 Options9> {10 async refundPayment(11 input: RefundPaymentInput12 ): Promise<RefundPaymentOutput> {13 const externalId = input.data?.id14 15 // assuming you have a client that refunds the payment16 const newData = await this.client.refund(17 externalId,18 input.amount19 )20 21 return {data: newData}22 }23 // ...24}
Parameters
input
RefundPaymentInputdata
field should contain the data from the payment provider. when the payment was created.Returns
Promise
Promise<PaymentProviderOutput>The new data to store in the payment's data
property, or an error object.
Promise
Promise<PaymentProviderOutput>data
property, or an error object.retrievePayment#
Retrieves the payment's data from the third-party service.
Example
1// other imports...2import {3 RetrievePaymentInput,4 RetrievePaymentOutput,5} from "@medusajs/framework/types"6 7class MyPaymentProviderService extends AbstractPaymentProvider<8 Options9> {10 async retrievePayment(11 input: RetrievePaymentInput12 ): Promise<RetrievePaymentOutput> {13 const externalId = input.data?.id14 15 // assuming you have a client that retrieves the payment16 return await this.client.retrieve(externalId)17 }18 // ...19}
Parameters
input
PaymentProviderInputdata
field should contain the data from the payment provider when the payment was created.Returns
Promise
Promise<PaymentProviderOutput>The payment's data as found in the the payment provider.
Promise
Promise<PaymentProviderOutput>updatePayment#
Update a payment in the third-party service that was previously initiated with the initiatePayment method.
Example
1// other imports...2import {3 UpdatePaymentInput,4 UpdatePaymentOutput,5} from "@medusajs/framework/types"6 7class MyPaymentProviderService extends AbstractPaymentProvider<8 Options9> {10 async updatePayment(11 input: UpdatePaymentInput12 ): Promise<UpdatePaymentOutput> {13 const { amount, currency_code, context } = input14 const externalId = input.data?.id15 16 // assuming you have a client that updates the payment17 const response = await this.client.update(18 externalId,19 {20 amount,21 currency_code,22 context.customer23 }24 )25 26 return response27 }28 29 // ...30}
Parameters
input
UpdatePaymentInputdata
field should contain the data from the payment provider. when the payment was created.Returns
Promise
Promise<PaymentProviderOutput>The new data to store in the payment's data
property. Throws in case of an error.
Promise
Promise<PaymentProviderOutput>data
property. Throws in case of an error.getWebhookActionAndData#
This method is executed when a webhook event is received from the third-party payment provider. Use it to process the action of the payment provider.
Learn more in this documentation
Example
1// other imports...2import {3 BigNumber4} from "@medusajs/framework/utils"5import {6 ProviderWebhookPayload,7 WebhookActionResult8} from "@medusajs/framework/types"9 10class MyPaymentProviderService extends AbstractPaymentProvider<11 Options12> {13 async getWebhookActionAndData(14 payload: ProviderWebhookPayload["payload"]15 ): Promise<WebhookActionResult> {16 const {17 data,18 rawData,19 headers20 } = payload21 22 try {23 switch(data.event_type) {24 case "authorized_amount":25 return {26 action: "authorized",27 data: {28 session_id: (data.metadata as Record<string, any>).session_id,29 amount: new BigNumber(data.amount as number)30 }31 }32 case "success":33 return {34 action: "captured",35 data: {36 session_id: (data.metadata as Record<string, any>).session_id,37 amount: new BigNumber(data.amount as number)38 }39 }40 default:41 return {42 action: "not_supported"43 }44 }45 } catch (e) {46 return {47 action: "failed",48 data: {49 session_id: (data.metadata as Record<string, any>).session_id,50 amount: new BigNumber(data.amount as number)51 }52 }53 }54 }55 56 // ...57}
Parameters
data
objectThe webhook event's data
data
objectReturns
Promise
Promise<WebhookActionResult>The webhook result. If the action
's value is captured
, the payment is captured within Medusa as well.
If the action
's value is authorized
, the associated payment session is authorized within Medusa.
Promise
Promise<WebhookActionResult>action
's value is captured
, the payment is captured within Medusa as well.
If the action
's value is authorized
, the associated payment session is authorized within Medusa.3. Create Module Definition File#
Create the file src/modules/my-payment/index.ts
with the following content:
This exports the module's definition, indicating that the MyPaymentProviderService
is the module's service.
4. Use Module#
To use your Payment Module Provider, add it to the providers
array of the Payment Module in medusa-config.ts
:
1module.exports = defineConfig({2 // ...3 modules: [4 {5 resolve: "@medusajs/medusa/payment",6 options: {7 providers: [8 {9 // if module provider is in a plugin, use `plugin-name/providers/my-payment`10 resolve: "./src/modules/my-payment",11 id: "my-payment",12 options: {13 // provider options...14 apiKey: "..."15 }16 }17 ]18 }19 }20 ]21})
5. Test it Out#
Before you use your payment provider, enable it in a region using the Medusa Admin.
Then, go through checkout to place an order. Your payment provider is used to authorize the payment.