Skip to content

🔔 异步通知(Webhook Notification)

当订单状态发生变化,尤其是在支付成功后,IntPay 会向商户在统一下单接口中提交的 notifyUrl 发起 服务端回调通知(Webhook)
若商户系统未正确接收或未按约定返回成功响应,IntPay 将按照重试策略进行延迟重发,以提高通知送达率。

异步通知是商户确认最终支付结果的标准依据
同步跳转页面(ReturnUrl)仅用于前端展示,不应作为订单最终状态判断依据。

一、通知机制说明

在以下情况下,IntPay 可能会向商户系统发起异步通知:

  • 支付成功
  • 支付失败
  • 订单进入待处理状态
  • 订单状态发生后续更新(如退款、拒付等)

处理建议

商户系统收到通知后,应按以下顺序处理:

  1. 校验请求来源与签名
  2. 解析通知体
  3. 根据外层状态码 successdata.status 进行业务处理
  4. 幂等更新本地订单状态
  5. 返回平台要求的成功响应

因为网络波动可能存在多次发送异步通知,强烈建议商户对 referencedata.id 建立幂等控制,避免因重复通知造成重复更新。

二、接口说明

项目说明
请求 URL使用统一下单接口中的 notifyUrl 参数
请求方式POST
Content-Typeapplication/json
字符编码UTF-8
签名要求必须验签
重试机制商户未正确返回成功响应时,平台会延迟重试
响应机制返回 HTTP 200 OK

重要说明

  • notifyUrl 必须为平台可公网访问的有效地址
  • notifyUrl 不可达、超时、返回异常状态码或返回格式不符合要求,商户可能无法及时收到支付结果通知
  • 商户系统应确保通知接口具备高可用、快速响应与重复处理能力

三、通知参数

3.1 通知示例

json
{
  "requestId": "75daa341-66fd-4d87-949b-28f7f0ef6d64",
  "success": true,
  "data": {
    "reference": "SAIDF01239-1230",
    "requiresAction": false,
    "amount": 100,
    "currency": "USD",
    "id": "P1231823",
    "failureMessage": "fail",
    "status": "FAILED"
  },
  "timestamp": "2025-10-10T16:39:23.765+08:00"
}

四、通知处理规则

4.1 顶层处理逻辑

收到通知后,商户应先判断顶层字段 success

  • success = false 时,表示本次接口调用失败
  • success = true 时,表示本次接口调用成功,此时需进一步根据 data.status 判断订单业务状态

4.2 处理说明

success = false

表示此次通知在接口层未成功执行,通常可能由以下原因导致:

  • 参数错误
  • 签名错误
  • 权限校验失败
  • 通道关闭
  • 通道限额
  • 商户配置异常

此时建议:

  1. 记录完整通知报文与请求头
  2. 读取错误信息字段(如返回体中存在 message 字段)
  3. 不要直接以此更新订单为支付成功
  4. 结合日志与平台返回内容排查问题

success = true

表示此次通知请求已成功送达并通过接口层校验。
此时订单的真实业务状态应以 data.status 为准。

五、顶层结构字段说明

字段类型示例值说明
requestIdstring75daa341-66fd-4d87-949b-28f7f0ef6d64本次通知的唯一追踪 ID,由服务端生成,用于日志追踪、链路排查与问题定位。每次通知通常唯一。
successbooleantrue表示本次接口调用是否成功(接口层面)。true 表示通知调用成功并已返回业务结果;false 表示接口调用异常,如签名错误、参数缺失、权限拒绝、通道关闭等。
dataobject{...}支付结果详情对象。当 success = true 时,商户应重点解析该对象中的业务字段。
timestampstring (ISO 8601)2025-10-10T16:39:23.765+08:00通知生成时间,采用 ISO 8601 标准格式,包含毫秒与时区偏移。

六、data 对象字段说明(支付结果详情)

字段类型示例值说明
idstringP1231823IntPay 平台交易号。用于标识平台侧唯一交易,可用于对账、查询与问题排查。
referencestringSAIDF01239-1230商户订单号或交易参考号,由商户系统生成,用于关联商户内部订单。
requiresActionbooleanfalse是否仍需用户执行额外操作。通常用于表示是否需要完成额外验证步骤(如 3DS、OTP 等)。
amountnumber100支付金额,单位为最小货币单位。例如 USD 下 100 表示 1.00 USD
currencystringUSD支付币种,遵循 ISO 4217 三位字母代码标准。
failureMessagestringfail失败原因描述。当交易失败时返回;成功状态下通常为空、为 null 或被省略。
statusstringFAILED当前订单状态枚举值,商户应以此字段作为订单状态更新依据。

七、交易状态说明

状态枚举

状态值说明商户建议
PENDING待处理。支付请求已创建,正在等待用户操作、发卡行响应或通道处理。订单保持处理中,不要提前判定最终结果。
REQUIRES_ACTION需要下一步操作。通常表示用户需完成额外验证,如 3D Secure、OTP 或其他身份校验。引导用户继续完成验证流程,并等待后续结果通知。
SUCCEEDED支付成功。发卡行授权通过,交易已成功完成。将订单更新为成功,进入发货、入账或后续履约流程。
FAILED支付失败。可能由于发卡行拒付、风控拦截、余额不足、卡信息错误或其他原因导致。将订单更新为失败,并视业务需要记录失败原因。
CANCELED已取消。订单被商户、用户或系统主动取消,通常未完成实际扣款。将订单更新为已取消,结束本次支付流程。
REFUND_PENDING退款处理中。退款请求已提交,正在等待银行或通道处理。将订单更新为退款处理中,并等待后续通知。
REFUNDED已退款。原支付金额已成功退回持卡人账户。将订单更新为已退款,并同步售后或财务状态。
REFUND_FAILED退款失败。退款请求被通道或银行拒绝,未完成退款。保留原支付成功状态,并记录退款失败原因。
CHARGEBACK拒付。持卡人已发起争议,款项可能被强制退回。启动拒付处理流程,并同步风控、财务或客服系统。

不同支付产品或通道路由下,部分状态可能不会全部出现。
商户系统应至少完整兼容:PENDINGREQUIRES_ACTIONSUCCEEDEDFAILEDCANCELEDREFUNDED

八、推荐处理逻辑

8.1 业务判断优先级

建议商户按以下优先级处理通知:

  1. 校验签名是否通过
  2. 判断 success
  3. 解析 data
  4. 根据 data.status 更新订单状态
  5. 返回成功响应

8.2 推荐伪代码

java
if (!verifySignature(request, rawBody)) {
    return fail();
}

WebhookNotify notify = parse(rawBody);

if (!notify.isSuccess()) {
    log.warn("Webhook call failed: {}", rawBody);
    return ok();
}

String reference = notify.getData().getReference();
String platformId = notify.getData().getId();
String status = notify.getData().getStatus();

if (isAlreadyProcessed(reference, status)) {
    return ok();
}

switch (status) {
    case "SUCCEEDED":
        markOrderSuccess(reference, platformId);
        break;
    case "FAILED":
        markOrderFailed(reference, notify.getData().getFailureMessage());
        break;
    case "PENDING":
    case "REQUIRES_ACTION":
        markOrderProcessing(reference);
        break;
    case "REFUNDED":
        markOrderRefunded(reference);
        break;
    case "CHARGEBACK":
        markOrderChargeback(reference);
        break;
    default:
        log.info("Unhandled webhook status: {}", status);
}
return ok();

九、响应结果处理说明

商户系统在成功处理通知后,应返回明确且稳定的成功响应
若未正确返回,IntPay 可能视为通知失败,并在后续进行重试。

建议要求

  • 返回 HTTP 200 OK
  • 响应耗时尽量控制在短时间内
  • 先快速落库,再异步执行耗时业务
  • 避免在通知接口中执行长事务或外部阻塞调用

推荐实践

  • 先应答,后处理
  • 幂等更新
  • 日志完整
  • 失败可重放

若商户系统存在高并发或链路复杂场景,建议将通知消息写入消息队列或事件总线后再异步消费。

十、Java 实体示例

java
import lombok.Data;

@Data
public class IntPayWebhookNotify {

    private String requestId;
    private Boolean success;
    private DataObject data;
    private String timestamp;

    @Data
    public static class DataObject {
        private String reference;
        private Boolean requiresAction;
        private Integer amount;
        private String currency;
        private String id;
        private String failureMessage;
        private String status;
    }
}

十一、通知接收示例(伪代码)

java
@PostMapping("/notify")
public ResponseEntity<String> notifyCallback(
        HttpServletRequest request,
        @RequestBody String rawBody
) {
    // 1. 验签
    boolean verified = verifySignature(request, rawBody);
    if (!verified) {
        return ResponseEntity.status(400).body("invalid signature");
    }

    // 2. 解析通知
    IntPayWebhookNotify notify = JSON.parseObject(rawBody, IntPayWebhookNotify.class);

    // 3. 接口层失败,不更新成功订单
    if (Boolean.FALSE.equals(notify.getSuccess())) {
        log.warn("Webhook call failed: {}", rawBody);
        return ResponseEntity.ok("success");
    }

    // 4. 幂等处理业务状态
    IntPayWebhookNotify.DataObject data = notify.getData();
    if (data != null) {
        String reference = data.getReference();
        String status = data.getStatus();

        switch (status) {
            case "SUCCEEDED":
                orderService.markSuccess(reference, data.getId());
                break;
            case "FAILED":
                orderService.markFailed(reference, data.getFailureMessage());
                break;
            case "PENDING":
            case "REQUIRES_ACTION":
                orderService.markProcessing(reference);
                break;
            case "REFUNDED":
                orderService.markRefunded(reference);
                break;
            case "CHARGEBACK":
                orderService.markChargeback(reference);
                break;
            default:
                log.info("Unhandled status: {}", status);
        }
    }

    // 5. 返回成功响应,避免平台重试
    return ResponseEntity.ok("success");
}

十二、常见错误

错误场景说明建议
未验签直接处理存在伪造通知风险必须先验签,再更新订单
使用同步跳转判断最终结果ReturnUrl 可能被用户关闭或篡改以 Webhook 为最终依据
未做幂等控制平台重试可能导致重复更新reference / id 做幂等
接口返回非 200平台会认为通知失败并重试成功处理后统一返回 200
接口处理过慢可能触发超时或重复通知快速响应,异步处理耗时任务
忽略处理中状态可能误判订单失败或成功正确兼容 PENDING / REQUIRES_ACTION

十三、最佳实践

  • 异步通知应作为最终支付结果依据
  • 通知接口必须支持重复回调幂等处理
  • 建议记录以下内容用于排查:
    • 请求头
    • 原始请求体
    • 验签结果
    • 本地订单更新结果
    • 响应内容与响应时间
  • 建议建立告警机制,监控:
    • 验签失败率
    • 通知处理失败率
    • 通知响应耗时
    • 重复通知比例

十四、核心总结

IntPay Webhook 的核心目标,是将订单最终状态以服务端到服务端的方式稳定送达商户系统。
商户应基于 验签、幂等、快速响应、状态驱动 的原则实现通知接收逻辑,
以确保订单状态更新准确、可靠且可追踪。