Stripe payment processing. Covers Checkout Sessions, Payment Intents, Subscriptions, Webhooks, idempotency, and Stripe.js frontend integration. USE WHEN: user mentions "stripe", "payment", "checkout", "subscription billing", "payment intent", "payment gateway", "credit card", "invoice", "Stripe.js" DO NOT USE FOR: PayPal-only integration; cryptocurrency payments; accounting software

GitHub
Install command
npx skhub add claude-dev-suite/stripe
Markdown
SKILL.md

Stripe Payment Processing

Setup

import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

Checkout Session (simplest integration)

app.post('/api/checkout', async (req, res) => {
  const session = await stripe.checkout.sessions.create({
    mode: 'payment', // or 'subscription'
    line_items: [{ price: req.body.priceId, quantity: 1 }],
    success_url: `${process.env.APP_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
    cancel_url: `${process.env.APP_URL}/cancel`,
    metadata: { userId: req.user.id },
  });
  res.json({ url: session.url });
});

Payment Intents (custom UI)

// Server: create intent
app.post('/api/payment-intent', async (req, res) => {
  const intent = await stripe.paymentIntents.create({
    amount: Math.round(req.body.amount * 100), // cents
    currency: 'usd',
    automatic_payment_methods: { enabled: true },
    metadata: { orderId: req.body.orderId },
  }, { idempotencyKey: `order_${req.body.orderId}` });
  res.json({ clientSecret: intent.client_secret });
});
// Client: React + Stripe.js
import { useStripe, useElements, PaymentElement } from '@stripe/react-stripe-js';

function CheckoutForm() {
  const stripe = useStripe();
  const elements = useElements();

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    const { error } = await stripe!.confirmPayment({
      elements: elements!,
      confirmParams: { return_url: `${window.location.origin}/success` },
    });
    if (error) setError(error.message!);
  };

  return (
    <form onSubmit={handleSubmit}>
      <PaymentElement />
      <button disabled={!stripe}>Pay</button>
    </form>
  );
}

Subscriptions

app.post('/api/subscribe', async (req, res) => {
  const subscription = await stripe.subscriptions.create({
    customer: customerId,
    items: [{ price: req.body.priceId }],
    payment_behavior: 'default_incomplete',
    expand: ['latest_invoice.payment_intent'],
  });
  const invoice = subscription.latest_invoice as Stripe.Invoice;
  const pi = invoice.payment_intent as Stripe.PaymentIntent;
  res.json({ subscriptionId: subscription.id, clientSecret: pi.client_secret });
});

// Cancel at period end (not immediately)
await stripe.subscriptions.update(subId, { cancel_at_period_end: true });

Webhooks (critical)

app.post('/webhooks/stripe', express.raw({ type: 'application/json' }), (req, res) => {
  const event = stripe.webhooks.constructEvent(
    req.body, req.headers['stripe-signature']!, process.env.STRIPE_WEBHOOK_SECRET!
  );

  switch (event.type) {
    case 'checkout.session.completed':
      await fulfillOrder(event.data.object as Stripe.Checkout.Session);
      break;
    case 'invoice.paid':
      await activateSubscription(event.data.object as Stripe.Invoice);
      break;
    case 'invoice.payment_failed':
      await handleFailedPayment(event.data.object as Stripe.Invoice);
      break;
    case 'customer.subscription.deleted':
      await deactivateSubscription(event.data.object as Stripe.Subscription);
      break;
  }
  res.json({ received: true });
});

Critical: Use express.raw() — parsed body breaks signature verification.

Anti-Patterns

Anti-PatternFix
Trust client-side amountCalculate amount server-side only
No webhook handlingAlways use webhooks for fulfillment
Storing card numbersUse Stripe.js — never touch card data
No idempotency keysAdd to all create operations
Fulfilling before payment confirmsWait for webhook confirmation
No error handling on clientShow error.message from Stripe

Quick Troubleshooting

IssueFix
Webhook 400Use raw body, verify STRIPE_WEBHOOK_SECRET
"No such price"Check test/live mode matches API key
Double chargesAdd idempotency key
CORS on redirectEnsure HTTPS everywhere

Production Checklist

  • Webhook endpoint registered and verified
  • Idempotency keys on all creates
  • Amount calculated server-side
  • Test mode for dev/staging, live for prod
  • PCI compliance (never handle raw card data)
  • Customer portal for subscription management
  • Refund flow implemented
Discovery
Tags

No tags published for this skill.

Version
Latest version metadata

Version

v2026.09.24

Published

Sep 24, 2026

Category

Uncategorized

License

MIT

Source path

skills/payments/stripe

Default branch

main

Latest commit

9496306

Tree SHA

fe4e2f1