接收付款结果
Paydify 回调 Partner 以实现相关业务流程。
提现结果通知
- URL:您应用的 payout 回调地址(由 Paydify 为应用配置,非下单时传入;如需配置或变更请联系 Paydify)
- Method:POST
- Content-Type:application/json
- 触发时机:提现单到达终态(
SUCCESS/FAILED)时推送,每笔提现仅推送一次;批量提现的每笔明细同样通过本回调逐笔通知,批次级状态请通过批量提现查询主动查询
完整字段定义参看接口文档 提现回调通知。
回调请求头
| 请求头 | 必须 | 描述 |
|---|---|---|
| content-type | 是 | 固定为 application/json |
| x-api-key | 是 | 您的 appId |
| x-api-timestamp | 是 | Unix 时间戳,单位毫秒 |
| x-api-signature | 是 | Paydify 生成的签名 |
回调请求体
| 字段名 | 类型 | 必须 | 描述 |
|---|---|---|---|
| appId | string | 是 | 订单归属应用的 appId |
| withdrawId | string | 是 | Paydify 提现单号 |
| mchWithdrawId | string | 是 | 商户提现单号(提现申请时传入的 mchWithdrawId) |
| status | string | 是 | 提现状态,回调仅推送终态:SUCCESS(链上转账并结算成功)/ FAILED(资金已回流余额,可查 errorMsg) |
| currency | string | 是 | 提现币种 |
| network | string | 是 | 链网络 |
| amount | string | 是 | 提现金额,无值时为 0 |
| gasFee | string | 是 | 链上 Gas 费,无值时为 0 |
| processingFee | string | 是 | 处理手续费,无值时为 0 |
| address | string | 是 | 提现目标地址 |
| txnHash | string | 否 | 链上交易 Hash,有值时返回 |
| finishedTime | long | 是 | 完成时间戳,单位毫秒,无值时为 0 |
| errorCode | string | 否 | 失败错误码,仅失败时可能返回 |
| errorMsg | string | 否 | 失败原因说明,仅失败时可能返回 |
示例
{ "appId": "A123456788", "withdrawId": "W5081643613805694001", "mchWithdrawId": "PAYOUT_20260610_001", "status": "SUCCESS", "currency": "USDT", "network": "TRON", "amount": "99.99000000", "gasFee": "0.01000000", "processingFee": "0.5", "address": "TX7dEjrN...", "txnHash": "0xabcdef...", "finishedTime": 1780971800000 }
法币提现结果通知
- URL:您法币提现应用配置的回调地址(由 Paydify 为应用配置,非下单时传入)
- Method:POST
- Content-Type:application/json
- 触发时机:法币提现单到达终态(
COMPLETED/FAILED/CANCELLED/REJECTED)时推送;非终态(PENDING/PROCESSING/SETTLING等)不推送,每笔提现仅推送一次
主要字段:withdrawId(Paydify 法币提现单号)、mchWithdrawId(发起提现时传入的 mchOrderId)、status、orderAmount(到账金额,当前固定 USD 口径)、amount(实际扣减总额)、withdrawFeeAmount(手续费)、quoteId、deductionSnapshot / bankAccountSnapshot(快照,JSON 字符串)、finishedTime、errorMsg。完整字段定义参看接口文档 法币提现回调通知。
回调与 Payin 共用一个签名机制。长时间未收到回调,可用 /withdraw/detail 或 /fiat-withdraw/detail 主动查询。代发订单(Partner 凭据创建)的回调签名使用下单凭据的 apiSecret,x-api-key 仍为订单归属应用的 appId。
回调响应
处理成功需应答 HTTP 200 + 纯文本 success(不区分大小写):
| 响应值 | 描述 |
|---|---|
| success | 回调确认成功 |
| 其它任何响应 | 均视为失败并触发重推(包括 fail、JSON 对象、HTML 错误页、空响应体) |
回调签名验证
使用您的 secretKey 在处理业务逻辑前验证回调签名。
签名内容包含以下字段:
| 字段 | 值 |
|---|---|
| apiPath | webhook URL 的路径部分,例如 /api/payout/webhook |
| body | 原始请求体字符串 |
| x-api-key | 请求头中的值 |
| x-api-timestamp | 请求头中的值 |
验证规则:
- 使用 HMAC-SHA256
- 在 JSON 序列化前按字段名升序排序
- JSON 序列化排序后的内容
- Base64 编码 HMAC 结果
- 将生成的值与
x-api-signature比较 - 仅使用请求路径,不要包含协议、域名或查询参数
- 使用接收到的原始请求体,不要在验证前解析并重新构建
实际签名对象示例(示例中的 body 仅截取两个字段作示意,并非完整回调请求体——实际验签必须使用接收到的完整原始请求体字符串):
{ "apiPath": "/api/payout/webhook", "body": "{\"withdrawId\":\"W5081643613805694001\",\"mchWithdrawId\":\"PAYOUT_20260610_001\"}", "x-api-key": "A123456788", "x-api-timestamp": "1762513487000" }
最终签名:
Base64(HMAC_SHA256(jsonString, secretKey))
推荐验证步骤
- 从请求头中读取
x-api-key、x-api-timestamp和x-api-signature - 以 UTF-8 字符串读取原始请求体
- 仅获取请求路径,例如
/api/payout/webhook - 使用
apiPath、body、x-api-key和x-api-timestamp构建内容 Map - 按键名升序排序
- JSON 序列化排序后的 Map
- 使用您的
secretKey计算 HMAC-SHA256 - Base64 编码结果
- 将生成的值与
x-api-signature比较 - 签名匹配后再处理 webhook 业务逻辑
Node.js 示例
const crypto = require("crypto"); function verifyPaydifyWebhook({ path, rawBody, apiKey, timestamp, receivedSignature, secretKey, }) { const content = { apiPath: path, body: rawBody || "", "x-api-key": apiKey, "x-api-timestamp": String(timestamp), }; const jsonString = JSON.stringify(content); const expectedSignature = crypto .createHmac("sha256", secretKey) .update(jsonString, "utf8") .digest("base64"); return expectedSignature === receivedSignature; }
常见验证失败原因
- 使用了普通 SHA256 而不是 HMAC-SHA256
- 在验证前重新构建了 JSON body
- 使用了完整 URL 而不是仅路径部分
- 在签名计算中包含了查询参数
- 使用了与请求头不同的时间戳值
- 将十六进制输出与 Base64 签名进行比较
回调重试机制
如果回调未收到预期的响应值 success,将按 15s / 45s / 90s / 180s / 10m / 30m / 60m / 3h / 6h / 12h 的间隔重推,连同首推最多 11 次:
| 请求次数 | 时间延迟 | 描述 |
|---|---|---|
| 1 | 0 | 首次回调,终态落库后立即执行 |
| 2 | 15 秒 | 第 1 次重试 |
| 3 | 45 秒 | 第 2 次重试 |
| 4 | 90 秒 | 第 3 次重试 |
| 5 | 180 秒 | 第 4 次重试 |
| 6 | 10 分钟 | 第 5 次重试 |
| 7 | 30 分钟 | 第 6 次重试 |
| 8 | 60 分钟 | 第 7 次重试 |
| 9 | 3 小时 | 第 8 次重试 |
| 10 | 6 小时 | 第 9 次重试 |
| 11 | 12 小时 | 第 10 次重试,仍失败则停止重推 |