如何更新Stripe Price(payment_intent)以匹配所选订阅时长?
解决Stripe订阅价格动态更新问题(无表单提交)
核心思路
放弃iframe方案,改用AJAX异步请求将选中的订阅时长传给后端PHP,后端返回对应Stripe价格配置,前端再初始化支付模态框,全程无需刷新页面。
具体实现步骤
1. 前端HTML与JavaScript逻辑
监听下拉菜单变化,异步获取对应价格ID,点击支付按钮后初始化Stripe卡片元素并弹出模态框:
<!-- 订阅时长下拉菜单 --> <select id="subscription-duration"> <option value="1">1个月</option> <option value="3">3个月</option> <option value="6">6个月</option> <option value="12">12个月</option> </select> <!-- 触发支付模态框的按钮 --> <button id="checkout-btn">确认订阅</button> <!-- Stripe支付模态框(默认隐藏) --> <div id="stripe-modal" style="display:none;"> <div id="card-element"></div> <button id="pay-btn">完成支付</button> </div> <script src="https://js.stripe.com/v3/"></script> <script> const stripe = Stripe('你的Stripe公钥'); const elements = stripe.elements(); let currentPriceId = ''; // 存储当前选中的订阅价格ID // 监听下拉菜单选择变化 document.getElementById('subscription-duration').addEventListener('change', async function() { const duration = this.value; // 异步请求后端获取对应价格ID const response = await fetch('get-price.php', { method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: `duration=${duration}` }); const data = await response.json(); currentPriceId = data.price_id; }); // 点击支付按钮,初始化卡片元素并显示模态框 document.getElementById('checkout-btn').addEventListener('click', function() { if (!currentPriceId) { alert('请先选择订阅时长'); return; } const cardElement = elements.create('card'); cardElement.mount('#card-element'); document.getElementById('stripe-modal').style.display = 'block'; }); // 提交支付请求 document.getElementById('pay-btn').addEventListener('click', async function() { // 先调用后端创建订阅/支付意图 const createResponse = await fetch('create.php', { method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: `price_id=${currentPriceId}` }); const createData = await createResponse.json(); // 确认卡片支付 const { error } = await stripe.confirmCardPayment(createData.client_secret, { payment_method: { card: cardElement, billing_details: {name: '用户姓名'} // 可替换为用户输入的姓名 } }); if (error) { console.error(error.message); } else { alert('订阅成功'); document.getElementById('stripe-modal').style.display = 'none'; } }); </script>
2. 后端get-price.php(映射时长到Stripe价格ID)
根据前端传递的订阅时长,返回预定义的Stripe价格ID(需提前在Stripe后台创建对应订阅价格):
<?php $duration = $_POST['duration'] ?? ''; // 定义各订阅时长对应的Stripe价格ID $priceMap = [ '1' => 'price_xxxxxx', // 1个月订阅价格ID '3' => 'price_yyyyyy', // 3个月订阅价格ID '6' => 'price_zzzzzz', // 6个月订阅价格ID '12' => 'price_aaaaaa' // 12个月订阅价格ID ]; if (isset($priceMap[$duration])) { echo json_encode(['price_id' => $priceMap[$duration]]); } else { echo json_encode(['error' => '无效的订阅时长']); } ?>
3. 后端create.php(创建Stripe订阅/支付意图)
接收前端传递的价格ID,创建Stripe订阅对象并返回客户端密钥:
<?php require_once 'vendor/autoload.php'; \Stripe\Stripe::setApiKey('你的Stripe私钥'); $priceId = $_POST['price_id'] ?? ''; if (!$priceId) { http_response_code(400); echo json_encode(['error' => '缺少价格ID']); exit; } try { // 创建订阅(替换cus_xxxxxx为实际用户的Customer ID,或先创建新Customer) $subscription = \Stripe\Subscription::create([ 'customer' => 'cus_xxxxxx', 'items' => [['price' => $priceId]], 'payment_behavior' => 'default_incomplete', 'payment_settings' => ['save_payment_method' => 'on_subscription'], 'expand' => ['latest_invoice.payment_intent'], ]); echo json_encode([ 'client_secret' => $subscription->latest_invoice->payment_intent->client_secret ]); } catch (\Stripe\Exception\ApiErrorException $e) { http_response_code(500); echo json_encode(['error' => $e->getMessage()]); } ?>
关键说明
- AJAX方案避免了iframe的上下文隔离问题,变量传递更直接可靠。
- 前端提前通过下拉变化获取价格ID,无需等待表单提交再处理价格逻辑。
- 后端拆分
get-price.php和create.php,职责更清晰,便于维护。
内容的提问来源于stack exchange,提问作者cleanlock
相关产品推荐
相关产品推荐

