mcpField
S
stripe

stripe

github.com/mindrally开发287709 次安装3 周前更新
代码仓库

关于 stripe

面向使用 TypeScript、React 和 Next.js 的开发者的 Stripe 集成技能,涵盖服务端支付、订阅、Webhook、客户管理、安全与测试。

主要功能

  1. 01服务端处理支付
  2. 02使用 Stripe Elements 构建客户端表单
  3. 03创建和取消订阅
  4. 04验证 Webhook 签名
  5. 05客户管理与错误处理
  6. 06测试模式和 Webhook 测试

适用场景

  1. 01在 Next.js 中构建支付流程
  2. 02添加订阅生命周期处理
  3. 03处理 Stripe Webhook 事件
  4. 04测试支付及错误场景

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/mindrally/skills --skill stripe

安装后在 Claude Code 中输入/stripe确认已加载。

详细介绍

Stripe 集成

你精通 Stripe 支付集成、TypeScript、React 和 Next.js,能够构建安全的支付解决方案。

核心原则

  • 始终在服务端处理支付
  • 使用 Stripe 最新的 API 版本
  • 实施适当的错误处理
  • 遵循 PCI 合规最佳实践
  • 使用 Webhook 可靠地处理事件

服务端设置

Stripe 客户端配置
import Stripe from 'stripe';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
  apiVersion: '2023-10-16',
  typescript: true,
});
创建支付意向
// app/api/create-payment-intent/route.ts
import { NextResponse } from 'next/server';
import Stripe from 'stripe';

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

export async function POST(request: Request) {
  try {
    const { amount, currency = 'usd' } = await request.json();

    const paymentIntent = await stripe.paymentIntents.create({
      amount,
      currency,
      automatic_payment_methods: { enabled: true },
    });

    return NextResponse.json({
      clientSecret: paymentIntent.client_secret,
    });
  } catch (error) {
    return NextResponse.json(
      { error: 'Payment intent creation failed' },
      { status: 500 }
    );
  }
}

客户端集成

Stripe Elements 设置
'use client';

import { Elements } from '@stripe/react-stripe-js';
import { loadStripe } from '@stripe/stripe-js';

const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!);

export function StripeProvider({ children }: { children: React.ReactNode }) {
  return (
    <Elements stripe={stripePromise}>
      {children}
    </Elements>
  );
}
支付表单组件
'use client';

import { useState } from 'react';
import {
  PaymentElement,
  useStripe,
  useElements,
} from '@stripe/react-stripe-js';

export function CheckoutForm() {
  const stripe = useStripe();
  const elements = useElements();
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();

    if (!stripe || !elements) return;

    setIsLoading(true);
    setError(null);

    const { error: submitError } = await stripe.confirmPayment({
      elements,
      confirmParams: {
        return_url: `${window.location.origin}/payment/success`,
      },
    });

    if (submitError) {
      setError(submitError.message ?? 'Payment failed');
    }

    setIsLoading(false);
  };

  return (
    <form onSubmit={handleSubmit}>
      <PaymentElement />
      {error && <div className="error">{error}</div>}
      <button type="submit" disabled={!stripe || isLoading}>
        {isLoading ? 'Processing...' : 'Pay Now'}
      </button>
    </form>
  );
}

订阅管理

创建订阅
export async function createSubscription(
  customerId: string,
  priceId: string
): Promise<Stripe.Subscription> {
  const subscription = await stripe.subscriptions.create({
    customer: customerId,
    items: [{ price: priceId }],
    payment_behavior: 'default_incomplete',
    payment_settings: {
      save_default_payment_method: 'on_subscription',
    },
    expand: ['latest_invoice.payment_intent'],
  });

  return subscription;
}
取消订阅
export async function cancelSubscription(
  subscriptionId: string
): Promise<Stripe.Subscription> {
  return stripe.subscriptions.cancel(subscriptionId);
}

Webhook 处理

// app/api/webhooks/stripe/route.ts
import { headers } from 'next/headers';
import Stripe from 'stripe';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET!;

export async function POST(request: Request) {
  const body = await request.text();
  const signature = headers().get('stripe-signature')!;

  let event: Stripe.Event;

  try {
    event = stripe.webhooks.constructEvent(body, signature, webhookSecret);
  } catch (error) {
    return new Response('Webhook signature verification failed', {
      status: 400,
    });
  }

  switch (event.type) {
    case 'payment_intent.succeeded':
      const paymentIntent = event.data.object as Stripe.PaymentIntent;
      await handlePaymentSuccess(paymentIntent);
      break;

    case 'customer.subscription.created':
      const subscription = event.data.object as Stripe.Subscription;
      await handleSubscriptionCreated(subscription);
      break;

    case 'customer.subscription.deleted':
      await handleSubscriptionCanceled(event.data.object);
      break;

    case 'invoice.payment_failed':
      await handlePaymentFailed(event.data.object);
      break;
  }

  return new Response('Webhook processed', { status: 200 });
}

客户管理

export async function createOrRetrieveCustomer(
  email: string,
  userId: string
): Promise<Stripe.Customer> {
  const existingCustomers = await stripe.customers.list({
    email,
    limit: 1,
  });

  if (existingCustomers.data.length > 0) {
    return existingCustomers.data[0];
  }

  return stripe.customers.create({
    email,
    metadata: { userId },
  });
}

安全最佳实践

  • 切勿在客户端公开密钥
  • 始终验证 Webhook 签名
  • 对关键操作使用幂等键
  • 实施适当的错误处理
  • 记录支付事件以便调试
  • 在开发时使用 Stripe 测试模式

错误处理

try {
  const paymentIntent = await stripe.paymentIntents.create({...});
} catch (error) {
  if (error instanceof Stripe.errors.StripeCardError) {
    // Card was declined
    console.error('Card declined:', error.message);
  } else if (error instanceof Stripe.errors.StripeInvalidRequestError) {
    // Invalid parameters
    console.error('Invalid request:', error.message);
  } else {
    // Other errors
    console.error('Stripe error:', error);
  }
}

测试

  • 使用 Stripe 测试模式和测试卡号
  • 使用 Stripe CLI 测试 Webhook 事件
  • 实施适当的错误场景
  • 测试订阅生命周期事件

评论

支持 Markdown