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

iOS集成Stripe支付requestPayment报无活跃卡片错误排查

问题根因
  • 绑卡逻辑缺失核心步骤:attatchPaymentMethod云函数直接更新客户默认支付方式配置,但从未调用Stripe的PaymentMethod附加接口将支付方式绑定到客户名下。未绑定的PaymentMethod不属于任何客户,Stripe控制台无法识别展示,自然无法用于扣款。
  • 接口调用时序错误:在用户选卡的paymentContextDidChange回调中并行异步触发绑卡、创建SetupIntent请求,既没有保证执行顺序,也没有等待请求完成就允许用户发起扣款,经常出现扣款时绑卡流程还未执行完成的情况。另外创建的SetupIntent从未做客户端确认,完全是无效逻辑。
  • 扣款接口版本不匹配:使用已废弃的老版stripe.charges.create接口且未传入支付方式参数,该接口默认只会读取客户名下老版Card/Sources体系的支付源(也就是手动在后台添加的卡类型),无法识别新版PaymentMethod体系下App端添加的卡,这是手动加卡可支付、App端加卡报错的核心原因。
  • 全局变量风险:服务端定义了全局customer变量存储客户ID,多用户并发请求时会出现客户数据串号的严重问题。
修复方案

服务端(index.js)修改

  1. 删除开头的全局var customer变量,避免并发串数据
  2. 重写绑卡方法,先执行支付方式附加操作,再设置为默认支付方式,补充权限校验
  3. 废弃老版Charges扣款接口,改用PaymentIntent接口完成扣款,显式传入支付方式ID
  4. 可删除无实际作用的setupIntent云函数,单次扣款场景不需要单独创建SetupIntent

对应修改后的核心代码:

// 重写绑卡方法
exports.attatchPaymentMethod = functions.https.onCall(async (data , context) => {
    if (!context.auth) {
        throw new functions.https.HttpsError('permission-denied' , '未授权访问');
    }
    const paymentMethodId = data.paymentMethod;
    const customerId = data.customer;

    // 补全之前缺失的支付方式绑定步骤
    await stripe.paymentMethods.attach(paymentMethodId, {
        customer: customerId,
    });

    // 绑定成功后再设置为默认支付方式
    const updatedCustomer = await stripe.customers.update(
        customerId,
        {
            invoice_settings: {
                default_payment_method: paymentMethodId
            }
        }
    );
    return updatedCustomer;
})

// 重写扣款方法
exports.makeCharge = functions.https.onCall(async (data , context) => {
    const customerId = data.customerID;
    const total = data.total;
    const paymentMethodId = data.paymentMethodId;
    const idempotency = data.idempotency;

    if (!context.auth) {
        throw new functions.https.HttpsError('permission-denied' , '非法访问')
    }

    // 用新版PaymentIntent接口完成扣款,显式指定支付方式
    const paymentIntent = await stripe.paymentIntents.create({
        amount: total,
        currency: 'JOD',
        customer: customerId,
        payment_method: paymentMethodId,
        off_session: true,
        confirm: true,
    }, {
        idempotency_key: idempotency
    });

    return paymentIntent;
})

iOS客户端修改

  1. 删除paymentContextDidChange回调中所有调用云函数的逻辑,仅保留支付方式选中状态的UI更新,STPPaymentContext会通过临时密钥自动拉取可用支付方式,不需要选卡时提前触发服务端操作
  2. 在didCreatePaymentResult回调中按顺序执行:先绑卡设为默认,再发起扣款,确保步骤串行执行,同时将当前选中的支付方式ID传给服务端扣款接口

对应修改后的核心代理代码:

// 精简选卡回调逻辑,只做UI更新
func paymentContextDidChange(_ paymentContext: STPPaymentContext) {
    if let paymentMethod = paymentContext.selectedPaymentOption {
        selectPaymentMethodBtn.setTitle(paymentMethod.label, for: .normal)
    } else {
        selectPaymentMethodBtn.setTitle("Select Method", for: .normal)
    }
}

// 重写支付结果回调,串行执行绑卡、扣款逻辑
func paymentContext(_ paymentContext: STPPaymentContext, didCreatePaymentResult paymentResult: STPPaymentResult, completion: @escaping STPPaymentStatusBlock) {
    let idempotency = UUID().uuidString.replacingOccurrences(of: "-", with: "")
    guard let paymentMethodId = paymentResult.paymentMethod?.stripeId else {
        completion(STPPaymentStatus.error, NSError(domain: "StripeError", code: -1, userInfo: [NSLocalizedDescriptionKey: "未获取到有效支付方式"]))
        return
    }
    let customerId = UserService.user.stripID

    // 第一步:绑定支付方式到客户
    Functions.functions().httpsCallable("attatchPaymentMethod").call([
        "paymentMethod": paymentMethodId,
        "customer": customerId
    ]) { _, error in
        if let error = error {
            completion(STPPaymentStatus.error, error)
            return
        }
        // 第二步:绑定成功后发起扣款
        let chargeData : [String : Any] = [
            "total" : StripeCart.total ,
            "customerID" : customerId ,
            "paymentMethodId": paymentMethodId,
            "idempotency" : idempotency
        ]
        Functions.functions().httpsCallable("makeCharge").call(chargeData) { result, error in
            if let error = error {
                completion(STPPaymentStatus.error , error)
                return
            }
            StripeCart.clearCart()
            self.tableView.reloadData()
            self.setupPaymentInfo()
            completion(STPPaymentStatus.success, nil)
        }
    }
}
验证要点
  • App端添加新卡后,Stripe控制台对应客户详情页的支付方式列表可看到该卡片,且标记为默认支付方式
  • 发起支付不再报「无活跃卡」错误,扣款成功后控制台可看到对应的PaymentIntent支付记录
  • 不需要手动在后台添加卡片,App端添加的卡片可直接完成支付流程

内容的提问来源于stack exchange,提问作者Abdullah Shaqrah

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 03:48:10