> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://mpp.dev/api/mcp` to find what you need.

# Accept card payments \[Accept card payments with Stripe]

Accept card payments on your MPP-enabled API using [Stripe](/payment-methods/stripe). Clients pay with Visa, Mastercard, and other card networks—no stablecoin wallet required. The server uses Stripe's [Shared Payment Tokens (SPTs)](https://docs.stripe.com/agentic-commerce/concepts/shared-payment-tokens) to process payments through Stripe's existing rails.

:::info[Stripe account setup]
Machine payments must be enabled on your Stripe account before you can accept MPP payments. [Request access](https://docs.stripe.com/payments/machine#sign-up) through the Stripe Dashboard.
:::

## How it works

```mermaid
sequenceDiagram
  participant Client
  participant Server
  participant Stripe
  Client->>Server: (1) GET /resource
  Server-->>Client: (2) 402 + Challenge (Stripe method)
  Client->>Stripe: (3) Create SPT from Challenge
  Stripe-->>Client: (4) spt_...
  Client->>Server: (5) GET /resource + Credential (SPT)
  Server->>Stripe: (6) Create PaymentIntent
  Stripe-->>Server: (7) pi_...
  Server-->>Client: (8) 200 OK + Receipt

```

1. **Client** requests a paid resource.
2. **Server** responds with `402` and a Challenge containing the price, currency, and Stripe method details.
3. **Client** creates a Shared Payment Token (SPT) through the Stripe API.
4. **Client** retries the request with a Credential containing the SPT.
5. **Server** creates a Stripe `PaymentIntent` using the SPT, verifies payment, and returns the resource with a Receipt.

Settlement, refunds, and reporting all happen through your Stripe Dashboard—the same tools you use for any other Stripe payment.

## Prompt mode

Paste this into your coding agent to build the entire guide in one prompt:

```text
Use https://mpp.dev/guides/accept-card-payments.md as reference.
Add mppx to my app with a payment-gated endpoint that accepts
card payments via Stripe. Charge $1.00 per request using the
Stripe payment method. When payment is verified, return a JSON response.
```

## Server setup

::::steps
### Install dependencies

:::code-group
```bash [npm]
$ npm install mppx stripe viem
```

```bash [pnpm]
$ pnpm add mppx stripe viem
```

```bash [bun]
$ bun add mppx stripe viem
```
:::

### Set environment variables

Set your Stripe secret key and [Business Network](https://docs.stripe.com/get-started/account/profile) profile ID.

```bash [.env]
STRIPE_SECRET_KEY=sk_test_...
STRIPE_NETWORK_ID=your_network_id
```

### Create the server with Stripe payments

Set up an `Mppx` instance with the `stripe.spt` constructor. The `networkId` is your Stripe Business Network profile ID, and `paymentMethodTypes` controls which card types you accept.

```ts twoslash
import Stripe from 'stripe'
import { Mppx, stripe } from 'mppx/server'

const stripeClient = new Stripe(process.env.STRIPE_SECRET_KEY!)

const mppx = Mppx.create({
  methods: [
    stripe.spt({
      client: stripeClient,
      networkId: process.env.STRIPE_NETWORK_ID!,
      paymentMethodTypes: ['card'],
    }),
  ],
})
```

### Add a paid endpoint

Use `mppx.charge` to gate your endpoint. Set the `amount` in the smallest currency unit (cents for USD), and specify `currency` and `decimals`.

```ts twoslash
import Stripe from 'stripe'
import { Mppx, stripe } from 'mppx/server'

const stripeClient = new Stripe(process.env.STRIPE_SECRET_KEY!)

const mppx = Mppx.create({
  methods: [
    stripe.spt({
      client: stripeClient,
      networkId: process.env.STRIPE_NETWORK_ID!,
      paymentMethodTypes: ['card'],
    }),
  ],
})

// ---cut---
export async function handler(request: Request) {
  const result = await mppx.charge({
    amount: '100',
    currency: 'usd',
    decimals: 2,
    description: 'Premium API access',
  })(request)

  if (result.status === 402) return result.challenge

  return result.withReceipt(Response.json({ data: 'your response here' }))
}
```
::::

## Client setup

Use `stripe` with `Mppx.create` to handle `402` responses automatically. The client parses the Challenge, creates an SPT through the `createToken` callback, and retries with the Credential.

```ts twoslash
import { Mppx, stripe } from 'mppx/client'

Mppx.create({
  methods: [
    stripe({
      createToken: async (params) => {
        const res = await fetch('/api/create-spt', {
          body: JSON.stringify(params),
          headers: { 'Content-Type': 'application/json' },
          method: 'POST',
        })
        if (!res.ok) throw new Error('Failed to create SPT')
        return (await res.json()).spt
      },
      paymentMethod: 'pm_card_visa',
    }),
  ],
})

const response = await fetch('https://api.example.com/resource')
// @log: Response { status: 200, ... }
```

:::warning[Security: server-side authorization]
The `createToken` callback proxies through your own server because SPT creation requires a Stripe secret key. The server **must** derive SPT parameters (amount, currency, expiry) itself—never accept them from the client. See the [SPT creation proxy endpoint](/payment-methods/stripe/charge#spt-creation-proxy-endpoint) for a secure implementation.
:::

## Accept cards and stablecoins together

Stripe works alongside other payment methods. Add `tempo` to accept both cards and stablecoins on the same endpoint—clients pay with whichever rail they support.

```ts twoslash
import Stripe from 'stripe'
import { Mppx, stripe, tempo } from 'mppx/server'

const stripeClient = new Stripe(process.env.STRIPE_SECRET_KEY!)

const mppx = Mppx.create({
  methods: [
    stripe.spt({
      client: stripeClient,
      networkId: process.env.STRIPE_NETWORK_ID!,
      paymentMethodTypes: ['card'],
    }),
    tempo.charge({
      currency: '0x20c0000000000000000000000000000000000000',
      recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
      testnet: true,
    }),
  ],
})
```

The server returns both methods in the `402` Challenge. See [Accept multiple payment methods](/guides/multiple-payment-methods) for a full walkthrough.

## Add a browser payment page

Set `html` on the Stripe method to render a Stripe Elements card form when a browser visits the endpoint. Programmatic clients with `Authorization` headers are unaffected.

```ts twoslash
import Stripe from 'stripe'
import { Mppx, stripe } from 'mppx/server'

const stripeClient = new Stripe(process.env.STRIPE_SECRET_KEY!)

const mppx = Mppx.create({
  methods: [
    stripe.spt({
      client: stripeClient,
      // [!code hl:start]
      html: {
        createTokenUrl: '/api/create-spt',
        publishableKey: process.env.STRIPE_PUBLISHABLE_KEY!,
      },
      // [!code hl:end]
      networkId: process.env.STRIPE_NETWORK_ID!,
      paymentMethodTypes: ['card'],
    }),
  ],
})
```

See [Create a payment link](/guides/payment-links) for a full walkthrough and live demo.

## Next steps
