如何将 Stripe Payments 集成到 Chrome 扩展程序(一步一步)
Source: Dev.to
概览
支付流程如下:
- 用户在扩展弹窗中点击 “升级为高级版”。
- 扩展调用无服务器函数生成 Stripe Checkout URL。
- 背景脚本将用户重定向到 Stripe 的安全 Checkout 页面。
- 用户完成支付。
- Stripe 向你的无服务器函数发送 webhook。
- webhook 处理程序将订阅数据保存到数据库。
- 当用户访问高级功能时,扩展会验证其高级状态。
第 1 部分:设置 Stripe
步骤 1 – 创建 Stripe 沙箱账户
用于开发和测试时,请先使用 Stripe 沙箱。请参阅官方文档: 。
步骤 2 – 创建你的产品
-
在 Stripe 仪表板中,进入 Products → Add Product。
-
填写详情:
- Name(名称):“Premium Subscription”
- Description(描述):“Monthly premium access”
- Pricing(定价):“$9.99 / month”(或你选择的价格)
-
复制 Price ID(以
price_开头)。稍后会用到。
步骤 3 – 获取 API 密钥
前往 Developers → API Keys 或访问 。
- 复制 Secret Key(以
sk_test_开头的用于沙箱)。 - 安全保存——切勿将其提交到源码控制。
参考文档:Stripe API Keys Documentation。
第 2 部分:创建无服务器函数
我们将创建两个无服务器端点:
- 生成 Checkout URL
- 处理 Stripe Webhook
项目初始化
安装 Stripe SDK:
npm install stripe --save
参考文档:Stripe SDK Docs。
初始化 Stripe 客户端
创建文件以配置 Stripe 客户端:
// lib/stripe/stripeClient.ts
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: '2025-11-17.clover',
typescript: true,
});
export default stripe;
STRIPE_SECRET_KEY 应在 .env 文件中定义。
函数 1 – 创建 Checkout 链接
此无服务器函数生成个性化的 Checkout URL(预填邮箱)。
// app/api/checkout/route.ts
import stripe from '@/lib/stripe/stripeClient';
interface CreateCheckoutParams {
user?: {
customerId?: string;
email?: string;
};
mode: 'payment' | 'subscription';
clientReferenceId?: string;
priceId: string;
couponId?: string | null;
}
export const createCheckoutStripe = async ({
user,
mode,
clientReferenceId,
priceId,
couponId,
}: CreateCheckoutParams): Promise => {
try {
const extraParams: {
customer?: string;
customer_creation?: 'always';
customer_email?: string;
invoice_creation?: { enabled: boolean };
payment_intent_data?: { setup_future_usage: 'on_session' };
tax_id_collection?: { enabled: boolean };
} = {};
// Existing vs. new customers
if (user?.customerId) {
extraParams.customer = user.customerId;
} else {
if (mode === 'payment') {
extraParams.customer_creation = 'always';
extraParams.invoice_creation = { enabled: true };
extraParams.payment_intent_data = { setup_future_usage: 'on_session' };
}
if (user?.email) extraParams.customer_email = user.email;
extraParams.tax_id_collection = { enabled: true };
}
const stripeSession = await stripe.checkout.sessions.create({
mode,
allow_promotion_codes: true,
client_reference_id: clientReferenceId,
line_items: [{ price: priceId, quantity: 1 }],
discounts: couponId ? [{ coupon: couponId }] : [],
success_url: 'https://example.com/checkout-success',
cancel_url: 'https://example.com/home',
locale: 'auto',
...extraParams,
});
return stripeSession.url;
} catch (e) {
console.error('Error creating checkout session:', e);
return null;
}
};
export async function POST(request: Request) {
try {
const { id, userEmail, mode } = await request.json();
if (!id) {
return new Response(JSON.stringify({ error: 'Price ID is missing' }), {
status: 400,
headers: { 'Content-Type': 'application/json' },
});
}
if (!mode) {
return new Response(
JSON.stringify({
error: "Mode is missing (choose 'payment' or 'subscription')",
}),
{ status: 400, headers: { 'Content-Type': 'application/json' } }
);
}
const checkoutUrl = await createCheckoutStripe({
user: { email: userEmail },
mode,
priceId: id,
});
if (!checkoutUrl) {
return new Response(JSON.stringify({ error: 'Failed to create URL' }), {
status: 5
00,
headers: { 'Content-Type': 'application/json' },
});
}
return new Response(JSON.stringify({ checkout_url: checkoutUrl }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
});
} catch (error) {
console.error('Checkout error:', error);
return new Response(JSON.stringify({ error: 'Internal server error' }), {
status: 500,
headers: { 'Content-Type': 'application/json' },
});
}
}
可选方案:如果不需要预填用户邮箱,你可以直接在 Stripe 仪表板(Products → Checkout Links)创建固定的 Checkout 链接,省去此函数。
函数 2 – 处理 Stripe Webhook
(此处省略实现细节;创建一个端点以验证 Stripe 签名、解析事件,并在数据库中更新订阅状态。)
第 3 部分:构建 Chrome 扩展
扩展文件结构
your-extension/
├── manifest.json
├── popup/
│ ├── popup.html
│ └── popup.tsx
├── background/
│ └── background.ts
├── utils/
│ └── handleCheckout.ts
└── components/
└── PremiumCard.tsx
步骤 1 – Checkout 处理器
调用无服务器 Checkout 端点并返回 URL 的工具函数。
// utils/handleCheckout.ts
/**
* Generates a Stripe checkout URL by calling your serverless function.
* The email is pre‑filled on the Stripe Checkout page.
*/
export async function createCheckoutURL(
email: string | null,
priceId: string,
mode: 'payment' | 'subscription' = 'subscription'
): Promise {
const CHECKOUT_ENDPOINT = 'https://your-app.vercel.app/api/checkout';
try {
const response = await fetch(CHECKOUT_ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
id: priceId,
userEmail: email,
mode,
}),
});
if (!response.ok) {
console.error('Failed to create checkout URL:', response.statusText);
return null;
}
const { checkout_url } = await response.json();
return checkout_url;
} catch (error) {
console.error('Error creating checkout URL:', error);
return null;
}
}
随后可在弹窗或后台脚本中调用 createCheckoutURL,在新标签页打开返回的 URL,并监听 webhook 以更新用户的高级状态。