clawskills-mcp 0.2.3 → 0.3.0
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/index.test.js +1 -1
- package/package.json +23 -1
- package/skills/INDEX.md +22 -1
- package/skills/ROADMAP.md +109 -184
- package/skills/stripe/skill.md +823 -0
package/dist/index.test.js
CHANGED
|
@@ -185,7 +185,7 @@ const REQUIRED_SECTIONS = [
|
|
|
185
185
|
const KNOWN_SKILLS = [
|
|
186
186
|
"asana", "dynamics365", "figma", "github", "hubspot",
|
|
187
187
|
"jira", "monday", "salesforce", "servicenow", "zendesk",
|
|
188
|
-
"slack",
|
|
188
|
+
"slack", "stripe",
|
|
189
189
|
];
|
|
190
190
|
describe("real skills", () => {
|
|
191
191
|
let skills;
|
package/package.json
CHANGED
|
@@ -1,7 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clawskills-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "MCP server exposing ClawSkills API integration skill docs to AI agents",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "ClawSkills Contributors",
|
|
7
|
+
"homepage": "https://github.com/Shanksg/clawskills#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/Shanksg/clawskills.git"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"mcp",
|
|
14
|
+
"claude",
|
|
15
|
+
"ai",
|
|
16
|
+
"integration",
|
|
17
|
+
"api",
|
|
18
|
+
"skill",
|
|
19
|
+
"salesforce",
|
|
20
|
+
"hubspot",
|
|
21
|
+
"jira",
|
|
22
|
+
"zendesk",
|
|
23
|
+
"slack",
|
|
24
|
+
"crm",
|
|
25
|
+
"saas"
|
|
26
|
+
],
|
|
5
27
|
"type": "module",
|
|
6
28
|
"bin": {
|
|
7
29
|
"clawskills-mcp": "dist/index.js"
|
package/skills/INDEX.md
CHANGED
|
@@ -21,6 +21,7 @@ A curated library of integration skill documents for the most common SaaS platfo
|
|
|
21
21
|
| GitHub | [github/skill.md](./github/skill.md) | Engineering, DevOps | Source control, PRs, CI/CD automation via REST + GraphQL |
|
|
22
22
|
| Figma | [figma/skill.md](./figma/skill.md) | Design, Product, DevOps | Design file inspection, asset export, webhooks, design-to-dev handoff |
|
|
23
23
|
| Slack | [slack/skill.md](./slack/skill.md) | All teams | Real-time messaging, notifications, event-driven automation via Web API |
|
|
24
|
+
| Stripe | [stripe/skill.md](./stripe/skill.md) | Engineering, Finance, RevOps | Payments, subscriptions, refunds, webhooks, and Connect for platforms |
|
|
24
25
|
|
|
25
26
|
---
|
|
26
27
|
|
|
@@ -180,6 +181,20 @@ A curated library of integration skill documents for the most common SaaS platfo
|
|
|
180
181
|
|
|
181
182
|
---
|
|
182
183
|
|
|
184
|
+
### Stripe → [skill.md](./stripe/skill.md)
|
|
185
|
+
1. Create a PaymentIntent for a one-time charge and confirm with Stripe.js
|
|
186
|
+
2. Save a card for future off-session charges (SetupIntent → attach PaymentMethod)
|
|
187
|
+
3. Create a recurring subscription (Customer + Price + Subscription)
|
|
188
|
+
4. Launch a hosted Checkout Session and fulfil on `checkout.session.completed`
|
|
189
|
+
5. Issue a full or partial refund
|
|
190
|
+
6. Handle a failed subscription payment: notify customer, update payment method, retry invoice
|
|
191
|
+
7. Receive and verify webhooks (Stripe-Signature HMAC-SHA256 verification)
|
|
192
|
+
8. Stripe Connect: create a connected account, generate onboarding link, destination charge
|
|
193
|
+
9. List all charges for a customer with cursor pagination
|
|
194
|
+
10. Cancel a subscription at period end
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
183
198
|
## Common Cross-Tool Patterns
|
|
184
199
|
|
|
185
200
|
These scenarios span multiple skill docs — reference both tools:
|
|
@@ -197,6 +212,9 @@ These scenarios span multiple skill docs — reference both tools:
|
|
|
197
212
|
| Bug report → GitHub issue + PR | Zendesk / ServiceNow | GitHub | Auto-create issue, label, assign; link ticket ID in body |
|
|
198
213
|
| Design ready → engineering task | Figma | Jira / Asana / GitHub | `DEV_MODE_STATUS_UPDATE` webhook → create/update issue with frame link |
|
|
199
214
|
| Asset pipeline | Figma | S3 / CDN | `FILE_VERSION_UPDATE` webhook → export frames as PNG, upload to storage |
|
|
215
|
+
| Payment succeeded → CRM deal closed | Stripe | Salesforce / HubSpot | `payment_intent.succeeded` webhook → update deal stage to Closed Won |
|
|
216
|
+
| Subscription cancelled → support ticket | Stripe | Zendesk / Jira | `customer.subscription.deleted` → create ticket for churn review |
|
|
217
|
+
| Invoice paid → project provisioned | Stripe | Asana / Monday.com | `invoice.paid` webhook → create onboarding project or board item |
|
|
200
218
|
|
|
201
219
|
---
|
|
202
220
|
|
|
@@ -215,6 +233,7 @@ These scenarios span multiple skill docs — reference both tools:
|
|
|
215
233
|
| GitHub | Fine-grained PAT or GitHub App installation token | Bearer token | Per-resource permissions (contents, issues, pull_requests, etc.) |
|
|
216
234
|
| Figma | Personal Access Token (PAT) or OAuth 2.0 | `X-Figma-Token` (PAT) / `Authorization: Bearer` (OAuth) | Granular scopes: `file_content:read`, `webhooks:write`, etc. |
|
|
217
235
|
| Slack | Bot token (OAuth 2.0 app install) | `xoxb-...` Bearer token | Per-method scopes: `chat:write`, `channels:read`, `users:read`, etc. |
|
|
236
|
+
| Stripe | Secret API key or Restricted key | `Authorization: Bearer sk_live_...` | Per-resource scopes on restricted keys (None / Read / Write) |
|
|
218
237
|
|
|
219
238
|
---
|
|
220
239
|
|
|
@@ -233,10 +252,11 @@ These scenarios span multiple skill docs — reference both tools:
|
|
|
233
252
|
| GitHub | PAT: 5,000/hr · GitHub App: 5k–12.5k/hr · Enterprise Cloud: 15,000/hr · Secondary: 900 pts/min, 80 create/min | 1 hour (primary) + sliding (secondary) | `x-ratelimit-remaining` / `x-ratelimit-reset`; 403 or 429 |
|
|
234
253
|
| Figma | Starter: 10/min (T1), 25/min (T2) · Professional: 15/min (T1), 50/min (T2) · Org: 20/min (T1), 100/min (T2) | Per minute (leaky bucket) | `Retry-After` / `X-Figma-Rate-Limit-Type` / `X-Figma-Plan-Tier` |
|
|
235
254
|
| Slack | Tier 1: 1/min · Tier 2: 20/min · Tier 3: 50/min · Tier 4: 100/min · `chat.postMessage`: 1/sec/channel sub-limit | Per method per workspace | HTTP 429 + `Retry-After` header (seconds) |
|
|
255
|
+
| Stripe | Live: **100 req/sec** · Sandbox: **25 req/sec** · Search: 20/sec | Per account | HTTP 429 + `Stripe-Rate-Limited-Reason` header |
|
|
236
256
|
|
|
237
257
|
---
|
|
238
258
|
|
|
239
|
-
*Last updated: 2026-
|
|
259
|
+
*Last updated: 2026-03-01 (Stripe skill added) | See [ROADMAP.md](./ROADMAP.md) for versioning and governance details.*
|
|
240
260
|
|
|
241
261
|
### API version reference (as of 2026-02-22)
|
|
242
262
|
|
|
@@ -253,3 +273,4 @@ These scenarios span multiple skill docs — reference both tools:
|
|
|
253
273
|
| GitHub | REST API + GraphQL v4 | Pin `X-GitHub-Api-Version: 2022-11-28`; fine-grained PATs recommended |
|
|
254
274
|
| Figma | REST API v1 / Webhooks V2 | Path-based versioning (`/v1/`, `/v2/`); `files:read` scope deprecated — use granular scopes |
|
|
255
275
|
| Slack | Web API (no versioned path) | Always check `ok` field (HTTP 200 even on errors); `files.upload` deprecated — use `files.getUploadURLExternal` |
|
|
276
|
+
| Stripe | `2026-02-25.clover` | Pin with `Stripe-Version` header; amounts in smallest currency unit (cents); never log raw card data |
|
package/skills/ROADMAP.md
CHANGED
|
@@ -12,229 +12,152 @@ This skill set enables end-to-end automation and integration across the most com
|
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
|
15
|
-
##
|
|
15
|
+
## Current state (as of 2026-03-01)
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
### What's shipped
|
|
18
18
|
|
|
19
|
-
|
|
|
20
|
-
|
|
21
|
-
| **
|
|
22
|
-
| **
|
|
23
|
-
| **
|
|
24
|
-
| **
|
|
25
|
-
| **
|
|
19
|
+
| Layer | Status |
|
|
20
|
+
|-------|--------|
|
|
21
|
+
| **11 skill docs** — Monday.com, Salesforce, Jira, Dynamics 365, HubSpot, ServiceNow, Zendesk, Asana, GitHub, Figma, Slack | ✅ Live |
|
|
22
|
+
| **MCP server** — `clawskills-mcp` v0.2.3 on npm; `list_skills`, `get_skill`, `search_skills` | ✅ Live |
|
|
23
|
+
| **CI pipeline** — build + test on every push/PR via `ci.yml` | ✅ Live |
|
|
24
|
+
| **Release automation** — `release.yml` workflow_dispatch → bump, tag, npm publish via OIDC | ✅ Live |
|
|
25
|
+
| **Test suite** — Vitest unit tests + real-skills validation (required sections, ≥5 KB, all 11 tools) | ✅ Live |
|
|
26
|
+
| **Public repo readiness** — MIT license, `.gitignore`, full `package.json` metadata | ✅ Ready |
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
### Content coverage by original phase
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
| Phase | Theme | Status |
|
|
31
|
+
|-------|-------|--------|
|
|
32
|
+
| Phase 1 — Foundation (Auth + Core CRUD) | Auth flows, basic CRUD, retry patterns | ✅ Complete — all 11 tools |
|
|
33
|
+
| Phase 2 — High-Frequency Workflows | 6–12 recipes per tool | ✅ Complete — all 11 tools |
|
|
34
|
+
| Phase 3 — Event-Driven & Real-Time | Webhook setup, signature verification, event handling | ✅ Complete — all 11 tools |
|
|
35
|
+
| Phase 4 — Bulk & Advanced Operations | Batch APIs, large-volume patterns | ⚠️ Partial — documented for most tools; not all have >500-record examples |
|
|
36
|
+
| Phase 5 — Cross-Tool Orchestration | Multi-system recipes spanning 2+ tools | ❌ Not started — patterns listed in INDEX.md but no dedicated recipes |
|
|
30
37
|
|
|
31
38
|
---
|
|
32
39
|
|
|
33
|
-
##
|
|
34
|
-
|
|
35
|
-
### Phase 1 — Foundation: Auth + Core CRUD (Weeks 1–4)
|
|
40
|
+
## Next development phase
|
|
36
41
|
|
|
37
|
-
|
|
42
|
+
### Priority 1 — Content expansion: 3 new skills
|
|
38
43
|
|
|
39
|
-
|
|
40
|
-
- All 8 `skill.md` files: Authentication & Permissions section fully validated against live environments
|
|
41
|
-
- Core CRUD recipes for each tool (Create, Read by ID, Update, List/Search)
|
|
42
|
-
- Error handling playbook (auth errors, 429s, 5xx retries)
|
|
43
|
-
- Shared retry/backoff library or pattern documented
|
|
44
|
+
Each new skill follows the 11-section template, lives on a `skill/<name>` branch, and is validated by CI before merge.
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
-
|
|
46
|
+
| Skill | Why | Key areas to cover |
|
|
47
|
+
|-------|-----|--------------------|
|
|
48
|
+
| **Stripe** | Most common payments API; revenue-critical workflows missing from current library | Webhooks (event types + signature), Checkout Sessions, subscriptions, refunds, idempotency keys, Connect (multi-account) |
|
|
49
|
+
| **Notion** | Knowledge base + lightweight PM; high demand for doc/wiki automation | Pages API, Databases + filters + sorts, Blocks API (rich text), Search, cursor pagination |
|
|
50
|
+
| **Linear** | Fast-growing engineering tool, increasingly replacing Jira at startups | Issues, Projects, Teams, Cycles, GraphQL API, webhooks, OAuth + PAT auth |
|
|
48
51
|
|
|
49
|
-
**
|
|
50
|
-
- [ ] Each tool: auth flow documented end-to-end with working curl example
|
|
51
|
-
- [ ] Each tool: create + read + update recipe tested against sandbox/dev environment
|
|
52
|
-
- [ ] 429 rate-limit retry returns success within 3 attempts for all tools
|
|
53
|
-
- [ ] No credentials stored in code or logs
|
|
52
|
+
**Order:** Stripe → Notion → Linear (payments coverage is the biggest gap).
|
|
54
53
|
|
|
55
|
-
**
|
|
54
|
+
**Acceptance criteria for each:**
|
|
55
|
+
- [ ] All 11 required sections present (CI enforces)
|
|
56
|
+
- [ ] `Last validated:` date in header
|
|
57
|
+
- [ ] Auth section has working `curl` example tested against sandbox/dev environment
|
|
58
|
+
- [ ] At least 8 recipes with request/response examples
|
|
59
|
+
- [ ] Rate limits verified against official docs
|
|
60
|
+
- [ ] INDEX.md and README.md updated
|
|
56
61
|
|
|
57
62
|
---
|
|
58
63
|
|
|
59
|
-
###
|
|
64
|
+
### Priority 2 — Freshness CI pipeline (the moat)
|
|
60
65
|
|
|
61
|
-
|
|
66
|
+
A weekly automated check that skill docs aren't stale and that key API patterns still work.
|
|
62
67
|
|
|
63
|
-
**
|
|
64
|
-
-
|
|
65
|
-
-
|
|
66
|
-
-
|
|
67
|
-
-
|
|
68
|
-
- HubSpot: Contact/Company sync, Deal stage update, Engagement logging (call/email/note)
|
|
69
|
-
- ServiceNow: Incident create from alert, state transition + work notes, assignment group routing
|
|
70
|
-
- Zendesk: Ticket creation, status/priority update, internal note vs public reply
|
|
71
|
-
- Asana: Task creation in project/section, status update, comment/story append
|
|
68
|
+
**Phase A — Staleness detection (low-effort, high-value):**
|
|
69
|
+
- Parse `Last validated:` date from each `skill.md` header
|
|
70
|
+
- Flag any doc older than 90 days in CI output
|
|
71
|
+
- Open a GitHub issue automatically when a doc exceeds the threshold
|
|
72
|
+
- Deliverable: `.github/workflows/freshness.yml` (weekly cron, `0 9 * * 1`) + `scripts/check-freshness.ts`
|
|
72
73
|
|
|
73
|
-
**
|
|
74
|
-
-
|
|
75
|
-
-
|
|
74
|
+
**Phase B — Live smoke tests (medium-effort, highest-value — the real moat):**
|
|
75
|
+
- For each tool, maintain a minimal test that hits: auth endpoint + one read
|
|
76
|
+
- Run weekly against sandbox environments using stored credentials (GitHub Secrets)
|
|
77
|
+
- Failure creates a doc-review issue with the tool name and failing assertion
|
|
78
|
+
- Candidates to start with: Jira (`/rest/api/3/myself`), GitHub (`/user`), HubSpot (`/crm/v3/objects/contacts?limit=1`)
|
|
76
79
|
|
|
77
80
|
**Acceptance criteria:**
|
|
78
|
-
- [ ]
|
|
79
|
-
- [ ]
|
|
80
|
-
- [ ]
|
|
81
|
-
- [ ] Dedup logic documented and tested (same input twice = no duplicate record)
|
|
82
|
-
|
|
83
|
-
**Definition of done:** Each recipe can be executed end-to-end by an automation builder without consulting external docs.
|
|
81
|
+
- [ ] Weekly cron runs without manual intervention
|
|
82
|
+
- [ ] A PR that backdates a `Last validated:` date beyond 90 days fails the staleness check
|
|
83
|
+
- [ ] Smoke test failure creates an issue and does not block `main` CI (it's a separate workflow)
|
|
84
84
|
|
|
85
85
|
---
|
|
86
86
|
|
|
87
|
-
###
|
|
87
|
+
### Priority 3 — Distribution
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
- HubSpot: Webhook subscriptions, event types (contact.creation, deal.propertyChange), signature verification
|
|
97
|
-
- ServiceNow: Outbound REST + Business Rules as webhook equivalent; Flow Designer basics
|
|
98
|
-
- Zendesk: Webhook + Trigger setup, event payload structure, signature validation (X-Zendesk-Webhook-Signature)
|
|
99
|
-
- Asana: Webhook registration, X-Hook-Secret handshake, event filtering
|
|
100
|
-
|
|
101
|
-
**Cross-tool dependencies:**
|
|
102
|
-
- Phase 2 update recipes required (webhooks trigger updates cross-system)
|
|
103
|
-
- Shared webhook receiver pattern: signature verification, deduplication by event ID, idempotent handler
|
|
104
|
-
|
|
105
|
-
**Acceptance criteria:**
|
|
106
|
-
- [ ] Each tool: webhook registered, test event fired, handler receives and validates payload
|
|
107
|
-
- [ ] Signature verification implemented and tested (reject tampered payload)
|
|
108
|
-
- [ ] Idempotent event handler: same event delivered twice = processed once
|
|
109
|
-
- [ ] Retry/dead-letter behavior documented per tool
|
|
110
|
-
|
|
111
|
-
**Definition of done:** End-to-end event flow demonstrated: source system change → webhook delivery → target system update, with no polling.
|
|
89
|
+
| Channel | Status | Next action |
|
|
90
|
+
|---------|--------|-------------|
|
|
91
|
+
| GitHub (public) | Ready to flip | Make repo public |
|
|
92
|
+
| npm (`clawskills-mcp`) | ✅ Live — v0.2.3 | Maintain via `release.yml` |
|
|
93
|
+
| MCP registry listings | Not submitted | Submit to glama.ai, mcp.so, smithery.ai after public launch |
|
|
94
|
+
| Docs website | Planned | Mintlify — fastest path to SEO-friendly browsable docs |
|
|
95
|
+
| Lane A vs B | TBD | Revisit after first traction signals from public launch |
|
|
112
96
|
|
|
113
97
|
---
|
|
114
98
|
|
|
115
|
-
###
|
|
116
|
-
|
|
117
|
-
**Theme:** Scale up. Handle large datasets, bulk operations, and complex cross-tool orchestration.
|
|
99
|
+
### Priority 4 — Complete Phase 4 & 5 gaps
|
|
118
100
|
|
|
119
|
-
|
|
120
|
-
- Salesforce: Bulk API v2 (CSV ingest), SOQL aggregate queries, Composite API for multi-step
|
|
121
|
-
- HubSpot: Batch create/update/read (up to 100 per batch), Import API for CSV
|
|
122
|
-
- Jira: Bulk label/component updates, sprint management, custom field batch updates
|
|
123
|
-
- ServiceNow: Import Sets with transform maps, batch table API calls
|
|
124
|
-
- Zendesk: Bulk ticket update (update_many), Incremental Export API for full sync
|
|
125
|
-
- Monday.com: Multiple mutations in single request, board duplication, subitems
|
|
126
|
-
- Dynamics 365: $batch endpoint, FetchXML for complex aggregations
|
|
127
|
-
- Asana: Parallel task creation patterns (respecting rate limits), Portfolio batch reads
|
|
101
|
+
These are lower priority than new skills but worth finishing:
|
|
128
102
|
|
|
129
|
-
**
|
|
130
|
-
-
|
|
131
|
-
-
|
|
103
|
+
**Phase 4 gaps (Bulk operations):**
|
|
104
|
+
- Salesforce Bulk API v2: end-to-end CSV ingest tested at ≥500 rows with error-row reporting
|
|
105
|
+
- HubSpot batch create/update: tested at max batch size (100 records), partial-success handling
|
|
106
|
+
- Zendesk Incremental Export API: full-sync recipe with cursor continuation
|
|
132
107
|
|
|
133
|
-
**
|
|
134
|
-
-
|
|
135
|
-
-
|
|
136
|
-
-
|
|
137
|
-
- [ ] Error partial-success handling documented (some rows fail, some succeed)
|
|
108
|
+
**Phase 5 (Cross-tool orchestration) — first 3 recipes:**
|
|
109
|
+
- Zendesk Ticket → Jira Bug (bidirectional status sync)
|
|
110
|
+
- HubSpot Deal Won → Asana project creation (onboarding trigger)
|
|
111
|
+
- Salesforce Lead → HubSpot Contact sync (dedup on email)
|
|
138
112
|
|
|
139
|
-
|
|
113
|
+
Each recipe lives as a `## Cross-tool recipe` appendix section in the relevant primary skill doc (e.g., the Zendesk→Jira recipe goes in both `zendesk/skill.md` and `jira/skill.md`).
|
|
140
114
|
|
|
141
115
|
---
|
|
142
116
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
**Theme:** Connect the systems. Build the integration recipes that span multiple tools, and prepare data flows for AI enrichment.
|
|
117
|
+
## Prioritization framework
|
|
146
118
|
|
|
147
|
-
|
|
148
|
-
- Cross-tool recipes (documented separately or as appendices in relevant skill.md files):
|
|
149
|
-
- Salesforce Lead → HubSpot Contact sync
|
|
150
|
-
- Zendesk Ticket → Jira Bug creation (bidirectional status)
|
|
151
|
-
- ServiceNow Incident → Monday.com item (ops escalation board)
|
|
152
|
-
- HubSpot Deal Won → Asana project creation (onboarding workflow)
|
|
153
|
-
- Dynamics 365 Opportunity → Jira Epic (delivery handoff)
|
|
154
|
-
- Enrichment patterns: append AI-generated summaries to ticket/task descriptions
|
|
155
|
-
- Reporting aggregation: pull data from multiple tools into unified payload
|
|
156
|
-
|
|
157
|
-
**Cross-tool dependencies:**
|
|
158
|
-
- All Phase 1–4 skills stable
|
|
159
|
-
- Canonical data model (shared field mapping table) agreed across tools
|
|
160
|
-
|
|
161
|
-
**Acceptance criteria:**
|
|
162
|
-
- [ ] Each cross-tool recipe: end-to-end tested in staging environments
|
|
163
|
-
- [ ] Canonical IDs (external_id / reference fields) used to link records across systems
|
|
164
|
-
- [ ] No data loss on failure (dead-letter queue or retry log)
|
|
165
|
-
- [ ] Latency < 30s for real-time cross-tool sync
|
|
166
|
-
|
|
167
|
-
**Definition of done:** A single external event (e.g., new support ticket) can automatically create linked records in 2+ systems with correct field mapping, documented in a runbook.
|
|
119
|
+
Each skill/recipe is scored on five dimensions (1–5 each):
|
|
168
120
|
|
|
169
|
-
|
|
121
|
+
| Dimension | Description |
|
|
122
|
+
|-----------|-------------|
|
|
123
|
+
| **Impact** | Business value if automated (revenue, hours saved, error reduction) |
|
|
124
|
+
| **Frequency** | How often the workflow runs daily/weekly |
|
|
125
|
+
| **Complexity** | Inverse score — simpler = higher priority |
|
|
126
|
+
| **Risk** | Inverse score — lower risk of data loss = higher priority |
|
|
127
|
+
| **Dependencies** | Whether other workflows depend on this one being stable |
|
|
170
128
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
| Tool | Phase | Deliverable | Notes | Dependencies |
|
|
174
|
-
|------|-------|-------------|-------|--------------|
|
|
175
|
-
| All tools | 1 | Auth + Core CRUD skill sections | Sandbox environments required | None |
|
|
176
|
-
| Monday.com | 1 | Auth (token + OAuth), Board/Item CRUD | GraphQL API — different pattern from REST tools | None |
|
|
177
|
-
| Monday.com | 2 | Intake workflow, status sync, approval tracking | Column value JSON shapes per type | Phase 1 |
|
|
178
|
-
| Monday.com | 3 | Webhook setup + column-change events | Webhooks require HTTPS endpoint | Phase 1 |
|
|
179
|
-
| Monday.com | 4 | Multi-mutation requests, subitems | Complexity points budget management | Phase 2 |
|
|
180
|
-
| Salesforce | 1 | Connected App, OAuth JWT, REST CRUD | Sandbox org required | None |
|
|
181
|
-
| Salesforce | 2 | Lead capture, Opportunity update, Task logging | Validation rules awareness | Phase 1 |
|
|
182
|
-
| Salesforce | 3 | Platform Events / CDC / Outbound Messaging | Streaming API is separate product | Phase 2 |
|
|
183
|
-
| Salesforce | 4 | Bulk API v2, Composite API, SOQL aggregates | Bulk API best for >1000 rows | Phase 2 |
|
|
184
|
-
| Salesforce | 5 | Lead → HubSpot sync, Opp → Jira Epic | Canonical field mapping required | Phase 4 |
|
|
185
|
-
| Jira | 1 | API Token auth, Issue CRUD | Cloud vs Server distinction | None |
|
|
186
|
-
| Jira | 2 | JQL search, workflow transitions, triage | ADF for rich text comments | Phase 1 |
|
|
187
|
-
| Jira | 3 | Webhook registration + issue-updated events | Webhook URL must be publicly reachable | Phase 2 |
|
|
188
|
-
| Jira | 4 | Bulk label/component updates, sprint ops | No native bulk create — workarounds needed | Phase 2 |
|
|
189
|
-
| Jira | 5 | Zendesk Ticket → Jira Bug (bidirectional) | Status mapping table required | Phase 4 |
|
|
190
|
-
| Dynamics 365 | 1 | Azure AD app registration, Web API CRUD | Azure tenant required; security roles | None |
|
|
191
|
-
| Dynamics 365 | 2 | Account/Contact upsert, Opportunity update, Activity log | Solution prefix awareness | Phase 1 |
|
|
192
|
-
| Dynamics 365 | 3 | Plugin/webhook setup, OData change notifications | Plugin Registration Tool needed | Phase 2 |
|
|
193
|
-
| Dynamics 365 | 4 | $batch endpoint, FetchXML complex queries | FetchXML for anything OData can't express | Phase 2 |
|
|
194
|
-
| Dynamics 365 | 5 | Opportunity → Jira Epic handoff | Canonical ID linking | Phase 4 |
|
|
195
|
-
| HubSpot | 1 | Private App token, OAuth, CRM Object CRUD | Scopes per object type | None |
|
|
196
|
-
| HubSpot | 2 | Contact/Company sync, Deal updates, Engagement logging | Properties API for custom fields | Phase 1 |
|
|
197
|
-
| HubSpot | 3 | Webhook subscriptions + event handling | App registration required for webhooks | Phase 2 |
|
|
198
|
-
| HubSpot | 4 | Batch APIs, Import API for CSV | 100-record batch limit | Phase 2 |
|
|
199
|
-
| HubSpot | 5 | Salesforce Lead → HubSpot Contact sync | Dedup on email field | Phase 4 |
|
|
200
|
-
| ServiceNow | 1 | Basic auth + OAuth, Table API CRUD | Dev instance recommended | None |
|
|
201
|
-
| ServiceNow | 2 | Incident create/update, work notes, assignment | State values are integers — verify per instance | Phase 1 |
|
|
202
|
-
| ServiceNow | 3 | Outbound REST + Business Rule webhooks | Flow Designer alternative | Phase 2 |
|
|
203
|
-
| ServiceNow | 4 | Import Sets + transform maps, batch GETs | Transform map must be configured in UI | Phase 2 |
|
|
204
|
-
| ServiceNow | 5 | Incident → Monday.com escalation board | State-to-status mapping required | Phase 4 |
|
|
205
|
-
| Zendesk | 1 | API Token, OAuth, Ticket CRUD | Subdomain required for all calls | None |
|
|
206
|
-
| Zendesk | 2 | Ticket create/update, note vs reply, bulk update | Status machine strictly enforced | Phase 1 |
|
|
207
|
-
| Zendesk | 3 | Webhook + Trigger setup, signature verification | New webhook API vs deprecated HTTP targets | Phase 2 |
|
|
208
|
-
| Zendesk | 4 | Bulk update_many, Incremental Export API | Incremental API requires Enterprise or above | Phase 2 |
|
|
209
|
-
| Zendesk | 5 | Zendesk Ticket → Jira Bug (bidirectional) | Requires external_id / tag mapping | Phase 4 |
|
|
210
|
-
| Asana | 1 | PAT + OAuth, Task/Project CRUD | opt_fields required for all reads | None |
|
|
211
|
-
| Asana | 2 | Task creation in section, status update, story/comment | Memberships array for project placement | Phase 1 |
|
|
212
|
-
| Asana | 3 | Webhook registration + X-Hook-Secret handshake | Webhook events are coarse-grained | Phase 2 |
|
|
213
|
-
| Asana | 4 | Parallel task creation, Portfolio reads | No native bulk — rate limit math required | Phase 2 |
|
|
214
|
-
| Asana | 5 | HubSpot Deal Won → Asana project (onboarding) | Template project duplication | Phase 4 |
|
|
129
|
+
**Priority score = (Impact × 2) + (Frequency × 1.5) + Complexity + Risk + Dependencies**
|
|
215
130
|
|
|
216
131
|
---
|
|
217
132
|
|
|
218
|
-
## Quality
|
|
133
|
+
## Quality bar & governance
|
|
219
134
|
|
|
220
135
|
### Keeping docs accurate
|
|
221
136
|
|
|
222
|
-
- **Version pinning:** Each skill.md
|
|
223
|
-
- **Quarterly review cycle:**
|
|
224
|
-
- **Changelog
|
|
137
|
+
- **Version pinning:** Each skill.md notes the API version it was validated against. Update the version header when re-validating.
|
|
138
|
+
- **Quarterly review cycle:** Docs older than 90 days are flagged by the freshness pipeline for re-review.
|
|
139
|
+
- **Changelog notes:** Major API changes (new auth flows, deprecated endpoints, rate limit changes) noted inline with `> ⚠️ Changed [date]:` callouts.
|
|
225
140
|
|
|
226
141
|
### Validation approach
|
|
227
142
|
|
|
228
|
-
- **
|
|
229
|
-
- **
|
|
143
|
+
- **CI test suite:** Real-skills tests in `mcp-server/src/index.test.ts` verify all skills load, have required sections, and are ≥5 KB.
|
|
144
|
+
- **Freshness pipeline:** Weekly cron checks `Last validated:` dates (see Priority 2 above).
|
|
145
|
+
- **Live smoke tests:** Optional phase B — weekly auth + read tests against sandboxes.
|
|
146
|
+
- **Breaking change monitoring:** Subscribe to each vendor's developer changelog:
|
|
230
147
|
- Monday: https://developer.monday.com/changelog
|
|
231
148
|
- Salesforce: https://help.salesforce.com/s/articleView?id=release-notes.salesforce_release_notes.htm
|
|
232
149
|
- Jira: https://developer.atlassian.com/cloud/jira/platform/changelog/
|
|
233
150
|
- Dynamics 365: https://learn.microsoft.com/en-us/dynamics365/release-plans/
|
|
234
151
|
- HubSpot: https://developers.hubspot.com/changelog
|
|
235
|
-
- ServiceNow: https://developer.servicenow.com/dev.do
|
|
152
|
+
- ServiceNow: https://developer.servicenow.com/dev.do
|
|
236
153
|
- Zendesk: https://developer.zendesk.com/api-reference/ticketing/introduction/#api-changelog
|
|
237
154
|
- Asana: https://developers.asana.com/docs/changelog
|
|
155
|
+
- GitHub: https://github.blog/changelog/
|
|
156
|
+
- Figma: https://www.figma.com/developers/api#changelog
|
|
157
|
+
- Slack: https://api.slack.com/changelog
|
|
158
|
+
- Stripe: https://stripe.com/docs/changelog
|
|
159
|
+
- Notion: https://developers.notion.com/page/changelog
|
|
160
|
+
- Linear: https://linear.app/changelog
|
|
238
161
|
|
|
239
162
|
### Handling deprecations
|
|
240
163
|
|
|
@@ -245,29 +168,31 @@ Phases are ordered by descending average priority score within a theme.
|
|
|
245
168
|
|
|
246
169
|
### Contribution process
|
|
247
170
|
|
|
248
|
-
- PRs to skill.md files require: (a) reference to official doc, (b) tested curl/pseudocode example, (c) updated `Last validated:` date.
|
|
249
|
-
-
|
|
171
|
+
- PRs to `skill.md` files require: (a) reference to official doc, (b) tested curl/pseudocode example, (c) updated `Last validated:` date.
|
|
172
|
+
- Branch convention: `skill/<toolname>` → PR → merge to main.
|
|
173
|
+
- CI runs `npm test` on every PR — validates skill loads and has all required sections.
|
|
174
|
+
- Releases: merge to main, then GitHub Actions → **Release** → Run workflow → pick `patch / minor / major`.
|
|
250
175
|
|
|
251
176
|
---
|
|
252
177
|
|
|
253
|
-
## Backlog (
|
|
254
|
-
|
|
255
|
-
These items are out of scope for Phases 1–5 but worth tracking for future investment:
|
|
178
|
+
## Backlog (tracked, not yet scheduled)
|
|
256
179
|
|
|
257
180
|
| Item | Tool(s) | Value | Complexity |
|
|
258
181
|
|------|---------|-------|------------|
|
|
259
|
-
| AI-powered ticket summarization (append
|
|
182
|
+
| AI-powered ticket summarization (append LLM summary to description) | Zendesk, Jira, ServiceNow | High | Medium |
|
|
260
183
|
| Automated lead scoring sync | Salesforce, HubSpot | High | Medium |
|
|
261
184
|
| Revenue analytics aggregation (pull pipeline across CRMs) | Salesforce, HubSpot, Dynamics 365 | High | High |
|
|
262
|
-
|
|
|
185
|
+
| Token rotation automation (refresh before expiry) | All OAuth tools | High | Low |
|
|
186
|
+
| Twilio skill (SMS/voice notifications) | Twilio | High | Medium |
|
|
187
|
+
| Intercom skill (in-app messaging, user segments) | Intercom | Medium | Medium |
|
|
188
|
+
| Okta/Auth0 skill (identity + SSO) | Okta | Medium | Medium |
|
|
189
|
+
| PagerDuty skill (on-call, incident escalation) | PagerDuty | Medium | Low |
|
|
190
|
+
| Docs website (Mintlify/Docusaurus) | — | High | Medium |
|
|
191
|
+
| VS Code extension (inject skill doc into editor context) | — | Medium | High |
|
|
192
|
+
| REST API / hosted endpoint for skill doc retrieval | — | Medium | High |
|
|
193
|
+
| Admin automation: user provisioning/deprovisioning | All tools | Medium | Medium |
|
|
263
194
|
| Compliance export (GDPR data subject requests) | Zendesk, Salesforce, HubSpot | Medium | High |
|
|
264
|
-
| Monday → Asana project migration utility | Monday, Asana | Low | Medium |
|
|
265
|
-
| Jira custom field schema introspection | Jira | Medium | Low |
|
|
266
|
-
| Dynamics 365 Power Automate connector alternatives | Dynamics 365 | Medium | Low |
|
|
267
|
-
| ServiceNow CMDB auto-discovery integration | ServiceNow | High | High |
|
|
268
195
|
| Zendesk → Salesforce Case sync (bidirectional) | Zendesk, Salesforce | High | High |
|
|
269
|
-
|
|
|
270
|
-
| Monday dashboards → BI tool (Looker/Tableau) export | Monday | Medium | Medium |
|
|
196
|
+
| ServiceNow CMDB auto-discovery integration | ServiceNow | High | High |
|
|
271
197
|
| Multi-workspace / multi-org management recipes | All tools | Medium | High |
|
|
272
|
-
| Token rotation automation (refresh before expiry) | All OAuth tools | High | Low |
|
|
273
198
|
| Audit log export to SIEM | Salesforce, Zendesk, ServiceNow | Medium | Medium |
|
|
@@ -0,0 +1,823 @@
|
|
|
1
|
+
# Stripe Skill
|
|
2
|
+
|
|
3
|
+
> **Last validated:** 2026-03-01 | **API version:** `2026-02-25.clover` (latest stable)
|
|
4
|
+
> **REST base URL:** `https://api.stripe.com/v1/`
|
|
5
|
+
> **Assumed product:** Stripe Payments + Billing. Stripe Connect (multi-account platform) is covered in the Auth and Recipes sections.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## What this skill enables
|
|
10
|
+
|
|
11
|
+
- Collect one-time payments and save cards for future use without handling raw card data (PCI-compliant via Stripe.js / Payment Element).
|
|
12
|
+
- Create and manage recurring subscriptions with automatic billing, dunning, and trial periods.
|
|
13
|
+
- Issue full or partial refunds and query payment history programmatically.
|
|
14
|
+
- Build hosted checkout flows (Checkout Sessions) that Stripe renders — no front-end payment UI required.
|
|
15
|
+
- Handle post-payment events reliably via webhooks: fulfil orders, provision access, update CRM records.
|
|
16
|
+
- Operate as a platform (Stripe Connect): onboard connected accounts, route payments, split revenue.
|
|
17
|
+
- Test the full payment lifecycle with Stripe's sandbox — no real money, no bank involvement.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Best-fit use cases
|
|
22
|
+
|
|
23
|
+
| Use case | Why it matters | Primary objects | Typical trigger | Success criteria |
|
|
24
|
+
|----------|---------------|-----------------|-----------------|-----------------|
|
|
25
|
+
| One-time purchase | Charge a customer once for a product or service | PaymentIntent, PaymentMethod, Customer | User submits payment form | `payment_intent.succeeded` webhook received; order fulfilled |
|
|
26
|
+
| Save card for future billing | Charge a returning customer without re-entering card | PaymentMethod, Customer | User checks "save card" | PaymentMethod attached to Customer; future charges succeed |
|
|
27
|
+
| Recurring subscription | Monthly/annual SaaS or membership billing | Subscription, Price, Invoice, Customer | User selects plan | Subscription `active`; first invoice `paid` |
|
|
28
|
+
| Hosted checkout page | Delegate payment UI to Stripe | Checkout Session | User clicks "Buy now" | `checkout.session.completed` webhook; `payment_status: paid` |
|
|
29
|
+
| Issue a refund | Reverse a charge (full or partial) | Refund, Charge | Support request or automated policy | Refund `succeeded`; `charge.refunded` webhook received |
|
|
30
|
+
| Failed payment recovery | Retry a failed subscription invoice | Invoice, PaymentIntent | `invoice.payment_failed` webhook | Payment retried; subscription back to `active` |
|
|
31
|
+
| Multi-vendor marketplace | Route payments between platform and sellers | Connect Account, Transfer, Charge | Order placed on platform | Connected account receives funds; platform fee collected |
|
|
32
|
+
| Payout reporting | Pull all payouts and match to bank statement | Payout, BalanceTransaction | Finance reconciliation run | Every payout has matching BalanceTransactions totalling net amount |
|
|
33
|
+
| Metered billing | Charge based on usage at end of billing period | SubscriptionItem, UsageRecord, Invoice | Usage event from product | Invoice includes correct usage line items |
|
|
34
|
+
| Payment link (no-code) | Generate a shareable URL for one-time or recurring payment | PaymentLink | Sales or support request | Customer completes payment via link; webhook confirms |
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Key concepts & data model
|
|
39
|
+
|
|
40
|
+
### Core objects
|
|
41
|
+
|
|
42
|
+
| Object | Description | ID prefix |
|
|
43
|
+
|--------|-------------|-----------|
|
|
44
|
+
| **Customer** | A person or business entity; stores payment methods and billing info | `cus_` |
|
|
45
|
+
| **PaymentIntent** | The authoritative record of a payment attempt; tracks state through to success/failure | `pi_` |
|
|
46
|
+
| **PaymentMethod** | A reusable token representing a card, bank account, or wallet | `pm_` |
|
|
47
|
+
| **Charge** | A legacy object created when a PaymentIntent succeeds; most APIs now return the PaymentIntent directly | `ch_` |
|
|
48
|
+
| **Invoice** | A bill sent to a Customer; created automatically for subscriptions | `in_` |
|
|
49
|
+
| **Subscription** | A recurring billing agreement linking a Customer to one or more Prices | `sub_` |
|
|
50
|
+
| **Price** | Defines the billing amount, currency, and interval for a Product | `price_` |
|
|
51
|
+
| **Product** | A good or service offered; Prices are attached to Products | `prod_` |
|
|
52
|
+
| **Refund** | A reversal of a Charge, full or partial | `re_` |
|
|
53
|
+
| **Checkout Session** | A hosted payment page managed by Stripe | `cs_` |
|
|
54
|
+
| **PaymentLink** | A shareable URL backed by a Checkout Session template | `plink_` |
|
|
55
|
+
| **Transfer** | Funds moved from platform balance to a connected account | `tr_` |
|
|
56
|
+
| **Payout** | Funds sent from Stripe balance to a bank account | `po_` |
|
|
57
|
+
| **BalanceTransaction** | Every money movement in Stripe (fee, charge, refund, payout) | `txn_` |
|
|
58
|
+
| **Event** | A webhook payload describing a state change | `evt_` |
|
|
59
|
+
| **Connected Account** | An account on your platform (Stripe Connect) | `acct_` |
|
|
60
|
+
|
|
61
|
+
### PaymentIntent lifecycle
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
requires_payment_method → requires_confirmation → requires_action → processing → succeeded
|
|
65
|
+
↘ canceled
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
| Status | Meaning |
|
|
69
|
+
|--------|---------|
|
|
70
|
+
| `requires_payment_method` | No valid payment method attached yet |
|
|
71
|
+
| `requires_confirmation` | Payment method attached; awaiting explicit confirm call |
|
|
72
|
+
| `requires_action` | 3D Secure or redirect authentication needed from the customer |
|
|
73
|
+
| `processing` | Bank processing in progress (instant cards transition through this quickly) |
|
|
74
|
+
| `succeeded` | Payment captured; funds reserved or settled |
|
|
75
|
+
| `canceled` | Cancelled; a new PaymentIntent must be created |
|
|
76
|
+
|
|
77
|
+
### Subscription lifecycle
|
|
78
|
+
|
|
79
|
+
| Status | Meaning |
|
|
80
|
+
|--------|---------|
|
|
81
|
+
| `trialing` | In free trial; no payment yet |
|
|
82
|
+
| `active` | In good standing; latest invoice paid |
|
|
83
|
+
| `incomplete` | First payment not yet made; customer has 23 hours |
|
|
84
|
+
| `incomplete_expired` | Initial 23-hour window expired without payment |
|
|
85
|
+
| `past_due` | Latest renewal invoice failed; automatic retries in progress |
|
|
86
|
+
| `unpaid` | Retries exhausted; subscription remains but no more attempts |
|
|
87
|
+
| `paused` | Trial ended without a default payment method |
|
|
88
|
+
| `canceled` | Terminal; cannot be reactivated |
|
|
89
|
+
|
|
90
|
+
### Amounts and currencies
|
|
91
|
+
|
|
92
|
+
All monetary amounts are in the **smallest currency unit** — cents for USD, pence for GBP, yen for JPY (which has no subunit, so `100` = ¥100). Always store and pass amounts as integers.
|
|
93
|
+
|
|
94
|
+
### Expandable fields
|
|
95
|
+
|
|
96
|
+
Any field that returns an ID (e.g., `"customer": "cus_xxx"`) can be expanded to the full object by passing `expand[]=customer` in the request. Nest with dots: `expand[]=latest_invoice.payment_intent`.
|
|
97
|
+
|
|
98
|
+
### Metadata
|
|
99
|
+
|
|
100
|
+
Every Stripe object has a `metadata` dictionary for storing arbitrary key/value pairs (e.g., your internal IDs). Limits: 50 keys per object, key ≤ 40 chars, value ≤ 500 chars.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Authentication & permissions
|
|
105
|
+
|
|
106
|
+
### API key types
|
|
107
|
+
|
|
108
|
+
| Key type | Prefix | Use | Never in |
|
|
109
|
+
|----------|--------|-----|----------|
|
|
110
|
+
| Secret (live) | `sk_live_` | Server-side; full API access | Client-side code, logs, version control |
|
|
111
|
+
| Secret (sandbox) | `sk_test_` | Server-side; test mode only | Production environments |
|
|
112
|
+
| Publishable (live) | `pk_live_` | Browser/mobile; only creates tokens | Server-side requests |
|
|
113
|
+
| Publishable (sandbox) | `pk_test_` | Browser/mobile; test mode | Production environments |
|
|
114
|
+
| Restricted | `rk_live_` / `rk_test_` | Server-side; limited to specific resources | When full access is needed |
|
|
115
|
+
|
|
116
|
+
Create restricted keys in **Dashboard → Developers → API keys → Create restricted key**. Assign **None / Read / Write** per resource (Customers, PaymentIntents, Subscriptions, etc.).
|
|
117
|
+
|
|
118
|
+
### Authentication header
|
|
119
|
+
|
|
120
|
+
Stripe uses HTTP Basic auth with the secret key as the username and an empty password. All major HTTP clients also accept `Authorization: Bearer sk_...`:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
# Basic auth (canonical Stripe style)
|
|
124
|
+
curl https://api.stripe.com/v1/customers \
|
|
125
|
+
-u "sk_test_51Abc123...:
|
|
126
|
+
|
|
127
|
+
# Bearer token (equivalent)
|
|
128
|
+
curl https://api.stripe.com/v1/customers \
|
|
129
|
+
-H "Authorization: Bearer sk_test_51Abc123..."
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### API version pinning
|
|
133
|
+
|
|
134
|
+
Always pin the API version explicitly to avoid unexpected behaviour when Stripe releases a new default:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
curl https://api.stripe.com/v1/payment_intents \
|
|
138
|
+
-u "sk_test_...:" \
|
|
139
|
+
-H "Stripe-Version: 2026-02-25.clover" \
|
|
140
|
+
-d amount=2000 \
|
|
141
|
+
-d currency=usd
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Set a default version for your account in **Dashboard → Developers → API version**. The SDK reads your account default unless you override per-request.
|
|
145
|
+
|
|
146
|
+
### Stripe Connect — acting on behalf of a connected account
|
|
147
|
+
|
|
148
|
+
Pass `Stripe-Account: acct_xxx` to perform an API call in the context of a connected account using your platform key:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
curl https://api.stripe.com/v1/customers \
|
|
152
|
+
-u "sk_live_platform...:" \
|
|
153
|
+
-H "Stripe-Account: acct_1ABCxyzConnected"
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Idempotency keys
|
|
157
|
+
|
|
158
|
+
Use `Idempotency-Key` on every `POST` request to safely retry without double-charging:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
curl https://api.stripe.com/v1/payment_intents \
|
|
162
|
+
-u "sk_test_...:" \
|
|
163
|
+
-H "Idempotency-Key: $(uuidgen)" \
|
|
164
|
+
-d amount=2000 \
|
|
165
|
+
-d currency=usd
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
- Keys are strings up to 255 characters; UUIDs v4 are recommended.
|
|
169
|
+
- Results are cached for **24 hours**. Reusing a key after expiry generates a new request.
|
|
170
|
+
- Reusing a key with **different parameters** returns a `409 Conflict` `idempotency_error`.
|
|
171
|
+
- Only use on `POST` — `GET` and `DELETE` are inherently idempotent.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Common workflows (recipes)
|
|
176
|
+
|
|
177
|
+
### 1. Create a one-time payment (PaymentIntent flow)
|
|
178
|
+
|
|
179
|
+
**Server-side — create PaymentIntent:**
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
curl https://api.stripe.com/v1/payment_intents \
|
|
183
|
+
-u "sk_test_...:" \
|
|
184
|
+
-H "Idempotency-Key: order-42-$(date +%s)" \
|
|
185
|
+
-d amount=4999 \
|
|
186
|
+
-d currency=usd \
|
|
187
|
+
-d "automatic_payment_methods[enabled]=true" \
|
|
188
|
+
-d "metadata[order_id]=order_42"
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**Response (key fields):**
|
|
192
|
+
|
|
193
|
+
```json
|
|
194
|
+
{
|
|
195
|
+
"id": "pi_3OxAbc2eZvKYlo2C1hb8mT9k",
|
|
196
|
+
"object": "payment_intent",
|
|
197
|
+
"amount": 4999,
|
|
198
|
+
"currency": "usd",
|
|
199
|
+
"status": "requires_payment_method",
|
|
200
|
+
"client_secret": "pi_3OxAbc...hb8mT9k_secret_Xyz",
|
|
201
|
+
"metadata": { "order_id": "order_42" }
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**Client-side — confirm with Stripe.js:**
|
|
206
|
+
|
|
207
|
+
```javascript
|
|
208
|
+
const { error } = await stripe.confirmPayment({
|
|
209
|
+
elements, // Stripe Elements instance
|
|
210
|
+
clientSecret: pi.client_secret,
|
|
211
|
+
confirmParams: {
|
|
212
|
+
return_url: "https://example.com/order/complete"
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
**Fulfil on webhook `payment_intent.succeeded`** — see Recipes #7.
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
### 2. Save a card for future use
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
# Step 1 — create or retrieve the Customer
|
|
225
|
+
curl https://api.stripe.com/v1/customers \
|
|
226
|
+
-u "sk_test_...:" \
|
|
227
|
+
-d email="alice@example.com" \
|
|
228
|
+
-d "metadata[user_id]=usr_99"
|
|
229
|
+
|
|
230
|
+
# Step 2 — create a SetupIntent (confirm later via Stripe.js)
|
|
231
|
+
curl https://api.stripe.com/v1/setup_intents \
|
|
232
|
+
-u "sk_test_...:" \
|
|
233
|
+
-d customer=cus_PAbcXyz \
|
|
234
|
+
-d "automatic_payment_methods[enabled]=true"
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
After the client confirms the SetupIntent, the PaymentMethod is attached to the Customer. To charge it later:
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
curl https://api.stripe.com/v1/payment_intents \
|
|
241
|
+
-u "sk_test_...:" \
|
|
242
|
+
-d amount=2000 \
|
|
243
|
+
-d currency=usd \
|
|
244
|
+
-d customer=cus_PAbcXyz \
|
|
245
|
+
-d payment_method=pm_1ABCdefGhIjKlMnO \
|
|
246
|
+
-d confirm=true \
|
|
247
|
+
-d off_session=true
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
`off_session=true` signals that the customer is not present — Stripe will attempt to complete without 3DS if allowed by the card issuer.
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
### 3. Create a subscription
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
# Step 1 — ensure Customer has a default PaymentMethod
|
|
258
|
+
curl https://api.stripe.com/v1/customers/cus_PAbcXyz \
|
|
259
|
+
-u "sk_test_...:" \
|
|
260
|
+
-X POST \
|
|
261
|
+
-d invoice_settings[default_payment_method]=pm_1ABCdefGhIjKlMnO
|
|
262
|
+
|
|
263
|
+
# Step 2 — create the Subscription
|
|
264
|
+
curl https://api.stripe.com/v1/subscriptions \
|
|
265
|
+
-u "sk_test_...:" \
|
|
266
|
+
-d customer=cus_PAbcXyz \
|
|
267
|
+
-d "items[0][price]=price_1ABCdefGhIjKlMnO" \
|
|
268
|
+
-d payment_behavior=default_incomplete \
|
|
269
|
+
-d "expand[]=latest_invoice.payment_intent"
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
`payment_behavior=default_incomplete` starts the subscription in `incomplete` status — confirm payment client-side using the `latest_invoice.payment_intent.client_secret` before it expires (23 hours).
|
|
273
|
+
|
|
274
|
+
**Response (key fields):**
|
|
275
|
+
|
|
276
|
+
```json
|
|
277
|
+
{
|
|
278
|
+
"id": "sub_1ABCdef...",
|
|
279
|
+
"status": "incomplete",
|
|
280
|
+
"current_period_end": 1740000000,
|
|
281
|
+
"latest_invoice": {
|
|
282
|
+
"id": "in_1ABCdef...",
|
|
283
|
+
"payment_intent": {
|
|
284
|
+
"id": "pi_1ABCdef...",
|
|
285
|
+
"client_secret": "pi_...secret_..."
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
### 4. Create a hosted Checkout Session
|
|
294
|
+
|
|
295
|
+
No front-end payment form — Stripe hosts and renders it:
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
curl https://api.stripe.com/v1/checkout/sessions \
|
|
299
|
+
-u "sk_test_...:" \
|
|
300
|
+
-d mode=payment \
|
|
301
|
+
-d "line_items[0][price]=price_1ABCdefGhIjKlMnO" \
|
|
302
|
+
-d "line_items[0][quantity]=1" \
|
|
303
|
+
-d success_url="https://example.com/success?session_id={CHECKOUT_SESSION_ID}" \
|
|
304
|
+
-d cancel_url="https://example.com/cancel" \
|
|
305
|
+
-d customer_email="alice@example.com"
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Redirect the browser to `session.url`. Stripe handles the payment UI and redirects to `success_url` on completion. Confirm fulfilment via `checkout.session.completed` webhook — do not trust the redirect URL alone.
|
|
309
|
+
|
|
310
|
+
For a subscription Checkout Session, change `mode=subscription` and pass a recurring Price.
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
### 5. Issue a full refund
|
|
315
|
+
|
|
316
|
+
```bash
|
|
317
|
+
curl https://api.stripe.com/v1/refunds \
|
|
318
|
+
-u "sk_test_...:" \
|
|
319
|
+
-H "Idempotency-Key: refund-ch_1ABC-$(date +%s)" \
|
|
320
|
+
-d charge=ch_1ABCdefGhIjKlMnO
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
**Partial refund** — add `amount` in cents:
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
curl https://api.stripe.com/v1/refunds \
|
|
327
|
+
-u "sk_test_...:" \
|
|
328
|
+
-d charge=ch_1ABCdefGhIjKlMnO \
|
|
329
|
+
-d amount=500
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
**Response (key fields):**
|
|
333
|
+
|
|
334
|
+
```json
|
|
335
|
+
{
|
|
336
|
+
"id": "re_1ABCdef...",
|
|
337
|
+
"object": "refund",
|
|
338
|
+
"amount": 500,
|
|
339
|
+
"status": "succeeded",
|
|
340
|
+
"charge": "ch_1ABCdef..."
|
|
341
|
+
}
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
Watch for `charge.refunded` webhook for async confirmation.
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
### 6. Handle a failed subscription payment
|
|
349
|
+
|
|
350
|
+
```python
|
|
351
|
+
import stripe
|
|
352
|
+
|
|
353
|
+
stripe.api_key = "sk_live_..."
|
|
354
|
+
|
|
355
|
+
# Called from your webhook handler when invoice.payment_failed fires
|
|
356
|
+
def handle_payment_failed(invoice_id: str) -> None:
|
|
357
|
+
invoice = stripe.Invoice.retrieve(invoice_id, expand=["subscription"])
|
|
358
|
+
sub = invoice.subscription
|
|
359
|
+
|
|
360
|
+
if sub.status == "past_due":
|
|
361
|
+
# Notify the customer to update their payment method
|
|
362
|
+
send_dunning_email(sub.customer, invoice.hosted_invoice_url)
|
|
363
|
+
|
|
364
|
+
# Stripe will automatically retry according to your retry schedule
|
|
365
|
+
# (Dashboard → Settings → Billing → Retry schedule)
|
|
366
|
+
# To trigger an immediate retry:
|
|
367
|
+
# stripe.Invoice.pay(invoice_id)
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
When the customer updates their payment method:
|
|
371
|
+
|
|
372
|
+
```bash
|
|
373
|
+
# Attach the new PaymentMethod to the Customer
|
|
374
|
+
curl https://api.stripe.com/v1/payment_methods/pm_NEW.../attach \
|
|
375
|
+
-u "sk_live_...:" \
|
|
376
|
+
-d customer=cus_PAbcXyz
|
|
377
|
+
|
|
378
|
+
# Attempt to pay the open invoice immediately
|
|
379
|
+
curl https://api.stripe.com/v1/invoices/in_OPEN.../pay \
|
|
380
|
+
-u "sk_live_...:" \
|
|
381
|
+
-X POST
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
---
|
|
385
|
+
|
|
386
|
+
### 7. Webhook receiver — verify and dispatch
|
|
387
|
+
|
|
388
|
+
```python
|
|
389
|
+
import stripe
|
|
390
|
+
from flask import Flask, request, jsonify
|
|
391
|
+
|
|
392
|
+
app = Flask(__name__)
|
|
393
|
+
stripe.api_key = "sk_live_..."
|
|
394
|
+
WEBHOOK_SECRET = "whsec_..."
|
|
395
|
+
|
|
396
|
+
@app.post("/stripe/webhook")
|
|
397
|
+
def webhook():
|
|
398
|
+
sig = request.headers.get("Stripe-Signature")
|
|
399
|
+
try:
|
|
400
|
+
event = stripe.Webhook.construct_event(
|
|
401
|
+
payload=request.data, # raw bytes — do NOT parse JSON first
|
|
402
|
+
sig_header=sig,
|
|
403
|
+
secret=WEBHOOK_SECRET,
|
|
404
|
+
)
|
|
405
|
+
except stripe.error.SignatureVerificationError:
|
|
406
|
+
return jsonify(error="invalid signature"), 400
|
|
407
|
+
|
|
408
|
+
match event["type"]:
|
|
409
|
+
case "payment_intent.succeeded":
|
|
410
|
+
handle_payment_succeeded(event["data"]["object"])
|
|
411
|
+
case "invoice.paid":
|
|
412
|
+
handle_invoice_paid(event["data"]["object"])
|
|
413
|
+
case "customer.subscription.deleted":
|
|
414
|
+
handle_subscription_cancelled(event["data"]["object"])
|
|
415
|
+
case "checkout.session.completed":
|
|
416
|
+
handle_checkout_complete(event["data"]["object"])
|
|
417
|
+
|
|
418
|
+
# Must return 2xx within ~30 seconds or Stripe retries
|
|
419
|
+
return jsonify(ok=True), 200
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
**Critical rules:**
|
|
423
|
+
- Always verify the `Stripe-Signature` header before processing.
|
|
424
|
+
- Read raw request body — any JSON parsing before `construct_event` may corrupt the signature.
|
|
425
|
+
- Return `200` before triggering long-running work (enqueue a job instead).
|
|
426
|
+
- Make handlers **idempotent** — Stripe may deliver the same event more than once. Use `event.id` to deduplicate.
|
|
427
|
+
|
|
428
|
+
---
|
|
429
|
+
|
|
430
|
+
### 8. Stripe Connect — charge on a connected account (destination charge)
|
|
431
|
+
|
|
432
|
+
```bash
|
|
433
|
+
# Create a PaymentIntent where funds go to the connected account
|
|
434
|
+
# Platform retains application_fee_amount
|
|
435
|
+
curl https://api.stripe.com/v1/payment_intents \
|
|
436
|
+
-u "sk_live_platform...:" \
|
|
437
|
+
-d amount=5000 \
|
|
438
|
+
-d currency=usd \
|
|
439
|
+
-d "automatic_payment_methods[enabled]=true" \
|
|
440
|
+
-d transfer_data[destination]=acct_1ConnectedAcct \
|
|
441
|
+
-d application_fee_amount=500
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
**Direct charge** (create on the connected account itself):
|
|
445
|
+
|
|
446
|
+
```bash
|
|
447
|
+
curl https://api.stripe.com/v1/payment_intents \
|
|
448
|
+
-u "sk_live_platform...:" \
|
|
449
|
+
-H "Stripe-Account: acct_1ConnectedAcct" \
|
|
450
|
+
-d amount=5000 \
|
|
451
|
+
-d currency=usd \
|
|
452
|
+
-d "automatic_payment_methods[enabled]=true"
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
**Create a connected account (Express onboarding):**
|
|
456
|
+
|
|
457
|
+
```bash
|
|
458
|
+
# Step 1 — create the account
|
|
459
|
+
curl https://api.stripe.com/v1/accounts \
|
|
460
|
+
-u "sk_live_platform...:" \
|
|
461
|
+
-d type=express \
|
|
462
|
+
-d country=US \
|
|
463
|
+
-d email="seller@example.com"
|
|
464
|
+
# → { "id": "acct_1ABCdef...", ... }
|
|
465
|
+
|
|
466
|
+
# Step 2 — generate onboarding link
|
|
467
|
+
curl https://api.stripe.com/v1/account_links \
|
|
468
|
+
-u "sk_live_platform...:" \
|
|
469
|
+
-d account=acct_1ABCdef \
|
|
470
|
+
-d "refresh_url=https://example.com/reauth" \
|
|
471
|
+
-d "return_url=https://example.com/onboarded" \
|
|
472
|
+
-d type=account_onboarding
|
|
473
|
+
# → { "url": "https://connect.stripe.com/setup/e/..." }
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
---
|
|
477
|
+
|
|
478
|
+
### 9. Retrieve customer payment history (list charges)
|
|
479
|
+
|
|
480
|
+
```python
|
|
481
|
+
import stripe
|
|
482
|
+
|
|
483
|
+
stripe.api_key = "sk_live_..."
|
|
484
|
+
|
|
485
|
+
def get_customer_charges(customer_id: str, limit: int = 100) -> list:
|
|
486
|
+
charges = []
|
|
487
|
+
params = {
|
|
488
|
+
"customer": customer_id,
|
|
489
|
+
"limit": 100,
|
|
490
|
+
}
|
|
491
|
+
while True:
|
|
492
|
+
page = stripe.Charge.list(**params)
|
|
493
|
+
charges.extend(page.data)
|
|
494
|
+
if not page.has_more:
|
|
495
|
+
break
|
|
496
|
+
params["starting_after"] = page.data[-1].id
|
|
497
|
+
return charges
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
---
|
|
501
|
+
|
|
502
|
+
### 10. Cancel a subscription (at period end)
|
|
503
|
+
|
|
504
|
+
```bash
|
|
505
|
+
# Cancel at the end of the current billing period (no proration)
|
|
506
|
+
curl https://api.stripe.com/v1/subscriptions/sub_1ABCdef... \
|
|
507
|
+
-u "sk_live_...:" \
|
|
508
|
+
-X POST \
|
|
509
|
+
-d cancel_at_period_end=true
|
|
510
|
+
|
|
511
|
+
# Cancel immediately
|
|
512
|
+
curl https://api.stripe.com/v1/subscriptions/sub_1ABCdef... \
|
|
513
|
+
-u "sk_live_...:" \
|
|
514
|
+
-X DELETE
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
Listen for `customer.subscription.deleted` webhook to revoke access.
|
|
518
|
+
|
|
519
|
+
---
|
|
520
|
+
|
|
521
|
+
## Query patterns & filtering
|
|
522
|
+
|
|
523
|
+
### Cursor-based pagination
|
|
524
|
+
|
|
525
|
+
All list endpoints return a consistent envelope:
|
|
526
|
+
|
|
527
|
+
```json
|
|
528
|
+
{
|
|
529
|
+
"object": "list",
|
|
530
|
+
"data": [ ... ],
|
|
531
|
+
"has_more": true,
|
|
532
|
+
"url": "/v1/charges"
|
|
533
|
+
}
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
Parameters:
|
|
537
|
+
|
|
538
|
+
| Parameter | Type | Default | Max | Description |
|
|
539
|
+
|-----------|------|---------|-----|-------------|
|
|
540
|
+
| `limit` | integer | 10 | 100 | Items per page |
|
|
541
|
+
| `starting_after` | string | — | — | ID of last item on previous page (forward pagination) |
|
|
542
|
+
| `ending_before` | string | — | — | ID of first item on next page (backward pagination) |
|
|
543
|
+
|
|
544
|
+
`starting_after` and `ending_before` are mutually exclusive.
|
|
545
|
+
|
|
546
|
+
### Date range filters
|
|
547
|
+
|
|
548
|
+
All list endpoints support `created[gte]` / `created[lte]` (Unix timestamps):
|
|
549
|
+
|
|
550
|
+
```bash
|
|
551
|
+
# All charges from the last 7 days
|
|
552
|
+
curl "https://api.stripe.com/v1/charges?limit=100&created[gte]=$(date -v-7d +%s)" \
|
|
553
|
+
-u "sk_live_...:"
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
Other common filter parameters:
|
|
557
|
+
|
|
558
|
+
| Endpoint | Filter | Example |
|
|
559
|
+
|----------|--------|---------|
|
|
560
|
+
| `/v1/charges` | `customer`, `payment_intent`, `created[gte/lte]` | `customer=cus_xxx` |
|
|
561
|
+
| `/v1/subscriptions` | `customer`, `status`, `price`, `current_period_end[gte]` | `status=past_due` |
|
|
562
|
+
| `/v1/invoices` | `customer`, `subscription`, `status`, `due_date[gte]` | `status=open` |
|
|
563
|
+
| `/v1/payment_intents` | `customer`, `created[gte/lte]` | `customer=cus_xxx` |
|
|
564
|
+
| `/v1/refunds` | `charge`, `payment_intent`, `created[gte/lte]` | `charge=ch_xxx` |
|
|
565
|
+
|
|
566
|
+
### Auto-pagination (Python SDK)
|
|
567
|
+
|
|
568
|
+
```python
|
|
569
|
+
# Iterates all pages automatically
|
|
570
|
+
for charge in stripe.Charge.list(customer="cus_xxx", limit=100).auto_paging_iter():
|
|
571
|
+
process(charge)
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
### Search API
|
|
575
|
+
|
|
576
|
+
Stripe has a structured Search API for Customers, PaymentIntents, Charges, Subscriptions, and Invoices:
|
|
577
|
+
|
|
578
|
+
```bash
|
|
579
|
+
curl "https://api.stripe.com/v1/customers/search?query=email:'alice@example.com'" \
|
|
580
|
+
-u "sk_live_...:"
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
Search is rate-limited to **20 read operations per second** (stricter than list endpoints).
|
|
584
|
+
|
|
585
|
+
---
|
|
586
|
+
|
|
587
|
+
## Reliability: rate limits, retries, idempotency
|
|
588
|
+
|
|
589
|
+
### Rate limits
|
|
590
|
+
|
|
591
|
+
| Mode | Global limit | Notes |
|
|
592
|
+
|------|-------------|-------|
|
|
593
|
+
| Live | **100 req/sec** | Per account; some endpoints are stricter (see below) |
|
|
594
|
+
| Sandbox | **25 req/sec** | Intentionally lower to surface rate-limit bugs early |
|
|
595
|
+
|
|
596
|
+
**Endpoint-specific limits (stricter):**
|
|
597
|
+
|
|
598
|
+
| Endpoint | Limit |
|
|
599
|
+
|----------|-------|
|
|
600
|
+
| Search API | 20 read ops/sec |
|
|
601
|
+
| Files API | 20 reads/sec, 20 writes/sec |
|
|
602
|
+
| Meter Events (`/v1/billing/meter_events`) | 1,000/sec live |
|
|
603
|
+
| PaymentIntent updates | 1,000 updates/PaymentIntent/hr |
|
|
604
|
+
| Create Payout | 15/sec, 30 concurrent |
|
|
605
|
+
| Connect — create account | 5/sec sandbox, 30/sec live |
|
|
606
|
+
|
|
607
|
+
### Rate limit response
|
|
608
|
+
|
|
609
|
+
HTTP `429` with header:
|
|
610
|
+
|
|
611
|
+
```
|
|
612
|
+
Stripe-Rate-Limited-Reason: global-rate
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
Possible values: `global-concurrency`, `global-rate`, `endpoint-concurrency`, `endpoint-rate`, `resource-specific`.
|
|
616
|
+
|
|
617
|
+
### Retry with exponential backoff + jitter
|
|
618
|
+
|
|
619
|
+
```python
|
|
620
|
+
import time, random, stripe
|
|
621
|
+
|
|
622
|
+
def stripe_call_with_retry(fn, *args, max_retries=5, **kwargs):
|
|
623
|
+
for attempt in range(max_retries):
|
|
624
|
+
try:
|
|
625
|
+
return fn(*args, **kwargs)
|
|
626
|
+
except stripe.error.RateLimitError:
|
|
627
|
+
if attempt == max_retries - 1:
|
|
628
|
+
raise
|
|
629
|
+
wait = (2 ** attempt) + random.uniform(0, 1)
|
|
630
|
+
time.sleep(wait)
|
|
631
|
+
except stripe.error.APIConnectionError:
|
|
632
|
+
if attempt == max_retries - 1:
|
|
633
|
+
raise
|
|
634
|
+
time.sleep(2 ** attempt)
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
**Python SDK auto-retry:** `stripe.max_network_retries = 3` enables built-in retry for network errors and `429`s.
|
|
638
|
+
|
|
639
|
+
### Idempotency
|
|
640
|
+
|
|
641
|
+
- Include `Idempotency-Key` on every `POST` (charge, refund, subscription creation).
|
|
642
|
+
- Use a stable key derived from your internal operation ID, e.g., `f"order-{order_id}-payment"`.
|
|
643
|
+
- Results cached **24 hours**; same key = same response (including `5xx` errors).
|
|
644
|
+
- Different params + same key = `409 idempotency_error`.
|
|
645
|
+
|
|
646
|
+
```python
|
|
647
|
+
stripe.PaymentIntent.create(
|
|
648
|
+
amount=4999,
|
|
649
|
+
currency="usd",
|
|
650
|
+
idempotency_key=f"order-{order_id}-payment-intent",
|
|
651
|
+
)
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
---
|
|
655
|
+
|
|
656
|
+
## Error handling & troubleshooting
|
|
657
|
+
|
|
658
|
+
### Error object
|
|
659
|
+
|
|
660
|
+
```json
|
|
661
|
+
{
|
|
662
|
+
"error": {
|
|
663
|
+
"type": "card_error",
|
|
664
|
+
"code": "card_declined",
|
|
665
|
+
"decline_code": "insufficient_funds",
|
|
666
|
+
"message": "Your card has insufficient funds.",
|
|
667
|
+
"param": null,
|
|
668
|
+
"charge": "ch_1ABCdef...",
|
|
669
|
+
"doc_url": "https://stripe.com/docs/error-codes/card-declined"
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
```
|
|
673
|
+
|
|
674
|
+
### HTTP status → action
|
|
675
|
+
|
|
676
|
+
| Status | Type | Action |
|
|
677
|
+
|--------|------|--------|
|
|
678
|
+
| `400` | `invalid_request_error` | Fix the request — missing or malformed parameter. Check `error.param`. |
|
|
679
|
+
| `401` | `api_error` | Invalid API key. Verify `sk_live_`/`sk_test_` prefix matches environment. |
|
|
680
|
+
| `402` | `card_error` | Card declined or transaction failed. Show `error.message` to the user. |
|
|
681
|
+
| `403` | `api_error` | Insufficient permissions — use a secret key or check restricted key scopes. |
|
|
682
|
+
| `404` | `invalid_request_error` | Object not found. Verify the ID prefix matches the endpoint. |
|
|
683
|
+
| `409` | `idempotency_error` | Same idempotency key reused with different params. Generate a new key. |
|
|
684
|
+
| `429` | — | Rate limited. Retry with exponential backoff; see `Stripe-Rate-Limited-Reason`. |
|
|
685
|
+
| `5xx` | `api_error` | Stripe infrastructure issue. Retry with backoff; check https://status.stripe.com. |
|
|
686
|
+
|
|
687
|
+
### Error type → handler
|
|
688
|
+
|
|
689
|
+
| `error.type` | Meaning | What to do |
|
|
690
|
+
|--------------|---------|------------|
|
|
691
|
+
| `card_error` | Card issue (decline, insufficient funds, expired) | Show `error.message` to the end user; do not retry without user action |
|
|
692
|
+
| `invalid_request_error` | Bad API request | Fix the code; do not retry unchanged |
|
|
693
|
+
| `api_error` | Stripe-side problem | Retry with exponential backoff |
|
|
694
|
+
| `idempotency_error` | Key reused with different params | Generate a new idempotency key |
|
|
695
|
+
|
|
696
|
+
### Common `decline_code` values
|
|
697
|
+
|
|
698
|
+
| `decline_code` | Meaning | User-facing message |
|
|
699
|
+
|----------------|---------|---------------------|
|
|
700
|
+
| `card_declined` | Generic decline from issuer | "Your card was declined. Please try a different card." |
|
|
701
|
+
| `insufficient_funds` | Not enough balance | "Your card has insufficient funds." |
|
|
702
|
+
| `expired_card` | Card expired | "Your card is expired. Please update your payment method." |
|
|
703
|
+
| `incorrect_cvc` | Wrong CVC | "Incorrect security code. Please check and try again." |
|
|
704
|
+
| `stolen_card` / `lost_card` | Flagged by issuer | "Your card was declined. Please contact your bank." |
|
|
705
|
+
| `do_not_honor` | Issuer catch-all decline | "Your card was declined. Please try a different card or contact your bank." |
|
|
706
|
+
| `fraudulent` | Stripe Radar blocked | Log internally; do not tell user it was flagged as fraud |
|
|
707
|
+
|
|
708
|
+
### Troubleshooting checklist
|
|
709
|
+
|
|
710
|
+
| Symptom | Likely cause | Fix |
|
|
711
|
+
|---------|-------------|-----|
|
|
712
|
+
| `401 Unauthorized` | Wrong key or test key in prod | Confirm key prefix: `sk_live_` for production |
|
|
713
|
+
| `404` on a valid-looking ID | Wrong mode (test ID in live mode) | `pi_test_...` IDs only work with `sk_test_...` |
|
|
714
|
+
| Webhook `400` signature failure | Raw body was parsed/modified | Use `request.data` (raw bytes) before passing to `construct_event` |
|
|
715
|
+
| `409 idempotency_error` | Same key, different params | Generate a fresh UUID for the new request |
|
|
716
|
+
| Subscription stuck `incomplete` | Customer never confirmed payment | Send them the `latest_invoice.hosted_invoice_url`; confirm within 23 hours |
|
|
717
|
+
| `402` on off-session charge | 3DS required | Handle `requires_action` — send `payment_intent.client_secret` to customer |
|
|
718
|
+
| Webhook events arriving twice | Stripe retry after timeout | Make handlers idempotent; deduplicate on `event.id` |
|
|
719
|
+
|
|
720
|
+
---
|
|
721
|
+
|
|
722
|
+
## Security & compliance
|
|
723
|
+
|
|
724
|
+
### PCI compliance
|
|
725
|
+
|
|
726
|
+
Stripe handles PCI scope when you use Stripe.js / Stripe Elements / Payment Element or Checkout Sessions — you never touch raw card numbers. Maintain **SAQ A** compliance:
|
|
727
|
+
|
|
728
|
+
- Never log, store, or transmit raw card numbers (`number`, `cvc`, full `exp_month`/`exp_year` combination).
|
|
729
|
+
- Never accept card data in your own API endpoint — only accept PaymentMethod IDs (`pm_...`) or tokens.
|
|
730
|
+
- Serve your payment page over HTTPS.
|
|
731
|
+
- If you find card data in logs, rotate the API keys immediately and notify Stripe.
|
|
732
|
+
|
|
733
|
+
### API key security
|
|
734
|
+
|
|
735
|
+
- Store keys in environment variables or a secrets manager (AWS Secrets Manager, HashiCorp Vault), never in source code.
|
|
736
|
+
- Use **restricted keys** for operations that don't need full access (e.g., a reporting service needs only `Read` on `Charges` and `Invoices`).
|
|
737
|
+
- Rotate keys immediately on suspected exposure: Dashboard → API keys → Roll key.
|
|
738
|
+
- Use `sk_test_` in all non-production environments — a test key can never trigger live charges.
|
|
739
|
+
|
|
740
|
+
### Webhook security
|
|
741
|
+
|
|
742
|
+
- Always verify `Stripe-Signature` before processing any webhook payload.
|
|
743
|
+
- Use a **separate webhook signing secret** (`whsec_...`) per endpoint — do not reuse across environments.
|
|
744
|
+
- Store the signing secret in environment variables.
|
|
745
|
+
- Reject webhooks with a timestamp more than **5 minutes** old to prevent replay attacks (the SDK does this by default with a 300-second tolerance).
|
|
746
|
+
|
|
747
|
+
### Stripe Connect — platform responsibilities
|
|
748
|
+
|
|
749
|
+
- Use the minimum Connect account type needed (Express > Standard > Custom — lower customisation = lower compliance burden).
|
|
750
|
+
- Platforms are responsible for complying with KYC/AML requirements for their connected accounts.
|
|
751
|
+
- Never display the connected account's full bank account or SSN to end users.
|
|
752
|
+
|
|
753
|
+
### Audit trail
|
|
754
|
+
|
|
755
|
+
Every API action is logged in **Dashboard → Developers → Logs** (7-day retention for all accounts; extended with Sigma). Use `metadata` to attach your internal IDs for easier cross-referencing.
|
|
756
|
+
|
|
757
|
+
---
|
|
758
|
+
|
|
759
|
+
## Testing checklist
|
|
760
|
+
|
|
761
|
+
Use sandbox keys (`sk_test_`, `pk_test_`) and the test card numbers below. No real money moves.
|
|
762
|
+
|
|
763
|
+
### Test card numbers
|
|
764
|
+
|
|
765
|
+
| Scenario | Card number | Expiry | CVC |
|
|
766
|
+
|----------|-------------|--------|-----|
|
|
767
|
+
| Payment succeeds | `4242 4242 4242 4242` | Any future | Any 3 digits |
|
|
768
|
+
| Payment declined (generic) | `4000 0000 0000 0002` | Any future | Any 3 digits |
|
|
769
|
+
| Insufficient funds | `4000 0000 0000 9995` | Any future | Any 3 digits |
|
|
770
|
+
| 3D Secure required | `4000 0025 0000 3155` | Any future | Any 3 digits |
|
|
771
|
+
| 3D Secure 2 — frictionless pass | `4000 0000 0000 3220` | Any future | Any 3 digits |
|
|
772
|
+
| Card expired | `4000 0000 0000 0069` | Any future | Any 3 digits |
|
|
773
|
+
| Incorrect CVC | `4000 0000 0000 0101` | Any future | Any 3 digits |
|
|
774
|
+
| Mastercard success | `5555 5555 5555 4444` | Any future | Any 3 digits |
|
|
775
|
+
|
|
776
|
+
### Trigger test webhook events (Stripe CLI)
|
|
777
|
+
|
|
778
|
+
```bash
|
|
779
|
+
# Install: brew install stripe/stripe-cli/stripe
|
|
780
|
+
stripe login
|
|
781
|
+
stripe listen --forward-to localhost:8000/stripe/webhook
|
|
782
|
+
|
|
783
|
+
# In a separate terminal:
|
|
784
|
+
stripe trigger payment_intent.succeeded
|
|
785
|
+
stripe trigger invoice.payment_failed
|
|
786
|
+
stripe trigger customer.subscription.deleted
|
|
787
|
+
stripe trigger checkout.session.completed
|
|
788
|
+
```
|
|
789
|
+
|
|
790
|
+
### QA checklist
|
|
791
|
+
|
|
792
|
+
- [ ] PaymentIntent created in sandbox; `client_secret` returned
|
|
793
|
+
- [ ] Test card `4242...` → PaymentIntent reaches `succeeded`
|
|
794
|
+
- [ ] Decline card `4000...0002` → `402` with `card_error`, `decline_code: card_declined`
|
|
795
|
+
- [ ] 3DS card `4000...3155` → PaymentIntent reaches `requires_action`; completes after challenge
|
|
796
|
+
- [ ] Refund issued on succeeded Charge; `re_...` returned with `status: succeeded`
|
|
797
|
+
- [ ] Subscription created with `payment_behavior=default_incomplete`; first invoice payment confirmed
|
|
798
|
+
- [ ] `invoice.payment_failed` webhook received and processed without error
|
|
799
|
+
- [ ] `cancel_at_period_end=true` sets flag; `customer.subscription.deleted` fires at period end
|
|
800
|
+
- [ ] Webhook signature verification passes for valid payload; returns `400` for tampered payload
|
|
801
|
+
- [ ] Idempotency key: same request sent twice → identical response, no duplicate charge
|
|
802
|
+
- [ ] Pagination loop retrieves all pages (`has_more=false` terminates loop)
|
|
803
|
+
- [ ] Rate limit hit in sandbox (25 req/sec); `429` handled with backoff, eventual success
|
|
804
|
+
|
|
805
|
+
---
|
|
806
|
+
|
|
807
|
+
## Sources
|
|
808
|
+
|
|
809
|
+
- [Stripe API Reference](https://docs.stripe.com/api)
|
|
810
|
+
- [Authentication & API Keys](https://docs.stripe.com/keys)
|
|
811
|
+
- [API Versioning & Changelog](https://docs.stripe.com/changelog)
|
|
812
|
+
- [Payment Intents](https://docs.stripe.com/payments/payment-intents)
|
|
813
|
+
- [Subscriptions Overview](https://docs.stripe.com/billing/subscriptions/overview)
|
|
814
|
+
- [Checkout Sessions](https://docs.stripe.com/payments/checkout)
|
|
815
|
+
- [Refunds](https://docs.stripe.com/refunds)
|
|
816
|
+
- [Webhook Integration](https://docs.stripe.com/webhooks)
|
|
817
|
+
- [Idempotent Requests](https://docs.stripe.com/idempotency)
|
|
818
|
+
- [Rate Limits](https://docs.stripe.com/rate-limits)
|
|
819
|
+
- [Error Codes](https://docs.stripe.com/error-codes)
|
|
820
|
+
- [Stripe Connect](https://docs.stripe.com/connect)
|
|
821
|
+
- [Testing](https://docs.stripe.com/testing)
|
|
822
|
+
- [Security Best Practices](https://docs.stripe.com/security)
|
|
823
|
+
- [API Status](https://status.stripe.com)
|