电商网站Checkout按钮与Stripe通信失败问题求助
电商网站Stripe结账跳转问题咨询
我正在搭建一个电商网站,遇到提交(结账按钮)无法跳转到Stripe结账页面的问题。我是按照自定义支付流程进行开发的,想咨询是否可能遗漏了某些步骤?
以下是我的代码,包含server.js、checkout.js和index.html:
server.js
const express = require("express"); const app = express(); // This is your test secret API key. const stripe = require("stripe")('sk_test_XXXXXXXX'); app.use(express.static("public")); app.use(express.json()); const calculateOrderAmount = (items) => { [1, { priceInCents: 8500, name: "Black Sweatsuit"}], [2, { priceInCents: 8500, name: "Grey Sweatsuit"}], [3, { priceInCents: 8500, name: "Red Sweatsuit"}], [4, { priceInCents: 8500, name: "Blue Sweatsuit"}] return 1400; }; app.post("/create-payment-intent", async (req, res) => { const { items } = req.body; // Create a PaymentIntent with the order amount and currency const paymentIntent = await stripe.paymentIntents.create({ amount: calculateOrderAmount(items), payment_method_types: ["card"], mode: "payment", currency: "usd", automatic_payment_methods: { enabled: true, }, }); res.send({ clientSecret: paymentIntent.client_secret, }); }); const endpointSecret = "whsec_93f2310c53f1bdbb9496049a38ea5ceadbce03fcf4223b2be51c5b6717f2cc60"; app.post('/webhook', express.raw({type: 'application/json'}), (request, response) => { const sig = request.headers['stripe-signature']; let event; try { event = stripe.webhooks.constructEvent(request.body, sig, endpointSecret); } catch (err) { response.status(400).send(`Webhook Error: ${err.message}`); return; } // Handle the event switch (event.type) { case 'payment_intent.succeeded': const paymentIntent = event.data.object; // Then define and call a function to handle the event payment_intent.succeeded break; // ... handle other event types default: console.log(`Unhandled event type ${event.type}`); } // Return a 200 response to acknowledge receipt of the event response.send(); }); app.listen(4242, () => console.log('Running on port 4242'));
checkout.js
// This is your test publishable API key. const stripe = Stripe("pk_test_XXXXX"); // The items the customer wants to buy const items = [{ id: "Black Sweatsuit"}] [{ id: "Grey Sweatsuit"}] [{ id: "Red Sweatsuit"}] [{ id: "Blue Sweatsuit"}]; let elements; initialize(); checkStatus(); document .querySelector("#payment-form") .addEventListener("submit", handleSubmit); // Fetches a payment intent and captures the client secret async function initialize() { const response = await fetch("/create-payment-intent", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ items }), }); const { clientSecret } = await response.json(); const appearance = { theme: 'stripe', }; elements = stripe.elements({ appearance, clientSecret }); const paymentElement = elements.create("payment"); paymentElement.mount("#payment-element"); } async function handleSubmit(e) { e.preventDefault(); setLoading(true); const { error } = await stripe.confirmPayment({ elements, confirmParams: { // Make sure to change this to your payment completion page return_url: "http://localhost:4242/checkout.html", }, }); // This point will only be reached if there is an immediate error when // confirming the payment. Otherwise, your customer will be redirected to // your `return_url`. For some payment methods like iDEAL, your customer will // be redirected to an intermediate site first to authorize the payment, then // redirected to the `return_url`. if (error.type === "card_error" || error.type === "validation_error") { showMessage(error.message); } else { showMessage("An unexpected error occurred."); } setLoading(false); } // Fetches the payment intent status after payment submission async function checkStatus() { const clientSecret = new URLSearchParams(window.location.search).get( "payment_intent_client_secret" ); if (!clientSecret) { return; } const { paymentIntent } = await stripe.retrievePaymentIntent(clientSecret); switch (paymentIntent.status) { case "succeeded": showMessage("Payment succeeded!"); break; case "processing": showMessage("Your payment is processing."); break; case "requires_payment_method": showMessage("Your payment was not successful, please try again."); break; default: showMessage("Something went wrong."); break; } } // ------- UI helpers ------- function showMessage(messageText) { const messageContainer = document.querySelector("#payment-message"); messageContainer.classList.remove("hidden"); messageContainer.textContent = messageText; setTimeout(function () { messageContainer.classList.add("hidden"); messageText.textContent = ""; }, 4000); } // Show a spinner on payment submission function setLoading(isLoading) { if (isLoading) { // Disable the button and show a spinner document.querySelector("#submit").disabled = true; document.querySelector("#spinner").classList.remove("hidden"); document.querySelector("#button-text").classList.add("hidden"); } else { document.querySelector("#submit").disabled = false; document.querySelector("#spinner").classList.add("hidden"); document.querySelector("#button-text").classList.remove("hidden"); } }
index.html
</div> <div class="subtotal">Subtotal: $0.00</div> <form id="payment-form"> <input type="text" id="email" placeholder="Enter email address" /> <div id="payment-element"> <!--Stripe.js injects the Payment Element--> </div> <button id="submit"> <div class="spinner hidden" id="spinner"></div> <span id="button-text">Checkout</span> </button> <div id="payment-message" class="hidden"></div> </form> </div>
问题排查与修复建议
1. 前端checkout.js语法错误(核心问题)
你在定义items时的写法完全错误,当前代码会抛出语法异常,导致后续的initialize()函数无法正常执行,Stripe Elements无法初始化,最终点击按钮无反应:
// 错误写法 const items = [{ id: "Black Sweatsuit"}] [{ id: "Grey Sweatsuit"}] [{ id: "Red Sweatsuit"}] [{ id: "Blue Sweatsuit"}];
正确的数组定义需要用逗号分隔每个对象:
// 正确写法 const items = [ { id: "Black Sweatsuit" }, { id: "Grey Sweatsuit" }, { id: "Red Sweatsuit" }, { id: "Blue Sweatsuit" } ];
这个语法错误会直接阻断代码执行,浏览器控制台可以看到报错信息。
2. 确认Stripe.js脚本已引入
你的index.html片段中没有显示引入Stripe的官方脚本,必须在页面中添加这行代码(建议放在<head>标签内),否则Stripe对象无法被识别:
<script src="https://js.stripe.com/v3/"></script>
同时要确保checkout.js也正确引入到页面中,比如在</body>前添加:
<script src="/checkout.js"></script>
3. 后端calculateOrderAmount函数未正确处理商品金额
当前函数只是罗列了商品数据,但完全没有使用传入的items参数计算金额,直接返回固定值1400(14美元)。虽然这不会直接导致跳转失败,但会导致订单金额与实际商品不符,建议修复为根据传入的items计算总金额:
const calculateOrderAmount = (items) => { // 先定义商品价格映射 const itemPrices = { "Black Sweatsuit": 8500, "Grey Sweatsuit": 8500, "Red Sweatsuit": 8500, "Blue Sweatsuit": 8500 }; // 计算选中商品的总金额 return items.reduce((total, item) => total + (itemPrices[item.id] || 0), 0); };
4. 检查浏览器控制台报错
点击结账按钮前,打开浏览器开发者工具(F12)的控制台标签,查看是否有其他报错信息,比如网络请求失败(/create-payment-intent接口返回错误)、元素未找到等,这些都可能导致流程中断。
按照上述步骤修复后,重新启动服务测试,应该能正常触发Stripe的支付流程。
内容的提问来源于stack exchange,提问作者user16147870
相关产品推荐
相关产品推荐

