Node.js报错Cannot read property 'reduce' of undefined的SwiftUI支付问题
Stripe支付对接报错修复:Cannot read property 'reduce' of undefined
问题背景
在Glitch部署基于Express的Node.js服务器,集成Stripe与CORS用于对接SwiftUI电商APP支付功能时,控制台出现以下错误:
(node:1314) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'reduce' of undefined at calculateOrderAmount (/app/server.js:11:23)
错误来自计算订单总金额的calculateOrderAmount函数。
错误原因
- 前后端数据格式不匹配:前端直接将商品数组作为请求体发送,但后端期望接收包含
items字段的对象,导致后端req.body.items为undefined,调用reduce时崩溃。 - reduce缺少初始值:即使
items是有效数组,当数组为空时,无初始值的reduce也会抛出错误。 - 额外问题:服务器代码误用Stripe公钥(应使用Secret Key),且未配置CORS中间件(跨域请求会失败)。
修复方案
1. 前端修改(SwiftUI)
修改startCheckout函数,将请求体包装为包含items键的对象:
extension OrderView { private func startCheckout(completion: @escaping (String?) -> Void) { let url = URL(string: "https://rogue-quartz-ounce.glitch.me/create-payment-intent")! var request = URLRequest(url: url) request.httpMethod = "POST" request.setValue("application/json", forHTTPHeaderField: "Content-Type") // 修复:将商品数组包装为带items键的字典 let requestBody = ["items": order.products] do { request.httpBody = try JSONEncoder().encode(requestBody) } catch { print("编码失败:\(error)") completion(nil) return } URLSession.shared.dataTask(with: request) { data, response, error in guard let data = data, error == nil, (response as? HTTPURLResponse)?.statusCode == 200 else { completion(nil) print(error?.localizedDescription ?? "请求失败") return } let checkoutIntentResponse = try? JSONDecoder().decode(CheckoutIntentResponse.self, from: data) completion(checkoutIntentResponse?.clientSecret) }.resume() } }
2. 后端修改(Node.js/Express)
- 添加CORS中间件
- 替换Stripe密钥为Secret Key
- 给
reduce添加初始值0 - 添加参数校验,避免
items为undefined或非数组时崩溃
const express = require("express"); const app = express(); // 修复:使用Stripe Secret Key(注意保密,不要公开) const stripe = require("stripe")("your_stripe_secret_key"); // 添加CORS中间件 const cors = require("cors"); app.use(express.static("public")); app.use(express.json()); app.use(cors()); // 启用CORS处理跨域请求 const calculateOrderAmount = (items) => { // 修复1:校验items是否为数组,非数组则返回0 if (!Array.isArray(items)) return 0; // 修复2:给reduce添加初始值0,避免空数组报错 const total = items.reduce((previous, current) => { // 额外:校验current是否有price属性,避免意外错误 return previous + (current.price || 0); }, 0); return total * 100; }; app.post("/create-payment-intent", async (req, res) => { const { items } = req.body; try { const paymentIntent = await stripe.paymentIntents.create({ amount: calculateOrderAmount(items), currency: "gbp", automatic_payment_methods: { enabled: true, }, }); res.send({ clientSecret: paymentIntent.client_secret, }); } catch (error) { // 修复:添加错误捕获,避免未处理的Promise拒绝 res.status(500).send({ error: error.message }); } }); app.listen(8080, () => console.log("Node server listening on port 8080!"));
额外注意事项
- Stripe密钥区分Publishable Key(前端用)和Secret Key(后端用),务必不要混淆或公开Secret Key。
- 前端避免使用
try!强制解包,改用do-catch处理编码错误,防止APP崩溃。
内容的提问来源于stack exchange,提问作者user21666271
相关产品推荐
相关产品推荐

