You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Stripe订阅取消报错‘No such subscription’:请求排查解决方案

问题根源:传错了资源标识

嘿,这个报错的原因其实特别直白:你把客户IDcus_CCKeYhNjyKTYTh,前缀cus_)传给了需要订阅ID(前缀sub_)的Cancel方法。Stripe里客户和订阅是完全独立的资源,Cancel方法要的是订阅的唯一标识,不是客户的,自然会提示找不到对应订阅。

看你这段代码就能明确问题所在:

subservice.Cancel(subscriptionId: check.CustomerId, cancelAtPeriodEnd: true);

这里的check.CustomerId是客户ID,但subscriptionId参数必须传入订阅ID才行。

修复步骤与优化建议

1. 正确获取目标订阅ID

你补充的代码已经在尝试获取订阅ID,但有个小瑕疵:stripeCustomerID.Subscriptions里的所有订阅的CustomerId必然等于当前客户的ID,所以那个筛选条件完全多余;而且如果客户没有有效订阅,FirstOrDefault会返回null,直接取.Id会触发空引用异常。

改成这样更稳妥:

var api = Settings.ConstName.StrinpAPIKey;
StripeConfiguration.SetApiKey(api);

var customerService = new StripeCustomerService(api);
var subscriptionService = new StripeSubscriptionService(api);

// 从Stripe拉取完整的客户对象
var stripeCustomer = customerService.Get(members.User.CustomerId);

// 筛选客户的活跃订阅(如果要取消特定订阅,可按计划ID/创建时间等维度筛选)
var targetSubscription = stripeCustomer.Subscriptions
    .FirstOrDefault(sub => sub.Status == "active");

if (targetSubscription == null)
{
    // 处理客户没有活跃订阅的场景,比如给用户提示或记录日志
    throw new InvalidOperationException("该客户没有可取消的活跃订阅");
}

2. 执行正确的取消操作

拿到合法的订阅ID后,再调用取消方法:

// 注意这里传的是targetSubscription.Id(sub_开头的订阅ID)
subscriptionService.Cancel(subscriptionId: targetSubscription.Id, cancelAtPeriodEnd: true);

cancelAtPeriodEnd: true表示订阅会在当前周期结束后自动取消,而非立即终止;如果需要立即取消会员权限,把这个参数改成false即可(不过通常会员取消都是到期失效,保持true更符合常规业务逻辑)。

3. 增加异常处理与环境校验

  • 确认API密钥的环境匹配:测试密钥只能操作测试数据,生产密钥对应生产数据,别搞混了,否则也会出现找不到资源的报错。
  • 增加异常捕获,处理Stripe返回的各类错误场景:
try
{
    subscriptionService.Cancel(subscriptionId: targetSubscription.Id, cancelAtPeriodEnd: true);
    // 取消成功后,记得同步你的数据库状态,比如更新会员的到期时间或有效状态
}
catch (StripeException ex)
{
    // 根据Stripe的错误代码做针对性处理
    switch (ex.StripeError.Code)
    {
        case "resource_missing":
            // 订阅不存在,可能已经被取消过
            break;
        case "subscription_already_canceled":
            // 订阅已经处于取消状态
            break;
        case "api_key_invalid":
            // API密钥配置错误,检查Settings中的值
            break;
        default:
            // 其他未预见错误,可向上抛出或记录详细日志
            throw;
    }
}
验证取消结果

取消操作完成后,你可以调用subscriptionService.Get(targetSubscription.Id)查看订阅状态,确认cancel_at_period_end是否为true(或者状态变为canceled,如果是立即取消的情况);同时务必同步你的数据库,保证会员状态与Stripe端一致,避免数据不一致问题。

内容的提问来源于stack exchange,提问作者J Petersen

火山引擎 最新活动