S

Stripe Integration Dynamic

Streamline your workflow with this paid, payments, subscriptions, billing. Includes structured workflows, validation checks, and reusable patterns for development.

SkillClipticsdevelopmentv1.0.0MIT
0 views0 copies

Stripe Integration Dynamic

A comprehensive skill for integrating Stripe payment processing into web applications. Covers checkout flows, subscriptions, webhooks, customer management, and production-ready payment patterns with proper error handling and security.

When to Use This Skill

Choose this skill when:

  • Implementing one-time payments with Stripe Checkout or Payment Intents
  • Building subscription billing with trials, upgrades, and cancellations
  • Setting up webhook handlers for payment events and lifecycle management
  • Managing Stripe customers, payment methods, and invoices
  • Handling edge cases: declined cards, disputes, refunds, and currency

Consider alternatives when:

  • Integrating a different payment provider → use that provider's skill
  • Building a general e-commerce platform → use an e-commerce skill
  • Working on financial reporting → use an analytics skill
  • Need PCI compliance guidance → use a security compliance skill

Quick Start

npm install stripe @stripe/stripe-js @stripe/react-stripe-js
// Backend: Create checkout session import Stripe from 'stripe'; const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!); app.post('/api/create-checkout', async (req, res) => { const { priceId, customerId } = req.body; const session = await stripe.checkout.sessions.create({ customer: customerId, mode: 'subscription', line_items: [{ price: priceId, quantity: 1 }], success_url: `${process.env.APP_URL}/success?session_id={CHECKOUT_SESSION_ID}`, cancel_url: `${process.env.APP_URL}/pricing`, subscription_data: { trial_period_days: 14, metadata: { userId: req.user.id }, }, }); res.json({ url: session.url }); });

Core Concepts

Stripe Payment Flow Architecture

FlowUse CaseComplexity
Checkout SessionOne-time or subscription, hosted pageLow
Payment IntentCustom UI, full controlMedium
Setup IntentSave payment method for laterMedium
InvoiceRecurring billing, metered usageMedium-High
ConnectMarketplace, split paymentsHigh

Webhook Handler Pattern

// Production-ready webhook handler import { buffer } from 'micro'; const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET!; app.post('/webhooks/stripe', express.raw({ type: 'application/json' }), async (req, res) => { const sig = req.headers['stripe-signature']!; let event: Stripe.Event; try { event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret); } catch (err: any) { console.error('Webhook signature failed:', err.message); return res.status(400).send(`Webhook Error: ${err.message}`); } // Idempotent processing — check if already handled const processed = await db.processedEvents.findUnique({ where: { stripeEventId: event.id }, }); if (processed) return res.json({ received: true }); try { switch (event.type) { case 'checkout.session.completed': { const session = event.data.object as Stripe.Checkout.Session; await handleCheckoutComplete(session); break; } case 'invoice.payment_succeeded': { const invoice = event.data.object as Stripe.Invoice; await handlePaymentSuccess(invoice); break; } case 'invoice.payment_failed': { const invoice = event.data.object as Stripe.Invoice; await handlePaymentFailure(invoice); break; } case 'customer.subscription.deleted': { const sub = event.data.object as Stripe.Subscription; await handleSubscriptionCanceled(sub); break; } } await db.processedEvents.create({ data: { stripeEventId: event.id } }); } catch (err) { console.error('Webhook handler error:', err); return res.status(500).json({ error: 'Handler failed' }); } res.json({ received: true }); });

Subscription Management

// Subscription lifecycle operations class SubscriptionService { async upgrade(subscriptionId: string, newPriceId: string) { const sub = await stripe.subscriptions.retrieve(subscriptionId); return stripe.subscriptions.update(subscriptionId, { items: [{ id: sub.items.data[0].id, price: newPriceId, }], proration_behavior: 'create_prorations', }); } async cancel(subscriptionId: string, immediate = false) { if (immediate) { return stripe.subscriptions.cancel(subscriptionId); } return stripe.subscriptions.update(subscriptionId, { cancel_at_period_end: true, }); } async reactivate(subscriptionId: string) { return stripe.subscriptions.update(subscriptionId, { cancel_at_period_end: false, }); } async applyDiscount(subscriptionId: string, couponId: string) { return stripe.subscriptions.update(subscriptionId, { coupon: couponId, }); } }

Configuration

ParameterTypeDefaultDescription
paymentModestring'checkout'Mode: checkout (hosted), payment-intent (custom)
subscriptionProrationstring'create_prorations'Proration on plan changes
trialDaysnumber14Default trial period for subscriptions
webhookTolerancenumber300Webhook signature tolerance (seconds)
currencystring'usd'Default currency for payments
idempotencyEnabledbooleantrueDeduplicate webhook events

Best Practices

  1. Always verify webhook signatures before processing — Never trust incoming webhook data without verifying the Stripe signature. Use stripe.webhooks.constructEvent() with your endpoint secret to validate authenticity.

  2. Process webhooks idempotently — Stripe retries webhooks on failure and may send the same event multiple times. Store processed event IDs and skip duplicates. Return 200 immediately after signature verification, then process asynchronously.

  3. Use Stripe Checkout for the simplest integration — Checkout handles PCI compliance, payment method collection, and SCA (Strong Customer Authentication) for you. Only use Payment Intents when you need a fully custom UI.

  4. Store Stripe IDs alongside your user records — Save customerId, subscriptionId, and priceId in your database. This enables direct lookups without searching Stripe, and provides resilience if webhook delivery is delayed.

  5. Handle all subscription lifecycle events — Don't just handle checkout.session.completed. Also handle invoice.payment_failed (dunning), customer.subscription.deleted (cancellation), and customer.subscription.updated (plan changes) for a complete implementation.

Common Issues

Webhook endpoint returns 400 on valid events — The raw request body must be passed to constructEvent(), not a parsed JSON body. If Express's JSON middleware parses the body before the webhook handler, the signature verification fails. Use express.raw() for the webhook route.

Subscription created but user not granted access — Relying only on the checkout redirect for provisioning is fragile (user can close the tab). Always provision access via the checkout.session.completed webhook, which is reliable and retried by Stripe.

Duplicate charges from webhook retries — If your webhook handler throws an error after charging the user but before returning 200, Stripe retries and you process the event again. Make all webhook handlers idempotent by checking event IDs before processing.

Community

Reviews

Write a review

No reviews yet. Be the first to review this template!

Similar Templates