Skip to content

Commit c6ad0f0

Browse files
authored
docs: add Stripe payment method docs (#120)
1 parent 727dd1c commit c6ad0f0

9 files changed

Lines changed: 291 additions & 39 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"class-variance-authority": "^0.7.1",
2828
"dayjs": "^1.11.19",
2929
"mermaid": "^11.12.2",
30-
"mpay": "^0.2.4",
30+
"mpay": "https://pkg.pr.new/wevm/mpay@65",
3131
"react": "^19",
3232
"react-dom": "^19",
3333
"tailwindcss": "^4.1.18",

pnpm-lock.yaml

Lines changed: 6 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/pages.gen.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ type Page =
2323
| { path: '/sdk/typescript/PaymentRequest.fromIntent'; render: 'static' }
2424
| { path: '/sdk/typescript'; render: 'static' }
2525
| { path: '/sdk/typescript/server/Method.tempo.charge'; render: 'static' }
26-
| { path: '/sdk/typescript/server/Method.tempo.stream'; render: 'static' }
26+
| { path: '/sdk/typescript/server/Method.tempo.session'; render: 'static' }
2727
| { path: '/sdk/typescript/server/Mpay.create'; render: 'static' }
2828
| { path: '/sdk/typescript/server/Mpay.toNodeListener'; render: 'static' }
2929
| { path: '/sdk/typescript/server/Request.fromNodeListener'; render: 'static' }
@@ -51,7 +51,7 @@ type Page =
5151
| { path: '/sdk/typescript/core/Credential.serialize'; render: 'static' }
5252
| { path: '/sdk/typescript/core/Expires'; render: 'static' }
5353
| { path: '/sdk/typescript/core/Intent.charge'; render: 'static' }
54-
| { path: '/sdk/typescript/core/Intent.stream'; render: 'static' }
54+
| { path: '/sdk/typescript/core/Intent.session'; render: 'static' }
5555
| { path: '/sdk/typescript/core/Mcp'; render: 'static' }
5656
| { path: '/sdk/typescript/core/PaymentRequest.deserialize'; render: 'static' }
5757
| { path: '/sdk/typescript/core/PaymentRequest.from'; render: 'static' }
@@ -62,7 +62,7 @@ type Page =
6262
| { path: '/sdk/typescript/core/Receipt.serialize'; render: 'static' }
6363
| { path: '/sdk/typescript/client/Method.tempo.charge'; render: 'static' }
6464
| { path: '/sdk/typescript/client/Method.tempo'; render: 'static' }
65-
| { path: '/sdk/typescript/client/Method.tempo.stream'; render: 'static' }
65+
| { path: '/sdk/typescript/client/Method.tempo.session'; render: 'static' }
6666
| { path: '/sdk/typescript/client/Mpay.create'; render: 'static' }
6767
| { path: '/sdk/typescript/client/Mpay.restore'; render: 'static' }
6868
| { path: '/sdk/typescript/client/Transport.from'; render: 'static' }
@@ -89,10 +89,11 @@ type Page =
8989
| { path: '/protocol/transports/mcp'; render: 'static' }
9090
| { path: '/payment-methods/custom'; render: 'static' }
9191
| { path: '/payment-methods'; render: 'static' }
92-
| { path: '/payment-methods/stripe'; render: 'static' }
9392
| { path: '/payment-methods/tempo/charge'; render: 'static' }
9493
| { path: '/payment-methods/tempo'; render: 'static' }
95-
| { path: '/payment-methods/tempo/stream'; render: 'static' }
94+
| { path: '/payment-methods/tempo/session'; render: 'static' }
95+
| { path: '/payment-methods/stripe/charge'; render: 'static' }
96+
| { path: '/payment-methods/stripe'; render: 'static' }
9697
| { path: '/intents/charge'; render: 'static' }
9798
| { path: '/guides/building-with-ai'; render: 'static' }
9899
| { path: '/_api/api/og'; render: 'static' };

src/pages/payment-methods/index.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ If supported, the client can then use the corresponding payment method to genera
2727
<Card
2828
icon="simple-icons:stripe"
2929
title="Stripe"
30-
description="Cards, bank transfers, and invoices using Stripe."
30+
description="Traditional card payment methods through Stripe."
31+
to="/payment-methods/stripe"
3132
/>
3233
<Card
3334
icon="lucide:anvil"

src/pages/payment-methods/stripe.mdx

Lines changed: 0 additions & 25 deletions
This file was deleted.
Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
import { Cards } from 'vocs'
2+
import { SpecCard } from '../../../components/SpecCard'
3+
4+
# Stripe charge [One-time payments using Stripe Payment Tokens]
5+
6+
The Stripe implementation of the [charge](/intents/charge) intent.
7+
8+
The client creates a [Stripe Payment Token (SPT)](https://docs.stripe.com/agentic-commerce/concepts/shared-payment-tokens) and sends it as a Credential. The server creates a Stripe `PaymentIntent` using the SPT, and settlement completes through Stripe's payment rails.
9+
10+
Use this method for single API calls, content access, or one-off purchases where you want to accept cards, wallets, or other Stripe-supported payment methods.
11+
12+
## Server
13+
14+
Use `stripe.charge` to require a one-time Stripe payment before returning a response. The method handles Challenge generation, Credential verification, PaymentIntent creation, and Receipt generation.
15+
16+
```ts twoslash
17+
import { Mpay, stripe } from 'mpay/server'
18+
19+
const mpay = Mpay.create({
20+
methods: [
21+
stripe.charge({
22+
networkId: 'internal',
23+
paymentMethodTypes: ['card'],
24+
secretKey: process.env.STRIPE_SECRET_KEY!,
25+
}),
26+
],
27+
})
28+
29+
export async function handler(request: Request) {
30+
const result = await mpay.charge({
31+
amount: '1',
32+
currency: 'usd',
33+
decimals: 2,
34+
description: 'Premium API access',
35+
})(request)
36+
37+
if (result.status === 402) return result.challenge
38+
39+
return result.withReceipt(Response.json({ data: '...' }))
40+
}
41+
```
42+
43+
### With metadata
44+
45+
Include `metadata` in the `stripe.charge` configuration to forward key-value pairs to Stripe. The metadata appears in the Challenge and attaches to the Stripe `PaymentIntent`.
46+
47+
```ts twoslash
48+
import { Mpay, stripe } from 'mpay/server'
49+
50+
const mpay = Mpay.create({
51+
methods: [
52+
stripe.charge({
53+
metadata: { plan: 'pro' }, // [!code hl]
54+
networkId: 'internal',
55+
paymentMethodTypes: ['card'],
56+
secretKey: process.env.STRIPE_SECRET_KEY!,
57+
}),
58+
],
59+
})
60+
61+
export async function handler(request: Request) {
62+
const result = await mpay.charge({
63+
amount: '1',
64+
currency: 'usd',
65+
decimals: 2,
66+
})(request)
67+
68+
if (result.status === 402) return result.challenge
69+
70+
return result.withReceipt(Response.json({ data: '...' }))
71+
}
72+
```
73+
74+
### With multiple payment method types
75+
76+
Allow multiple payment methods, like cards and Link, by specifying them in `paymentMethodTypes`.
77+
78+
```ts twoslash
79+
import { Mpay, stripe } from 'mpay/server'
80+
81+
const mpay = Mpay.create({
82+
methods: [
83+
stripe.charge({
84+
networkId: 'internal',
85+
paymentMethodTypes: ['card', 'link'], // [!code hl]
86+
secretKey: process.env.STRIPE_SECRET_KEY!,
87+
}),
88+
],
89+
})
90+
```
91+
92+
### Server parameters
93+
94+
| Parameter | Type | Required | Description |
95+
| --- | --- | --- | --- |
96+
| `metadata` | `Record<string, string>` | Optional | Key-value pairs forwarded to Stripe |
97+
| `networkId` | `string` | Required | Stripe [Business Network](https://docs.stripe.com/get-started/account/profile) profile ID |
98+
| `paymentMethodTypes` | `string[]` | Required | Allowed Stripe payment method types |
99+
| `secretKey` | `string` | Required | Stripe secret API key |
100+
101+
## Client
102+
103+
Use `stripe` with `Mpay.create` to automatically handle `402` responses. The client parses the Challenge, creates an SPT through the `createSpt` callback, and retries with the Credential.
104+
105+
SPT creation requires a Stripe secret key, so the client accepts a `createSpt` callback that proxies through a server endpoint.
106+
107+
```ts twoslash
108+
import { Mpay, stripe } from 'mpay/client'
109+
110+
Mpay.create({
111+
methods: [
112+
stripe({
113+
createSpt: async (params) => {
114+
const res = await fetch('/api/create-spt', {
115+
body: JSON.stringify(params),
116+
headers: { 'Content-Type': 'application/json' },
117+
method: 'POST',
118+
})
119+
if (!res.ok) throw new Error('Failed to create SPT')
120+
return (await res.json()).spt
121+
},
122+
}),
123+
],
124+
})
125+
126+
const response = await fetch('https://api.example.com/resource')
127+
// @log: Response { status: 200, ... }
128+
```
129+
130+
### With Stripe Elements
131+
132+
Collect the payment method from the user using Stripe Elements, then pass it at credential-creation time through `context`.
133+
134+
```ts twoslash
135+
import { Challenge } from 'mpay'
136+
import { stripe } from 'mpay/client'
137+
138+
declare const stripeClient: {
139+
createPaymentMethod(opts: any): Promise<{ paymentMethod: { id: string } | null; error: any }>
140+
}
141+
declare const elements: any
142+
// ---cut---
143+
const charge = stripe.charge({
144+
createSpt: async (params) => {
145+
const res = await fetch('/api/create-spt', {
146+
body: JSON.stringify(params),
147+
headers: { 'Content-Type': 'application/json' },
148+
method: 'POST',
149+
})
150+
return (await res.json()).spt
151+
},
152+
})
153+
154+
// 1. Get the 402 challenge
155+
const response = await fetch('/api/resource')
156+
const challenge = Challenge.fromResponse(response, { methods: [charge] })
157+
158+
// 2. Collect payment method from Stripe Elements
159+
const { paymentMethod } = await stripeClient.createPaymentMethod({ elements })
160+
161+
// 3. Create credential with the collected payment method
162+
const credential = await charge.createCredential({
163+
challenge,
164+
context: { paymentMethod: paymentMethod!.id },
165+
})
166+
167+
// 4. Retry with credential
168+
const paid = await fetch('/api/resource', {
169+
headers: { Authorization: credential },
170+
})
171+
```
172+
173+
### Without polyfill
174+
175+
If you don't want to patch `globalThis.fetch`, use `mpay.fetch` directly:
176+
177+
```ts twoslash
178+
import { Mpay, stripe } from 'mpay/client'
179+
180+
const mpay = Mpay.create({
181+
polyfill: false,
182+
methods: [
183+
stripe({
184+
createSpt: async (params) => {
185+
const res = await fetch('/api/create-spt', {
186+
body: JSON.stringify(params),
187+
headers: { 'Content-Type': 'application/json' },
188+
method: 'POST',
189+
})
190+
return (await res.json()).spt
191+
},
192+
}),
193+
],
194+
})
195+
196+
const response = await mpay.fetch('https://api.example.com/resource')
197+
```
198+
199+
### Client parameters
200+
201+
| Parameter | Type | Required | Description |
202+
| --- | --- | --- | --- |
203+
| `createSpt` | `(params) => Promise<string>` | Required | Callback to create an SPT (proxied through a server endpoint) |
204+
| `externalId` | `string` | Optional | Client reference ID included in the Credential payload |
205+
| `paymentMethod` | `string` | Optional | Default Stripe payment method ID (overridden by `context.paymentMethod`) |
206+
207+
### `createSpt` callback parameters
208+
209+
The `createSpt` callback receives a single object with the following fields:
210+
211+
| Field | Type | Description |
212+
| --- | --- | --- |
213+
| `amount` | `string` | Payment amount in smallest currency unit |
214+
| `currency` | `string` | Three-letter ISO currency code |
215+
| `expiresAt` | `number` | SPT expiration as a Unix timestamp (seconds) |
216+
| `metadata` | `Record<string, string>` | Optional metadata from the Challenge |
217+
| `networkId` | `string \| undefined` | Stripe Business Network profile ID |
218+
| `paymentMethod` | `string` | Stripe payment method ID |
219+
220+
## Request fields
221+
222+
The Challenge request includes the base charge fields plus Stripe method details.
223+
224+
| Field | Type | Required | Description |
225+
| --- | --- | --- | --- |
226+
| `amount` | `string` | Required | Amount in the smallest currency unit |
227+
| `currency` | `string` | Required | ISO currency code |
228+
| `decimals` | `number` | Required | Number of decimal places in the amount (for example, `2` for cents) |
229+
| `description` | `string` | Optional | Human-readable payment description |
230+
| `expires` | `string` | Optional | ISO 8601 expiration timestamp (defaults to 5 minutes) |
231+
| `externalId` | `string` | Optional | Merchant reference ID |
232+
| `methodDetails.metadata` | `Record<string, string>` | Optional | Metadata forwarded to Stripe |
233+
| `methodDetails.networkId` | `string` | Required | Stripe Business Network profile ID |
234+
| `methodDetails.paymentMethodTypes` | `string[]` | Required | Allowed Stripe payment method types |
235+
236+
## Credential payload
237+
238+
The Credential payload contains the SPT and an optional client reference ID.
239+
240+
| Field | Type | Required | Description |
241+
| --- | --- | --- | --- |
242+
| `externalId` | `string` | Optional | Client reference ID |
243+
| `spt` | `string` | Required | Stripe Payment Token ID (starts with `spt_`) |
244+
245+
## Specification
246+
247+
<Cards>
248+
<SpecCard to="/specs/draft-stripe-charge-00" />
249+
</Cards>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { Card, Cards } from 'vocs'
2+
3+
# Stripe [Cards, wallets, and other Stripe supported payment methods]
4+
5+
The Stripe payment method enables payments using [Stripe Payment Tokens (SPTs)](https://docs.stripe.com/agentic-commerce/concepts/shared-payment-tokens)—single-use tokens that represent payment authorization. SPTs abstract payment method details (cards, wallets) and provide a unified interface for payment acceptance.
6+
7+
Both the client and server need a Stripe account. The client creates an SPT using the Stripe API or Stripe.js, and the server consumes it to create a Stripe `PaymentIntent`.
8+
9+
## How it works
10+
11+
1. The server responds with `402` and a Challenge containing the amount, currency, and Stripe method details (Business Network profile, allowed payment method types).
12+
2. The client creates an SPT with Stripe and sends a Credential containing the SPT.
13+
3. The server creates a Stripe `PaymentIntent` using the SPT.
14+
4. The server returns the resource with a Receipt referencing the `PaymentIntent`.
15+
16+
## Intents
17+
18+
<Cards>
19+
<Card
20+
icon="lucide:credit-card"
21+
title="Charge"
22+
description="One-time card or bank payment using Stripe Payment Tokens"
23+
to="/payment-methods/stripe/charge"
24+
/>
25+
</Cards>

0 commit comments

Comments
 (0)