- 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 Directory#
Start by creating a new directory for your module. For example, src/modules/my-payment
.
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
:
Type parameters#
TConfig
objectOptionalconstructor#
You can use the constructor
of the provider's service to access resources in your module's container.
You can also use the constructor to initialize your integration with the third-party provider. For example, if you use a client to connect to the third-party provider’s APIs, you can initialize it in the constructor and use it in other methods in the service.
The provider can also access the module's options as a second parameter.
Example
1import {2 AbstractPaymentProvider3} from "@medusajs/framework/utils"4import { Logger } from "@medusajs/framework/types"5 6type InjectedDependencies = {7 logger: Logger8}9 10type Options = {11 apiKey: string12}13 14class MyPaymentProviderService extends AbstractPaymentProvider<15 Options16> {17 static identifier = "my-payment"18 protected logger_: Logger19 protected options_: Options20 // Assuming you're using a client to integrate21 // with a third-party service22 protected client23 24 constructor(25 { logger }: InjectedDependencies,26 options: Options27 ) {28 // @ts-ignore29 super(...arguments)30 31 this.logger_ = logger32 this.options_ = options33 34 // Assuming you're initializing a client35 this.client = new Client(options)36 }37 38 // ...39}40 41export default MyPaymentProviderService
Parameters
container
MedusaContainerconfig
TConfigvalidateOptions#
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>Returns
void
voidmedusa-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.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 PaymentProviderError,4 PaymentProviderSessionResponse,5} from "@medusajs/framework/types"6 7class MyPaymentProviderService extends AbstractPaymentProvider<8 Options9> {10 async capturePayment(11 paymentData: Record<string, unknown>12 ): Promise<PaymentProviderError | PaymentProviderSessionResponse["data"]> {13 const externalId = paymentData.id14 15 try {16 const newData = await this.client.capturePayment(externalId)17 18 return {19 ...newData,20 id: externalId21 }22 } catch (e) {23 return {24 error: e,25 code: "unknown",26 detail: e27 }28 }29 }30 31 // ...32}
Parameters
paymentData
Record<string, unknown>data
property of the payment. Make sure to store in it
any helpful identification for your third-party integration.Returns
Promise
Promise<Record<string, unknown> | PaymentProviderError>The new data to store in the payment's data
property, or an error object.
Promise
Promise<Record<string, unknown> | PaymentProviderError>data
property, or an error object.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 PaymentProviderError,4 PaymentProviderSessionResponse,5 PaymentSessionStatus6} from "@medusajs/framework/types"7 8class MyPaymentProviderService extends AbstractPaymentProvider<9 Options10> {11 async authorizePayment(12 paymentSessionData: Record<string, unknown>,13 context: Record<string, unknown>14 ): Promise<15 PaymentProviderError | {16 status: PaymentSessionStatus17 data: PaymentProviderSessionResponse["data"]18 }19 > {20 const externalId = paymentSessionData.id21 22 try {23 const paymentData = await this.client.authorizePayment(externalId)24 25 return {26 data: {27 ...paymentData,28 id: externalId29 },30 status: "authorized"31 }32 } catch (e) {33 return {34 error: e,35 code: "unknown",36 detail: e37 }38 }39 }40 41 // ...42}
Parameters
paymentSessionData
Record<string, unknown>data
property of the payment session. Make sure to store in it
any helpful identification for your third-party integration.context
Record<string, unknown>cart_id
property indicating the ID of the associated cart.Returns
Promise
Promise<PaymentProviderError | object>Either an object of the new data to store in the created payment's data
property and the
payment's status, or an error object. Make sure to set in data
anything useful to later retrieve the session.
Promise
Promise<PaymentProviderError | object>data
property and the
payment's status, or an error object. Make sure to set in data
anything useful to later retrieve the session.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 paymentData: Record<string, unknown>12 ): Promise<PaymentProviderError | PaymentProviderSessionResponse["data"]> {13 const externalId = paymentData.id14 15 try {16 const paymentData = await this.client.cancelPayment(externalId)17 } catch (e) {18 return {19 error: e,20 code: "unknown",21 detail: e22 }23 }24 }25 26 // ...27}
Parameters
paymentData
Record<string, unknown>data
property of the payment. Make sure to store in it
any helpful identification for your third-party integration.Returns
Promise
Promise<Record<string, unknown> | PaymentProviderError>An error object if an error occurs, or the data received from the integration.
Promise
Promise<Record<string, unknown> | PaymentProviderError>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 PaymentProviderError,4 PaymentProviderSessionResponse,5} from "@medusajs/framework/types"6 7class MyPaymentProviderService extends AbstractPaymentProvider<8 Options9> {10 async initiatePayment(11 context: CreatePaymentProviderSession12 ): Promise<PaymentProviderError | PaymentProviderSessionResponse> {13 const {14 amount,15 currency_code,16 context: customerDetails17 } = context18 19 try {20 const response = await this.client.init(21 amount, currency_code, customerDetails22 )23 24 return {25 ...response,26 data: {27 id: response.id28 }29 }30 } catch (e) {31 return {32 error: e,33 code: "unknown",34 detail: e35 }36 }37 }38 39 // ...40}
Parameters
context
CreatePaymentProviderSessionReturns
Promise
Promise<PaymentProviderError | PaymentProviderSessionResponse>An object whose data
property is set in the created payment session, or an error
object. Make sure to set in data
anything useful to later retrieve the session.
Promise
Promise<PaymentProviderError | PaymentProviderSessionResponse>data
property is set in the created payment session, or an error
object. Make sure to set in data
anything useful to later retrieve the session.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 PaymentProviderError,4 PaymentProviderSessionResponse,5} from "@medusajs/framework/types"6 7class MyPaymentProviderService extends AbstractPaymentProvider<8 Options9> {10 async deletePayment(11 paymentSessionData: Record<string, unknown>12 ): Promise<13 PaymentProviderError | PaymentProviderSessionResponse["data"]14 > {15 const externalId = paymentSessionData.id16 17 try {18 await this.client.cancelPayment(externalId)19 } catch (e) {20 return {21 error: e,22 code: "unknown",23 detail: e24 }25 }26 }27 28 // ...29}
Parameters
paymentSessionData
Record<string, unknown>data
property of the payment session. Make sure to store in it
any helpful identification for your third-party integration.Returns
Promise
Promise<Record<string, unknown> | PaymentProviderError>An error object or the response from the third-party service.
Promise
Promise<Record<string, unknown> | PaymentProviderError>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 PaymentSessionStatus4} from "@medusajs/framework/types"5 6class MyPaymentProviderService extends AbstractPaymentProvider<7 Options8> {9 async getPaymentStatus(10 paymentSessionData: Record<string, unknown>11 ): Promise<PaymentSessionStatus> {12 const externalId = paymentSessionData.id13 14 try {15 const status = await this.client.getStatus(externalId)16 17 switch (status) {18 case "requires_capture":19 return "authorized"20 case "success":21 return "captured"22 case "canceled":23 return "canceled"24 default:25 return "pending"26 }27 } catch (e) {28 return "error"29 }30 }31 32 // ...33}
Parameters
paymentSessionData
Record<string, unknown>data
property of the payment session. Make sure to store in it
any helpful identification for your third-party integration.Returns
Promise
Promise<PaymentSessionStatus>The payment session's status.
Promise
Promise<PaymentSessionStatus>refundPayment#
This method refunds an amount of a payment previously captured.
Example
1// other imports...2import {3 PaymentProviderError,4 PaymentProviderSessionResponse,5} from "@medusajs/framework/types"6 7class MyPaymentProviderService extends AbstractPaymentProvider<8 Options9> {10 async refundPayment(11 paymentData: Record<string, unknown>,12 refundAmount: number13 ): Promise<14 PaymentProviderError | PaymentProviderSessionResponse["data"]15 > {16 const externalId = paymentData.id17 18 try {19 const newData = await this.client.refund(20 externalId,21 refundAmount22 )23 24 return {25 ...newData,26 id: externalId27 }28 } catch (e) {29 return {30 error: e,31 code: "unknown",32 detail: e33 }34 }35 }36 37 // ...38}
Parameters
paymentData
Record<string, unknown>data
property of the payment. Make sure to store in it
any helpful identification for your third-party integration.refundAmount
numberReturns
Promise
Promise<Record<string, unknown> | PaymentProviderError>The new data to store in the payment's data
property, or an error object.
Promise
Promise<Record<string, unknown> | PaymentProviderError>data
property, or an error object.retrievePayment#
Retrieves the payment's data from the third-party service.
Example
1// other imports...2import {3 PaymentProviderError,4 PaymentProviderSessionResponse,5} from "@medusajs/framework/types"6 7class MyPaymentProviderService extends AbstractPaymentProvider<8 Options9> {10 async retrievePayment(11 paymentSessionData: Record<string, unknown>12 ): Promise<13 PaymentProviderError | PaymentProviderSessionResponse["data"]14 > {15 const externalId = paymentSessionData.id16 17 try {18 return await this.client.retrieve(externalId)19 } catch (e) {20 return {21 error: e,22 code: "unknown",23 detail: e24 }25 }26 }27 28 // ...29}
Parameters
paymentSessionData
Record<string, unknown>data
property of the payment. Make sure to store in it
any helpful identification for your third-party integration.Returns
Promise
Promise<Record<string, unknown> | PaymentProviderError>An object to be stored in the payment's data
property, or an error object.
Promise
Promise<Record<string, unknown> | PaymentProviderError>data
property, or an error object.updatePayment#
Update a payment in the third-party service that was previously initiated with the initiatePayment method.
Example
1// other imports...2import {3 UpdatePaymentProviderSession,4 PaymentProviderError,5 PaymentProviderSessionResponse,6} from "@medusajs/framework/types"7 8class MyPaymentProviderService extends AbstractPaymentProvider<9 Options10> {11 async updatePayment(12 context: UpdatePaymentProviderSession13 ): Promise<PaymentProviderError | PaymentProviderSessionResponse> {14 const {15 amount,16 currency_code,17 context: customerDetails,18 data19 } = context20 const externalId = data.id21 22 try {23 const response = await this.client.update(24 externalId,25 {26 amount,27 currency_code,28 customerDetails29 }30 )31 32 return {33 ...response,34 data: {35 id: response.id36 }37 }38 } catch (e) {39 return {40 error: e,41 code: "unknown",42 detail: e43 }44 }45 }46 47 // ...48}
Parameters
context
UpdatePaymentProviderSessionReturns
Promise
Promise<PaymentProviderError | PaymentProviderSessionResponse>An object whose data
property is set in the updated payment session, or an error
object. Make sure to set in data
anything useful to later retrieve the session.
Promise
Promise<PaymentProviderError | PaymentProviderSessionResponse>data
property is set in the updated payment session, or an error
object. Make sure to set in data
anything useful to later retrieve the session.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
:
1import { Modules } from "@medusajs/framework/utils"2 3// ...4 5module.exports = defineConfig({6 // ...7 modules: [8 {9 resolve: "@medusajs/medusa/payment",10 options: {11 providers: [12 {13 resolve: "./src/modules/my-payment",14 id: "my-payment",15 options: {16 // provider options...17 apiKey: "..."18 }19 }20 ]21 }22 }23 ]24})
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.