Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Stripe payment integration patterns for checkout flows, webhooks, and subscriptions. Use when implementing payment flows, handling Stripe webhooks, working with subscriptions or invoices, testing payment functionality, or handling refunds and disputes. Do NOT use for non-payment API routes.
.claude/skills/bybren-llc-stripe-patterns/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 17% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 2% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 15% | 0% |
| case-05 | ✗→✓ | ▲ Improved | -21% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 95% | 0% |
Guide safe and consistent Stripe integration. Routes to existing payment patterns and provides evidence templates for testing.
lib/stripe-config.tscreateStripeClient() for consistent API versionapp/api/payments/create-checkout-session/route.tsapp/api/payments/webhook/route.tsutils/data/payments/ (use RLS context)utils/data/subscriptions/utils/data/invoices/Before ANY payment work:
STRIPE_SECRET_KEY starts with sk_test_whsec_... from Stripe CLI)For webhook handlers:
typescript// Idempotent webhook pattern await withSystemContext(prisma, "webhook", async (client) => { // Check if already processed const existing = await client.webhook_events.findUnique({ where: { stripe_event_id: event.id }, }); if (existing) { console.log(`Skipping duplicate event: ${event.id}`); return; } // Process and record await client.webhook_events.create({ data: { stripe_event_id: event.id, event_type: event.type, processed_at: new Date(), }, }); });
ALWAYS verify webhook signatures:
typescriptimport { stripe } from "@/lib/stripe-config"; const signature = request.headers.get("stripe-signature"); const event = stripe.webhooks.constructEvent( body, signature, process.env.STRIPE_WEBHOOK_SECRET, );
typescriptimport { createStripeClient } from "@/lib/stripe-config"; import { withUserContext } from "@/lib/rls-context"; export async function createCheckout(userId: string, priceId: string) { const stripe = createStripeClient(); // Store user context for success handling const session = await stripe.checkout.sessions.create({ mode: "subscription", line_items: [{ price: priceId, quantity: 1 }], success_url: `${process.env.NEXT_PUBLIC_APP_URL}/success?session_id={{CHECKOUT_SESSION_ID}}`, cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}/pricing`, metadata: { userId }, }); return session; }
typescript// Webhook event types to handle const SUBSCRIPTION_EVENTS = [ "customer.subscription.created", "customer.subscription.updated", "customer.subscription.deleted", "invoice.payment_succeeded", "invoice.payment_failed", ];
When completing payment work, attach this evidence block:
markdown**Payment Testing Evidence** - [ ] Test mode verified (`sk_test_` key) - [ ] Webhook signature verification tested - [ ] Idempotency tested (duplicate event handling) - [ ] Success flow tested (card 4242...) - [ ] Failure flow tested (card 4000000000000002) - [ ] Subscription lifecycle tested (create/update/cancel) **Test Results:** - Checkout session: [session_id] - Webhook events processed: [count] - Subscription status: [active/cancelled]
lib/stripe-config.tsapp/api/payments/webhook/route.ts__tests__/payments/| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 14,140 | 10,646 | -25% | 1 | 1 | 0% | 3,074 | 3,606 | +17% | 0 | 0 | — |
case-02 | fail→pass | 13,228 | 7,587 | -43% | 1 | 1 | 0% | 2,604 | 2,667 | +2% | 0 | 0 | — |
case-03 | pass→pass | 9,442 | 2,763 | -71% | 1 | 1 | 0% | 1,787 | 1,632 | -9% | 0 | 0 | — |
case-04 | fail→pass | 16,121 | 10,894 | -32% | 1 | 1 | 0% | 2,871 | 3,310 | +15% | 0 | 0 | — |
case-05 | fail→pass | 10,663 | 2,813 | -74% | 1 | 1 | 0% | 1,977 | 1,568 | -21% | 0 | 0 | — |
case-06 | fail→pass | 3,819 | 2,571 | -33% | 1 | 1 | 0% | 730 | 1,421 | +95% | 0 | 0 | — |
case-07 | fail→pass | 3,266 | 1,359 | -58% | 1 | 1 | 0% | 578 | 1,263 | +119% | 0 | 0 | — |
case-08 | fail→pass | 10,094 | 2,326 | -77% | 1 | 1 | 0% | 1,786 | 1,395 | -22% | 0 | 0 | — |
case-14 | pass→pass | 11,584 | 5,381 | -54% | 1 | 1 | 0% | 2,132 | 2,007 | -6% | 0 | 0 | — |
case-09 | fail→pass | 10,833 | 1,744 | -84% | 1 | 1 | 0% | 1,908 | 1,370 | -28% | 0 | 0 | — |
case-10 | fail→pass | 10,464 | 1,706 | -84% | 1 | 1 | 0% | 1,647 | 1,268 | -23% | 0 | 0 | — |
case-11 | pass→pass | 8,065 | 2,933 | -64% | 1 | 1 | 0% | 1,348 | 1,517 | +13% | 0 | 0 | — |
case-12 | pass→pass | 10,089 | 2,384 | -76% | 1 | 1 | 0% | 1,874 | 1,469 | -22% | 0 | 0 | — |
case-13 | pass→pass | 13,548 | 2,435 | -82% | 1 | 1 | 0% | 2,552 | 1,430 | -44% | 0 | 0 | — |
case-15 | fail→pass | 9,904 | 1,335 | -87% | 1 | 1 | 0% | 1,872 | 1,259 | -33% | 0 | 0 | — |
case-16 | pass→pass | 4,230 | 1,231 | -71% | 1 | 1 | 0% | 720 | 1,212 | +68% | 0 | 0 | — |
case-17 | fail→pass | 6,846 | 1,804 | -74% | 1 | 1 | 0% | 1,200 | 1,364 | +14% | 0 | 0 | — |
case-18 | pass→pass | 8,819 | 5,438 | -38% | 1 | 1 | 0% | 1,748 | 2,078 | +19% | 0 | 0 | — |
case-19 | fail→pass | 7,846 | 1,560 | -80% | 1 | 1 | 0% | 1,303 | 1,278 | -2% | 0 | 0 | — |
case-20 | fail→fail | 19,404 | 13,070 | -33% | 1 | 1 | 0% | 4,294 | 4,068 | -5% | 0 | 0 | — |
case-21 | fail→pass | 17,549 | 6,646 | -62% | 1 | 1 | 0% | 3,723 | 2,379 | -36% | 0 | 0 | — |
case-22 | fail→fail | 11,142 | 7,535 | -32% | 1 | 1 | 0% | 2,138 | 2,556 | +20% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +59 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.