You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift发送带JSON Body的POST请求对接Stripe后端服务问题排查

问题排查及修复方案
  • 参数类型不匹配:你代码中parameters字典的price字段值为字符串"50",接口要求传入数字类型50,类型错误会直接导致服务端参数解析失败。
  • JSON序列化无异常处理:使用try?进行JSON序列化会忽略所有序列化错误,一旦序列化失败会得到空的jsonData,导致请求无有效Body。
  • Content-Length头设置错误:对可选类型的jsonData?.count使用String(describing:)格式化,会生成带Optional()标识的非法值,不符合HTTP头规范。实际上URLSession会自动根据httpBody计算Content-Length,无需手动设置该头字段。
  • 控件逻辑不一致:代码开头禁用的是checkoutButton,请求成功后启用的是buyButton,如果为同一控件会导致按钮始终无法点击,需确认控件命名对应关系。
  • 无错误日志定位:当前代码无任何错误打印逻辑,请求失败、返回值解析失败时直接静默返回,无法定位具体错误原因。

修正后代码

override func viewDidLoad() {
    super.viewDidLoad()
    checkoutButton.addTarget(self, action: #selector(didTapCheckoutButton), for: .touchUpInside)
    checkoutButton.isEnabled = false
    
    // 修正:price改为数字类型,符合接口要求
    let parameters: [String: Any] = [
        "price": 50
    ]
    
    // 修正:使用do-catch处理序列化异常,避免得到空Body
    do {
        let jsonData = try JSONSerialization.data(withJSONObject: parameters)
        var request = URLRequest(url: backendCheckoutUrl)
        request.httpMethod = "POST"
        // 修正:删除手动设置Content-Length,由系统自动计算
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.httpBody = jsonData
        
        let task = URLSession.shared.dataTask(with: request, completionHandler: { [weak self] (data, response, error) in
            // 新增:先打印错误和响应状态码,方便定位问题
            if let error = error {
                print("请求错误:\(error.localizedDescription)")
                return
            }
            if let httpResponse = response as? HTTPURLResponse {
                print("响应状态码:\(httpResponse.statusCode)")
            }
            if let data = data, let responseStr = String(data: data, encoding: .utf8) {
                print("服务端返回内容:\(responseStr)")
            }
            
            guard let self = self,
                  let data = data,
                  let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String : Any],
                  let customerId = json["customer"] as? String,
                  let customerEphemeralKeySecret = json["ephemeralKey"] as? String,
                  let paymentIntentClientSecret = json["paymentIntent"] as? String,
                  let publishableKey = json["publishableKey"] as? String else {
                print("返回参数解析失败")
                return
            }
            
            STPAPIClient.shared.publishableKey = publishableKey
            
            var configuration = PaymentSheet.Configuration()
            configuration.merchantDisplayName = "Example, Inc."
            configuration.applePay = .init(
                merchantId: "com.foo.example", merchantCountryCode: "US"
            )
            configuration.customer = .init(
                id: customerId, ephemeralKeySecret: customerEphemeralKeySecret
            )
            configuration.returnURL = "....."
            configuration.allowsDelayedPaymentMethods = true
            self.paymentSheet = PaymentSheet(
                paymentIntentClientSecret: paymentIntentClientSecret,
                configuration: configuration
            )
            
            DispatchQueue.main.async {
                // 修正:统一控件命名,和开头的checkoutButton保持一致
                self.checkoutButton.isEnabled = true
            }
        })
        task.resume()
    } catch {
        print("JSON序列化失败:\(error.localizedDescription)")
    }
}

额外排查建议

如果修改后仍然无法正常运行,可以根据控制台打印的状态码、返回内容判断问题:

  • 状态码400:参数仍然不符合服务端要求,核对参数名、参数类型是否和接口要求完全一致
  • 状态码401/403:接口有权限校验,检查是否需要携带token等鉴权信息
  • 状态码500:服务端内部错误,联系后端人员排查服务端逻辑

内容的提问来源于stack exchange,提问作者Tarik SLIMANI

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.23 21:45:05