Webhook 接入指南
DogPay 系统支持通过向您注册的 Webhook 端点发送事件通知,使您的系统能够实时接收状态更新。本指南将引导您完成 Webhook 的配置、响应规范以及至关重要的安全验签。
⚙️ 1. 基础配置 (Configuration)
在开始接收事件之前,您需要向 DogPay 提供用于接收通知的 Webhook Endpoint(接收端点)。
- 配置方式:您可以在商户的 API 管理后台 (API Management) 中,与管理 ApiKey 一样自助配置您的 Webhook 接收地址。
- 数量限制:当前每个商户/应用仅支持创建一个 Webhook 端点。
- 协议要求:为了保障数据传输安全,您的端点仅支持
HTTPS协议,且系统将使用POST方法发起请求。
📦 2. 数据结构 (Event Formats)
Webhook 配置成功后,一旦系统内发生相关业务事件,DogPay 将向您的端点推送 JSON 格式的 Payload 数据。其通用外层结构如下:
{
"event_id": "全局唯一的事件 ID,可用于防重",
"event_identifier": "事件标识符,例如 pay.transaction.update",
"data": {
// 具体的业务上下文对象,依 event_identifier 的不同而变化
}
}⏱️ 3. 响应与重试机制 (Retry Mechanism)
当您的服务器接收到 Webhook 推送时,必须及时作出响应。
响应时间与重试策略要求您的服务器必须在 30 秒内 返回标准的 HTTP 状态码
200或201。
如果您的服务器未能在规定时间内响应,或者返回了异常的 HTTP 状态码(如 500),DogPay 系统将视作推送失败,并按以下延迟阶梯自动重试:10s, 30s, 60s, 120s, 300s, 600s。
🛡️ 4. 安全验签 (Webhook Verification)
由于您的 Webhook 端点是公开暴露在公网上的,为了防止恶意攻击者伪造请求,您必须对收到的每一条通知进行签名验证,以确保该事件确实由 DogPay 官方发出。
系统会在向您发送请求的 HTTP Header 中包含一个 wh-signature 字段。
验签算法步骤
- 算法标准:使用
HMAC-SHA512哈希消息认证码算法。 - 密钥 (Key):使用您在 API 管理后台获取的
ApiKey。 - 消息体 (Message):使用接收到的未经任何修改的 JSON Payload 原始字符串(即 Request Body 内容)。
- 将您计算出的哈希值转换为小写的十六进制字符串,并与请求头中的
wh-signature进行比对。若一致,则验签通过。
在线调试辅助在开发阶段,您可以使用此 HMAC 在线验证工具 来交叉比对您代码生成的签名结果是否准确。
💻 5. 验签代码示例 (Code Example)
以下是使用 Java 语言实现 HMAC-SHA512 验签的完整参考代码:
package com.example.dogpay2;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
public class DogPayWebhookVerifier {
private static String bytesToHex(byte[] bytes) {
if (bytes == null) {
throw new IllegalArgumentException("Input byte array cannot be null");
}
StringBuilder hexString = new StringBuilder();
for (byte b : bytes) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}
public static boolean verifySignature(String apiKey, String payload, String whSignature) {
try {
// Initializing a Mac Instance with the HMAC-SHA512 Algorithm
Mac hmacSha512 = Mac.getInstance("HmacSHA512");
SecretKeySpec secretKeySpec = new SecretKeySpec(apiKey.getBytes(StandardCharsets.UTF_8), "HmacSHA512");
hmacSha512.init(secretKeySpec);
// Compute HMAC signatures
byte[] computedHash = hmacSha512.doFinal(payload.getBytes(StandardCharsets.UTF_8));
String computedSignature = bytesToHex(computedHash);
return computedSignature.equals(whSignature);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public static void main(String[] args) {
String apiKey = "{{apiKey}}";
String payload = "{"
+ "\"event_id\":\"997daf9b-4162-4864-914c-960ff6cc16ad\","
+ "\"event_identifier\":\"card.transaction\","
+ "\"data\":{"
+ "\"id\":\"043fe653-e916-4989-a044-c8404cc4730c\","
+ "\"cardId\":\"9afe2c3c-306c-492f-aa99-6ce6574440bd\","
+ "\"cardChannel\":\"c_002_budget\","
+ "\"accountId\":\"1bd30ef3-0f13-468c-a9b7-61b141a02864\","
+ "\"orderNum\":\"202503101055594738096\","
+ "\"transactionId\":null,"
+ "\"currency\":\"USD\","
+ "\"fee\":\"0.65000000\","
+ "\"amount\":\"10.00000000\","
+ "\"type\":\"consumption\","
+ "\"status\":\"pending\","
+ "\"createAt\":\"2025-03-10T02:55:59.470Z\","
+ "\"transactionAt\":\"2025-03-10T02:55:59.474Z\","
+ "\"completeAt\":\"2025-03-10T02:55:59.474Z\","
+ "\"detail\":\"WECHAT*TENCENT ShenZhen CN\","
+ "\"sourceId\":\"a435-2ad446944ebe-bf04-5c86-6c3e-5d51\","
+ "\"tradeTag\":null,"
+ "\"account\":null,"
+ "\"card\":null,"
+ "\"reasonCode\":0,"
+ "\"mcc\":\"123456\""
+ "}"
+ "}";
String whSignature = "example_wh_signature";
boolean isValid = verifySignature(apiKey, payload, whSignature);
if (isValid) {
System.out.println("Success");
} else {
System.out.println("Failed");
}
}
}Updated about 12 hours ago
