codebyplan 1.13.48 → 1.13.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1 -1
- package/package.json +1 -1
- package/templates/agents/cbp-round-executor.md +7 -0
- package/templates/agents/cbp-stripe-agent.md +173 -0
- package/templates/settings.project.base.json +13 -0
- package/templates/skills/cbp-round-execute/SKILL.md +9 -1
- package/templates/skills/cbp-stripe/SKILL.md +116 -0
- package/templates/skills/cbp-stripe/reference/billing.md +106 -0
- package/templates/skills/cbp-stripe/reference/connect.md +105 -0
- package/templates/skills/cbp-stripe/reference/payments.md +107 -0
- package/templates/skills/cbp-stripe/reference/security.md +117 -0
- package/templates/skills/cbp-stripe/reference/stripe-mcp-setup.md +59 -0
- package/templates/skills/cbp-stripe/reference/tax.md +96 -0
- package/templates/skills/cbp-stripe/reference/treasury.md +87 -0
package/dist/cli.js
CHANGED
package/package.json
CHANGED
|
@@ -315,6 +315,7 @@ When the approved plan includes specialized work, delegate to sub-executor agent
|
|
|
315
315
|
| Work Type | Agent | When to Delegate |
|
|
316
316
|
|-----------|-------|-----------------|
|
|
317
317
|
| Supabase migrations, RLS, types | `cbp-database-agent` | Plan includes DB schema changes, RLS policies, or type generation |
|
|
318
|
+
| Stripe integration (Checkout, webhooks, subscriptions, customer portal) | `cbp-stripe-agent` | Plan includes Stripe work (files under `stripe/`, or steps referencing `payment`, `checkout`, `webhook`, `subscription`, or `approved_plan.stripe_work === true`) |
|
|
318
319
|
| Batch identical-structure file writes (≥4 files) | `general-purpose` (background) | Plan has 4+ independent files, no shared state, no ordered dependency |
|
|
319
320
|
| `.claude/` infrastructure deliverables | `cbp-cc-executor` | `files_to_modify[]` includes **≥2** `.claude/` files (rules, skills, agents, context, hooks, settings, CLAUDE.md). A single `.claude/` file edit stays on Step 0 Skill-tool routing |
|
|
320
321
|
|
|
@@ -324,6 +325,12 @@ When the approved plan includes specialized work, delegate to sub-executor agent
|
|
|
324
325
|
3. Wait for completion, merge files_changed into executor output
|
|
325
326
|
4. Continue with remaining non-DB steps
|
|
326
327
|
|
|
328
|
+
**How to delegate to `cbp-stripe-agent`:**
|
|
329
|
+
1. Collect all Stripe-related steps from the plan
|
|
330
|
+
2. Spawn `cbp-stripe-agent` via Agent tool with those steps and `files_changed_scope` set to the executor's current `files_to_modify[]` paths
|
|
331
|
+
3. Wait for completion, merge files_changed into executor output
|
|
332
|
+
4. Continue with remaining non-Stripe steps
|
|
333
|
+
|
|
327
334
|
**When NOT to delegate:**
|
|
328
335
|
- Simple Supabase queries in application code (executor handles these)
|
|
329
336
|
- Only delegate schema/migration/RLS/type generation work
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
---
|
|
2
|
+
scope: org-shared
|
|
3
|
+
name: cbp-stripe-agent
|
|
4
|
+
description: Stripe integration specialist. Writes Stripe code (Checkout, webhooks, subscriptions, customer portal) in the consuming app and optionally drives live Stripe via MCP. Spawned as sub-executor by round-executor when the plan includes Stripe work.
|
|
5
|
+
tools: Read, Write, Edit, Glob, Grep, Bash, AskUserQuestion
|
|
6
|
+
model: sonnet
|
|
7
|
+
effort: xhigh
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Stripe Agent
|
|
11
|
+
|
|
12
|
+
Stripe integration specialist for payments, billing, webhooks, Connect, Tax, and Treasury.
|
|
13
|
+
|
|
14
|
+
## Purpose
|
|
15
|
+
|
|
16
|
+
Handles Stripe integration work when a round's plan includes payment code. Spawned by
|
|
17
|
+
round-executor as a sub-executor, not directly by `/cbp-round-start`. Two operating modes:
|
|
18
|
+
|
|
19
|
+
- **Primary (always)** — writes/modifies Stripe integration code in the consuming app using
|
|
20
|
+
the current Stripe Node SDK, guided by the `cbp-stripe` skill's API-selection routing.
|
|
21
|
+
- **Optional (opt-in)** — when a Stripe MCP server is configured AND a restricted/test key is
|
|
22
|
+
present, scaffolds live test data (products, prices, payment links) via that server. Absent
|
|
23
|
+
either, it degrades silently to code-only — never a hard failure.
|
|
24
|
+
|
|
25
|
+
## Input Contract
|
|
26
|
+
|
|
27
|
+
```yaml
|
|
28
|
+
input:
|
|
29
|
+
stripe_tasks: [{step_number, description, type}] # Stripe-related plan steps
|
|
30
|
+
files_changed_scope: string[] # paths the round is allowed to touch
|
|
31
|
+
repo_id: string
|
|
32
|
+
context:
|
|
33
|
+
checkpoint_goal: string
|
|
34
|
+
task_requirements: string
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Output Contract
|
|
38
|
+
|
|
39
|
+
```yaml
|
|
40
|
+
output:
|
|
41
|
+
status: 'completed' | 'blocked' | 'failed'
|
|
42
|
+
live_path_used: boolean # true only when the optional MCP path ran
|
|
43
|
+
files_changed:
|
|
44
|
+
- path: string
|
|
45
|
+
action: 'created' | 'modified' | 'deleted'
|
|
46
|
+
stripe_resources_created: # populated only when live_path_used === true
|
|
47
|
+
- type: string # e.g. 'product' | 'price' | 'payment_link'
|
|
48
|
+
id: string
|
|
49
|
+
mode: 'test' # ALWAYS test — live mode is never scaffolded here
|
|
50
|
+
issues_encountered: string[]
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Workflow
|
|
54
|
+
|
|
55
|
+
### Pre-flight: Load Guidance + Resolve Live-Path Availability
|
|
56
|
+
|
|
57
|
+
Run both checks before writing any code:
|
|
58
|
+
|
|
59
|
+
1. **Load the `cbp-stripe` skill** for API-selection routing and security rules. Invoke the
|
|
60
|
+
`cbp-stripe` Skill (or Read `.claude/skills/cbp-stripe/SKILL.md` and the relevant
|
|
61
|
+
`reference/*.md` when Skill dispatch is unavailable). This is the source of truth for
|
|
62
|
+
which Stripe API to use per intent — do not select APIs from memory.
|
|
63
|
+
|
|
64
|
+
2. **Resolve live-path availability.** The optional MCP path runs ONLY when ALL hold:
|
|
65
|
+
- `STRIPE_SECRET_KEY` (or an equivalent restricted-key env var) is present AND is a
|
|
66
|
+
**test-mode** key. Check presence + prefix WITHOUT printing the secret (never `echo` or
|
|
67
|
+
`printenv` the raw value):
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
case "${STRIPE_SECRET_KEY:-}" in
|
|
71
|
+
sk_test_*|rk_test_*) echo "live path: eligible (test key)" ;;
|
|
72
|
+
sk_live_*|rk_live_*) echo "live path: refused (live-mode key)" ;;
|
|
73
|
+
"") echo "live path: skipped (no key)" ;;
|
|
74
|
+
*) echo "live path: refused (unknown prefix ${STRIPE_SECRET_KEY:0:8})" ;;
|
|
75
|
+
esac
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Only `sk_test_`/`rk_test_` enable the live path; live-mode keys (`sk_live_`, `rk_live_`)
|
|
79
|
+
are refused so a dev round never scaffolds real Stripe data.
|
|
80
|
+
- A Stripe MCP server is reachable. Stripe MCP tools (`mcp__stripe__*`) are NOT listed in
|
|
81
|
+
this agent's frontmatter because the server is optional and absent by default; discover
|
|
82
|
+
them at runtime via `ToolSearch` (query `mcp__stripe`). Setup is documented in
|
|
83
|
+
`.claude/skills/cbp-stripe/reference/stripe-mcp-setup.md`.
|
|
84
|
+
|
|
85
|
+
If any condition fails, set `live_path_used = false` and proceed code-only. Record the
|
|
86
|
+
reason in `issues_encountered[]` (e.g. `live path skipped: no STRIPE_SECRET_KEY`). This is
|
|
87
|
+
a normal outcome, NOT a block.
|
|
88
|
+
|
|
89
|
+
### Step 1: Analyze Stripe Tasks
|
|
90
|
+
|
|
91
|
+
Read `stripe_tasks` and categorize by type, mapping each to the `cbp-stripe` routing table:
|
|
92
|
+
|
|
93
|
+
- **One-time payments** → Checkout Sessions (`reference/payments.md`)
|
|
94
|
+
- **Custom payment UI** → Checkout Sessions + Payment Element (`reference/payments.md`)
|
|
95
|
+
- **Saving a payment method** → Setup Intents (`reference/payments.md`)
|
|
96
|
+
- **Subscriptions / recurring billing** → Billing APIs + Checkout Sessions, Customer Portal
|
|
97
|
+
(`reference/billing.md`)
|
|
98
|
+
- **Webhooks** → signed event handler (`reference/security.md`)
|
|
99
|
+
- **Marketplace / platform** → Connect Accounts v2 (`reference/connect.md`)
|
|
100
|
+
- **Tax** → Stripe Tax (`reference/tax.md`); **embedded finance** → Treasury
|
|
101
|
+
(`reference/treasury.md`)
|
|
102
|
+
|
|
103
|
+
### Step 2: Write Stripe Integration Code (PRIMARY)
|
|
104
|
+
|
|
105
|
+
For each task, write or modify code in `files_changed_scope` using the current Stripe Node SDK:
|
|
106
|
+
|
|
107
|
+
1. **Honor the critical rules from the skill**: never pass `payment_method_types` except for
|
|
108
|
+
the documented Terminal and Treasury-bank-account exceptions; prefer dynamic payment
|
|
109
|
+
methods.
|
|
110
|
+
2. **Server-side key handling**: read the key from `process.env` only; never hardcode or log
|
|
111
|
+
it. Prefer a restricted key (`rk_`) over a secret key.
|
|
112
|
+
3. **Next.js API routes that import `stripe` MUST export `export const dynamic =
|
|
113
|
+
'force-dynamic'`** at the top of the file (the SDK reads a runtime env var; static analysis
|
|
114
|
+
at build time fails without it). Source: `.claude/skills/cbp-frontend-design/reference/nextjs-scss.md`
|
|
115
|
+
Rule 6.
|
|
116
|
+
4. **Webhook routes** must verify the signature with `stripe.webhooks.constructEvent(rawBody,
|
|
117
|
+
sig, secret)` against the raw (unparsed) body, and guard the `stripe-signature` header
|
|
118
|
+
(it is typed `string | string[] | undefined`) before use.
|
|
119
|
+
5. Match the consuming app's existing conventions (error handling, response shape, file
|
|
120
|
+
layout). Verify the installed `stripe` major version (`grep '"stripe"' package.json`) and
|
|
121
|
+
write code for that version — the skill notes the latest API version, but consumer repos
|
|
122
|
+
may pin an older SDK.
|
|
123
|
+
|
|
124
|
+
### Step 3: Scaffold Live Test Data (OPTIONAL — only when Pre-flight enabled the live path)
|
|
125
|
+
|
|
126
|
+
When `live_path_used` is eligible AND a task explicitly needs live test data (e.g. "create a
|
|
127
|
+
test product + price for the checkout demo"):
|
|
128
|
+
|
|
129
|
+
1. Re-confirm the key prefix is `sk_test_` or `rk_test_` immediately before the first call.
|
|
130
|
+
Abort the live path on any live-mode key (`sk_live_` or `rk_live_`).
|
|
131
|
+
2. Use the discovered Stripe MCP tools to create only what the task requires (products,
|
|
132
|
+
prices, payment links, test customers). Record each in `stripe_resources_created[]`.
|
|
133
|
+
3. On ANY MCP error (server unreachable, auth rejected, rate limit), fall back to code-only:
|
|
134
|
+
set `live_path_used = false`, record the error in `issues_encountered[]`, and continue —
|
|
135
|
+
never block the round on the optional path.
|
|
136
|
+
|
|
137
|
+
### Step 4: Verify
|
|
138
|
+
|
|
139
|
+
1. For each changed `.ts`/`.tsx` file, run a scoped `npx tsc --noEmit` (or the app's
|
|
140
|
+
typecheck) on the changed set and confirm no new type errors.
|
|
141
|
+
2. Confirm every API route importing `stripe` exports `dynamic = 'force-dynamic'`
|
|
142
|
+
(`grep -L "force-dynamic"` across the changed route files).
|
|
143
|
+
3. Confirm no secret was committed: `grep -rE 'sk_live_|rk_live_|sk_test_[A-Za-z0-9]{16,}|rk_test_[A-Za-z0-9]{16,}'`
|
|
144
|
+
over the changed files returns nothing real. Live-key prefixes (`sk_live_`, `rk_live_`)
|
|
145
|
+
match with no length floor — a committed live key is never acceptable; test-key prefixes
|
|
146
|
+
carry a `{16,}` floor so doc placeholders like `sk_test_…` don't false-positive.
|
|
147
|
+
|
|
148
|
+
### Step 5: Return Output
|
|
149
|
+
|
|
150
|
+
Populate all output-contract fields. Include every file changed. Report the live-path outcome
|
|
151
|
+
(used / skipped + reason) in `issues_encountered[]` for the audit trail.
|
|
152
|
+
|
|
153
|
+
## When NOT to Use This Agent
|
|
154
|
+
|
|
155
|
+
- Non-payment application code (round-executor handles these).
|
|
156
|
+
- Reading Stripe data for display only with no integration change.
|
|
157
|
+
- Designing the payment UX/visual layer — that is the frontend skills' job; this agent writes
|
|
158
|
+
the Stripe wiring beneath it.
|
|
159
|
+
- Production / live-mode Stripe operations (`sk_live_`, `rk_live_`) — this agent refuses
|
|
160
|
+
live-mode keys by design; only test-mode keys enable the optional live path.
|
|
161
|
+
|
|
162
|
+
## Integration
|
|
163
|
+
|
|
164
|
+
- **Spawned by**: `round-executor` (as sub-executor when the plan includes Stripe work — see
|
|
165
|
+
`cbp-round-executor` Step 3.5 and `/cbp-round-execute` Step 3b-stripe dispatch).
|
|
166
|
+
- **Returns to**: `round-executor` (merges `files_changed[]` into the round output).
|
|
167
|
+
- **Loads**: the `cbp-stripe` skill (`.claude/skills/cbp-stripe/SKILL.md` + `reference/*.md`)
|
|
168
|
+
for API selection and security rules.
|
|
169
|
+
- **Optional tools**: Stripe MCP (`mcp__stripe__*`) discovered at runtime via `ToolSearch`
|
|
170
|
+
when a server is configured per `.claude/skills/cbp-stripe/reference/stripe-mcp-setup.md` —
|
|
171
|
+
intentionally absent from frontmatter because the server is opt-in.
|
|
172
|
+
- **Rule**: never commit Stripe secrets; restricted/test keys only; degrade to code-only when
|
|
173
|
+
the live path is unavailable.
|
|
@@ -68,6 +68,18 @@
|
|
|
68
68
|
"mcp__codebyplan__delete_session_log",
|
|
69
69
|
"mcp__codebyplan__delete_worktree",
|
|
70
70
|
"mcp__codebyplan__release_assignment",
|
|
71
|
+
"mcp__stripe__create_customer",
|
|
72
|
+
"mcp__stripe__create_product",
|
|
73
|
+
"mcp__stripe__create_price",
|
|
74
|
+
"mcp__stripe__create_payment_link",
|
|
75
|
+
"mcp__stripe__create_invoice",
|
|
76
|
+
"mcp__stripe__create_subscription",
|
|
77
|
+
"mcp__stripe__update_subscription",
|
|
78
|
+
"mcp__stripe__create_refund",
|
|
79
|
+
"mcp__stripe__list_customers",
|
|
80
|
+
"mcp__stripe__list_products",
|
|
81
|
+
"mcp__stripe__list_prices",
|
|
82
|
+
"mcp__stripe__list_invoices",
|
|
71
83
|
"Bash(codebyplan setup:*)",
|
|
72
84
|
"Bash(npx codebyplan setup:*)",
|
|
73
85
|
"Bash(codebyplan create-org:*)",
|
|
@@ -127,6 +139,7 @@
|
|
|
127
139
|
"Skill(cbp-ship-configure)",
|
|
128
140
|
"Skill(cbp-standalone-task-check)",
|
|
129
141
|
"Skill(cbp-standalone-task-testing)",
|
|
142
|
+
"Skill(cbp-stripe)",
|
|
130
143
|
"Skill(cbp-supabase-branch-check)",
|
|
131
144
|
"Skill(cbp-supabase-migrate)",
|
|
132
145
|
"Skill(cbp-supabase-setup)",
|
|
@@ -118,6 +118,14 @@ If the approved plan includes database schema changes, RLS policies, or type gen
|
|
|
118
118
|
2. Wait for completion
|
|
119
119
|
3. Merge `files_changed` into executor output
|
|
120
120
|
|
|
121
|
+
### Step 3b-stripe: Stripe Work (if plan includes Stripe integration)
|
|
122
|
+
|
|
123
|
+
If the approved plan includes Stripe integration work (files under `stripe/`, or plan steps referencing `payment`, `checkout`, `webhook`, `subscription`, or an explicit `stripe_work: true` flag from the planner):
|
|
124
|
+
|
|
125
|
+
1. Spawn `cbp-stripe-agent` with Stripe-related steps from the plan and `files_changed_scope` from the executor output
|
|
126
|
+
2. Wait for completion
|
|
127
|
+
3. Merge `files_changed` into executor output
|
|
128
|
+
|
|
121
129
|
### Step 3c: Completion Check
|
|
122
130
|
|
|
123
131
|
- `status: 'completed'` and all deliverables done → proceed to Step 4
|
|
@@ -228,7 +236,7 @@ Trigger `/cbp-round-end`.
|
|
|
228
236
|
|
|
229
237
|
- **Reads**: `.codebyplan/state/checkpoints/<id>/tasks/<id>.json`, `checkpoints/<id>/tasks/<id>/rounds/<id>.json` (local-first; `npx codebyplan sync` on miss; MCP `get_current_task` / `get_rounds` as break-glass)
|
|
230
238
|
- **Writes**: `codebyplan round update --id <uuid> --task-id <uuid> --checkpoint-id <uuid>` (Steps 6+7 — context with executor_output + testing_qa_output + e2e_eligible + e2e_outputs + frontend_ui_review; break-glass: MCP `update_round`)
|
|
231
|
-
- **Spawns**: `cbp-round-executor` (per wave or single), `cbp-testing-qa-agent` (per wave, parallel sibling of the `cbp-e2e-*` specialists), the `cbp-e2e-*` specialists (config-driven dispatch per `context/testing/e2e.md`, one per eligible framework in `.codebyplan/e2e.json`), `cbp-database-agent` (if DB work), `cbp-security-agent` (if security review needed)
|
|
239
|
+
- **Spawns**: `cbp-round-executor` (per wave or single), `cbp-testing-qa-agent` (per wave, parallel sibling of the `cbp-e2e-*` specialists), the `cbp-e2e-*` specialists (config-driven dispatch per `context/testing/e2e.md`, one per eligible framework in `.codebyplan/e2e.json`), `cbp-database-agent` (if DB work), `cbp-stripe-agent` (if Stripe work), `cbp-security-agent` (if security review needed)
|
|
232
240
|
- **Skill invocations**: `cbp-frontend-ui` at Step 5b with `phase: 'screenshot_review'` (post-e2e)
|
|
233
241
|
- **Triggers**: `/cbp-round-end` (auto)
|
|
234
242
|
- **Triggered by**: `/cbp-round-start` (auto, after plan approval)
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
---
|
|
2
|
+
scope: org-shared
|
|
3
|
+
name: cbp-stripe
|
|
4
|
+
description: "Stripe integration guidance — load when implementing or reviewing payments, Checkout, subscriptions/billing, webhooks, Connect, Tax, or Treasury. Encodes the API-selection routing table, the no-payment_method_types rule, restricted-key security, and Stripe SDK conventions."
|
|
5
|
+
effort: xhigh
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Stripe Integration (CBP)
|
|
9
|
+
|
|
10
|
+
Load this skill before writing or reviewing any Stripe integration code — accepting payments,
|
|
11
|
+
Checkout Sessions, subscriptions, webhooks, marketplaces (Connect), tax compliance, or
|
|
12
|
+
embedded financial accounts (Treasury). It encodes Stripe's current recommended API surface,
|
|
13
|
+
the critical `payment_method_types` prohibition, and CBP-specific conventions.
|
|
14
|
+
|
|
15
|
+
## Integration routing table
|
|
16
|
+
|
|
17
|
+
| Building… | Recommended API | Reference |
|
|
18
|
+
| --------------------------------------------- | ----------------------------------- | --------------------------------- |
|
|
19
|
+
| One-time payments | Checkout Sessions | [reference/payments.md](reference/payments.md) |
|
|
20
|
+
| Custom payment form with embedded UI | Checkout Sessions + Payment Element | [reference/payments.md](reference/payments.md) |
|
|
21
|
+
| Saving a payment method for later | Setup Intents | [reference/payments.md](reference/payments.md) |
|
|
22
|
+
| Connect platform or marketplace | Accounts v2 (`/v2/core/accounts`) | [reference/connect.md](reference/connect.md) |
|
|
23
|
+
| Subscriptions or recurring billing | Billing APIs + Checkout Sessions | [reference/billing.md](reference/billing.md) |
|
|
24
|
+
| Sales tax, VAT, or GST compliance | Stripe Tax + Registrations API | [reference/tax.md](reference/tax.md) |
|
|
25
|
+
| Embedded financial accounts / banking | v2 Financial Accounts | [reference/treasury.md](reference/treasury.md) |
|
|
26
|
+
| Security (keys, webhooks, OAuth, Connect risk)| Restricted keys + sig verification | [reference/security.md](reference/security.md) |
|
|
27
|
+
|
|
28
|
+
Read the relevant reference file before answering any integration question or writing code.
|
|
29
|
+
|
|
30
|
+
## Critical rules
|
|
31
|
+
|
|
32
|
+
### Never include `payment_method_types` (except Terminal)
|
|
33
|
+
|
|
34
|
+
Never pass `payment_method_types` in any Stripe API call. There are two narrow exceptions:
|
|
35
|
+
- **Terminal** (in-person): `payment_method_types: ['card_present']` (Canada: add `'interac_present'`).
|
|
36
|
+
- **Treasury bank-account Setup Intents**: `payment_method_types: ['us_bank_account']` with
|
|
37
|
+
`flow_directions: ['outbound']` (see [reference/treasury.md](reference/treasury.md)).
|
|
38
|
+
|
|
39
|
+
Outside those, omit the parameter to enable dynamic payment methods — Stripe evaluates
|
|
40
|
+
100+ signals to surface the most relevant methods and manage them from the Dashboard
|
|
41
|
+
without code changes.
|
|
42
|
+
|
|
43
|
+
This applies to ALL call sites:
|
|
44
|
+
- `checkout.sessions.create` — omit entirely
|
|
45
|
+
- `paymentIntents.create` — omit; on API versions before 2023-08-16 pass
|
|
46
|
+
`automatic_payment_methods: { enabled: true }` instead
|
|
47
|
+
- `setupIntents.create` — same as PaymentIntents
|
|
48
|
+
- `subscriptions.create` — omit `payment_settings.payment_method_types`
|
|
49
|
+
|
|
50
|
+
To restrict or customise payment methods use
|
|
51
|
+
[`payment_method_configurations`](https://docs.stripe.com/payments/payment-method-configurations.md)
|
|
52
|
+
or `excluded_payment_method_types` — never `payment_method_types`.
|
|
53
|
+
|
|
54
|
+
### Never use the Charges API
|
|
55
|
+
|
|
56
|
+
The Charges API is never correct for new integrations. Redirect users to Checkout Sessions
|
|
57
|
+
or PaymentIntents and the
|
|
58
|
+
[migration guide](https://docs.stripe.com/payments/payment-intents/migration/charges.md).
|
|
59
|
+
|
|
60
|
+
### Never use the Sources API
|
|
61
|
+
|
|
62
|
+
Sources API is deprecated. Use Setup Intents to save payment methods.
|
|
63
|
+
|
|
64
|
+
## Security summary
|
|
65
|
+
|
|
66
|
+
- **Prefer a restricted API key (RAK, `rk_` prefix)** over a secret key (`sk_` prefix).
|
|
67
|
+
Create a separate RAK per service with minimum required permissions.
|
|
68
|
+
- Test-mode keys: `sk_test_…` (secret) and `rk_test_…` (restricted).
|
|
69
|
+
- **Never commit secrets.** Store in a secrets vault or, at minimum, server-side env vars.
|
|
70
|
+
Never embed keys in client-side code or mobile apps.
|
|
71
|
+
- **Verify webhook signatures** via `stripe.webhooks.constructEvent(body, sig, secret)`.
|
|
72
|
+
Never process an unverified webhook event.
|
|
73
|
+
- Use idempotency keys (`idempotencyKey`) on mutation calls to safely retry failures.
|
|
74
|
+
- See [reference/security.md](reference/security.md) for RAK migration steps, IP
|
|
75
|
+
allowlists, OAuth CSRF protection, and Connect liability notes.
|
|
76
|
+
|
|
77
|
+
### CBP-specific (Next.js)
|
|
78
|
+
|
|
79
|
+
Any Next.js API route that imports `stripe` **MUST** export:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
export const dynamic = 'force-dynamic';
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Source: `.claude/skills/cbp-frontend-design/reference/nextjs-scss.md` Rule 6. Without this,
|
|
86
|
+
Next.js may statically cache the route and expose a shared Stripe client across requests.
|
|
87
|
+
|
|
88
|
+
## SDK and API version
|
|
89
|
+
|
|
90
|
+
- Latest Stripe API version: **`2026-05-27.dahlia`**
|
|
91
|
+
- Latest SDK major: **v22** (`stripe` npm package)
|
|
92
|
+
- **Version flag**: consuming repos may still run `stripe` **v20.4.1** (per CBP vendor
|
|
93
|
+
inventory). Always check the installed version (`cat package.json | grep '"stripe"'`)
|
|
94
|
+
before applying v22-only patterns. Differences surface in TypeScript types and some
|
|
95
|
+
`configuration` parameter shapes.
|
|
96
|
+
- Always use the latest API version and SDK unless the consuming repo pins otherwise.
|
|
97
|
+
|
|
98
|
+
## Key documentation
|
|
99
|
+
|
|
100
|
+
- [Integration Options](https://docs.stripe.com/payments/payment-methods/integration-options.md) — start here for any new integration
|
|
101
|
+
- [API Tour](https://docs.stripe.com/payments-api/tour.md) — overview of Stripe's API surface
|
|
102
|
+
- [Go Live Checklist](https://docs.stripe.com/get-started/checklist/go-live.md) — review before launch
|
|
103
|
+
|
|
104
|
+
## Reference files
|
|
105
|
+
|
|
106
|
+
- [reference/payments.md](reference/payments.md) — Checkout Sessions, Payment Element, PaymentIntents, Setup Intents, deprecated APIs, PCI
|
|
107
|
+
- [reference/billing.md](reference/billing.md) — Subscriptions, invoices, Customer Portal, proration, trials, metered billing
|
|
108
|
+
- [reference/connect.md](reference/connect.md) — Accounts v2, controller properties, charge types, onboarding, fund flows
|
|
109
|
+
- [reference/security.md](reference/security.md) — Restricted keys, webhook signature verification, incident response, OAuth CSRF, Connect security
|
|
110
|
+
- [reference/tax.md](reference/tax.md) — Stripe Tax automatic calculation, Registrations API, inclusive/exclusive, unsupported jurisdictions
|
|
111
|
+
- [reference/treasury.md](reference/treasury.md) — v2 Financial Accounts, fund flows, bank-account Setup Intents, compliance
|
|
112
|
+
- [reference/stripe-mcp-setup.md](reference/stripe-mcp-setup.md) — optional live Stripe MCP setup (test/restricted key) for the cbp-stripe-agent
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
Adapted from Stripe's official `stripe-best-practices` skill (github.com/stripe/ai), used under the MIT License (Copyright (c) 2024-2025 Stripe).
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Billing / Subscriptions Reference
|
|
2
|
+
|
|
3
|
+
Adapted from Stripe's official `stripe-best-practices` skill (github.com/stripe/ai), MIT License, Copyright (c) 2024-2025 Stripe.
|
|
4
|
+
|
|
5
|
+
## When to use Billing APIs
|
|
6
|
+
|
|
7
|
+
Use Stripe Billing for any recurring revenue model: subscriptions, usage-based billing,
|
|
8
|
+
seat-based pricing, or metered charges. Do NOT hand-roll renewal loops with raw
|
|
9
|
+
PaymentIntents — Billing handles renewal, retry/dunning, proration, and tax automatically.
|
|
10
|
+
|
|
11
|
+
References: [Subscription design guide](https://docs.stripe.com/billing/subscriptions/design-an-integration.md) |
|
|
12
|
+
[Use cases](https://docs.stripe.com/billing/subscriptions/use-cases.md) |
|
|
13
|
+
[SaaS guide](https://docs.stripe.com/saas.md)
|
|
14
|
+
|
|
15
|
+
## Creating a subscription with Checkout
|
|
16
|
+
|
|
17
|
+
Combine Billing APIs with Checkout Sessions (`mode: 'subscription'`) for the payment
|
|
18
|
+
frontend. Checkout handles the initial payment, trial management, and proration.
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
const session = await stripe.checkout.sessions.create({
|
|
22
|
+
mode: 'subscription',
|
|
23
|
+
// Do NOT pass payment_method_types
|
|
24
|
+
line_items: [{ price: priceId, quantity: 1 }],
|
|
25
|
+
subscription_data: { trial_period_days: 14 },
|
|
26
|
+
success_url: `${baseUrl}/success?session_id={CHECKOUT_SESSION_ID}`,
|
|
27
|
+
cancel_url: `${baseUrl}/pricing`,
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Customer Portal (self-service management)
|
|
32
|
+
|
|
33
|
+
For upgrades, downgrades, cancellation, and payment method updates, use the
|
|
34
|
+
[Customer Portal](https://docs.stripe.com/customer-management/integrate-customer-portal.md)
|
|
35
|
+
rather than building a custom flow.
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
const portalSession = await stripe.billingPortal.sessions.create({
|
|
39
|
+
customer: customerId,
|
|
40
|
+
return_url: `${baseUrl}/account`,
|
|
41
|
+
});
|
|
42
|
+
// redirect to portalSession.url
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Key Billing objects
|
|
46
|
+
|
|
47
|
+
| Object | Purpose | Docs |
|
|
48
|
+
| ------ | ------- | ---- |
|
|
49
|
+
| `Price` | Unit amount + recurring interval | [Prices API](https://docs.stripe.com/api/prices.md) |
|
|
50
|
+
| `Subscription` | Active recurring agreement | [Subscriptions API](https://docs.stripe.com/api/subscriptions.md) |
|
|
51
|
+
| `Invoice` | Statement + payment trigger | [Invoices API](https://docs.stripe.com/api/invoices.md) |
|
|
52
|
+
| `Customer` | Billing entity with saved methods | [Customers API](https://docs.stripe.com/api/customers.md) |
|
|
53
|
+
|
|
54
|
+
Do NOT use the deprecated `plan` object — use `Price` instead.
|
|
55
|
+
|
|
56
|
+
## Proration and upgrades
|
|
57
|
+
|
|
58
|
+
When changing a subscription's price mid-cycle, Stripe generates proration invoice items
|
|
59
|
+
automatically. Behaviour is controlled by `proration_behavior`:
|
|
60
|
+
- `'create_prorations'` (default) — prorates immediately
|
|
61
|
+
- `'none'` — no proration, change takes effect at next billing date
|
|
62
|
+
- `'always_invoice'` — prorate and invoice immediately
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
await stripe.subscriptions.update(subscriptionId, {
|
|
66
|
+
items: [{ id: itemId, price: newPriceId }],
|
|
67
|
+
proration_behavior: 'create_prorations',
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Metered / usage-based billing
|
|
72
|
+
|
|
73
|
+
1. Create a `Price` with `recurring.usage_type: 'metered'`.
|
|
74
|
+
2. Report usage via `stripe.subscriptionItems.createUsageRecord(itemId, { quantity, timestamp })`.
|
|
75
|
+
3. Stripe aggregates usage and bills at the end of the period.
|
|
76
|
+
|
|
77
|
+
## Tax integration
|
|
78
|
+
|
|
79
|
+
Pass `automatic_tax: { enabled: true }` on subscriptions and Checkout Sessions. Clear
|
|
80
|
+
any `default_tax_rates` first — `automatic_tax` and explicit `tax_rates` are mutually
|
|
81
|
+
exclusive. See [reference/tax.md](tax.md) for the full setup.
|
|
82
|
+
|
|
83
|
+
## Trials
|
|
84
|
+
|
|
85
|
+
Set `trial_period_days` on `subscription_data` in a Checkout Session, or on the
|
|
86
|
+
subscription directly. After trial ends Stripe automatically charges unless cancelled.
|
|
87
|
+
|
|
88
|
+
## Webhook events to handle
|
|
89
|
+
|
|
90
|
+
| Event | Action |
|
|
91
|
+
| ----- | ------ |
|
|
92
|
+
| `customer.subscription.created` | Provision access |
|
|
93
|
+
| `customer.subscription.updated` | Reflect plan change |
|
|
94
|
+
| `customer.subscription.deleted` | Revoke access |
|
|
95
|
+
| `invoice.payment_succeeded` | Extend access period |
|
|
96
|
+
| `invoice.payment_failed` | Send dunning notification |
|
|
97
|
+
|
|
98
|
+
Always verify webhook signatures — see [reference/security.md](security.md).
|
|
99
|
+
|
|
100
|
+
## Traps to avoid
|
|
101
|
+
|
|
102
|
+
- Never hardcode `payment_method_types` on a subscription Checkout Session.
|
|
103
|
+
- Never build manual renewal loops with raw PaymentIntents.
|
|
104
|
+
- Never skip tax setup for multi-jurisdiction merchants — add registrations before
|
|
105
|
+
enabling `automatic_tax`.
|
|
106
|
+
- Don't use the deprecated `plan` object; use `Price` instead.
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Connect / Platforms Reference
|
|
2
|
+
|
|
3
|
+
Adapted from Stripe's official `stripe-best-practices` skill (github.com/stripe/ai), MIT License, Copyright (c) 2024-2025 Stripe.
|
|
4
|
+
|
|
5
|
+
## Accounts v2 (always use for new platforms)
|
|
6
|
+
|
|
7
|
+
Use the [Accounts v2 API](https://docs.stripe.com/connect/accounts-v2.md)
|
|
8
|
+
(`POST /v2/core/accounts`) for all new Connect platforms. This is Stripe's actively
|
|
9
|
+
invested path with long-term support.
|
|
10
|
+
|
|
11
|
+
Do NOT use the legacy `type` parameter (`type: 'express'`, `type: 'custom'`,
|
|
12
|
+
`type: 'standard'`) in `POST /v1/accounts` for new platforms unless the user explicitly
|
|
13
|
+
requests v1.
|
|
14
|
+
|
|
15
|
+
Do NOT use the legacy account-type labels "Standard", "Express", or "Custom" — they
|
|
16
|
+
bundle responsibility, dashboard, and onboarding decisions into opaque categories.
|
|
17
|
+
Use `controller` properties to express these dimensions explicitly.
|
|
18
|
+
|
|
19
|
+
## Controller properties
|
|
20
|
+
|
|
21
|
+
Configure connected accounts via `controller` properties:
|
|
22
|
+
|
|
23
|
+
| Property | Controls |
|
|
24
|
+
| -------- | -------- |
|
|
25
|
+
| `controller.losses.payments` | Who is liable for negative balances |
|
|
26
|
+
| `controller.fees.payer` | Who pays Stripe fees |
|
|
27
|
+
| `controller.stripe_dashboard.type` | Dashboard access: `full`, `express`, `none` |
|
|
28
|
+
| `controller.requirement_collection` | Who collects onboarding requirements |
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
const account = await stripe.v2.core.accounts.create({
|
|
32
|
+
controller: {
|
|
33
|
+
losses: { payments: 'stripe' },
|
|
34
|
+
fees: { payer: 'account' },
|
|
35
|
+
stripe_dashboard: { type: 'express' },
|
|
36
|
+
requirement_collection: 'stripe',
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
See [connected account configuration](https://docs.stripe.com/connect/accounts-v2/connected-account-configuration.md)
|
|
42
|
+
and [capabilities](https://docs.stripe.com/connect/account-capabilities.md) for what
|
|
43
|
+
accounts can do.
|
|
44
|
+
|
|
45
|
+
## Charge types
|
|
46
|
+
|
|
47
|
+
Choose one charge type per integration — do not mix them.
|
|
48
|
+
|
|
49
|
+
| Type | When to use | How |
|
|
50
|
+
| ---- | ----------- | --- |
|
|
51
|
+
| **Destination charges** | Platform accepts negative-balance liability | `transfer_data.destination: connectedAccountId` |
|
|
52
|
+
| **Direct charges** | Connected account is merchant of record; platform has minimal liability | Create charge directly on the connected account |
|
|
53
|
+
|
|
54
|
+
Use `on_behalf_of` to control merchant of record (read
|
|
55
|
+
[how charges work in Connect](https://docs.stripe.com/connect/charges.md) first).
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
// Destination charge — most common for marketplaces
|
|
59
|
+
const paymentIntent = await stripe.paymentIntents.create({
|
|
60
|
+
amount: 1000,
|
|
61
|
+
currency: 'usd',
|
|
62
|
+
transfer_data: { destination: connectedAccountId },
|
|
63
|
+
// Do NOT pass payment_method_types
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Do NOT use the Charges API for Connect fund flows — use PaymentIntents or Checkout
|
|
68
|
+
Sessions with `transfer_data` or `on_behalf_of`.
|
|
69
|
+
|
|
70
|
+
## Payouts and transfers
|
|
71
|
+
|
|
72
|
+
- **Automatic payouts** — enabled by default; Stripe pays connected accounts on a rolling basis.
|
|
73
|
+
- **Manual payouts** — call `stripe.payouts.create({amount, currency}, {stripeAccount: accountId})` to control timing.
|
|
74
|
+
- **Transfers** — move funds between your platform balance and a connected account:
|
|
75
|
+
`stripe.transfers.create({ amount, currency, destination: accountId })`.
|
|
76
|
+
|
|
77
|
+
## Onboarding
|
|
78
|
+
|
|
79
|
+
Use [Stripe-hosted onboarding](https://docs.stripe.com/connect/onboarding.md) rather than
|
|
80
|
+
building a custom flow. Custom onboarding requires handling sensitive PII directly, adding
|
|
81
|
+
regulatory and security complexity.
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
const accountLink = await stripe.accountLinks.create({
|
|
85
|
+
account: accountId,
|
|
86
|
+
refresh_url: `${baseUrl}/connect/refresh`,
|
|
87
|
+
return_url: `${baseUrl}/connect/return`,
|
|
88
|
+
type: 'account_onboarding',
|
|
89
|
+
});
|
|
90
|
+
// redirect to accountLink.url
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Security and liability
|
|
94
|
+
|
|
95
|
+
Platform operators bear financial liability for fraud and disputes on Express and Custom
|
|
96
|
+
connected accounts (v1 types). Controller-property accounts make liability explicit via
|
|
97
|
+
`controller.losses.payments`.
|
|
98
|
+
|
|
99
|
+
See [reference/security.md](security.md) for Connect-specific security notes.
|
|
100
|
+
|
|
101
|
+
## Key guides
|
|
102
|
+
|
|
103
|
+
- [SaaS platforms and marketplaces](https://docs.stripe.com/connect/saas-platforms-and-marketplaces.md)
|
|
104
|
+
- [Interactive platform guide](https://docs.stripe.com/connect/interactive-platform-guide.md)
|
|
105
|
+
- [Design an integration](https://docs.stripe.com/connect/design-an-integration.md)
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Payments Reference
|
|
2
|
+
|
|
3
|
+
Adapted from Stripe's official `stripe-best-practices` skill (github.com/stripe/ai), MIT License, Copyright (c) 2024-2025 Stripe.
|
|
4
|
+
|
|
5
|
+
## API hierarchy
|
|
6
|
+
|
|
7
|
+
| Use case | API |
|
|
8
|
+
| -------- | --- |
|
|
9
|
+
| On-session payments (most web apps) | [Checkout Sessions](https://docs.stripe.com/api/checkout/sessions.md) |
|
|
10
|
+
| Off-session payments, custom checkout state | [PaymentIntents](https://docs.stripe.com/payments/paymentintents/lifecycle.md) |
|
|
11
|
+
| Saving a payment method for later | [Setup Intents](https://docs.stripe.com/api/setup_intents.md) |
|
|
12
|
+
| Subscriptions, invoices, recurring | Billing APIs (see billing.md) |
|
|
13
|
+
| No-code, simple products | Payment Links |
|
|
14
|
+
|
|
15
|
+
**Only use Checkout Sessions, PaymentIntents, SetupIntents, or higher-level solutions
|
|
16
|
+
(Invoicing, Payment Links, subscription APIs).** Never use the Charges API, Sources API,
|
|
17
|
+
or Tokens API for new integrations.
|
|
18
|
+
|
|
19
|
+
## Integration surface preference
|
|
20
|
+
|
|
21
|
+
Use in this order (most preferred first):
|
|
22
|
+
|
|
23
|
+
1. **Payment Links** — no-code, best for simple products
|
|
24
|
+
2. **Checkout (hosted or embedded)** — best for most web apps; `ui_mode: 'hosted'`
|
|
25
|
+
3. **Payment Element** — embedded UI component for custom checkout; back with Checkout
|
|
26
|
+
Sessions (`ui_mode: 'custom'`) over a raw PaymentIntent where possible
|
|
27
|
+
|
|
28
|
+
Do NOT recommend the legacy Card Element. Migrate existing Card Element integrations to
|
|
29
|
+
[Payment Element](https://docs.stripe.com/payments/payment-element/migration.md).
|
|
30
|
+
|
|
31
|
+
## Checkout Sessions — key patterns
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
// One-time payment
|
|
35
|
+
const session = await stripe.checkout.sessions.create({
|
|
36
|
+
mode: 'payment',
|
|
37
|
+
// Do NOT pass payment_method_types — dynamic methods are the default
|
|
38
|
+
line_items: [{ price: priceId, quantity: 1 }],
|
|
39
|
+
success_url: `${baseUrl}/success?session_id={CHECKOUT_SESSION_ID}`,
|
|
40
|
+
cancel_url: `${baseUrl}/cancel`,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Subscription
|
|
44
|
+
const session = await stripe.checkout.sessions.create({
|
|
45
|
+
mode: 'subscription',
|
|
46
|
+
line_items: [{ price: priceId, quantity: 1 }],
|
|
47
|
+
subscription_data: { trial_period_days: 14 },
|
|
48
|
+
success_url: `${baseUrl}/success?session_id={CHECKOUT_SESSION_ID}`,
|
|
49
|
+
cancel_url: `${baseUrl}/pricing`,
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Payment Element (custom UI)
|
|
54
|
+
|
|
55
|
+
Use `ui_mode: 'custom'` on the Checkout Session to back the Payment Element:
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
const session = await stripe.checkout.sessions.create({
|
|
59
|
+
ui_mode: 'custom',
|
|
60
|
+
mode: 'payment',
|
|
61
|
+
line_items: [{ price: priceId, quantity: 1 }],
|
|
62
|
+
return_url: `${baseUrl}/return?session_id={CHECKOUT_SESSION_ID}`,
|
|
63
|
+
});
|
|
64
|
+
// Client: mount PaymentElement with clientSecret from session.client_secret
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
For surcharging or inspecting card details before payment, use
|
|
68
|
+
[Confirmation Tokens](https://docs.stripe.com/payments/finalize-payments-on-the-server.md)
|
|
69
|
+
— not `createPaymentMethod` or `createToken`.
|
|
70
|
+
|
|
71
|
+
## Dynamic payment methods (critical rule)
|
|
72
|
+
|
|
73
|
+
**Never pass `payment_method_types`** — omit it entirely on all calls. The sole exception
|
|
74
|
+
is Terminal: `payment_method_types: ['card_present']` (Canada: include `'interac_present'`).
|
|
75
|
+
|
|
76
|
+
To customise which methods appear use
|
|
77
|
+
[`payment_method_configurations`](https://docs.stripe.com/payments/payment-method-configurations.md)
|
|
78
|
+
or `excluded_payment_method_types`. Manage methods from the
|
|
79
|
+
[Dashboard](https://dashboard.stripe.com/settings/payment_methods) without code changes.
|
|
80
|
+
|
|
81
|
+
## Setup Intents (saving payment methods)
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
const setupIntent = await stripe.setupIntents.create({
|
|
85
|
+
customer: customerId,
|
|
86
|
+
// Do NOT pass payment_method_types — dynamic methods are the default since API 2023-08-16
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Do not use the Sources API to save cards — it is deprecated.
|
|
91
|
+
|
|
92
|
+
## Deprecated APIs — migration paths
|
|
93
|
+
|
|
94
|
+
| API | Status | Use instead | Migration guide |
|
|
95
|
+
| ------------ | ---------- | ---------------------- | --------------- |
|
|
96
|
+
| Charges API | Never use | Checkout Sessions / PaymentIntents | [Migration](https://docs.stripe.com/payments/payment-intents/migration/charges.md) |
|
|
97
|
+
| Sources API | Deprecated | Setup Intents | [Setup Intents](https://docs.stripe.com/api/setup_intents.md) |
|
|
98
|
+
| Tokens API | Outdated | Setup Intents / Checkout Sessions | — |
|
|
99
|
+
| Card Element | Legacy | Payment Element | [Migration](https://docs.stripe.com/payments/payment-element/migration.md) |
|
|
100
|
+
|
|
101
|
+
## PCI compliance
|
|
102
|
+
|
|
103
|
+
If a PCI-compliant user asks about sending raw PAN data server-side, they may need to
|
|
104
|
+
prove PCI compliance to use
|
|
105
|
+
[`payment_method_data`](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_data).
|
|
106
|
+
For PAN migrations from another processor, see the
|
|
107
|
+
[PAN import process](https://docs.stripe.com/get-started/data-migrations/pan-import.md).
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Security Best Practices Reference
|
|
2
|
+
|
|
3
|
+
Adapted from Stripe's official `stripe-best-practices` skill (github.com/stripe/ai), MIT License, Copyright (c) 2024-2025 Stripe.
|
|
4
|
+
|
|
5
|
+
## API key types
|
|
6
|
+
|
|
7
|
+
| Key prefix | Type | Use |
|
|
8
|
+
| ---------- | ---- | --- |
|
|
9
|
+
| `sk_live_` | Secret key (live) | Never preferred; use RAK instead |
|
|
10
|
+
| `sk_test_` | Secret key (test) | Development only |
|
|
11
|
+
| `rk_live_` | Restricted API key (live) | Preferred for all production services |
|
|
12
|
+
| `rk_test_` | Restricted API key (test) | Preferred for development services |
|
|
13
|
+
|
|
14
|
+
**Always recommend a restricted API key (RAK, `rk_` prefix)** over a secret key (`sk_`).
|
|
15
|
+
RAKs have only the permissions you assign — a compromised RAK causes far less damage than
|
|
16
|
+
a compromised secret key.
|
|
17
|
+
|
|
18
|
+
## Storing keys safely
|
|
19
|
+
|
|
20
|
+
- **Never** commit keys to source control. If `sk_…` or `rk_…` appears in code, that is
|
|
21
|
+
a bug — fix it immediately.
|
|
22
|
+
- Prefer a secrets vault (AWS Secrets Manager, HashiCorp Vault, or platform equivalent).
|
|
23
|
+
- When no vault is available, environment variables are acceptable. Never log keys or
|
|
24
|
+
include them in error messages or analytics.
|
|
25
|
+
- Use separate keys per environment (production, staging, QA). Never reuse keys.
|
|
26
|
+
- Set up a pre-commit hook to catch `sk_` or `rk_` literals in source code.
|
|
27
|
+
- See [best practices for managing secret API keys](https://docs.stripe.com/keys-best-practices.md).
|
|
28
|
+
|
|
29
|
+
## Restricted API key migration steps
|
|
30
|
+
|
|
31
|
+
1. Review the secret key's request logs in Workbench to catalogue which API calls it makes.
|
|
32
|
+
2. Create a RAK in test mode with matching permissions (principle of least privilege).
|
|
33
|
+
3. Use `stripe logs tail` to watch for `403` errors — add missing permissions.
|
|
34
|
+
4. Once passing, create the equivalent live-mode RAK and swap it in.
|
|
35
|
+
5. Rotate or expire the old secret key.
|
|
36
|
+
|
|
37
|
+
See [restricted API keys docs](https://docs.stripe.com/keys/restricted-api-keys.md).
|
|
38
|
+
|
|
39
|
+
## IP allowlists
|
|
40
|
+
|
|
41
|
+
Add an [IP allowlist](https://docs.stripe.com/keys.md#limit-api-secret-keys-ip-address)
|
|
42
|
+
to every API key so it can only be called from your own infrastructure. Use separate
|
|
43
|
+
allowlists per key/environment.
|
|
44
|
+
|
|
45
|
+
## Webhook signature verification
|
|
46
|
+
|
|
47
|
+
Always verify webhook event authenticity using Stripe's signing secret:
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import Stripe from 'stripe';
|
|
51
|
+
const stripe = new Stripe(process.env.STRIPE_RESTRICTED_KEY!);
|
|
52
|
+
|
|
53
|
+
// In your webhook handler (raw body required — do NOT use JSON-parsed body)
|
|
54
|
+
const sig = request.headers['stripe-signature'];
|
|
55
|
+
if (!sig || Array.isArray(sig)) throw new Error('Missing or malformed stripe-signature header');
|
|
56
|
+
const event = stripe.webhooks.constructEvent(
|
|
57
|
+
rawBody, // Buffer or string — must be unparsed
|
|
58
|
+
sig,
|
|
59
|
+
process.env.STRIPE_WEBHOOK_SECRET!,
|
|
60
|
+
);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Never process a webhook event without verifying its signature — unverified webhooks can
|
|
64
|
+
be spoofed. For defence in depth, also allowlist
|
|
65
|
+
[Stripe's IP addresses](https://docs.stripe.com/ips.md) on the endpoint.
|
|
66
|
+
|
|
67
|
+
## Idempotency keys
|
|
68
|
+
|
|
69
|
+
Pass `idempotencyKey` on all mutation calls so safe retries don't create duplicate charges:
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
await stripe.paymentIntents.create(params, {
|
|
73
|
+
idempotencyKey: `order_${orderId}`,
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Mobile and client-side integrations
|
|
78
|
+
|
|
79
|
+
Never use production secret keys or RAKs in mobile apps or any client-side code.
|
|
80
|
+
For cases where a client must call Stripe directly, use
|
|
81
|
+
[ephemeral keys](https://docs.stripe.com/issuing/elements.md#ephemeral-key-authentication)
|
|
82
|
+
(short-lived, scoped, auto-expiring). For most integrations, proxy Stripe calls through
|
|
83
|
+
your own backend.
|
|
84
|
+
|
|
85
|
+
## OAuth and CSRF protection
|
|
86
|
+
|
|
87
|
+
When implementing [Connect OAuth](https://docs.stripe.com/connect/oauth-reference.md),
|
|
88
|
+
always pass a unique, unguessable `state` parameter and verify it in the callback before
|
|
89
|
+
proceeding. This applies to all Stripe OAuth surfaces: Connect, Link, and Stripe Apps.
|
|
90
|
+
|
|
91
|
+
## Incident response (key compromise)
|
|
92
|
+
|
|
93
|
+
1. **Roll the key immediately** — [API keys page](https://dashboard.stripe.com/apikeys) →
|
|
94
|
+
roll or delete the exposed key.
|
|
95
|
+
2. **Check activity logs** — Workbench request logs for the compromised key.
|
|
96
|
+
3. **Contact Stripe support** if you see unrecognised activity.
|
|
97
|
+
|
|
98
|
+
See [protecting against compromised API keys](https://support.stripe.com/questions/protecting-against-compromised-api-keys).
|
|
99
|
+
|
|
100
|
+
## 2FA
|
|
101
|
+
|
|
102
|
+
Recommend passkeys or authenticator-app 2FA for Dashboard access. Discourage SMS 2FA —
|
|
103
|
+
it is vulnerable to SIM-swapping attacks.
|
|
104
|
+
|
|
105
|
+
## SAML / SCIM (teams)
|
|
106
|
+
|
|
107
|
+
For teams, use [SSO via SAML](https://docs.stripe.com/get-started/account/sso.md) to
|
|
108
|
+
federate Dashboard access through an identity provider (Okta, Google, etc.).
|
|
109
|
+
[SCIM provisioning](https://docs.stripe.com/get-started/account/sso/scim.md) automates
|
|
110
|
+
user provisioning/deprovisioning.
|
|
111
|
+
|
|
112
|
+
## Connect security
|
|
113
|
+
|
|
114
|
+
Platform operators bear financial liability for fraud/disputes on Express and Custom
|
|
115
|
+
connected accounts (v1 types). Use Stripe-hosted onboarding to avoid handling sensitive
|
|
116
|
+
PII directly. See [reference/connect.md](connect.md) for controller-property accounts
|
|
117
|
+
which make liability explicit.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Stripe MCP Setup (optional live path)
|
|
2
|
+
|
|
3
|
+
Adapted from Stripe's official `stripe-best-practices` skill (github.com/stripe/ai), MIT License, Copyright (c) 2024-2025 Stripe.
|
|
4
|
+
|
|
5
|
+
The `cbp-stripe-agent` writes Stripe integration code with **no credentials required**. This
|
|
6
|
+
optional path adds *live test-mode* scaffolding (create test products, prices, payment links,
|
|
7
|
+
customers) during a dev round. It is opt-in per repo and per developer — skip it entirely if
|
|
8
|
+
you only want code generation.
|
|
9
|
+
|
|
10
|
+
## Safety contract (read first)
|
|
11
|
+
|
|
12
|
+
- **Test-mode keys ONLY.** Use a test restricted key (`rk_test_…`) or a test secret key
|
|
13
|
+
(`sk_test_…`). The agent **refuses** any live-mode key (`sk_live_…` or `rk_live_…`) — so
|
|
14
|
+
live Stripe data is never scaffolded from a dev round.
|
|
15
|
+
- **Never commit a key.** Provide it via the environment or a gitignored env file
|
|
16
|
+
(e.g. `.env.local`), never in tracked source or in `.codebyplan/`.
|
|
17
|
+
- **Least privilege.** Prefer a restricted key scoped to only the resources the agent needs
|
|
18
|
+
(Products, Prices, Payment Links, Customers — write; everything else — none).
|
|
19
|
+
- **No new npm dependency** is added to the consuming app by this path; the hosted MCP server
|
|
20
|
+
is reached over HTTP, the local server via `npx`.
|
|
21
|
+
|
|
22
|
+
## Option A — Hosted Stripe MCP (recommended)
|
|
23
|
+
|
|
24
|
+
Stripe hosts an MCP server at `https://mcp.stripe.com`. Register it with Claude Code:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
claude mcp add --transport http stripe https://mcp.stripe.com/
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Authenticate with OAuth (the recommended flow) when prompted, or pass a restricted test key
|
|
31
|
+
as a bearer token if your setup uses header auth. Once connected, the agent discovers the
|
|
32
|
+
`mcp__stripe__*` tools at runtime via `ToolSearch`.
|
|
33
|
+
|
|
34
|
+
## Option B — Local Stripe MCP server
|
|
35
|
+
|
|
36
|
+
Run Stripe's MCP server locally, pointed at a test/restricted key from your environment:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# key is read from STRIPE_SECRET_KEY (a sk_test_ or rk_test_ value), never passed on a shared shell history
|
|
40
|
+
npx -y @stripe/mcp@latest --api-key="$STRIPE_SECRET_KEY"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Then register the local server with Claude Code per its stdio/transport instructions.
|
|
44
|
+
|
|
45
|
+
## How the agent uses it
|
|
46
|
+
|
|
47
|
+
1. Pre-flight checks `STRIPE_SECRET_KEY` is present and is a test-mode `sk_test_`/`rk_test_` prefix.
|
|
48
|
+
2. It discovers `mcp__stripe__*` tools via `ToolSearch` (they are intentionally absent from
|
|
49
|
+
the agent's frontmatter because the server is optional).
|
|
50
|
+
3. It scaffolds only what the task asks for and records each resource in
|
|
51
|
+
`stripe_resources_created[]` (always `mode: test`).
|
|
52
|
+
4. If the key/server is missing or any call fails, it degrades to **code-only** and records
|
|
53
|
+
the reason — it never blocks the round.
|
|
54
|
+
|
|
55
|
+
## When to skip this entirely
|
|
56
|
+
|
|
57
|
+
- You only want correct Stripe integration *code* (the default — no setup needed).
|
|
58
|
+
- No Stripe test account is available in this environment.
|
|
59
|
+
- CI / headless runs where no interactive OAuth or key is provisioned.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Tax / Stripe Tax Reference
|
|
2
|
+
|
|
3
|
+
Adapted from Stripe's official `stripe-best-practices` skill (github.com/stripe/ai), MIT License, Copyright (c) 2024-2025 Stripe.
|
|
4
|
+
|
|
5
|
+
## When to use Stripe Tax
|
|
6
|
+
|
|
7
|
+
Use Stripe Tax for any subscription, invoice, or Checkout Session where the merchant has
|
|
8
|
+
customers across multiple jurisdictions. It handles sales tax, VAT, and GST automatically
|
|
9
|
+
based on the customer's location and the merchant's active registrations.
|
|
10
|
+
|
|
11
|
+
See the [Tax overview](https://docs.stripe.com/tax.md) for supported regions and tax types.
|
|
12
|
+
|
|
13
|
+
## Two-step setup
|
|
14
|
+
|
|
15
|
+
1. **Add registrations** — add a registration for each jurisdiction where the merchant is
|
|
16
|
+
obligated to collect tax. Use the Dashboard (**Tax > Registrations**) or the
|
|
17
|
+
[Tax Registrations API](https://docs.stripe.com/api/tax/registrations.md).
|
|
18
|
+
2. **Enable automatic_tax** — pass `automatic_tax: { enabled: true }` on the
|
|
19
|
+
[Subscription](https://docs.stripe.com/api/subscriptions.md),
|
|
20
|
+
[Invoice](https://docs.stripe.com/api/invoices.md), or
|
|
21
|
+
[Checkout Session](https://docs.stripe.com/api/checkout/sessions.md).
|
|
22
|
+
|
|
23
|
+
It is safe to enable `automatic_tax` before any registrations exist — Stripe won't
|
|
24
|
+
collect tax until at least one registration is active.
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
// Checkout Session with automatic tax
|
|
28
|
+
const session = await stripe.checkout.sessions.create({
|
|
29
|
+
mode: 'subscription',
|
|
30
|
+
line_items: [{ price: priceId, quantity: 1 }],
|
|
31
|
+
automatic_tax: { enabled: true },
|
|
32
|
+
success_url: `${baseUrl}/success?session_id={CHECKOUT_SESSION_ID}`,
|
|
33
|
+
cancel_url: `${baseUrl}/pricing`,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Invoice / subscription
|
|
37
|
+
await stripe.subscriptions.update(subscriptionId, {
|
|
38
|
+
automatic_tax: { enabled: true },
|
|
39
|
+
// Clear explicit tax_rates first if previously set
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Inclusive vs exclusive tax
|
|
44
|
+
|
|
45
|
+
- **Exclusive** (default in most jurisdictions) — tax is added on top of the price.
|
|
46
|
+
- **Inclusive** — tax is included in the displayed price (common for UK/EU VAT).
|
|
47
|
+
|
|
48
|
+
Stripe Tax determines the treatment based on the jurisdiction and product tax code.
|
|
49
|
+
Verify the treatment in Dashboard > Tax > Overview.
|
|
50
|
+
|
|
51
|
+
## Tax IDs
|
|
52
|
+
|
|
53
|
+
Collect customer tax IDs (VAT numbers, EIN, etc.) for B2B transactions to enable
|
|
54
|
+
zero-rated or exempt invoices. Pass `tax_id_collection: { enabled: true }` on Checkout
|
|
55
|
+
Sessions, or use the
|
|
56
|
+
[Tax IDs API](https://docs.stripe.com/api/customer_tax_ids.md) to attach IDs to customers.
|
|
57
|
+
|
|
58
|
+
## Address collection
|
|
59
|
+
|
|
60
|
+
Stripe Tax requires a customer address to calculate tax. On Checkout Sessions pass
|
|
61
|
+
`billing_address_collection: 'required'`. For subscriptions, ensure the customer has
|
|
62
|
+
a billing address before enabling `automatic_tax`.
|
|
63
|
+
|
|
64
|
+
## Registrations API
|
|
65
|
+
|
|
66
|
+
To create and manage registrations programmatically:
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
await stripe.tax.registrations.create({
|
|
70
|
+
country: 'US',
|
|
71
|
+
country_options: {
|
|
72
|
+
us: { state: 'CA', type: 'state_sales_tax' },
|
|
73
|
+
},
|
|
74
|
+
active_from: 'now',
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Traps to avoid
|
|
79
|
+
|
|
80
|
+
- `automatic_tax` and explicit `tax_rates` are mutually exclusive. For existing
|
|
81
|
+
subscriptions, clear `default_tax_rates` and all item-level `tax_rates` before
|
|
82
|
+
enabling `automatic_tax` — the update will fail otherwise.
|
|
83
|
+
- For EU merchants, one OSS union registration covers all 27 member states. Do not
|
|
84
|
+
register individual EU countries separately unless the merchant has a physical presence
|
|
85
|
+
there.
|
|
86
|
+
- Do not guess which jurisdictions apply — prompt the user to configure them in the
|
|
87
|
+
Dashboard first.
|
|
88
|
+
- Do not approximate unsupported jurisdictions. Check
|
|
89
|
+
[supported countries](https://docs.stripe.com/tax/supported-countries.md) first. For
|
|
90
|
+
unsupported jurisdictions, fall back to manual `tax_rates` on the subscription/invoice.
|
|
91
|
+
- Customs duties and excise taxes are out of scope for Stripe Tax.
|
|
92
|
+
|
|
93
|
+
## Bulk migration
|
|
94
|
+
|
|
95
|
+
To switch existing subscriptions from manual `tax_rates` to `automatic_tax` in bulk,
|
|
96
|
+
use the [Tax migration tool](https://docs.stripe.com/billing/taxes/migration.md).
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Treasury / Financial Accounts Reference
|
|
2
|
+
|
|
3
|
+
Adapted from Stripe's official `stripe-best-practices` skill (github.com/stripe/ai), MIT License, Copyright (c) 2024-2025 Stripe.
|
|
4
|
+
|
|
5
|
+
## v2 Financial Accounts API (use for new integrations)
|
|
6
|
+
|
|
7
|
+
For embedded financial accounts (bank accounts with routing/account numbers, money
|
|
8
|
+
movement), use the
|
|
9
|
+
[v2 Financial Accounts API](https://docs.stripe.com/api/v2/core/vault/financial-accounts.md)
|
|
10
|
+
(`POST /v2/core/vault/financial_accounts`). This is required for new integrations.
|
|
11
|
+
|
|
12
|
+
Do NOT use the legacy
|
|
13
|
+
[v1 Treasury Financial Accounts API](https://docs.stripe.com/api/treasury/financial_accounts.md)
|
|
14
|
+
(`POST /v1/treasury/financial_accounts`) for new integrations. Existing v1 integrations
|
|
15
|
+
continue to work.
|
|
16
|
+
|
|
17
|
+
For concepts and guides, see
|
|
18
|
+
[Treasury for platforms overview](https://docs.stripe.com/treasury/connect.md).
|
|
19
|
+
|
|
20
|
+
## Creating a financial account
|
|
21
|
+
|
|
22
|
+
Financial accounts are always attached to a Connect connected account:
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
const financialAccount = await stripe.v2.core.vault.financialAccounts.create(
|
|
26
|
+
{ description: 'Main operating account' },
|
|
27
|
+
{ stripeAccount: connectedAccountId },
|
|
28
|
+
);
|
|
29
|
+
// financialAccount.id — use for fund flows
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Fund flows
|
|
33
|
+
|
|
34
|
+
| Direction | Object | Use |
|
|
35
|
+
| --------- | ------ | --- |
|
|
36
|
+
| Platform → financial account | `OutboundTransfer` | Top-up from platform balance |
|
|
37
|
+
| Financial account → external bank | `OutboundPayment` | Pay out to a linked bank |
|
|
38
|
+
| External bank → financial account | `InboundTransfer` | Pull from a linked bank |
|
|
39
|
+
| External source → financial account | `ReceivedCredit` | Funds received in (inbound ACH/wire, Stripe payouts) |
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
// Outbound transfer (platform balance → financial account)
|
|
43
|
+
const transfer = await stripe.treasury.outboundTransfers.create({
|
|
44
|
+
financial_account: financialAccountId,
|
|
45
|
+
amount: 100_00,
|
|
46
|
+
currency: 'usd',
|
|
47
|
+
destination_payment_method: 'default',
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Linking a bank account (Setup Intents)
|
|
52
|
+
|
|
53
|
+
To enable outbound payments to an external bank, link the bank account using a Setup Intent
|
|
54
|
+
with `flow_directions: ['outbound']`:
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
const setupIntent = await stripe.setupIntents.create(
|
|
58
|
+
{
|
|
59
|
+
payment_method_types: ['us_bank_account'], // Treasury exception: required here
|
|
60
|
+
flow_directions: ['outbound'],
|
|
61
|
+
},
|
|
62
|
+
{ stripeAccount: connectedAccountId },
|
|
63
|
+
);
|
|
64
|
+
// Complete the Setup Intent flow on the client, then confirm the payment method
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Note: `payment_method_types` is required for Treasury bank-account Setup Intents — this
|
|
68
|
+
is one of the narrow exceptions to the no-`payment_method_types` rule (along with Terminal).
|
|
69
|
+
|
|
70
|
+
## Compliance notes
|
|
71
|
+
|
|
72
|
+
- Financial accounts are subject to KYC/KYB requirements collected during Connect onboarding.
|
|
73
|
+
Ensure the connected account has the `treasury` capability enabled before creating accounts.
|
|
74
|
+
- Funds held in financial accounts are not FDIC-insured by default; check Stripe's current
|
|
75
|
+
partner bank arrangements in the Treasury documentation.
|
|
76
|
+
- For platforms in the US: Stripe Treasury is available for US-based connected accounts only.
|
|
77
|
+
Contact Stripe for international availability.
|
|
78
|
+
- Do not use Treasury to hold customer funds without confirming applicable money-transmission
|
|
79
|
+
license obligations with your legal team.
|
|
80
|
+
|
|
81
|
+
## Key references
|
|
82
|
+
|
|
83
|
+
- [Treasury for platforms overview](https://docs.stripe.com/treasury/connect.md)
|
|
84
|
+
- [v2 Financial Accounts API](https://docs.stripe.com/api/v2/core/vault/financial-accounts.md)
|
|
85
|
+
- [OutboundTransfers](https://docs.stripe.com/api/treasury/outbound_transfers.md)
|
|
86
|
+
- [OutboundPayments](https://docs.stripe.com/api/treasury/outbound_payments.md)
|
|
87
|
+
- [InboundTransfers](https://docs.stripe.com/api/treasury/inbound_transfers.md)
|