API 参考
English异步通知
支付、退款、代付状态变化时,HansaPay 向 notify_url 发送的签名通知。
HansaPay 系统会把支付、退款、代付结果 POST 到原请求中传入的 notify_url。
拒付、链接过期仍未支付的收银台订单,以及支付变为 REFUNDED 时均不发送通知(此时会针对该退款发送 refund.updated)。
通知请求头
HansaPay 系统通知时会带:
X-MerchantIDX-TimestampX-NonceX-Sign
验签规则与请求签名完全一致:
merchantID + "\n" + timestamp + "\n" + nonce + "\n" + rawBody商户侧建议处理流程:
- 原样读取 HTTP body
- 取请求头
X-MerchantID、X-Timestamp、X-Nonce、X-Sign - 用商户密钥重新计算签名
- 比对签名是否一致
- 验签成功后再做业务处理
- 返回纯文本
success
Java 验签示例
String merchantId = request.getHeader("X-MerchantID");
String timestamp = request.getHeader("X-Timestamp");
String nonce = request.getHeader("X-Nonce");
String receivedSign = request.getHeader("X-Sign");
String rawBody = requestBodyAsString;
String secret = "your_merchant_secret";
String payload = merchantId + "\n" + timestamp + "\n" + nonce + "\n" + rawBody;
String expectedSign = hmacSha256Hex(payload, secret);
if (!expectedSign.equalsIgnoreCase(receivedSign)) {
throw new RuntimeException("invalid signature");
}PHP 验签示例
<?php
$merchantId = $_SERVER['HTTP_X_MERCHANTID'];
$timestamp = $_SERVER['HTTP_X_TIMESTAMP'];
$nonce = $_SERVER['HTTP_X_NONCE'];
$receivedSign = $_SERVER['HTTP_X_SIGN'];
$rawBody = file_get_contents('php://input');
$secret = 'your_merchant_secret';
$payload = $merchantId . "\n" . $timestamp . "\n" . $nonce . "\n" . $rawBody;
$expectedSign = hash_hmac('sha256', $payload, $secret);
if (strtolower($expectedSign) !== strtolower($receivedSign)) {
throw new Exception('invalid signature');
}Go 验签示例
payload := merchantID + "\n" + timestamp + "\n" + nonce + "\n" + rawBody
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(payload))
expected := hex.EncodeToString(mac.Sum(nil))
if !strings.EqualFold(expected, receivedSign) {
return errors.New("invalid signature")
}通知体结构
HansaPay 系统对支付、退款、代付事件使用统一的通知体格式。
通知字段
| 字段 | 类型 | 说明 |
|---|---|---|
event | string | payment.updated / refund.updated / payout.updated |
order_no | string | HansaPay 系统订单号 |
merchant_order_no | string | 商户订单号 |
payment_method | string | 支付方式 |
trans_amount | object | 支付与退款:实收或实退的美元金额,其他币种订单与 API 响应不同。代付:代付金额 |
requested_amount | object | 商户原始请求金额;仅当金额被向下取整到上游支持的价位时返回(钱包,以及部分通道的卡),此时 trans_amount 为实际收取金额 |
trade_info | object | 支付:商品信息。退款:goods_name 为退款原因 |
status | string | 当前状态 |
status_reason | string | 仅代付失败或取消时返回:Payout request failed、Payout failed 或 Payout canceled |
metadata | string | 商户透传参数 |
original_order_no | string | 原 HansaPay 系统订单号,退款时填写 |
original_merchant_order_no | string | 原商户订单号,退款时填写 |
finished_at | string | 完成时间。退款与失败的支付不返回 |
created_at | string | 创建时间 |
支付失败通知不包含失败原因。
支付通知示例
{
"event": "payment.updated",
"order_no": "O202406240001",
"merchant_order_no": "M202406240001",
"payment_method": "CARD",
"trans_amount": {
"currency": "USD",
"value": "99.99"
},
"trade_info": {
"goods_name": "VIP Membership",
"description": "Monthly subscription"
},
"status": "SUCCESS",
"metadata": "biz=member&uid=10001",
"finished_at": "2026-06-24T10:01:23+08:00",
"created_at": "2026-06-24T10:00:00+08:00"
}退款通知示例
{
"event": "refund.updated",
"order_no": "R202406240001",
"merchant_order_no": "MR202406240001",
"payment_method": "CARD",
"trans_amount": {
"currency": "USD",
"value": "99.99"
},
"trade_info": {
"goods_name": "customer requested"
},
"status": "SUCCESS",
"metadata": "refund=manual",
"original_order_no": "O202406240001",
"original_merchant_order_no": "M202406240001",
"created_at": "2026-06-24T11:00:00+08:00"
}代付通知示例
{
"event": "payout.updated",
"order_no": "P202406240001",
"merchant_order_no": "MP202406240001",
"payment_method": "CASH_APP",
"trans_amount": {
"currency": "USD",
"value": "88.50"
},
"status": "SUCCESS",
"metadata": "batch=20260624",
"finished_at": "2026-06-24T12:00:08+08:00",
"created_at": "2026-06-24T12:00:00+08:00"
}商户回调响应要求
商户处理通知成功后,必须返回:
success要求:
- 纯文本
- 不带 JSON 包裹
- 不带引号
- 小写
success - 5 秒内应答;耗时处理放在应答之后
- HansaPay 只检查 body;其他任何 body 均视为失败,即使 HTTP 状态为
200 - 未被确认的通知会重新发送,约 90 分钟内共最多 9 次。每次重发都会使用新的时间戳和 nonce 重新签名
- 同一通知可能到达多次,且可能乱序到达。body 反映发送时的状态。通知不带事件 ID:请按
event+order_no+status去重
