Stripe支付API发送OPTIONS请求而非POST的问题解决咨询
问题分析
首先明确两个核心问题:
- 你的后端
create-checkout-session接口未使用前端传入的customerId和tokenId参数——创建Session时未关联已有的客户,也未处理传入的token,导致前期创建客户和token的操作完全无效,Checkout会重新收集客户信息。 - 支付失败的根源是AWS拦截了前端与Stripe通信时的OPTIONS预检请求,这主要发生在前端直接调用Stripe API(如
stripe.createToken)的场景。
以下是基于现有JavaScript代码的可行解决方向:
方案一:移除stripe.createToken,直接用Checkout关联已有客户
既然已成功创建Stripe客户,完全不需要手动生成card token——Stripe Checkout可直接关联已有客户并自行处理卡片信息收集,避免前端直接调用Stripe API触发OPTIONS请求。
前端代码修改
const appearance = { theme: 'night', }; const stripe = Stripe('pk_test_51PccgFRo1v56iAOxEVEimciGkyyLOcjVE98T3rqOHLuvx28QNxI8sfvYN0zwSka8vLRIIIEdkOHorjb5OJUdwhIx00VRs64Seu'); const elements = stripe.elements({appearance: {theme: 'night'}}); const card = elements.create('card'); card.mount('#payment-element'); document.querySelector("#payment-form").addEventListener("submit", async (event) => { event.preventDefault(); document.getElementById('submit').disabled = true; const name = document.querySelector("#name").value; const email = document.querySelector("#email").value; try { // 创建客户 const customerResponse = await fetch("http://localhost:4242/create-customer", { method: "POST", headers: {"Content-Type": "application/json"}, body: JSON.stringify({ name, email }), }); if (!customerResponse.ok) throw new Error("Failed to create customer."); const customerData = await customerResponse.json(); // 仅传入customerId,无需token await initializePayment(customerData.customerId); } catch (error) { document.querySelector("#error-message").textContent = error.message || "Failed to create customer."; console.error("Error:", error); document.getElementById('submit').disabled = false; } }); async function initializePayment(customerId) { try { const sessionResponse = await fetch("http://localhost:4242/create-checkout-session", { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ customer: customerId, priceId: 'price_1PfUDSRo1v56iAOxxLORAlv9' }) }); if (!sessionResponse.ok) { const errorData = await sessionResponse.json(); throw new Error(errorData.message || `Failed to create checkout session.`); } const sessionData = await sessionResponse.json(); window.location.href = sessionData.url; } catch (error) { document.querySelector("#error-message").textContent = error.message || "Failed to process payment."; console.error('Error:', error); } finally { document.getElementById('submit').disabled = false; } }
后端代码修改(create-checkout-session接口)
post("/create-checkout-session", (request, response) -> { response.type("application/json"); Map<String, Object> requestData = gson.fromJson(request.body(), Map.class); String priceId = (String) requestData.get("priceId"); String customerId = (String) requestData.get("customer"); SessionCreateParams params = new SessionCreateParams.Builder() .setSuccessUrl(domainUrl + "/success.html?session_id={CHECKOUT_SESSION_ID}") .setCancelUrl(domainUrl + "/canceled.html") .setMode(SessionCreateParams.Mode.SUBSCRIPTION) .setCustomer(customerId) // 关联已有客户 .addLineItem(new SessionCreateParams.LineItem.Builder() .setQuantity(1L) .setPrice(priceId) .build() ) .build(); try { Session session = Session.create(params); Map<String, Object> responseData = new HashMap<>(); responseData.put("url", session.getUrl()); response.status(200); return gson.toJson(responseData); } catch (Exception e) { Map<String, Object> messageData = new HashMap<>(); messageData.put("message", e.getMessage()); Map<String, Object> responseData = new HashMap<>(); responseData.put("error", messageData); response.status(400); return gson.toJson(responseData); } });
方案二:改用Stripe Payment Intents API(不跳转Checkout)
如果不想跳转Stripe Checkout页面,可直接用Stripe Elements结合Payment Intents完成支付,全程在你的页面内操作,减少跨域请求触发点。
前端代码修改
const appearance = { theme: 'night', }; const stripe = Stripe('pk_test_51PccgFRo1v56iAOxEVEimciGkyyLOcjVE98T3rqOHLuvx28QNxI8sfvYN0zwSka8vLRIIIEdkOHorjb5OJUdwhIx00VRs64Seu'); const elements = stripe.elements({appearance: {theme: 'night'}}); const card = elements.create('card'); card.mount('#payment-element'); document.querySelector("#payment-form").addEventListener("submit", async (event) => { event.preventDefault(); document.getElementById('submit').disabled = true; const name = document.querySelector("#name").value; const email = document.querySelector("#email").value; try { // 1. 创建客户 const customerResponse = await fetch("http://localhost:4242/create-customer", { method: "POST", headers: {"Content-Type": "application/json"}, body: JSON.stringify({ name, email }), }); if (!customerResponse.ok) throw new Error("Failed to create customer."); const customerData = await customerResponse.json(); // 2. 后端创建Payment Intent const intentResponse = await fetch("http://localhost:4242/create-payment-intent", { method: "POST", headers: {"Content-Type": "application/json"}, body: JSON.stringify({ customerId: customerData.customerId, priceId: 'price_1PfUDSRo1v56iAOxxLORAlv9' }) }); if (!intentResponse.ok) { const errorData = await intentResponse.json(); throw new Error(errorData.message || "Failed to create payment intent."); } const intentData = await intentResponse.json(); // 3. 前端确认支付,无需跳转 const { error } = await stripe.confirmCardPayment(intentData.clientSecret, { payment_method: { card: card, billing_details: { name: name, email: email } } }); if (error) { throw new Error(error.message); } else { window.location.href = "/success.html"; } } catch (error) { document.querySelector("#error-message").textContent = error.message || "Payment failed."; console.error("Error:", error); document.getElementById('submit').disabled = false; } });
后端新增create-payment-intent接口
post("/create-payment-intent", (request, response) -> { response.type("application/json"); Map<String, Object> requestData = gson.fromJson(request.body(), Map.class); String customerId = (String) requestData.get("customerId"); String priceId = (String) requestData.get("priceId"); Price price = Price.retrieve(priceId); long amount = price.getUnitAmount(); PaymentIntentCreateParams params = PaymentIntentCreateParams.builder() .setCustomer(customerId) .setAmount(amount) .setCurrency("usd") // 根据你的价格币种调整 .setPaymentMethodTypes(List.of("card")) .setSetupFutureUsage(PaymentIntentCreateParams.SetupFutureUsage.OFF_SESSION) .build(); try { PaymentIntent intent = PaymentIntent.create(params); Map<String, Object> responseData = new HashMap<>(); responseData.put("clientSecret", intent.getClientSecret()); return gson.toJson(responseData); } catch (StripeException e) { response.status(400); Map<String, Object> errorData = new HashMap<>(); errorData.put("error", e.getMessage()); return gson.toJson(errorData); } });
额外注意事项
- 确保后端CORS配置允许
http://localhost:8443(或HTTPS地址)的请求,避免前端调用后端接口时触发OPTIONS失败。 - 若AWS限制来自WAF规则,需检查是否放行Stripe相关域名(
api.stripe.com、checkout.stripe.com)的OPTIONS请求。
内容的提问来源于stack exchange,提问作者raph mwanza
相关产品推荐
相关产品推荐

