沉默的 $800 MRR 杀手:我为何创建 BillingWatch
Source: Dev.to
请提供您希望翻译的文章正文内容,我将把它翻译成简体中文并保持原有的格式、Markdown 语法以及技术术语不变。谢谢!
我注意到下降的那一天
它始于我几乎忽略的 Slack 通知:“Stripe: charge failed.”
一次收费失败——并不异常。我把它抛在脑后,继续写代码。
三天后,在查看仪表盘时,我看到 MRR 下降了超过 $800。这不是逐渐的下滑,而是一个悬崖。八位订阅者在我忙于开发新功能时悄然流失。收费失败一次又一次堆积,Stripe 重试仍然失败,而我使用的任何工具都没有触发一次警报。
于是,我开始构建 BillingWatch。
缺失的模式
Stripe 仪表盘会显示事件,但不会告诉你何时出现模式错误。我遗漏了:
- 重复收费 – 同一客户、相同金额,在 60 秒内。Stripe 重试非常激进;幂等键容易写错。
- 收费失败级联 – 不是单张卡片失败,而是一小时内出现五次失败。这不是“坏卡”,而是卡片测试攻击。
- 订阅失效 – 当订阅变为
past_due时会触发customer.subscription.updated。容易被过滤,却可能错过数天。 - 负数发票异常 – 意外的抵扣导致负数项目。若是有意为之还好,若不是则是潜在的静默错误。
这些都不是可以直接订阅的单一事件。它们是跨事件的模式,而模式需要监控。
BillingWatch 架构
BillingWatch 是一个 FastAPI 服务,位于你的 Stripe webhook 端点之前。每个事件在你的应用处理之前都会通过检测引擎。
# webhook.py
from fastapi import FastAPI, Request
from billingwatch.detectors import run_all_detectors
from billingwatch.store import save_event
import stripe
app = FastAPI()
@app.post("/webhook")
async def stripe_webhook(request: Request):
payload = await request.body()
event = stripe.Webhook.construct_event(
payload,
request.headers["stripe-signature"],
STRIPE_WEBHOOK_SECRET,
)
# Store first, then detect
await save_event(event)
alerts = await run_all_detectors(event)
if alerts:
await send_alerts(alerts)
return {"received": True}检测器是可组合的。每个检测器接受一个事件并返回一个警报列表(或无返回)。它们可以查看历史事件以发现模式。
示例检测器:重复收费
# detectors/duplicate_charge.py
import time
from typing import List
class DuplicateChargeDetector:
async def detect(self, event: dict) -> List[Alert]:
if event["type"] != "charge.succeeded":
return []
charge = event["data"]["object"]
window_start = time.time() - 60 # 60‑second window
recent = await get_charges(
customer=charge["customer"],
amount=charge["amount"],
since=window_start,
)
if len(recent) > 1:
return [
Alert(
level="warning",
message=(
f"Duplicate charge detected: {charge['customer']} "
f"charged ${charge['amount']/100:.2f} twice in 60s"
),
event_id=event["id"],
)
]
return []在每个 charge.succeeded 事件上运行它,你将永远不会错过重复收费。
现实世界的隐形杀手
我与另外五位 SaaS 创始人交谈过;每个人都有不同的“隐形杀手”:
- 一个 webhook 端点静默返回 200,但在内部抛出了异常。
- 一个订阅在信用卡过期后仍保持活跃 90 天。
- 卡号测试攻击数周未被检测到。
BillingWatch 附带了针对最常见模式的检测器,但真正的价值在于,你可以为业务特定的故障模式编写自己的检测器。
Core Principle
监控事件,而不仅仅是指标。
指标告诉你出了问题。事件模式告诉你原因——并且常常在指标显示之前就捕获到它。
Open Source
BillingWatch 是开源的。如果你在任何规模上使用 Stripe,但没有监控你的 webhook 流以发现异常,你就是盲目操作。我之所以构建它,是因为我必须这么做。你也可能会——最好使用已经可用的东西。