一、支付平台为什么必须有自己的监控告警
转卡码、自动发卡这类支付平台,最怕的不是写不出代码,而是出了问题没人知道:凌晨三点用户付款成功却没收到卡密、回调服务悄悄挂掉、支付通道被风控拦截——每一分钟都在真金白银地流失。等第二天早上打开后台才发现,损失已经无法挽回。
监控告警的意义,就是把"事后发现"变成"事中告警":掉单率、回调失败率、发货延迟、库存水位这些关键指标一旦越过阈值,钉钉/企业微信立刻把告警推到手机。本文以 Python + FastAPI 的转卡码后端为例,从零搭建一套完整的支付监控告警体系,全程可运行。
二、先定指标:支付平台必盯的 8 个核心指标
监控不是越多越好,指标太多反而淹没真正的问题。支付平台建议优先盯住这 8 个:
| 指标 | 类型 | 建议阈值 |
|---|---|---|
| 支付成功率(成功/发起) | 比率 | < 95% 告警 |
| 回调成功率(验签通过/总回调) | 比率 | < 99% 告警 |
| 回调延迟 P95 | 耗时 | > 5s 告警 |
| 发货延迟 P95(支付成功→卡密发出) | 耗时 | > 3s 告警 |
| 卡密库存水位 | 数值 | < 100 告警 |
| 支付通道健康状态 | 0/1 | = 0 立即告警 |
| MQ 队列积压数 | 数值 | > 1000 告警 |
| 5xx 错误率 | 比率 | > 1% 告警 |
这 8 个指标覆盖了"收得到钱、发得出货、通道活着"三条生命线。下面用 prometheus_client 挨个落地。
三、Python 埋点:prometheus_client 实战
Prometheus 的埋点模型分四种:Counter(只增不减,适合计数)、Gauge(可增可减,适合水位)、Histogram(分布,适合延迟)、Summary(分位数)。安装依赖并定义指标:
pip install prometheus-client fastapi uvicorn
# metrics.py 定义全局指标
from prometheus_client import Counter, Gauge, Histogram
pay_total = Counter('pay_total', '支付发起总次数', ['channel'])
pay_success = Counter('pay_success_total', '支付成功次数', ['channel'])
callback_total = Counter('callback_total', '回调接收次数')
callback_fail = Counter('callback_fail_total', '验签失败次数')
deliver_latency = Histogram('deliver_latency_seconds',
'支付成功到发货完成耗时', buckets=(0.1, 0.5, 1, 2, 3, 5, 10))
stock = Gauge('card_stock', '卡密库存剩余', ['product_id'])
queue_depth = Gauge('mq_queue_depth', '消息队列积压数')
在支付回调接口里打点,注意把通道维度打上去,便于事后按通道拆分:
# notify.py 支付宝/微信回调入口
async def notify(request):
ok = verify_sign(request) # 验签
if ok:
callback_total.inc()
# 幂等处理订单,发货成功再打点
await deliver_card(order)
deliver_latency.observe(time.time() - order.paid_at)
pay_success.labels(channel=order.channel).inc()
else:
callback_fail.inc() # 验签失败也要计数,防伪造攻击
return {"code": 0}
最后暴露 /metrics 端点给 Prometheus 抓取:
# main.py 挂载 metrics 路由
from prometheus_client import make_asgi_app
from fastapi import FastAPI
app = FastAPI()
app.mount("/metrics", make_asgi_app()) # GET /metrics 返回指标文本
四、Prometheus 采集与告警规则
用 Docker 一键起 Prometheus,配置文件指向你的服务:
# prometheus.yml
scrape_configs:
- job_name: 'pay-api'
metrics_path: '/metrics'
static_configs:
- targets: ['127.0.0.1:8000']
# 支付接口频率高,缩短抓取周期
scrape_interval: 15s
docker run -d --name prometheus -p 9090:9090 \
-v /etc/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml \
prom/prometheus
告警规则用 PromQL 写。下面三条分别覆盖回调失败率突增、发货延迟超标、卡密库存告罄:
# pay_alerts.yml
groups:
- name: pay-alerts
rules:
- alert: CallbackFailRateHigh
expr: rate(callback_fail_total[5m]) / rate(callback_total[5m]) > 0.01
for: 2m
labels: { severity: critical }
annotations:
summary: "回调失败率超过 1%,可能存在验签问题或伪造攻击"
- alert: DeliverLatencyHigh
expr: histogram_quantile(0.95, rate(deliver_latency_seconds_bucket[5m])) > 3
for: 5m
labels: { severity: warning }
annotations:
summary: "发货 P95 延迟超过 3 秒"
- alert: StockLow
expr: card_stock < 100
for: 5m
labels: { severity: warning }
annotations:
summary: "卡密库存不足 100,请及时补充"
五、Alertmanager 对接钉钉机器人
告警规则触发后,由 Alertmanager 统一收敛并推送到钉钉群。先建一个钉钉群机器人(安全设置选"加签"),再写一个 webhook 转发服务:
# dingtalk.py 钉钉机器人加签 + 发送
import time, hmac, hashlib, base64, urllib.parse, requests
def sign(secret: str) -> str:
ts = str(round(time.time() * 1000))
string_to_sign = f"{ts}\n{secret}"
h = hmac.new(secret.encode(), string_to_sign.encode(),
hashlib.sha256).digest()
sign = urllib.parse.quote_plus(base64.b64encode(h))
return ts, sign
def send(title: str, text: str):
ts, s = sign(SECRET)
url = (f"https://oapi.dingtalk.com/robot/send?access_token={TOKEN}"
f"×tamp={ts}&sign={s}")
requests.post(url, json={
"msgtype": "markdown",
"markdown": {"title": title, "text": text}})
Alertmanager 配置里把 webhook 指到这个服务:
# alertmanager.yml
route:
group_by: ['alertname']
group_wait: 10s
repeat_interval: 1h
receiver: 'dingtalk'
receivers:
- name: 'dingtalk'
webhook_configs:
- url: 'http://127.0.0.1:5000/alert'
send_resolved: true
这样告警一旦触发,1 分钟内就能出现在手机钉钉上,值班人员不用一直盯着 Grafana 面板。
六、trace_id 日志链路:出问题时 30 秒定位
指标只能告诉你"哪里坏了",日志才能告诉你"为什么坏"。给每个请求分配一个 trace_id,从下单到支付回调、发货全程透传,出问题时一条 grep 拉出完整链路:
# 中间件生成 trace_id 并注入日志
import uuid, logging
@app.middleware("http")
async def trace_middleware(request, call_next):
trace_id = request.headers.get("X-Trace-Id") or uuid.uuid4().hex[:12]
request.state.trace_id = trace_id
logger = logging.LoggerAdapter(logging.getLogger("pay"),
{"trace_id": trace_id})
response = await call_next(request)
response.headers["X-Trace-Id"] = trace_id
return response
# 日志格式带上 trace_id 和订单号
# 2026-08-20 03:12:44 [a1b2c3d4e5f6] 回调验签成功 order=WX20260820031244
grep "a1b2c3d4e5f6" /var/log/pay/app.log
配合 curl -H "X-Trace-Id: test001" 手动复现问题,整条调用链一目了然。日志量大了之后再上 Loki/ELK,但先把 trace_id 打对,比什么组件都管用。
七、故障演练:告警体系要定期"点火"
监控搭完不演练等于没搭。建议每月做一次故障演练:
- 杀进程:kill 掉回调服务,确认 5 分钟内收到告警;
- 改错密钥:把验签密钥改错,确认回调失败率告警触发;
- 清零库存:把卡密库存改到阈值以下,确认低库存告警;
- 断网模拟:拔掉外网,确认通道健康检查告警。
记住:告警的本质不是"通知",而是可行动的决策信息——收到告警后 30 秒内能定位问题,这套监控才算合格。
八、总结
监控告警是支付平台的"仪表盘"和"守夜人"。本文从指标设计、Prometheus 埋点、告警规则到钉钉通知、trace_id 链路,串起了完整闭环,全部代码可以直接抄进你的转卡码系统。如果你不想从零写这些基础设施,源码商城的 转卡码系统 v3 已内置 Prometheus 指标埋点、库存水位告警和钉钉通知模块,部署即用;配合 Codex Desktop 本地 AI 编程助手,改告警规则、调 PromQL 的效率还能再翻一倍。