返回 Skill 列表
extension
分类: 开发与工程无需 API Key

stripe-integration

关于将Stripe支付集成到现有项目中的指南。涵盖了单次支付、订阅以及结合安全最佳实践的高级模式。

person作者: jakexiaohubgithub

Stripe Integration Guide

This skill helps you integrate Stripe into an existing project. It provides decision trees, code patterns, and security checklists.

Prerequisites

  • Stripe account (test mode is fine)
  • Node.js 18+ (or equivalent runtime)
  • Existing web application with backend capability

Step 1: Determine Integration Type

Ask the user these questions to determine the right pattern:

Q1: What payment model do you need?

| Answer | Go to | |--------|-------| | One-time payment (buy a product/service once) | patterns/one-time-checkout.md | | Subscription (recurring billing) | patterns/subscription.md | | Usage-based billing (metered) | patterns/usage-based.md | | Custom payment flow (full control) | patterns/payment-intent.md |

Q2: Do you need multi-party payments?

| Answer | Action | |--------|--------| | Yes (marketplace, platform fees) | Also read patterns/connect.md | | No | Skip Connect |

Q3: What's your tech stack?

| Stack | Additional Guide | |-------|------------------| | Next.js (App Router) | stacks/nextjs.md | | Express.js | stacks/express.md | | Serverless (Vercel/Netlify Functions) | stacks/serverless.md | | Other | Use Express guide as reference |

Step 2: Core Principles (MUST READ)

Before writing any code, understand these non-negotiable rules:

2.1 Server-Side PaymentIntent Creation

// WRONG - Client-side (attackers can modify amount)
const paymentIntent = await stripe.paymentIntents.create({
  amount: userProvidedAmount, // DANGEROUS!
  currency: 'usd',
});

// CORRECT - Server-side with validated amount
app.post('/create-payment-intent', async (req, res) => {
  const { productId } = req.body;
  const product = await db.products.findById(productId);

  const paymentIntent = await stripe.paymentIntents.create({
    amount: product.priceInCents, // From YOUR database
    currency: 'usd',
  });

  res.json({ clientSecret: paymentIntent.client_secret });
});

2.2 Webhook Idempotency

Stripe may send the same event multiple times. You MUST handle duplicates:

app.post('/webhook', async (req, res) => {
  const event = stripe.webhooks.constructEvent(/*...*/);

  // Check if already processed
  const existing = await db.processedEvents.findById(event.id);
  if (existing) {
    return res.status(200).json({ received: true }); // Already handled
  }

  // Process the event
  await handleEvent(event);

  // Mark as processed
  await db.processedEvents.create({ id: event.id, processedAt: new Date() });

  res.status(200).json({ received: true });
});

2.3 Async Webhook Processing

Return 2xx immediately, process business logic asynchronously:

// WRONG - Synchronous processing (may timeout)
app.post('/webhook', async (req, res) => {
  const event = verifyEvent(req);
  await sendEmail(event);           // Slow!
  await updateDatabase(event);      // Slow!
  await notifyExternalService(event); // May fail!
  res.status(200).send();
});

// CORRECT - Queue for async processing
app.post('/webhook', async (req, res) => {
  const event = verifyEvent(req);
  await queue.add('stripe-event', event); // Fast enqueue
  res.status(200).json({ received: true }); // Return immediately
});

2.4 Currency in Smallest Units

Always use cents (or smallest unit), never dollars:

// WRONG
amount: 19.99  // Floating point errors!

// CORRECT
amount: 1999   // $19.99 in cents

2.5 Environment Variables

Never hardcode API keys:

# .env
STRIPE_SECRET_KEY=sk_test_xxx
STRIPE_PUBLISHABLE_KEY=pk_test_xxx
STRIPE_WEBHOOK_SECRET=whsec_xxx
// WRONG
const stripe = require('stripe')('sk_test_xxx');

// CORRECT
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

Step 3: Implementation Checklist

After implementing, verify these items:

Security Checklist

  • [ ] API keys are in environment variables, not code
  • [ ] Webhook signature is verified
  • [ ] PaymentIntent is created server-side only
  • [ ] Amounts come from your database, not client requests
  • [ ] HTTPS is enforced in production
  • [ ] Webhook endpoint is not rate-limited by your own middleware

Reliability Checklist

  • [ ] Webhook events are deduplicated by event.id
  • [ ] Webhook handler returns 2xx within 5 seconds
  • [ ] Failed webhook processing is retried via queue
  • [ ] Database transactions wrap payment state changes

Testing Checklist

  • [ ] Tested with 4242 4242 4242 4242 (success)
  • [ ] Tested with 4000 0000 0000 0002 (decline)
  • [ ] Tested with 4000 0025 0000 3155 (requires 3D Secure)
  • [ ] Tested webhook with Stripe CLI: stripe listen --forward-to localhost:3000/webhook
  • [ ] Tested duplicate webhook handling

Step 4: Go-Live Checklist

Before switching to live mode:

  • [ ] Replace test keys with live keys
  • [ ] Update webhook endpoint in Stripe Dashboard
  • [ ] Verify webhook signing secret is updated
  • [ ] Test one real transaction with small amount
  • [ ] Set up monitoring/alerting for failed payments
  • [ ] Document refund process for support team

Quick Reference: Test Card Numbers

| Card Number | Scenario | |-------------|----------| | 4242 4242 4242 4242 | Success | | 4000 0000 0000 0002 | Decline | | 4000 0025 0000 3155 | Requires 3D Secure | | 4000 0000 0000 9995 | Insufficient funds | | 4000 0000 0000 0069 | Expired card |

Any future expiry date and any 3-digit CVC will work in test mode.

Related Documentation

  • essentials/webhook-handling.md - Deep dive into webhooks
  • essentials/error-handling.md - Error categorization and recovery
  • essentials/idempotency.md - Preventing duplicate operations
  • essentials/security-checklist.md - Full security audit checklist