Stripe订阅计划创建前后收费方案及页面无限加载问题排查
问题分析与解决方案
核心问题
- 混淆Stripe对象作用:你把
Subscription Schedule和Checkout Session搞混了——Subscription Schedule仅用于管理订阅的阶梯阶段,不负责收集用户支付信息,也没有可跳转的支付页面URL。原代码里尝试跳转到$session->url,但这里的$session是Subscription Schedule对象,根本不存在url属性,直接导致跳转失败,页面无限加载。 - 缺失支付方式绑定:直接创建Customer和Subscription Schedule时,客户没有关联支付方式,Stripe无法自动发起扣费,最终订阅会处于
incomplete状态。
方案选择:Stripe Checkout vs 自定义支付页面
- 优先用Stripe Checkout:无需自行处理卡片收集逻辑,自动符合PCI合规要求,自带成熟的支付页面、错误处理和跳转流程,开发效率高,适合绝大多数场景。
- 自定义页面仅用于高度定制需求:需要集成Stripe Elements收集卡片信息,还要严格处理PCI合规(必须通过Stripe.js处理,不能直接获取卡片明文),开发成本高,仅当Checkout无法满足UI定制需求时考虑。
正确实现步骤(基于Stripe Checkout + Subscription Schedule)
1. 前端触发Checkout流程
修改前端按钮逻辑,通过AJAX调用后端接口创建Checkout Session,而非直接提交表单:
public static function firebase_checkout3_func() { $html = ""; $html .= "<button type='button' id='checkout-button'>Începe acum</button>"; $html .= "<script> document.getElementById('checkout-button').addEventListener('click', async () => { const response = await fetch('/wp-json/api/checkout3', { method: 'POST' }); const session = await response.json(); window.location.href = session.url; }); </script>"; return $html; }
2. 后端创建Checkout Session
后端接口不再直接创建Subscription Schedule,而是生成Checkout Session让用户完成支付、绑定支付方式并创建初始订阅:
function checkout3() { \Stripe\Stripe::setApiKey('sk_test_51e7DRPLRnISGb5vSFxnvvuDx1GzhlBIFeazcmpEevsUFf29jHXJ1YgE2xaJ1lGfzjtKzE8uoN0eR9Klaq00CnMFWvfB'); // 创建Checkout Session,用于收集支付方式并生成初始订阅 $checkout_session = \Stripe\Checkout\Session::create([ 'mode' => 'subscription', 'payment_method_types' => ['card'], 'line_items' => [ [ 'price' => 'price_1LRF5CIne7DRPLRnwuLVE2pu', // 首月收费价格 'quantity' => 1, ], ], 'success_url' => 'https://your-domain.com/success', // 支付成功跳转页 'cancel_url' => 'https://your-domain.com/cancel', // 支付取消跳转页 'subscription_data' => [ 'trial_end' => null, // 无试用期 ], ]); // 返回Checkout页面URL给前端跳转 wp_send_json(['url' => $checkout_session->url]); }
3. 通过Webhook设置阶梯订阅
用户完成支付后,Stripe会发送checkout.session.completed事件到你的Webhook端点,此时可以获取Customer和订阅ID,创建Subscription Schedule替换原订阅,设置阶梯收费规则:
function handle_stripe_webhook() { \Stripe\Stripe::setApiKey('sk_test_...'); $payload = @file_get_contents('php://input'); $event = \Stripe\Event::constructFromJson($payload); if ($event->type === 'checkout.session.completed') { $session = $event->data->object; $customer_id = $session->customer; $subscription_id = $session->subscription; // 创建Subscription Schedule,配置阶梯收费阶段 $schedule = \Stripe\SubscriptionSchedule::create([ 'from_subscription' => $subscription_id, 'phases' => [ [ 'items' => [ [ 'price' => 'price_1LRF5CIne7DRPLRnwuLVE2pu', // 首月收费 'quantity' => 1, ], ], 'iterations' => 1, ], [ 'items' => [ [ 'price' => 'price_1LRF5cIne7DRPLRngevvIZiw', // 次月收费 'quantity' => 1, ], ], 'iterations' => 1, ], [ 'items' => [ [ 'price' => 'price_1LPujQIne7DRPLRnj3EOweJN', // 一年后永久收费 'quantity' => 1, ], ], ], ], ]); } http_response_code(200); }
4. 原代码的紧急修复(若坚持直接创建Subscription Schedule)
如果必须跳过Checkout流程,需先让用户通过Stripe Elements绑定支付方式到Customer,再创建Subscription Schedule:
- 前端用Stripe Elements收集卡片信息,生成Payment Method。
- 后端创建Customer时,设置
invoice_settings[default_payment_method]为生成的Payment Method ID。 - 最后创建Subscription Schedule,此时Stripe会自动发起扣费。
但这种方式需要自行处理支付方式收集逻辑,远不如Checkout流程简便。
额外注意事项
- 在Stripe后台配置正确的Webhook端点,并启用
checkout.session.completed事件。 - 生产环境务必切换为Live Secret Key,严禁泄露测试密钥。
- 测试时使用Stripe官方测试卡号(如
4242 4242 4242 4242)验证流程。
内容的提问来源于stack exchange,提问作者Roland Iordache
相关产品推荐
相关产品推荐

