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

如何通过Stripe API计算订阅流失率并记录未续订日志?

Stripe Webhook 实现订阅流失追踪与流失率计算方案

一、核心Webhook事件选择

直接用这三个事件就能覆盖绝大多数未续订场景:

  • customer.subscription.deleted:订阅被删除时触发(包括用户主动取消、到期未续订、支付失败导致的取消)
  • customer.subscription.updated:订阅状态变更时触发,可提前捕获用户"到期不续订"的操作(比如用户设置了cancel_at_period_end: true)
  • invoice.payment_failed:支付失败时触发,用于追踪因支付问题导致的潜在流失

二、实现步骤

1. Stripe后台配置Webhook

  • 登录Stripe Dashboard,进入「Developers > Webhooks」
  • 点击「Add endpoint」,填入你的服务器接收地址(比如https://your-domain.com/stripe-webhook)
  • 在「Events to send」里勾选上述三个事件,保存后复制「Signing secret」(用于请求验证)

2. 服务器端验证Webhook请求

必须验证签名,防止伪造请求。以Node.js为例:

const stripe = require('stripe')('your-stripe-secret-key');
const express = require('express');
const app = express();

// 解析Stripe的Webhook请求体
app.post('/stripe-webhook', express.raw({type: 'application/json'}), async (req, res) => {
  const sig = req.headers['stripe-signature'];
  let event;

  try {
    // 用签名密钥验证请求合法性
    event = stripe.webhooks.constructEvent(
      req.body,
      sig,
      'your-webhook-signing-secret'
    );
  } catch (err) {
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }

  // 处理事件逻辑
  handleStripeEvent(event);

  res.json({received: true});
});

3. 事件处理与数据库记录

根据事件类型提取关键信息,写入你的业务数据库:

async function handleStripeEvent(event) {
  switch (event.type) {
    case 'customer.subscription.deleted':
      const subscription = event.data.object;
      // 提取关键字段
      const data = {
        customer_id: subscription.customer,
        subscription_id: subscription.id,
        end_date: new Date(subscription.ended_at * 1000),
        cancel_reason: subscription.cancel_reason || 'unknown',
        // 判断是否是到期未续订:cancel_at_period_end为true且终止时间等于订阅周期结束时间
        is_non_renewal: subscription.cancel_at_period_end && (subscription.ended_at === subscription.current_period_end)
      };
      // 写入数据库(示例用伪代码)
      await db.query('INSERT INTO churned_subscriptions SET ?', data);
      break;

    case 'customer.subscription.updated':
      const oldSub = event.data.previous_attributes;
      const newSub = event.data.object;
      // 捕获用户设置"到期不续订"的操作
      if (oldSub.cancel_at_period_end === false && newSub.cancel_at_period_end === true) {
        await db.query('INSERT INTO pending_churn SET ?', {
          customer_id: newSub.customer,
          subscription_id: newSub.id,
          planned_end_date: new Date(newSub.current_period_end * 1000),
          created_at: new Date()
        });
      }
      break;

    case 'invoice.payment_failed':
      const invoice = event.data.object;
      // 记录支付失败的订阅,用于后续流失分析
      await db.query('INSERT INTO payment_failures SET ?', {
        customer_id: invoice.customer,
        subscription_id: invoice.subscription,
        failure_reason: invoice.payment_intent.last_payment_error?.message || 'unknown',
        failed_at: new Date()
      });
      break;

    default:
      console.log(`Unhandled event type ${event.type}`);
  }
}

4. 计算流失率

基于数据库中的记录,按时间段统计:

  • 月度流失率:(当月流失的订阅数 / 当月初活跃订阅数) × 100%
  • 可以按流失维度细分:主动取消率、支付失败流失率等
  • 示例SQL(统计月度流失):
SELECT 
  DATE_FORMAT(end_date, '%Y-%m') AS month,
  COUNT(*) AS churned_count,
  (COUNT(*) / (SELECT COUNT(*) FROM subscriptions WHERE start_date <= DATE_FORMAT(end_date, '%Y-%m-01') AND (ended_at IS NULL OR ended_at > DATE_FORMAT(end_date, '%Y-%m-01')))) * 100 AS churn_rate
FROM churned_subscriptions
GROUP BY month;

三、注意事项

  • 确保Webhook端点的可靠性:建议配置重试机制(Stripe会自动重试失败的Webhook请求),同时做好幂等处理(避免重复记录)
  • 订阅状态判断:cancel_at_period_end: true表示用户选择到期不续订,而非立即取消;ended_at字段是订阅实际终止的时间
  • 支付失败的后续处理:多次支付失败后Stripe会自动取消订阅,此时会触发customer.subscription.deleted,无需额外处理

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 03:23:28