主题
回调(webhook)是什么,为什么必须配
不配的结果:买家付了钱,订单还是「待付款」
然后被超时关单。钱收了,货没发,买家来投诉。
这是所有支付接入里最常见、也最贵的一个漏配。
它解决什么问题
买家点「支付」之后会离开你的网站(跳到 Stripe 或 PayPal 的页面)。付完款他会跳回来, 但你的服务器并不知道这笔钱到底有没有付成功 ——
- 他可能付完就关掉了浏览器,根本没跳回来
- 跳回来的那个页面参数是买家浏览器带回来的,可以被伪造
所以真正可信的只有一条路:支付平台的服务器主动打一个请求到你的服务器, 告诉你"订单 X 已付款"。这个请求就是 webhook(回调)。
支付成功页看到的状态是"轮询"出来的
买家跳回来时,页面会每隔一两秒问一次后端"这单付了吗"。 真正把订单改成已付款的,是 webhook。
所以你会看到支付页短暂显示"处理中" —— 那是在等 webhook 到达,是正常的。
回调地址从哪儿拿
Alipay+ / Antom 不用配这一步
它的回调地址是系统在每一笔请求里自己带过去的,你不需要去 Antom 后台建端点。整篇下面讲的都是 Stripe / PayPal。 见 Alipay+ / Antom 配置。
不用自己拼,后台直接给你
站点管理 → 支付通道,每一行都有一列**「回调地址(webhook)」, 后面有个复制**按钮。点一下,直接去支付平台粘上就行。
配置密钥的弹窗里,「密钥」那一段上面也有同一个地址和复制按钮 —— 因为填 Webhook 签名密钥的前提,就是先拿这个地址去平台建端点。
它长这样:
https://你的域名/api/open/payment/callback/{provider}/{通道ID}实际例子:
https://jdz.io/api/open/payment/callback/stripe/3顺序:先建通道并保存 → 再复制地址去配回调
地址里带着通道 ID,而 ID 是保存之后才有的。
所以新建通道时,那个弹窗里会提示「回调地址要保存之后才有」—— 先把密钥填上保存一次,再打开编辑,就能复制到地址了。
用复制按钮,别手打
手拼最常见的三种错:
- 漏掉
/api/open/这一段 - provider 写成
Stripe(要小写stripe) - 域名写成了后台地址
每一种的结果都是支付平台收到 404,而你看到的只是"订单一直待付款", 完全联想不到是地址拼错了。
每个通道一个地址
沙箱通道和正式通道是两个不同的 ID,所以是两个不同的回调地址, 要在支付平台的沙箱环境和正式环境各配一次。
漏配正式环境的那次,是上线当天最典型的事故。
系统怎么保证回调是真的
你可能会想:这个地址是公开的,别人随便打一下不就能伪造"已付款"?
系统做了这几层:
| 检查 | 作用 |
|---|---|
| 验签 | 用你填的 Webhook 密钥校验报文签名。签名不对 → 401 拒绝 |
| 验时间戳 | 超过 5 分钟的报文拒收 —— 否则截获一条"支付成功"就能无限重放 |
| 金额校验 | 回调里的金额和订单金额对不上 → 拒绝 |
| 幂等 | 同一笔支付重复回调只认一次 |
所以 Webhook 密钥必须填对
不填或填错的话,所有回调都会被判为伪造并拒绝 —— 表现和"没配回调"一模一样:订单永远是待付款。
平台会重推,这是好事
网络抖动、或者你正好在重启后端时,回调会失败。 支付平台会自动重试(Stripe 会重试几天)。
系统对重复回调是幂等的,重推不会造成重复加款。
万一重试次数用完了还是没送到?
系统有一个主动查单补偿任务:定时去支付平台问"这几笔待付款的单到底付了没", 付了就补上。所以即使 webhook 全丢了,订单最终也能自愈。
但这只是兜底,不能拿它当主要路径 —— 补偿任务有间隔, 买家付完钱要等好几分钟才看到订单变化,体验很差。
怎么确认回调配好了
方法 1:看支付平台的回调日志(最准)
- Stripe:Developers → Webhooks → 点你配的那个端点 → 下面有每次投递的记录
200= 我们收到了并处理成功401= 验签失败,多半是 Webhook 密钥填错404= 地址写错了(通道 ID 不对?域名不对?)- 超时 / 连不上 = 域名解析或服务器的问题
- PayPal:Developer Dashboard → 你的 App → Webhooks → Event logs
方法 2:走一遍沙箱支付
用测试卡付一笔,看订单几秒内是不是变成「已付款」。
变了但很慢(超过 1 分钟)
那多半不是 webhook 起的作用,而是补偿任务兜底了。 去看平台的回调日志,确认那条 webhook 的响应码。
常见问题
平台日志显示 401
Webhook 密钥填错了。
- Stripe:那串是
whsec_...,在你添加端点之后才会生成, 在端点详情页里点「Reveal」才看得到 —— 不是 API keys 页面上的那两个 - 换过端点、删了重建过端点,密钥会变,要回后台更新
平台日志显示 404
基本都是手拼地址拼错了。回后台用「复制」按钮重新取一次, 覆盖掉支付平台上那条端点的地址。
如果复制的地址也是 404:
- 域名对吗 —— 后台的地址栏域名必须是能从公网访问的正式域名
- 通道还在吗 —— 通道被删过的话 ID 就失效了(正常操作是"停用"不是删)
本地开发时怎么测回调
支付平台打不到你的电脑。用 Stripe CLI 的 stripe listen --forward-to 把回调转发到本机, 或者用 ngrok 之类的内网穿透。
但没必要在本地折腾这个 —— 直接在服务器上用沙箱通道测更省事。
除了验签失败,系统一律返回 2xx
即使我们内部处理出错,也会返回 2xx。
为什么:返非 2xx 只会让平台反复重推,把"一单出错"放大成"每分钟几百次请求"。 出错的信息记在服务器日志里(/opt/mall/logs/mall.log),不靠 HTTP 状态码传递。