我的接口有多长时间响应?
**15 秒。**这是投递调用的读超时;3 秒内 未建立的连接会更早失败。超过任一项都算作 投递失败,该记录回到重试计划。 所以不要在请求内做您的业务处理。验证签名、 持久化事件、应答,然后异步处理——这是 最佳实践页面推荐的模式。 一个在应答前调用您自己三个服务的处理器,是写得正确的集成 显示为FAILED 的最常见原因。
我返回了 HTTP 200,为什么投递仍算失败?
因为状态码不是决定因素。判定读取的是您的 响应体,只有以下两条同时成立时投递才算 成功:- 响应是 2xx——任何 4xx 或 5xx 在读取响应体之前就失败。
- 响应体是带
message字段且其值为success的 JSON 对象。
failed 或 unknown(两者都表示
“请重试我”)、空响应体、不同的字段名、HTML 错误页,或
完全无法解析为 JSON 的响应体。
三个具体点解释了大部分意外:
响应的 Content-Type 必须是 application/json 吗?
实际上是——请发送Content-Type: application/json。响应按
JSON 读取,所以以 text/plain 或 text/html 提供的响应体无法解析,
即便字节本身是 {"message":"success"},
投递也会被记为失败。已发布的
概览正因此在商户
响应上展示了该请求头。
如果签名验证失败,我应该拒绝请求还是照常处理?
绝不处理。先验证,只在验证通过后才对事件 采取行动——这也是一项 验收要求: 验证失败时不得更新本地状态,必须改为从查询 API 读取真实 状态。 该返回什么由此得出:应答{"message":"failed"},这让
记录留在重试计划中,然后去查明原因。无法
验证的签名是某一方的配置问题,不会
因为重试而自行修复——所以把重试当作缓冲时间,而不是修复手段,
同时通过查询 API 对账。
如果您的签名验证只在重新序列化报文后才失败,
那就是原因:请对原始请求体字节验证,绝不要对
解析后再字符串化的副本验证。字段顺序、空白和数字格式在
一次往返后都会改变,而签名是对原始
字节计算的。见
Webhook 通知签名验证。
“8 次重试”到底是什么意思?间隔有保证吗?
包括第一次在内共八次尝试,间隔逐渐增大——计划见 Webhook 通知重试,端到端约 20 小时。 计划本身不会告诉您的两点限定:- **间隔是最小值,不是约定时刻。**重试在其计划时间变为可执行, 并在投递工作进程下次拾取时发出。 积压时某次尝试可能比表格显示的更晚到达。永远不要编写 假定重试在精确时间到达的逻辑。
- 预算用尽后记录最终失败,不再有进一步
自动尝试。Portal 显示为没有下次重试时间的
FAILED记录。
为什么”下次重试时间”有时是空的?
因为它只在自动重试确实仍在计划中时才填充。 失败且还有重试预算的记录会填充该字段; 已投递的记录和重试耗尽的记录为空。 因此两列的组合才是真正的解读,而不是单看状态:所有尝试都失败后,我该怎么做?
从该对象自己的查询 API 读取当前状态并对账—— 权威状态始终在对象上,不在通知中。 故障恢复指引列出了同样的 三个步骤:查询对象、定期批量对账、如果投递从未到达 则联系支持团队。 修好接口后您也可以从 Portal 重放,支持单条、 多选或按日期范围模式,每次请求最多 50 条。注意对 仍然有故障的接口重放会消耗同一预算中的一次尝试—— 所以先修复,再重放。详情见 Webhook 通知事件词典与批量重放。为什么某条记录上的 Resend 是灰的?
记录无法重发恰好有三个原因:
注意不在该列表上的:已成功投递的记录
可以重发。这是有意的——Waffo 记为已接受的投递
在您这边可能仍未处理,所以允许重放。
重发还需要您 Portal 角色具有Webhook 通知重发权限。没有
该权限时,操作和行选择根本不会渲染,而不是显示为
禁用——所以完全看不到重发控件的操作人面对的是
权限问题,不是记录资格问题。请联系您的 Portal Admin。
每种事件的回调 URL 来自哪里?
这是”没有收到通知”的最常见单一原因:没有解析出 URL, 所以什么都没发,也没有写任何记录。
两个值得据此设计的后果:
- **没有全局支付通知 URL。**支付通知只发往
您在创建订单时传的
notifyUrl,该字段之所以必填, 正是因为没有回退。您不能在 Portal 配置一个地址 然后不再发送该参数。 - **拒付通知没有请求级参数。**如果 Portal
URL 为空,这些事件就不会投递,也不留下日志记录。
Webhook 通知日志页上拒付结果为空,几乎总是意味着
Chargeback Notify URL 从未配置。支付过程中绑卡的
绑卡通知同理;没有
notifyUrl的独立绑卡 请求同样不会被通知。
我改了通知 URL,已在重试队列中的通知会发到哪里?
**发到旧 URL。**回调 URL 在每条通知记录创建时 存储在该记录上,每次重试——以及每次手动重发——都发到 存储的值。编辑 Portal 配置,或在新订单上传入不同的notifyUrl,只影响之后创建的记录。
请相应地规划 URL 迁移:保持旧接口继续接受流量,直到
在途窗口耗尽,而不是在新 URL 保存的那一刻
就切换。
通知 URL 字段接受什么?必须是 HTTPS 吗?
保存时的校验接受:- 以
http://或https://开头的 URL - 仅 ASCII 字符,且无空白
- 带主机的可解析地址——端口、路径和查询字符串 都允许
- Refund 和 Chargeback Notify URL 最多 512 个字符
- 空值,即清除配置
http:// 能通过校验,但已发布的
配置要求规定 HTTPS
且 TLS 1.2 或以上,并且Webhook 通知携带支付数据。
一个从校验规则看不出来的硬约束:**您的 TLS
证书必须被公共证书颁发机构信任。**投递
客户端使用默认信任库且没有例外路径,所以自签名或
其他不受信任的证书会导致连接失败,每次尝试都失败。
私有 CA 在这里不可用。
同一事件会到达多次吗?我应该按什么去重?
会,重复是可能的,您的处理器必须幂等—— 验收标准正是测试这一点。重复的来源包括 内部重投、手动重发,以及同一订单上授权阶段的通知 后跟最终通知。 **报文中没有事件 id 或投递 id。**报文体是{"eventType": ..., "result": {...}},唯一值得注意的请求头是
签名。所以请按 eventType 加 result 内的业务标识符去重——
事件所涉及的支付订单、退款订单、订阅、Payout 订单、
提现订单或 token——再结合您正在
处理的状态。仅按标识符去重会让您丢掉
已记录授权的订单的最终状态。
到达顺序有保证吗?我会先收到”failed”再收到”succeeded”吗?
不,顺序没有保证,而且是的——您可能乱序收到通知。 重试、排队和网络延迟都会重排投递。 让这一点安全的规则在 最佳实践页面:**不要用 到达顺序驱动您的状态机。**把每条通知当作触发器, 从对象获取终态——要么从报文的状态字段 并加上防止回退的保护,要么在任何通知后调用查询 API。 对于订阅这是文档化的模式:在状态通知或周期变更通知之后, 调用一次订阅查询,并对结果运行 单一的统一流程。先授权后请款的订单,在授权阶段我会收到通知吗?
会,它以同一订单上的PAYMENT_NOTIFICATION 到达,与最终
通知相同——不是独立的事件类型。您通过报文中的订单状态
区分它们,而不是通过事件:
这也是为什么仅按订单 id 去重是错的:这里一个订单收到
两条通知是预期的,且只有一条是终态。见
授权与请款。
通知会携带非终态状态吗?我需要重新查询吗?
会——如上表所示,订单可能在仍处理中时被通知。 对终态状态采取行动;对非终态状态,记录并等待。 如果您当时需要确定性,请调用查询 API 而不是推断。 唯一绝不能让您自行关闭订单的状态是未知 结果:遇到E0001 时,保持订单开放,用相同参数重试,并等待
查询结果或通知。自行关闭是文档化的
验收失败——见
集成验收标准。
订单被取消、超时或失败时我会收到通知吗?
会——三者都产生PAYMENT_NOTIFICATION,且三者都落在同一个
终态:ORDER_CLOSE。没有单独的 CANCELLED、EXPIRED
或 FAILED 状态值。
要区分它们,请读取报文中 orderFailedReason 携带的失败详情,
它包含 orderFailedCode 和
orderFailedDescription。所以正确的处理形态是:ORDER_CLOSE 表示
“不要履约”;orderFailedReason 告诉您对客户说什么以及
重试是否有意义。
我需要订阅事件吗,还是所有事件都会自动触发?
没有订阅注册表,也没有按事件的选择加入。只要按上表 能为某事件解析出回调 URL,它就会被投递——这意味着您 对接收内容的控制恰好等于您对哪些 URL 存在的控制。 对最小集成的实际解读:在创建订单时传notifyUrl
并处理支付通知;对您尚未编写处理器的事件族,
把 Portal 配置的 URL 留空,改从
查询 API 获取那些状态。轮询对每一种您能枚举的对象都可行:
拒付通过 /api/v1/chargeback/list 和 /api/v1/chargeback/inquiry,
token 状态通过 /api/v1/tokenization/inquiry。代价是延迟——而
对拒付而言,举证截止期从案件创建起算,所以如果您处理
争议,请配置 Chargeback Notify URL 而不是依赖
轮询。
我能在 Portal Webhook 通知页面找到 Payout 和提现通知吗?
通过该页面不能。Webhook 通知日志页搜索固定的七个事件 族——支付、退款、绑卡、拒付和三个订阅 事件——Payout 和提现通知不在其中,所以无论怎么筛选都 不会出现。 对于这些,请从 Payout 和提现查询 API 读取状态。并注意 对应的配置不对称:两者都没有 Portal 配置的 URL,所以其 通知地址就是您在创建它们的请求上传的值。我怎么知道某条通知从未投递成功?
目前是靠自己看,而不是被告知。投递失败时没有面向商户的告警, 也没有查询通知记录的商户 API。您的 两条路径是:- Portal Webhook 通知日志,在其 90 天窗口内——筛选失败, 并用下次重试列区分”仍在重试”与”最终 失败”。
- **对照查询 API 对账。**定期查询您期待终态的对象, 并与您自己的记录比较。 这是推荐的安全网,也是唯一同时覆盖 Portal 页面不跟踪的事件的手段。
你们从固定的 IP 地址集合投递吗?我能加白名单吗?
没有公布的Webhook 通知投递出口 IP 段,也没有配置静态出口 地址——所以没有什么可以固定。按 IP 为 Waffo 的 回调来源加白名单不是可以据此设计的做法。 替代做法:认证回调本身而不是其来源。每条 Webhook 通知都携带对原始报文体的签名,用 Waffo 私钥签署, 可用商户 Portal → Integration 中的 Waffo 公钥验证。 经过验证的签名比来源 IP 匹配是更强的保证,且 不会因基础设施变化而失效。开发时我怎么在本机接收回调?
您的notifyUrl 必须能从公网访问——localhost 和
私有地址无法访问。请使用隧道工具,并把隧道的公网 URL
注册为您的 notifyUrl;已发布的
概览展示了两种常见
工具的命令。
两个实际问题:隧道主机名通常每次重启都会变化,
所以请按订单传 URL 而不是保存在 Portal 配置中,且
所呈现的证书必须被公共信任——托管隧道
能提供,而本地自签名服务器不能。
Sandbox 投递遵循同一契约,包括重试计划——sandbox 首先能 做到和不能做到什么,见 Sandbox 模拟限制 FAQ。