SaaS Tools7 min read

Stripe Payments Integration

Integrate Stripe for one-time payments, subscriptions, webhooks, and the Customer Portal using Stripe Elements and the Node.js SDK.

stripepaymentssubscriptionssaas

Stripe is the industry-standard payments platform for internet businesses. Its API handles one-time payments, recurring subscriptions, marketplace payouts, and global payment methods. Correct Stripe integration requires understanding the payment intent flow, webhook reliability, and idempotency.

Use Stripe Elements (UI) + Payment Intents (backend) for PCI compliance without storing card data. Validate all Stripe webhook signatures - never process unsigned webhooks. Use idempotency keys for mutation requests. Test in Test mode with documented test card numbers.

Workflow

  1. Set up the checkout flow with Payment ElementsPayment Elements handles card input, 3D Secure, and payment method routing - Stripe maintains PCI DSS compliance for you.
  2. Build subscription billingSubscriptions require customer, payment method, and subscription objects - Stripe manages renewal, retry, and dunning.
  3. Handle webhooks reliablyWebhooks are Stripe's mechanism for async event notification - payment success, subscription renewal, refunds. Processing them reliably is critical for accurate business state.
  4. Implement the Customer PortalStripe Customer Portal gives subscribers self-service subscription management without you building it.
  5. Use idempotency keys and error handlingNetwork failures can cause duplicate charges if retries aren't idempotent.

Output Format

Payment Elements checkout, subscription lifecycle, webhook verification with deduplication, Customer Portal, idempotency keys on all mutations.

Examples

Payment Intent: `const pi = await stripe.paymentIntents.create({ amount: totalCents, currency: "usd", customer: customerId, metadata: { orderId: order.id } }, { idempotencyKey: `pi-${order.id}` })`
Webhook handler: `const event = stripe.webhooks.constructEvent(req.rawBody, req.headers["stripe-signature"], WEBHOOK_SECRET); if (event.type === "payment_intent.succeeded") { await fulfillOrder(event.data.object.metadata.orderId) }`
Portal: `const portal = await stripe.billingPortal.sessions.create({ customer: user.stripeCustomerId, return_url: `${BASE_URL}/account` }); res.redirect(303, portal.url)`

Edge Cases & Gotchas

  • Webhook delivery order: Stripe may deliver webhooks out of order; check current state via API instead of relying on event sequence
  • SCA/3D Secure: European cards require additional authentication; Payment Elements handles this automatically; server-side confirmation requires redirect handling
  • Metered billing: for usage-based pricing, report usage with `stripe.subscriptionItems.createUsageRecord` - don't report usage in the webhook handler (causes loops)

When to Break These Rules

  • For markets where Stripe is unavailable (some APAC regions, MENA), use regional alternatives (Razorpay for India, Moyasar for Saudi Arabia) with a payment abstraction layer.

Reference Files

  • Stripe Docs: https://stripe.com/docs
  • Stripe Elements: https://stripe.com/docs/stripe-js
  • Stripe Webhooks: https://stripe.com/docs/webhooks
  • Stripe Customer Portal: https://stripe.com/docs/customer-management

More skills in this library

Browse all technical skills - Mobile, AI/ML, Web, Backend, Design, and Product.