Payment Adapters
A deeper look into the payment adapter pattern used by the Ecommerce Plugin, and how to create your own.
The current list of supported payment adapters are:
REST API
The plugin will create REST API endpoints for each payment adapter you add to your configuration. The endpoints will be available at /api/payments/{provider_name}/{action}
where provider_name
is the name of the payment adapter and action
is one of the following:
Action | Method | Description |
---|---|---|
| POST | Initiate a payment for an order. See initiatePayment for more details. |
| POST | Confirm an order after a payment has been made. See confirmOrder for more details. |
Stripe
Out of the box we integrate with Stripe to handle one-off purchases. To use Stripe, you will need to install the Stripe package:
We recommend at least 18.5.0
to ensure compatibility with the plugin.
Then, in your plugins
array of your Payload Config, call the plugin with:
Configuration
The Stripe payment adapter takes the following configuration options:
Option | Type | Description |
---|---|---|
secretKey | | Your Stripe Secret Key, found in the Stripe Dashboard. |
publishableKey | | Your Stripe Publishable Key, found in the Stripe Dashboard. |
webhookSecret | | (Optional) Your Stripe Webhooks Signing Secret, found in the Stripe Dashboard. Required if you want to use webhooks. |
appInfo | | (Optional) An object containing |
webhooks | | (Optional) An object where the keys are Stripe event types and the values are functions that will be called when that event is received. See Webhooks for more details. |
groupOverrides | | (Optional) An object to override the default fields of the payment group. See Payment Fields for more details. |
Stripe Webhooks
You can also add your own webhooks to handle events from Stripe. This is optional and the plugin internally does not use webhooks for any core functionality. It receives the following arguments:
Argument | Type | Description |
---|---|---|
event | | The Stripe event object |
req | | The Payload request object |
stripe | | The initialized Stripe instance |
You can add a webhook like so:
To use webhooks you also need to have them configured in your Stripe Dashboard.
You can use the Stripe CLI to forward webhooks to your local development environment.
Frontend usage
The most straightforward way to use Stripe on the frontend is with the EcommerceProvider
component and the stripeAdapterClient
function. Wrap your application in the provider and pass in the Stripe adapter with your publishable key:
Then you can use the usePayments
hook to access the initiatePayment
and confirmOrder
functions, see the Frontend docs for more details.
Making your own Payment Adapter
You can make your own payment adapter by implementing the PaymentAdapter
interface. This interface requires you to implement the following methods:
Property | Type | Description |
---|---|---|
| | The name of the payment method. This will be used to identify the payment method in the API and on the frontend. |
| | (Optional) A human-readable label for the payment method. This will be used in the admin panel and on the frontend. |
| | The function that is called via the |
| | The function that is called via the |
| | (Optional) An array of endpoints to be bootstrapped to Payload's API in order to support the payment method. All API paths are relative to |
| | A group field config to be used in transactions to track the necessary data for the payment processor, eg. PaymentIntentID for Stripe. See Payment Fields for more details. |
The arguments can be extended but should always include the PaymentAdapterArgs
type which has the following types:
Property | Type | Description |
---|---|---|
| | (Optional) Allow overriding the default UI label for this adaper. |
| | (Optional) Allow overriding the default fields of the payment group. See Payment Fields for more details. |
initiatePayment
The initiatePayment
function is called when a payment is initiated. At this step the transaction is created with a status "Processing", an abandoned purchaase will leave this transaction in this state. It receives an object with the following properties:
Property | Type | Description |
---|---|---|
| | The transaction being processed. |
| | The cart associated with the transaction. |
| | The customer associated with the transaction. |
| | The Payload request object. |
The data object will contain the following properties:
Property | Type | Description |
---|---|---|
| | The billing address associated with the transaction. |
| | (Optional) The shipping address associated with the transaction. If this is missing then use the billing address. |
| | The cart collection item. |
| | In the case that |
| | The currency for the cart associated with the transaction. |
The return type then only needs to contain the following properties though the type supports any additional data returned as needed for the frontend:
Property | Type | Description |
---|---|---|
| | A success message to be returned to the client. |
At any point in the function you can throw an error to return a 4xx or 5xx response to the client.
A heavily simplified example of implementing initiatePayment
could look like:
confirmOrder
The confirmOrder
function is called after a payment is completed on the frontend and at this step the order is created in Payload. It receives the following properties:
Property | Type | Description |
---|---|---|
| | The orders collection slug. |
| | The transactions collection slug. |
| | The carts collection slug. |
| | The customers collection slug. |
| | The cart associated with the transaction. |
| | The Payload request object. |
The data object will contain any data the frontend chooses to send through and at a minimum the following:
Property | Type | Description |
---|---|---|
| | In the case that |
The return type can also contain any additional data with a minimum of the following:
Property | Type | Description |
---|---|---|
| | A success message to be returned to the client. |
| | The ID of the created order. |
| | The ID of the associated transaction. |
A heavily simplified example of implementing confirmOrder
could look like:
Payment Fields
Payment fields are used primarily on the transactions collection to store information about the payment method used. Each payment adapter must provide a group
field which will be used to store this information.
For example, the Stripe adapter provides the following group field:
Client side Payment Adapter
The client side adapter should implement the PaymentAdapterClient
interface:
Property | Type | Description |
---|---|---|
| | The name of the payment method. This will be used to identify the payment method in the API and on the frontend. |
| | (Optional) A human-readable label for the payment method. This can be used as a human readable format. |
| | Flag to toggle on the EcommerceProvider's ability to call the |
| | Flag to toggle on the EcommerceProvider's ability to call the |
And for the args use the PaymentAdapterClientArgs
type:
Property | Type | Description |
---|---|---|
| | (Optional) Allow overriding the default UI label for this adaper. |
Best Practices
Always handle sensitive operations like creating payment intents and confirming payments on the server side. Use webhooks to listen for events from Stripe and update your orders accordingly. Never expose your secret key on the frontend. By default Nextjs will only expose environment variables prefixed with NEXT_PUBLIC_
to the client.
While we validate the products and prices on the server side when creating a payment intent, you should override the validation function to add any additional checks you may need for your specific use case.
You are safe to pass the ID of a transaction to the frontend however you shouldn't pass any sensitive information or the transaction object itself.
When passing price information to your payment provider it should always come from the server and it should be verified against the products in your database. Never trust price information coming from the client.
When using webhooks, ensure that you verify the webhook signatures to confirm that the requests are genuinely from Stripe. This helps prevent unauthorized access and potential security vulnerabilities.