---
title: "Ecommerce — Payment Adapters"
aliases: [payment-adapter, stripe-adapter, payload-payments]
tags: [payloadcms, ecommerce, stripe, payments, plugin]
sources: [raw/ecommerce__payments.md]
created: 2026-05-15
updated: 2026-05-15
---

## Overview

The Ecommerce Plugin uses a **payment adapter pattern** — each payment provider is a self-contained unit that plugs into `/api/payments/{provider_name}/{action}`. Currently only Stripe ships out of the box, but you can implement your own.

## REST Endpoints (Auto-generated)

| Action | Method | Description |
|--------|--------|-------------|
| `initiate` | POST | Create transaction with status "Processing" |
| `confirm-order` | POST | Create order after payment completes on frontend |

Endpoint path: `/api/payments/{provider_name}/{action}`

## Stripe Setup

```bash
pnpm add stripe  # >= 18.5.0
```

```ts
import { ecommercePlugin } from '@payloadcms/plugin-ecommerce'
import { stripeAdapter } from '@payloadcms/plugin-ecommerce/payments/stripe'

ecommercePlugin({
  payments: {
    paymentMethods: [
      stripeAdapter({
        secretKey: process.env.STRIPE_SECRET_KEY,
        publishableKey: process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY,
        webhookSecret: process.env.STRIPE_WEBHOOKS_SIGNING_SECRET, // optional
      }),
    ],
  },
})
```

### Stripe Config Options

| Option | Type | Description |
|--------|------|-------------|
| `secretKey` | `string` | From Stripe Dashboard → API keys |
| `publishableKey` | `string` | From Stripe Dashboard → API keys |
| `webhookSecret` | `string` | Optional — required for webhooks |
| `appInfo` | `object` | Optional `{ name, version }` for Stripe logs |
| `webhooks` | `object` | Optional — map of `event_type → handler fn` |
| `groupOverrides` | `object` | Optional — override default transaction group fields |

### Stripe Webhooks

Webhooks are **optional** — the plugin doesn't use them internally. Handler signature:

```ts
webhooks: {
  'payment_intent.succeeded': ({ event, req, stripe }) => {
    req.payload.logger.info('Payment succeeded')
  },
}
```

#### Local Development (Stripe CLI)

```bash
stripe login
stripe listen --forward-to localhost:3000/api/payments/stripe/webhooks
# Copy the whsec_... secret shown → paste to .env as STRIPE_WEBHOOKS_SIGNING_SECRET
stripe trigger payment_intent.succeeded  # test
```

> **Gotcha:** The webhook signing secret from `stripe listen` is ephemeral — it changes on CLI restart. Update `.env` and restart dev server each time.

### Frontend Usage

```ts
import { EcommerceProvider } from '@payloadcms/plugin-ecommerce/client/react'
import { stripeAdapterClient } from '@payloadcms/plugin-ecommerce/payments/stripe'

<EcommerceProvider
  paymentMethods={[
    stripeAdapterClient({ publishableKey: process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY || '' }),
  ]}
>
  {children}
</EcommerceProvider>
```

Use `usePayments` hook for `initiatePayment` / `confirmOrder` — see [[wiki/payloadcms/ecommerce-frontend|Ecommerce Frontend]].

## Custom Payment Adapter

Implement the `PaymentAdapter` interface:

| Property | Type | Description |
|----------|------|-------------|
| `name` | `string` | Provider ID — used in API path and frontend |
| `label` | `string` | Optional human-readable label |
| `initiatePayment` | `fn` | Called at `/initiate` — creates transaction with status "Processing" |
| `confirmOrder` | `fn` | Called at `/confirm-order` — creates order after client payment completes |
| `endpoints` | `Endpoint[]` | Optional extra endpoints at `/api/payments/{name}/...` |
| `group` | `GroupField` | Payload group field for storing provider-specific data on transactions |

### `initiatePayment` Args

| Property | Description |
|----------|-------------|
| `transactionsSlug` | The transaction being processed |
| `data.billingAddress` | Billing address |
| `data.shippingAddress` | Optional — fall back to billing |
| `data.cart` | Cart collection item |
| `data.customerEmail` | Required for guest checkouts |
| `data.currency` | Payment currency |
| `req` | PayloadRequest |

Returns `{ message: string, ...extra }`. Throw to return 4xx/5xx.

Transaction is created here with status **"Processing"** — abandoned purchases stay here.

### `confirmOrder` Args

| Property | Description |
|----------|-------------|
| `ordersSlug` | Orders collection slug |
| `transactionsSlug` | Transactions slug |
| `cartsSlug` | Carts slug |
| `customersSlug` | Customers slug |
| `data.customerEmail` | For guest checkouts |
| `req` | PayloadRequest |

Returns `{ message, orderID, transactionID }`. Creates order, marks cart `purchasedAt`, updates transaction status to `"succeeded"`.

### Payment Fields (Group Field)

Each adapter must supply a `group` GroupField to store provider-specific data on transactions:

```ts
// Stripe example — stored under transaction.stripe.*
const groupField: GroupField = {
  name: 'stripe',
  type: 'group',
  fields: [
    { name: 'customerID', type: 'text' },
    { name: 'paymentIntentID', type: 'text' },
  ],
}
```

### Client-Side Adapter

Implements `PaymentAdapterClient`:

| Property | Type | Description |
|----------|------|-------------|
| `name` | `string` | Provider ID |
| `label` | `string` | Optional display label |
| `initiatePayment` | `boolean` | Whether to call `/initiate` endpoint |
| `confirmOrder` | `boolean` | Whether to call `/confirm-order` endpoint |

## Key Takeaways

- **Two-step checkout:** `initiatePayment` (server-side, creates Stripe PaymentIntent) → client confirms payment → `confirmOrder` (server-side, creates Order)
- **Server-side price validation is mandatory** — never trust price info from client
- **Pass only transaction ID to frontend**, never the full transaction object or secret key
- **Webhook signing secret is ephemeral** in local dev — update `.env` on every `stripe listen` restart
- **`NEXT_PUBLIC_` prefix required** for publishable key to reach client-side in Next.js
- Plugin creates endpoints automatically; custom adapters just implement the interface
- Stripe webhooks are optional — core plugin flow works without them

## Sources

- `raw/ecommerce__payments.md`
- [[wiki/payloadcms/ecommerce|Ecommerce Plugin Overview]]
- [[wiki/payloadcms/ecommerce-frontend|Ecommerce Frontend React Utilities]]
- [[wiki/payloadcms/ecommerce-advanced|Ecommerce Advanced / Piecemeal Collections]]
