界面已本地化;技能介绍与说明文档按所选语言由 AI 自动翻译,代码、命令、路径和专业术语保留原文,以原作者文档为准。
探索技能
设计与创意 · SKILL.md

stripe

stripe

【设计与创意】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; cryptocur…

独立 GitHub 快照 · 自动分类 · 以原作者文档为准

技能说明

GitHub

name: stripe description: | 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 allowed-tools: Read, Grep, Glob, Write, Edit

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
我的收藏