# Webhook 机制

# 介绍

Webhook 是一种消息回调机制,包括:商品、变体、库存、订单等对象改动的消息通知,主要用于实时同步

# 对接步骤

  • 1)设置消息监听,参考消息设置接口
  • 2)客户端设置监听接口,试例如下(Java 语言)
  • 3)测试监听,处理业务逻辑

# Webhook配置要求

# 一、协议要求

  • 支持协议: HTTPS
  • 加密要求: 建议使用 TLS 1.2 或 TLS 1.3 进行安全传输
  • 请求方法: POST
  • 内容类型: Content-Type: application/json

# 二、签名认证

为了确保接收到的 Webhook 消息来自 CJ 官方,请对请求进行签名验证。

1. 签名算法

  • 算法:HMAC-SHA256,结果使用 Base64 编码(标准 Base64,不去除 padding)。
  • 密钥(secret):openId 的字符串形式(例如 12312 -> "12312")。
  • 待签名内容(message):HTTP 请求体(Body)的原始 JSON 字符串。
  • 输出:将 HMAC-SHA256 字节数组进行 Base64 编码后得到的字符串,CJ 通过 HTTP 请求头 sign 下发。

2. 关于 openId

  • 获取方式:调用获取 Token 接口成功后,响应体中会返回 openId
  • 使用方式:请将该 openId 保存到您的系统中,注册 Webhook 后,每次接收推送时使用该 openId 作为密钥计算签名并与请求头 sign 进行比对。

3. 签名验证步骤

  1. 从 HTTP 请求头中取出 sign(CJ 下发的签名值)。
  2. 取出请求体的原始字节(请直接使用接收到的原始 Body,不要先反序列化再重新序列化,避免字段顺序差异导致签名不一致)。
  3. openId 作为 secret,请求体字符串作为 message,按上述算法计算 HMAC-SHA256 并 Base64 编码。
  4. 将计算出的签名与请求头 sign 进行字符串相等比较,一致则视为校验通过;不一致则拒绝处理。

安全提示

Webhook 推送中携带的 openId 与您调用获取 Token 接口所得到的 openId 是同一个值,它是身份与签名密钥的核心凭证。请勿将 Webhook 接收地址、原始推送报文或带有 openId 的日志/截图分享给任何第三方(包括对接服务商、ERP 插件、第三方调试工具等),否则会导致您的 openId 外泄、签名密钥可被伪造,进而被他人冒充推送或还原您的账号身份。建议在转发或排查问题时对 openId 进行打码处理。

4. 签名计算示例(Java)

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;

public class SignUtil {

    /**
     * 计算签名
     *
     * @param openId  接收 Webhook 的开发者 openId(从获取 Token 接口返回值中得到并保存)
     * @param bodyJson 请求体的原始 JSON 字符串
     * @return Base64 编码后的 HMAC-SHA256 签名
     */
    public static String encrypt(Long openId, String bodyJson) throws Exception {
        String secret = openId.toString();
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(new SecretKeySpec(secret.getBytes(), "HmacSHA256"));
        byte[] bytes = mac.doFinal(bodyJson.getBytes());
        return Base64.getEncoder().encodeToString(bytes);
    }
}

调用示例与期望输出:

long openId = 123L;
// 请求体(与 CJ 推送过来的 JSON 字符串完全一致,字段顺序也保持一致)
// 注意:CJ 服务端使用 fastjson 序列化,默认按字段名字母顺序输出
String body = "{\"messageId\":\"123111\",\"messageType\":\"INSERT\",\"params\":\"123\",\"type\":\"PRODUCT\"}";
String sign = SignUtil.encrypt(openId, body);
// 期望签名: AHxoGFMoS/4mZfJ5vFes5//Pz2QibFQhh3GlrTtnWpk=

5. 签名验证示例(Java + Spring Boot)

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class WebhookController {

    /** 已通过获取 Token 接口拿到并保存在自己系统中的 openId */
    private static final Long OPEN_ID = 12312L;

    @PostMapping("/webhook/cj")
    public Object receive(@RequestHeader("sign") String sign,
                          @RequestBody String body) throws Exception {
        String expected = SignUtil.encrypt(OPEN_ID, body);
        if (!expected.equals(sign)) {
            throw new RuntimeException("sign verify failed");
        }
        // 校验通过后再做业务处理
        return "{\"code\":200,\"result\":\"success\",\"message\":\"ok\"}";
    }
}

6. 签名计算与验证示例(Python)

import base64
import hashlib
import hmac

def encrypt(open_id: int, body: bytes) -> str:
    """
    计算签名。

    :param open_id: 通过获取 Token 接口拿到并保存在本地系统中的 openId
    :param body:    HTTP 请求体的原始字节(不要先反序列化再重新序列化)
    :return:        Base64 编码后的 HMAC-SHA256 签名
    """
    secret = str(open_id).encode("utf-8")
    digest = hmac.new(secret, body, hashlib.sha256).digest()
    return base64.b64encode(digest).decode("utf-8")


# 计算示例
open_id = 123
body = b'{"messageId":"123111","messageType":"INSERT","params":"123","type":"PRODUCT"}'
print(encrypt(open_id, body))
# 期望签名: AHxoGFMoS/4mZfJ5vFes5//Pz2QibFQhh3GlrTtnWpk=

基于 Flask 的验签示例(同样适用于 FastAPI 等其它框架,关键是拿到未被中间件修改过的原始 Body 字节):

from flask import Flask, request, abort, jsonify
import hmac

OPEN_ID = 12312  # 已通过获取 Token 接口拿到并保存
app = Flask(__name__)


@app.post("/webhook/cj")
def receive():
    sign = request.headers.get("sign", "")
    raw_body = request.get_data()  # bytes,必须使用原始字节
    expected = encrypt(OPEN_ID, raw_body)
    # 使用 hmac.compare_digest 做常量时间比较,避免计时攻击
    if not hmac.compare_digest(expected, sign):
        abort(401)
    return jsonify({"code": 200, "result": "success", "message": "ok"})

注意

  1. 必须使用接收到的原始 Body 字符串计算签名,不要使用框架反序列化后再重新序列化的结果。
  2. 签名算法不使用 API Key,密钥就是 openId 本身(字符串形式)。
  3. openId 不在请求头中携带,请使用自己保存的 openId 进行计算。

# 2. 响应规范

  • 成功状态码: 200 OK
  • 响应超时时间: 3秒
    (请避免在接收端执行耗时的业务逻辑,确保快速响应)

# 3. 请求失败处理

  • 重试机制: 如果接收端返回非 200 OK 状态码,CJ 将在 5 分钟后重试,最多重试 3 次
  • 日志记录: 建议在接收端记录请求日志,便于排查问题
  • 如果请求失败超过 3 次,CJ 将停止发送该消息,如果整个请求失败率达到 50%,CJ 将暂停发送所有消息,直到接收端恢复正常,且你重新注册Webhook

要求: 请求方法: POST

参数名称 参数意义 参数类型 是否必传 长度 备注
code 状态码 int 成功200
result 处理结果 string success=成功, 其他=fail
message 处理结果消息 string 具体消息内容

# 消息列表

# 商品消息

# 接收到的商品消息体

{
    "messageId": "ca72a4834cd14b9588e88ce206f614a0",
    "type": "PRODUCT",
    "messageType": "UPDATE",
    "openId": 12312,
    "params": {
        "categoryId": null,
        "categoryName": null,
        "pid": "1424608189734850560",
        "productDescription": "xxxxxx",
        "productImage": null,
        "productName": null,
        "productNameEn": null,
        "productProperty1": null,
        "productProperty2": null,
        "productProperty3": null,
        "productSellPrice": null,
        "productSku": null,
        "productStatus": null,
        "fields" : [
            "productDescription"
        ]
    }
}
参数名称 参数意义 参数类型 是否必传 长度 备注
messageId 商品消息 Id string Y 200 Message Id
type 数据类型 string Y 200 PRODUCT
messageType 消息类型 string Y 15 INSERT、UPDATE、DELETE
openId Open Id number Y
params object Y 5
- categoryId 商品品种 Id string Y 200
- categoryName 商品品种名称 string Y 200
- pid 商品 id string Y 200
- productDescription 商品详情 string Y 2000
- productImage 商品照片 string Y 200
- productName 商品名称 string Y 200
- productNameEn 商品名称(英文名) string Y 200
- productProperty1 商品属性1 string Y 200
- productProperty2 商品属性2 string Y 200
- productProperty3 商品属性3 string Y 200
- productSellPrice 商品售价 double Y 20
- productSku 商品 sku string Y 200
- productStatus 商品消息状态 int Y 5 status:2-未在售, 3-在售
- fields 修改字段列表 list Y 5
ProductStatus 说明
2 未在售
3 在售

# 接收到的变体消息

{
    "messageId": "7cceede817dc47ed9748328b64353c5c",
    "type": "VARIANT",
    "messageType": "UPDATE",
    "openId": 12312,
    "params": {
        "vid": "1424608152007086080",
        "variantName": null,
        "variantWeight": null,
        "variantLength": null,
        "variantWidth": null,
        "variantHeight": null,
        "variantImage": null,
        "variantSku": null,
        "variantKey": null,
        "variantSellPrice": null,
        "variantStatus": null,
        "variantValue1": null,
        "variantValue2": null,
        "variantValue3": null,
        "fields" : [
            "variantLength"
        ],
    }
}
参数名称 参数意义 参数类型 是否必传 长度 备注
messageId 变体消息 Id string Y 200 Message Id
type 数据类型 string Y 200 VARIANT
messageType 消息类型 string Y 15 INSERT、UPDATE、DELETE
openId Open Id number Y
params object Y 5
- vid 变体 Id string Y 200
- pid 商品 id string Y 200
- variantName 变体名称 string Y 200
- variantWeight 变体重量, 单位:g int Y
- variantLength 变体长度, 单位:毫米 int Y
- variantWidth 变体宽度, 单位:毫米 int Y
- variantHeight 变体高度, 单位:毫米 int Y
- variantImage 变体图片 string Y 200
- variantSku 变体 sku string Y 200
- variantKey 变体关键字 string Y 200
- variantSellPrice 变体售价 double Y
- variantStatus 变体状态 int Y 5
- variantValue1 变体属性1 string Y 200
- variantValue2 变体属性2 string Y 200
- variantValue3 变体属性3 string Y 200
- fields 修改字段列表 list Y 5
variantStatus Description
0 下架
1 在售

# 库存消息

{
    "messageId": "ca72a4834cd14b9588e88ce206f614a0",
    "type": "STOCK",
    "messageType": "UPDATE",
    "openId": 12312,
    "params": {
        "1424608152007086080": [
            {
                "vid": "1424608152007086080",
                "pid": "123890023",
                "areaId": "2",
                "areaEn": "US Warehouse",
                "countryCode": "US",
                "storageNum": 12
            }
        ],
        "AE7DB9BC-4290-4C85-B8A6-F8957F3DB053": [
            {
                "vid": "AE7DB9BC-4290-4C85-B8A6-F8957F3DB053",
                 "pid": "123890023",
                "areaId": "2",
                "areaEn": "US Warehouse",
                "countryCode": "US",
                "storageNum": 1
            }
        ]
    }
}

# 接收到的物流轨迹变更消息

{
    "messageId": "7cceede817dc47ed9748328b64353c5c",
    "type": "LOGISTIC",
    "messageType": "UPDATE",
    "openId": 12312,
    "params": {
        "orderId": "123",
        "storeOrderNumbers": ["orderNum1"],
        "trackingNumber": "CJPKL7160102171YQ",
        "logisticName": "CJPacket Sensitive",
        "trackingStatus": 0,
        "logisticsTrackEvents": "[{"activity": "The electronic information of the waybill has been received.", "eventTime": "2021-10-28 21:17:18", "thirdActivity": "运单电子信息已收到", "thirdEventTime": "2021-10-28 21:17:18"}]"
    }
}
参数名称 参数意义 参数类型 是否必传 长度 备注
messageId 物流消息 Id string Y 200 Message Id
type 数据类型 string Y 200 LOGISTIC
messageType 消息类型 string Y 15 INSERT、UPDATE、DELETE
openId Open Id number Y
params object Y
- orderId CJ订单Id string Y 200
- storeOrderNumbers 店铺订单Id string[] Y 200 ["orderNum1"]
- logisticName 物流公司名称 string Y 200 CJPacket Ordinary
- trackingNumber 追踪单号 string Y 200
- trackingUrl 追踪号跟踪URL string N 200
- trackingStatus 追踪单状态 integer Y 20
- logisticsTrackEvents 追踪信息events事件信息 string Y 200 [{"activity": "The electronic information of the waybill has been received.", "eventTime": "2021-10-28 21:17:18", "thirdActivity": "运单电子信息已收到", "thirdEventTime": "2021-10-28 21:17:18"}]

# 接收到的纠纷变更消息

{
    "messageId": "7cceede817dc47ed9748328b64353c5c",
    "type": "DISPUTES",
    "messageType": "UPDATE",
    "openId": 12312,
    "params": {
        "orderId": "123",
        "status": "1"
    }
}
参数名称 参数意义 参数类型 是否必传 长度 备注
messageId 变体消息 Id string Y 200 Message Id
type 数据类型 string Y 200 DISPUTES
messageType 消息类型 string Y 15 INSERT、UPDATE、DELETE
openId Open Id number Y
params object Y 5
- orderId CJ订单 Id string Y 200
- status 纠纷状态 string Y 20

status状态值

参数名称 参数意义
1 新增纠纷
2 完成纠纷
3 取消纠纷

# 接收到的工单变更消息

{
    "messageId": "7cceede817dc47ed9748328b64353c5c",
    "type": "TICKET",
    "messageType": "UPDATE",
    "openId": 12312,
    "params": {
        "orderId": "123",
        "status": "1"
    }
}
参数名称 参数意义 参数类型 是否必传 长度 备注
messageId 工单消息 Id string Y 200 Message Id
type 数据类型 string Y 200 TICKET
messageType 消息类型 string Y 15 INSERT、UPDATE、DELETE
openId Open Id number Y
params object Y 5
- orderId CJ订单号 string Y 200
- status 工单状态 string Y 20

status状态值

参数名称 参数意义
1 新增
2 已完成
3 已取消

# 订单消息

私有库存出库单复用本「订单消息(ORDER)」主题推送:开通订单 webhook 后即可收到;与普通订单不同,私有库存出库单在创建时即推送一条 messageType=INSERT 消息,可通过 params.privateOutboundOrder=true 区分。

{
    "messageId": "7cceede817dc47ed9748328b64353c5c",
    "type": "ORDER",
    "messageType": "UPDATE",
    "openId": 12312,
    "params": {
        "orderNumber": "api_52f268d40b8d460e82c0683955e63cc9",
        "cjOrderId": "210823100016290555",
        "orderStatus": "CREATED",
        "logisticName": "CJPacket Ordinary",
        "trackNumber": null,
        "trackingUrl": null,
        "createDate": "2021-08-23 11:31:45",
        "updateDate": "2021-08-23 11:31:45",
        "payDate": null,
        "deliveryDate": null,
        "completeDate": null,
        "privateOutboundOrder": false,
        "orderItems": [
            "vid": "1392053744945991680",
			"quantity": 1,
			"sellPrice": 0.57,
            "lineItemId": "2505170958390976500",
            "storeLineItemId": "16045188153625",
            "productionOrderStatus": 1,
            "abnormalType": [
                6,
                9
            ]
        ]
    }
}
返回字段 字段意思 字段类型 长度 备注
messageId 工单消息 Id string 200 Message Id
type 数据类型 string 200 ORDER
messageType 消息类型 string 15 INSERT、UPDATE、DELETE、ORDER_CONNNECTED:(该类型下特别注意:商品在CJ系统已经重新关联,订单由不完整更新为完整状态,该消息中返回真实CJ订单号)
params object
- openId Open Id number
- cjOrderId CJ订单ID string 200
- orderNum 店铺订单号 string 200 将废弃,请使用orderNumber
- orderNumber 店铺订单号 string 200
- orderStatus 订单状态 string 200 参考订单状态
- logisticName 物流名称 string 200
- trackNumber 物流编号 string 200
- trackingUrl 追踪号跟踪URL string 200
- postage 运费 BigDecimal (18,2) 单位:$(美元)
- updateDate 更新时间 string 200
- createDate 创建时间 string 200
- payDate 支付时间 string 200
- deliveryDate 发货时间 string 200
- completeDate 完成时间 string 200
- privateOutboundOrder 是否私有库存出库单 boolean true=私有库存出库单(创建时即以 messageType=INSERT 推送), false=普通订单
- orderItems 订单item列表 List
-- vid 变体ID string 200
-- quantity 数量 int 200
-- sellPrice 售价 BigDecimal (18,2) 单位:$(美元)
-- storeLineItemId 店铺订单的lineItemId string 125
-- lineItemId CJ订单item的唯一id string 50
-- productionOrderStatus 生产状态 Number 1 1=待排单, 2=待生产, 3=生产中, 4=生产完成, 5=生产异常
-- abnormalType 异常原因 int[] 6=图片链接错误, 9=生产图和效果图对不上, 10=缺少挂环, 11=刀线图和印刷图不匹配, 12=边缘不平整, 13=字母未连接, 14=下单图片缺失

# 订单拆单消息

{
    "messageId": "7cceede817dc47ed9748328b64353c5c",
    "type": "ORDERSPLIT",
    "messageType": "UPDATE",
    "openId": 12312,
    "params": {
        "originalOrderId": "原订单",
        "splitOrderList": [
            {
                "createAt":1673490845706,
                "orderCode":"SD1613355441583259648-2",
                "orderStatus":300,
                "productList":[
                {
                    "sku":"CJNSSYLY01043-Claret-S",
                    "vid":"2547992D-CEE1-4BFD-99AC-9E30354F771F",
                    "quantity":1,
                    "productCode":"1613355657229205504"
                },
                {
                    "sku":"CJJSAQXF00016-Orange",
                    "vid":"A9C95BCB-D824-4AA1-A389-E86F3CCB10EF",
                    "quantity":1,
                    "productCode":"1613355657229205506"
                },
                {
                    "sku":"CJNSSYCS03214-Photo Color-XXL",
                    "vid":"E5FED43E-F9DE-483F-ADCE-8C95D3380315",
                    "quantity":1,
                    "productCode":"1613355657229205507"
                }
                ]
            },
            {
                "createAt":1673490845706,
                "orderCode":"SD1613355441583259648-1",
                "orderStatus":300,
                "productList":[
                    {
                        "sku":"CJNSSYLY01043-White-M",
                        "vid":"0550DFC6-7FF7-4662-AE7D-B4DF0E4EB24A",
                        "quantity":1,
                        "productCode":"1613355657229205505"
                    }
                ]
            }
        ],
        "orderSplitTime": "拆单时间"
    }
}
返回字段 字段意思 字段类型 长度 备注
messageId 工单消息 Id string 200 Message Id
type 数据类型 string 200 ORDERSPLIT
messageType 消息类型 string 15 INSERT、UPDATE、DELETE
openId Open Id number
params object
- originalOrderId 原订单号 string 200
- orderSplitTime 拆单时间 string 200
- splitOrderList 拆单列表 list
- - orderCode 拆单后的订单id string 200
- - createAt 拆单时间 string 200
- - orderStatus 订单状态 int 11
- - productList 商品信息列表 list 200
- - - productCode 商品code string 200
- - - vid 变体id string 200
- - - quantity 数量 int 10
- - - sku sku string 200

# 纠纷消息

{
    "messageId": "7cceede817dc47ed9748328b64353c5c",
    "type": "DISPUTE",
    "messageType": "UPDATE",
    "openId": 12312,
    "params": {
        "disputeId": "123",
        "orderId": "210823100016290555",
        "orderNumber": "api_52f268d40b8d460e82c0683955e63cc9",
        "status": "",
        "refuseReason": "ftdsr",
        "refundAmount": "123",
        "totalAmount": "123",
        "disputeType": "1",
        "reissueOrderId": "123",
        "trackingNumber": "123",
        "lineItems": [
            {
                "lineItemId": "lineItemId",
                "productId": "商品id",
                "variantId": "变体id",
                "sku": "sku",
                "price": 价格,
                "quantity": "数量"
            }
        ]
    }
}

返回字段 字段意思 字段类型 长度 备注
messageId 工单消息 Id string 200 Message Id
type 数据类型 string 200 DISPUTE
messageType 消息类型 string 15 INSERT、UPDATE、DELETE
openId Open Id number
params object
- orderId CJ订单ID string 200
- orderNumber 客户订单号 string 200
- disputeId 纠纷Id string 200
- shippingProvince 交易省份 string 200
- status 状态 string 200 (新增,取消,拒绝,同意退款,同意补发,完成)
- refuseReason 拒绝原因 string 500
- refundAmount 退款金额 BigDecimal (18,2) 单位:$(美元)
- totalAmount 总金额 BigDecimal (18,2) 单位:$(美元)
- disputeType 纠纷类型 BigDecimal (18,2) 单位:$(美元)
- reissueOrderId 补发单号 string 100 参考订单状态
- trackingNumber 补发追踪号 string 100
- productInfoList 商品信息 Object[]
- - productCode 商品code string 200
- - productId 商品id string 200
- - variantId 变体id string 200
- - price 商品价格 BigDecimal (18,2) 单位:$(美元)
- - quantity 数量 integer 100
- - sku sku string 100
状态
新增纠纷 1
取消纠纷 2
拒绝纠纷 3
同意退款 4
同意补发 5
完成纠纷 6

# 搜品创建结果消息

{
    "messageId": "7cceede817dc47ed9748328b64353c5c",
    "type": "SOURCINGCREATE",
    "messageType": "UPDATE",
    "openId": 12312,
    "params": {
        "cjProductId":"0550DFC6-7FF7-4662-AE7D-B4DF0E4EB24A",
        "cjVariantId":"0550DFC6-7FF7-4662-AE7D-B4DF0E4EB24A",
        "cjVariantSku":"CJ123582565212",
        "cjSourcingId":"125522",
        "status": "completed",
        "failReason":"",
        "createDate": "2023-02-07 00:00:00"
    }
}
返回字段 字段意思 字段类型 长度 备注
messageId 工单消息 Id string 200 Message Id
type 数据类型 string 200 SOURCINGCREATE
messageType 消息类型 string 15 INSERT、UPDATE、DELETE
openId Open Id number
params object
- cjProductId 商品id string 100
- cjVariantId 变体id list 100
- cjVariantSku CJ变体sku string 50
- cjSourcingId 搜品任务id string 50
- status 创建结果状态 string 20
- failReason 失败原因 string 20
- createDate 创建时间 String 50

# 物流变更消息

{
    "messageId": "7cceede817dc47ed9748328b64353c5c",
    "type": "LOGISTIC",
    "messageType": "UPDATE",
    "openId": 12312,
    "params": {
        "orderId": "210823100016290555",
        "logisticName": "CJPacket Ordinary",
        "trackingNumber": "number12345678",
        "trackingStatus": 12,
        "logisticsTrackEvents": "[{\"status\":12,\"activity\":\" Delivered, PO Box\",\"location\":\" NENANA,AK 99760\",\"eventTime\":\"2024-01-18 07:59:22\",\"statusDesc\":\"Delivered\",\"thirdActivity\":\"Delivered, PO Box\",\"thirdLocation\":\"NENANA,AK 99760\",\"thirdEventTime\":\"2024-01-18 07:59:22\"}]"
    }
}
Parameter Definition Type Required Length Note
messageId 工单消息 Id string 200 Message Id
type 数据类型 string 200 LOGISTIC
messageType 消息类型 string 15 INSERT、UPDATE、DELETE
openId Open Id number
params object
- orderId CJ订单号 string Y 200 210823100016290555
- logisticName 物流商名称 string Y 200 CJPacket Ordinary
- trackingNumber 物流追踪号 string Y 200 number12345678
- trackingUrl 追踪号跟踪URL string N 200
- trackingStatus 追踪状态 int Y 20 0=暂无追踪信息, 1=仓库已出库, 2=货代已入库, 3=货代退件, 4=货代出库, 5=头程运输, 6=抵达目的国, 7=开始清关, 8=清关完成, 9=末端提取, 10=派送中, 11=到达待取, 12=已签收, 13=失败/异常, 14=退回
- logisticsTrackEvents 物流轨迹信息 string Y 200 [{"status":12,"activity":" Delivered, PO Box","location":" NENANA,AK 99760","eventTime":"2024-01-18 07:59:22","statusDesc":"Delivered","thirdActivity":"Delivered, PO Box","thirdLocation":"NENANA,AK 99760","thirdEventTime":"2024-01-18 07:59:22"}]

# 补款单消息(Makeup)

补款单发生添加、取消、支付成功时推送到已注册的 makeup 监听地址(注册方式参考消息设置接口)。

messageType 触发时机 params.status
INSERT 补款单创建(添加) CREATED
CANCEL 补款单取消 CANCELED
PAID 补款单支付成功(完成) PAID
{
    "messageId": "f3c2a1d09e8b4c5da6b7c8d9e0f1a2b3",
    "type": "MAKEUP",
    "messageType": "PAID",
    "openId": 12312,
    "params": {
        "orderId": "BK260526000001",
        "relationOrderId": "ZF260526000001",
        "payOrderId": "2605260000000001",
        "amount": 12.35,
        "reason": "Postage difference",
        "type": 1,
        "diffUseType": 0,
        "status": "PAID",
        "createDate": "2026-06-04 10:00:00",
        "paymentDate": "2026-06-04 12:00:00"
    }
}
参数名称 参数意义 参数类型 是否必传 长度 备注
messageId 补款消息 Id string Y 200 Message Id(重试时不变,可用于幂等去重)
type 数据类型 string Y 20 MAKEUP
messageType 消息类型 string Y 15 INSERT-添加、CANCEL-取消、PAID-支付成功
openId Open Id number Y
params object Y
- orderId 补款单号 string Y 200 与补款列表 orderCode 一致
- relationOrderId 关联的 CJ 订单号 string Y 200
- payOrderId 补款支付单号 string N 200 支付后返回
- amount 补款金额 double Y (18,2) 单位:$(美元)
- reason 补款原因 string N 500 英文
- type 单据类型 int Y 5 1=补款
- diffUseType 补款用途 int Y 5 0=订单补款, 1=Balance Top-up, 2=Repayment, 3=Transfer Shipping Fee
- status 补款单状态 string Y 20 CREATED / CANCELED / PAID
- createDate 创建时间 string Y 50 yyyy-MM-dd HH:mm:ss
- paymentDate 支付时间 string N 50 PAID 时返回

status状态值

参数名称 参数意义
CREATED 新增补款单
CANCELED 取消补款单
PAID 补款单支付成功

# 示例

# 商品消息接收示例

package com.cj.cn.controller;

import com.alibaba.fastjson.JSON;
import com.cj.cn.constant.callback.domain.CallbackParams;
import com.cj.cn.util.result.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * CJ webhook 监听示例
 *
 * @author : kay
 */
@RestController
@RequestMapping("/webhookListener")
@Slf4j
public class TestController {

    @PostMapping("/productMessage")
    public Result productMessage(@RequestBody @Validated CallbackParams query) {
        log.info("product message:{}", JSON.toJSONString(query));
        return Result.success(Boolean.TRUE);
    }
}
package com.cj.cn.constant.callback.domain;

import lombok.Data;

/**
 * @author : kay
 */
@Data
public class CallbackParams {
    private String messageId;
    private String type;
    private Object params;
}
package com.cj.cn.constant.callback.domain;

import lombok.Getter;
import org.springframework.util.StringUtils;

/**
 * @author : kay
 */
@Getter
public enum CallbackBusinessTypeEnum {
    PRODUCT,
    VARIANT,
    STOCK;

    public static CallbackBusinessTypeEnum create(String name) {
        if (!StringUtils.isEmpty(name)) {
            for (CallbackBusinessTypeEnum typeEnum: CallbackBusinessTypeEnum.values()) {
                if (typeEnum.name().equals(name.toUpperCase())) {
                    return typeEnum;
                }
            }
        }
        return null;
    }
}