Next.js 13集成Stripe API:如何在用户付款后升级订阅计划
解决Stripe订阅仅在付款完成后升级的问题
直接调用stripe.subscriptions.update会立即修改订阅记录,完全跳过付款验证环节,这就是你遇到问题的核心原因。正确的做法是通过Stripe Checkout引导用户完成付款流程,只有付款成功后再确认订阅升级,具体步骤如下:
1. 创建订阅升级的Checkout会话
在你的Next.js API中,不要直接更新订阅,而是生成一个Checkout会话,让用户通过Stripe的付款页面完成支付。Stripe会自动处理订阅的升级逻辑,只有付款成功后才会变更订阅。
示例代码:
// pages/api/subscription-upgrade.js import stripe from '@/utils/stripe'; export default async function handler(req, res) { if (req.method !== 'POST') { return res.status(405).json({ error: '仅支持POST请求' }); } const { currentSubscriptionId, newPlanId, customerId } = req.body; try { const checkoutSession = await stripe.checkout.sessions.create({ mode: 'subscription', // 指定要更新的现有订阅 subscription_update: { subscription: currentSubscriptionId, // 可选:控制 prorate 计费方式,比如'none'表示不按剩余周期收费 proration_behavior: 'create_prorations' }, line_items: [ { price: newPlanId, quantity: 1 } ], // 付款成功后的跳转地址 success_url: `${process.env.NEXT_PUBLIC_APP_URL}/dashboard?upgrade=success`, // 取消付款后的跳转地址 cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}/dashboard?upgrade=cancel`, // 关联用户的Stripe客户ID customer: customerId }); res.status(200).json({ checkoutUrl: checkoutSession.url }); } catch (err) { res.status(500).json({ error: err.message }); } }
2. 通过Webhook确认付款完成并更新本地数据
付款完成后,Stripe会发送checkout.session.completed的Webhook事件,你需要在后端监听这个事件,确认付款成功后,再更新自己数据库中的用户订阅信息(比如priceId、订阅状态等)。
示例Webhook处理代码:
// pages/api/webhooks/stripe.js import stripe from '@/utils/stripe'; import { buffer } from 'micro'; import { updateUserSubscription } from '@/utils/db'; // 自定义的数据库更新函数 export const config = { api: { bodyParser: false } }; export default async function handler(req, res) { if (req.method !== 'POST') { return res.status(405).end(); } const buf = await buffer(req); const sig = req.headers['stripe-signature']; const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET; let event; try { event = stripe.webhooks.constructEvent(buf, sig, webhookSecret); } catch (err) { return res.status(400).send(`Webhook验证失败: ${err.message}`); } // 处理付款完成事件 if (event.type === 'checkout.session.completed') { const session = event.data.object; // 获取更新后的订阅详情 const updatedSubscription = await stripe.subscriptions.retrieve(session.subscription); const newPriceId = updatedSubscription.items.data[0].price.id; // 更新本地数据库中的用户订阅信息 await updateUserSubscription(session.customer, { stripeSubscriptionId: updatedSubscription.id, currentPriceId: newPriceId, subscriptionStatus: updatedSubscription.status }); } res.status(200).json({ received: true }); }
关键注意事项
- 绝对不能依赖前端的
success_url回调来更新本地数据,因为前端可以被篡改,必须以Stripe发送的Webhook事件为准。 - 如果是同价格的计划切换,Stripe会直接更新订阅而无需付款,但如果是升级到更高价格的计划,会强制用户完成付款流程。
- 确保在Stripe后台配置好Webhook地址,并启用
checkout.session.completed事件。
内容的提问来源于stack exchange,提问作者Emmanuel Dufitumukiza
相关产品推荐
相关产品推荐

