stripe-integration
Skill1 starsChanged 38 days agoMITView on GitHub
Implements Stripe payments, subscriptions, and webhooks so billing state stays correct - Checkout Sessions, signature-verified idempotent webhook handlers, dunning, and SCA handling. Use when someone asks "add Stripe subscriptions to my app", "why is my webhook signature verification failing", "my database says subscribed but Stripe says canceled", "how do I avoid double-charging on retries", or "how should I handle failed payments". Do NOT use for choosing price points, tiers, or packaging - use saas-pricing instead; for general-purpose webhook receiver hardening beyond Stripe, use webhook-receiver-hardener; for idempotency patterns outside payments, use idempotency-enforcer.
13 other projects use a version of this.
Adopted by
Most recently taken up by
- Cure-Consulting-Group/ProductEngineeringSkillstheir own version, first seen Nov 4, 2026
- salmandev/everything-geminitheir own version, first seen Oct 17, 2026
- xfstudio/skillstheir own version, first seen Oct 13, 2026
- Samueladewole/claude-skillstheir own version, first seen Oct 13, 2026
- roshankodi/agent-skillstheir own version, first seen Sep 22, 2026
- Fandry96/k3-agentic-skillstheir own version, first seen May 12, 2026
Other versions13 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 13
- kivo360/OmoiOS★ 81same name
- Fandry96/k3-agentic-skills★ 27same name
- xfstudio/skills★ 9same name
- Cure-Consulting-Group/ProductEngineeringSkills★ 3same name
- ai4brands-design/claude-skills★ 2same name
- CodeWithBehnam/cc-docs★ 2same name
- FelipeAngeli/skills★ 2same name
- roshankodi/agent-skills★ 2same name
- salmandev/everything-gemini★ 2same name
- tysongreenan/claude-code-webapp-boilerplate★ 2same name
3 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository6 other files it gives its agents.
- api-designSkill
- database-schemaSkill
- graphql-schemaSkill
- nextjs-app-routerSkill
- supabase-expertSkill
- web-performanceSkill
The file
We split this skill into the rules it gives an agent
10 rules. 0 are common, 9 are distinctive, and 1 is very specific to this project.
0
Common
Most of the 12 edited versions we read keep these.
9
Distinctive
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 27
Create the Checkout Session server-side.
- Distinctive: line 29
Treat the success page as UI only ("thanks, your access is activating").
- Distinctive: line 45
Every item is mandatory; each one missing is a specific production incident.
- Very specific: line 69
Run stripe listen --forward-to localhost:PORT/webhook and use test-mode keys.
- Distinctive: line 108
Do not gate access on the success redirect - it is spoofable and unreliable.
- Distinctive: line 109
Do not parse the webhook body before signature verification - verification fails on mutated bytes and you will "fix" it by disabling …
- Distinctive: line 110
Do not process an event ID twice - at-least-once delivery guarantees you will eventually receive duplicates.
- Distinctive: line 111
Do not trust price IDs or amounts from the client - a tampered request buys the enterprise plan at the hobby price.
- Distinctive: line 112
Do not auto-retry card declines or create more than one Stripe customer per user.
- Distinctive: line 113
Do not do slow work inline in the handler - return 2xx fast, queue the rest.
“Common” is measured: kept by at least 6 in 10 of the 12 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Stripe Expert
Billing bugs are the most expensive class of bug: an unverified webhook is an open door to fake "payment succeeded" events, a non-idempotent handler double-provisions on Stripe's at-least-once delivery, and state inferred from a client redirect grants free access to anyone who can type a URL. This skill wires Stripe so money moves correctly and your database never disagrees with Stripe about who has paid.
Core principle
Stripe is the source of truth for billing state. Your database is a mirror, updated only by verified webhooks. Never grant access from a client-side success callback or redirect - the redirect can be spoofed, dropped, or replayed; the webhook cannot.
Operating procedure
Step 1: Gather inputs
- Billing model - one-time purchase, flat subscription, per-seat, or usage-based? Default for SaaS: flat subscription via Checkout.
- Existing user model - is there a
stripe_customer_idcolumn? One Stripe customer per user, created lazily on first purchase. - Access-gating point - the exact code path that checks "is this user paid?". It must read your mirrored subscription status, nowhere else.
- Dunning policy - how many days of grace on
past_duebefore access is cut? Default: 7 days with email notices.
Step 2: Build the purchase flow (Checkout)
- Create the Checkout Session server-side. Never trust amounts or price IDs from the client - accept only an internal plan identifier and resolve the Stripe price ID server-side.
- Redirect the customer to the hosted page.
- Treat the success page as UI only ("thanks, your access is activating"). Entitlement flips when the webhook lands, typically within seconds.
const session = await stripe.checkout.sessions.create({
mode: 'subscription',
line_items: [{ price: priceId, quantity: 1 }], // resolved server-side
success_url: `${base}/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${base}/pricing`,
customer: stripeCustomerId,
}, { idempotencyKey: `checkout:${userId}:${planId}` });Pass an Idempotency-Key on every create request (sessions, charges, customers) so a network retry cannot create two of anything.
Step 3: Build the webhook handler against this checklist
Every item is mandatory; each one missing is a specific production incident.
- Signature verified with
stripe.webhooks.constructEvent(rawBody, sig, endpointSecret)- reject with 400 on failure. Skipping this lets anyone POST fake payment events. - Raw request body used for verification. Body-parsing middleware (e.g.
express.json()) mutates bytes and breaks the signature - exempt the webhook route. - Idempotent: store processed
event.ids (unique-constrained table) and skip duplicates. Stripe delivers at-least-once; without this, a redelivery double-provisions. - Returns 2xx within a few seconds; heavy work (emails, provisioning) goes to a queue. Stripe retries non-2xx responses for up to ~3 days with exponential backoff - a slow handler turns retries into a duplicate storm.
- Handles out-of-order delivery: apply the event's data (or re-fetch the object from the API) rather than assuming events arrive in sequence.
- Minimum event set handled:
checkout.session.completed,customer.subscription.updated,customer.subscription.deleted,invoice.payment_failed. - Unhandled event types return 200, not 500 - otherwise Stripe retries events you never intended to process.
Step 4: Mirror subscription state
On each subscription event, upsert one row per user: stripe_customer_id, stripe_subscription_id, status, current_period_end, price_id. The access gate reads this row only. States to gate on: active and trialing get access; past_due gets the grace window from Step 1; canceled and unpaid do not.
Step 5: Handle failure and lifecycle paths
invoice.payment_failed: start dunning - notify the user, keep access through the grace window, cut on expiry orsubscription.deleted.- Declines: catch
StripeCardErrorand surface a friendly message. Retry only transient errors (network, 5xx, rate limit); never auto-retry a card decline - that is card-testing behavior and triggers fraud flags. - SCA/3DS: with Payment Intents, handle
requires_actionby completing authentication client-side. Checkout handles this for you - one more reason to prefer it. - Plan changes: decide proration explicitly (
proration_behavior) and communicate the resulting invoice line to the user before confirming. - Trials: handle
customer.subscription.trial_will_end(fires 3 days before) to warn the user before the first charge.
Step 6: Test before going live
Run stripe listen --forward-to localhost:PORT/webhook and use test-mode keys. Verify with stripe trigger: successful checkout, duplicate event delivery (handler must no-op the second), payment failure, and cancellation. A webhook path that has never seen a replayed event in test will double-provision in production.
Worked contrast
Bad - grants access from the redirect:
// success page handler
app.get('/success', async (req, res) => {
await db.user.update({ where: { id: req.user.id }, data: { plan: 'pro' } }); // spoofable
res.render('success');
});Good - grants access from the verified, idempotent webhook:
app.post('/webhook', express.raw({ type: 'application/json' }), async (req, res) => {
let event;
try {
event = stripe.webhooks.constructEvent(req.body, req.headers['stripe-signature'], endpointSecret);
} catch { return res.status(400).send('bad signature'); }
const inserted = await db.processedEvents.insertIgnoreDuplicate({ id: event.id });
if (!inserted) return res.sendStatus(200); // already handled
if (event.type === 'checkout.session.completed') {
await queue.enqueue('activate-subscription', event.data.object);
}
res.sendStatus(200);
});Deliverable
Produce a working integration containing: the server-side Checkout Session endpoint with idempotency keys; a webhook handler passing every item on the Step 3 checklist; the mirrored subscription table and the single access-gate read path; the dunning policy wired to invoice.payment_failed; and a test log showing duplicate-delivery and failure events handled correctly via the Stripe CLI.
Do NOT
- Do not gate access on the success redirect - it is spoofable and unreliable; only the verified webhook flips entitlement.
- Do not parse the webhook body before signature verification - verification fails on mutated bytes and you will "fix" it by disabling verification.
- Do not process an event ID twice - at-least-once delivery guarantees you will eventually receive duplicates.
- Do not trust price IDs or amounts from the client - a tampered request buys the enterprise plan at the hobby price.
- Do not auto-retry card declines or create more than one Stripe customer per user.
- Do not do slow work inline in the handler - return 2xx fast, queue the rest.
Quality bar
- Signature verification on raw body, with a 400 path that is actually exercised in tests.
- Duplicate event delivery proven to no-op in a test run.
- Zero client-supplied amounts or price IDs reach the Stripe API.
- Access gating reads exactly one mirrored status field, and every subscription lifecycle state maps to a defined access decision.
stripe listentest log covers success, duplicate, failure, and cancellation.
Escalation
This skill covers integration mechanics, not pricing strategy - route tier and packaging questions to saas-pricing. Tax registration, revenue recognition, and regulated money-movement (marketplaces holding funds, Connect platform liability) warrant a payments-savvy accountant or lawyer, not just Stripe defaults.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

