如何通过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
相关产品推荐
相关产品推荐

