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

关于Stripe Checkout.Session.Completed事件触发条件的技术咨询

关于Stripe Checkout.Session.Completed事件的触发逻辑及代码建议

核心问题解答

当仅接受信用卡支付时,Checkout.Session.Completed事件确实只会在支付成功后触发。用户输入卡号点击「支付」后,Stripe会完成信用卡授权/扣费流程,只有确认支付成功(包括3D验证通过、即时扣费成功等场景),才会向你的WebHook端点发送该事件。支付失败(如卡号无效、余额不足、3D验证未通过)时,不会触发此事件,用户会留在Checkout页面看到错误提示,不会进入完成流程。

代码实现的优化建议

你给出的WebHook处理逻辑方向正确,这里提供几个实际优化点:

  1. 增加支付状态二次校验:
    虽然事件触发时支付已成功,但极端场景下(比如Stripe内部事件重试的边缘情况),建议从事件中取出Session对象,确认payment_status为paid后再执行业务逻辑,避免意外:

    var session = stripeEvent.Data.Object as Session;
    if (session?.PaymentStatus == "paid")
    {
        // 执行保存CustomerId、升级套餐等操作
    }
    
  2. 区分异常类型:
    不要直接捕获所有异常,Stripe的EventUtility.ConstructEvent会抛出SignatureVerificationException,单独捕获该异常返回400,其他异常返回500。这样Stripe会自动重试服务器错误(500)导致的失败事件,而签名错误(400)不会重试:

    catch (SignatureVerificationException sigEx)
    {
        return BadRequest("无效签名");
    }
    catch (Exception ex)
    {
        // 记录异常日志
        return StatusCode(500);
    }
    
  3. 避免硬编码订阅周期:
    在InvoicePaid事件中,不要固定设置32天有效期,建议从发票关联的订阅对象中获取实际计费周期:

    var invoice = stripeEvent.Data.Object as Invoice;
    var subscription = await _stripe.Subscriptions.GetAsync(invoice.SubscriptionId);
    var nextBillingDate = subscription.CurrentPeriodEnd;
    // 用nextBillingDate更新用户有效期
    
  4. 保存订阅ID:
    如果高级套餐是订阅模式(非单次付款),Checkout.Session.Completed事件的Session对象会包含subscription字段,建议同时保存该ID,后续关联发票、订阅变更事件会更便捷。

优化后的完整代码示例

[HttpPost("webhook")]
[AllowAnonymous]
public async Task<ActionResult> WebHook([FromServices] IOptions<StripeKeys> stripeOptions, [FromServices] StripeService stripeService)
{
    var json = await new StreamReader(HttpContext.Request.Body).ReadToEndAsync();
    try
    {
        var stripeEvent = EventUtility.ConstructEvent(json, Request.Headers["Stripe-Signature"], stripeOptions.Value.WebHookSecret);
        switch (stripeEvent.Type)
        {
            case Events.CheckoutSessionCompleted:
                {                    
                    var session = stripeEvent.Data.Object as Session;
                    if (session?.PaymentStatus == "paid")
                    {
                        // 保存CustomerId和SubscriptionId到数据库
                        // 将用户套餐升级为高级版
                        // 发送支付成功邮件
                    }
                    break;
                }
            case Events.InvoicePaid:
                {
                    var invoice = stripeEvent.Data.Object as Invoice;
                    var subscription = await stripeService.Subscriptions.GetAsync(invoice.SubscriptionId);
                    // 根据订阅的CurrentPeriodEnd更新用户有效期
                    // 发送收据邮件
                    break;
                }
            case Events.InvoicePaymentFailed:
                {
                    // 将用户套餐降级为基础版
                    // 发送支付失败提醒邮件
                    break;
                }
            case Events.InvoiceUpcoming:
                {
                    // 发送续费提醒邮件
                    break;
                }
            case Events.CustomerSubscriptionDeleted:
                {
                    // 将用户套餐降级为基础版
                    // 发送订阅取消邮件
                    break;
                }
            default:
                break;
        }
        return Ok();
    }
    catch (SignatureVerificationException)
    {
        return BadRequest("无效的WebHook签名");
    }
    catch (Exception ex)
    {
        // 记录异常日志
        return StatusCode(500);
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 00:02:48