通知商户(异步回调 notify_url)

1.1 说明

  • 调用方向通知中台 → 商户在签约时登记的 notifyUrlHTTPS POST)。
  • 报文分层加签输入 全部在 HTTP 请求头业务数据 仅在 请求体Content-Type: application/json)。不要 使用单行 Authorization: RSA mch_id=... 形式承载通知参数。
  • 时间语义:业务 Body 不含 notifyTime;时间与 Header Baofu-Timestamp(秒级)及验签待签串一致。

1.2 请求头

键名与仓库 MerchantNotifyHeaderConstants 一致(subscription-pay 经 SDK 传入同名 inputParams,由中台映射为出站 Header):

Header 说明
Baofu-Timestamp 秒级时间戳(与中台组签一致)
Baofu-Nonce 随机串
Baofu-Serial 证书序列号(通常为商户 API 签证书序列号)
Request-ID 链路日志 ID(与开放网关 Request-ID 语义一致)
Baofu-Signature-Type 签名算法类型,如 RSASM2(与签约 Baofu-Sign-Type / sign_type 对齐)
Baofu-Signature 签名值

示例

POST /your/notify/path HTTP/1.1
Host: merchant.example.com
Content-Type: application/json
Accept: application/json
Baofu-Timestamp: 1766038862
Baofu-Nonce: 5FC2FC21E871BD5B8C7A3E0B0339BC8F
Baofu-Serial: 139248429
Request-ID: 7c9e2b1a-4d3f-4e1a-9c2b-0a1b2c3d4e5f
Baofu-Signature-Type: RSA
Baofu-Signature: <signature>

{"notifyType":"PAYMENT",...}

1.3 商户验签(待签串)

使用 com.baofoo.subscription.common.util.MerchantNotifySignUtil#buildResponseSignString(与开放网关应答侧约定一致):

  1. 取 Header 中秒级时间戳、随机串;数字信封若本次通知无则传空串(拼接时仍保留单独一行)。
  2. HTTP Body 原始字节 对应的 JSON 字符串(与接收到的完全一致,勿重新格式化)。
  3. 「时间戳 + \n + 随机串 + \n + 数字信封 + \n + Body + \n 拼接(每行以换行结束,包括最后一行)。
  4. Baofu-Signature-Type 使用约定证书验签 Baofu-Signature

1.4 请求体(业务 JSON,驼峰)

公共约定:

  • 不含 字段 merchantNo;主体见 unifiedMemberNo 等。
  • 不含 notifyTime
  • 支付/扣款类通知 Body 的 notifyType 固定为 PAYMENT;成功、失败等终态以 statusfailCodefailMessage 区分(失败终态时后两者有业务含义,成功时可为空串)。
  • 所有字段值均为 字符串类型(数值、日期等以字符串形式传输)。

1.4.1 扣款/支付类通知(notifyType=PAYMENT

字段 类型 说明
notifyType string 固定值 PAYMENT
deductionNo string 扣款单号
subscriptionNo string 订阅号
unifiedMemberNo string 统一会员编号
outTradeNo string 商户签约订单号(与订阅同源)
status string 扣款单状态(SUCCESS / FAILED),见 §3.2
periodNumber string 当前账单期数
scheduledPeriodDate string 本期计划扣款日(自然日,如 2026-06-19
originalAmount string 原始应扣金额(分)
actualAmount string 实际扣款金额(分)
subscriptionStatus string 当前订阅状态,见 §3.1
channelOrderNo string 渠道订单号(可为空串)
failCode string 失败错误码(成功时为空串)
failMessage string 失败原因(成功时为空串)

扣款成功示例status=SUCCESS

{
  "notifyType": "PAYMENT",
  "deductionNo": "DD01010120260319120000000001",
  "subscriptionNo": "SP01010120260319120000000001",
  "unifiedMemberNo": "UM_NEW_10001",
  "outTradeNo": "MCH202603190001",
  "status": "SUCCESS",
  "periodNumber": "3",
  "scheduledPeriodDate": "2026-06-19",
  "originalAmount": "1990",
  "actualAmount": "1592",
  "subscriptionStatus": "ACTIVE",
  "channelOrderNo": "CH2026031912000001",
  "failCode": "",
  "failMessage": ""
}

扣款失败示例status=FAILED

{
  "notifyType": "PAYMENT",
  "deductionNo": "DD01010120260319120000000002",
  "subscriptionNo": "SP01010120260319120000000001",
  "unifiedMemberNo": "UM_NEW_10001",
  "outTradeNo": "MCH202603190001",
  "status": "FAILED",
  "periodNumber": "3",
  "scheduledPeriodDate": "2026-06-19",
  "originalAmount": "1990",
  "actualAmount": "",
  "subscriptionStatus": "SUSPENDED",
  "channelOrderNo": "",
  "failCode": "INSUFFICIENT_BALANCE",
  "failMessage": "余额不足"
}

1.4.2 订阅类通知(notifyType=SUBSCRIPTION

字段 类型 说明
notifyType string 固定值 SUBSCRIPTION
subscriptionNo string 订阅号
unifiedMemberNo string 统一会员编号
outTradeNo string 商户签约订单号
productCode string 内部产品编码
userId string 用户标识
status string 订阅状态(ACTIVE / CANCELLED / FAILED 等),见 §3.1
currentPeriod string 当前期数
totalPeriod string 总期数(永续时为空串)
baseAmount string 每期扣款基准金额(分)
appliedPricingType string 应用的定价类型,见 §3.6
nextDeductionTime string 下次计划扣款时间
signTime string 签约时间
activateTime string 激活时间(未激活时为空串)
cancelTime string 取消时间(未取消时为空串)

订阅签约成功示例status=ACTIVE

{
  "notifyType": "SUBSCRIPTION",
  "subscriptionNo": "SP01010120260319120000000001",
  "unifiedMemberNo": "UM_NEW_10001",
  "outTradeNo": "MCH202603190001",
  "productCode": "MONTHLY_VIP",
  "userId": "U10001",
  "status": "ACTIVE",
  "currentPeriod": "1",
  "totalPeriod": "12",
  "baseAmount": "1990",
  "appliedPricingType": "FIRST_PERIOD_DISCOUNT",
  "nextDeductionTime": "2026-07-19 10:00:00",
  "signTime": "2026-06-19 10:00:00",
  "activateTime": "2026-06-19 10:00:01",
  "cancelTime": ""
}

订阅取消示例status=CANCELLED

{
  "notifyType": "SUBSCRIPTION",
  "subscriptionNo": "SP01010120260319120000000001",
  "unifiedMemberNo": "UM_NEW_10001",
  "outTradeNo": "MCH202603190001",
  "productCode": "MONTHLY_VIP",
  "userId": "U10001",
  "status": "CANCELLED",
  "currentPeriod": "3",
  "totalPeriod": "12",
  "baseAmount": "1990",
  "appliedPricingType": "FIRST_PERIOD_DISCOUNT",
  "nextDeductionTime": "",
  "signTime": "2026-06-19 10:00:00",
  "activateTime": "2026-06-19 10:00:01",
  "cancelTime": "2026-08-15 14:30:00"
}

订阅失败示例status=FAILED,签约首期支付失败)

{
  "notifyType": "SUBSCRIPTION",
  "subscriptionNo": "SP01010120260319120000000002",
  "unifiedMemberNo": "UM_NEW_10001",
  "outTradeNo": "MCH202603190003",
  "productCode": "MONTHLY_VIP",
  "userId": "U10001",
  "status": "FAILED",
  "currentPeriod": "0",
  "totalPeriod": "12",
  "baseAmount": "1990",
  "appliedPricingType": "FIRST_PERIOD_DISCOUNT",
  "nextDeductionTime": "",
  "signTime": "2026-06-19 10:00:00",
  "activateTime": "",
  "cancelTime": ""
}
  • 验签通过后处理业务,建议返回 HTTP 2xx
  • 幂等:建议以 deductionNo / subscriptionNo + 业务终态关键字段(如 statusperiodNumber)组合去重;大类 notifyTypeSUBSCRIPTION / PAYMENT / REFUND(预留)时须结合 Body 区分具体场景。
  • 是否需要固定 JSON 体(如 { "code": "SUCCESS" })以 通知中台回调规范 为准。