简介

开放API允许租户通过标准 HTTP 接口驱动商城中台业务,实现商品查询、订单管理、售后处理、用户管理和营销功能,无需依赖中台托管的 H5/PC 商城前端。发货与售后状态变化另见左侧「消息推送」:由平台主动 POST 到接入方 URL,不是再调一组查询接口。

先选接入方式

你要做的看哪份文档
自建 App / 小程序 / 后台,自己调商品、下单、售后本文(开放API)
已有企业商城,嵌一条可装修的完整福利商城《对外H5链接(托管商城)》
直接用品牌落地页(淘宝闪购、淘票票、好食期券码)找运营开通,阅读 《H5 接入说明》
i
托管 H5 和开放API 可以同时使用:H5 给用户现成页面,开放API 给自建前端调接口。不要把 H5 入口链接当成开放API 去签名调用。只用托管页面、不自建前端时,不必申请 AppKey。

接口基础信息

项目说明
Base URLhttp://mall.hzkting.com:9988/open/api/v1
协议HTTP
数据格式JSON(Content-Type: application/json)
字符编码UTF-8

接入流程

  1. 联系平台运营,在管理后台创建开放应用,获取 AppKey 和 AppSecret
  2. 根据业务需要申请对应的权限范围(PermScope)
  3. 在沙箱模式下完成接口联调测试
  4. 测试通过后,由运营人员审核升级为生产模式

权限范围(PermScope)

权限值说明覆盖接口
PRODUCT_READ商品查询商品列表、详情、价格、库存、分类
ORDER_WRITE订单写入创建订单、取消订单
ORDER_READ订单查询订单详情、列表、物流
AFTERSALE售后管理申请售后、查询、取消
USER用户管理注册、登录、个人信息、收货地址
PROMOTION营销优惠券列表、领取
FINANCE资金流账户余额、资金流水、结算账单、提现
FILM电影票淘票票影片、场次、下单
AI_CHATAI对话同步/流式选品对话
i
消息推送配置(/notify/config)与租户配置相同,只需有效 AppKey,无需额外 PermScope。

签名鉴权

所有接口请求必须携带以下三个请求头:

请求头说明示例
X-App-Key应用标识,由平台分配a1b2c3d4e5f6...
X-TimestampUnix 时间戳(秒),与服务器时差不超过 300 秒1745548800
X-Sign请求签名,见下方签名算法A3F2B1C4...

签名算法

  1. 收集所有请求参数:URL Query 参数 + JSON Body 顶层字段(嵌套对象不参与签名,空值字段跳过)
  2. 将参数按 key 字母升序排列,拼接为 key1=value1&key2=value2&...
  3. 末尾追加 &app_secret={AppSecret}
  4. 对拼接字符串执行 MD5,结果转大写即为签名值
i
示例:参数 page=1, product_id=123,AppSecret=mysecret
拼接:page=1&product_id=123&app_secret=mysecret
签名:MD5("page=1&product_id=123&app_secret=mysecret").toUpperCase()

Java 示例

public static String sign(Map<String, String> params, String appSecret) {
    TreeMap<String, String> sorted = new TreeMap<>(params);
    StringBuilder sb = new StringBuilder();
    sorted.forEach((k, v) -> {
        if (v != null && !v.isEmpty()) {
            if (sb.length() > 0) sb.append("&");
            sb.append(k).append("=").append(v);
        }
    });
    sb.append("&").append("app_secret=").append(appSecret);
    return DigestUtils.md5DigestAsHex(sb.toString().getBytes(StandardCharsets.UTF_8)).toUpperCase();
}

Python 示例

import hashlib

def sign(params: dict, app_secret: str) -> str:
    items = sorted((k, v) for k, v in params.items() if v)
    query = "&".join(f"{k}={v}" for k, v in items)
    query += f"&app_secret={app_secret}"
    return hashlib.md5(query.encode()).hexdigest().upper()

完整 curl 示例

# 1. 拼接签名字符串:pageNo=1&app_secret=mysecret
# 2. 计算签名:MD5("pageNo=1&app_secret=mysecret").toUpperCase()
# 3. 发起请求:
curl -X GET "http://mall.hzkting.com:9988/open/api/v1/products?pageNo=1" \
  -H "X-App-Key: abc123" \
  -H "X-Timestamp: 1745548800" \
  -H "X-Sign: D41D8CD98F00B204E9800998ECF8427E"

消费者 Token(X-User-Token)

用户登录成功后,响应体中返回 user_token。后续调用需要消费者身份的接口时,需在请求头中携带:

请求头说明
X-User-Token用户登录后获取的 Token,有效期 7 天
!
注册和登录接口无需 X-User-Token。X-User-Token 与 AppKey 签名机制并存、独立校验。

统一响应格式

所有接口均返回以下 JSON 结构:

{
  "code": 0,            // 0=成功,非0=失败
  "message": "success",
  "data": { ... },      // 业务数据,失败时为 null
  "is_sandbox": false   // true 表示沙箱模式响应
}
字段类型说明
codeint0 表示成功,非 0 表示失败,见错误码表
messagestring成功时为 "success",失败时为错误描述
dataobject/null业务数据
is_sandboxboolean沙箱模式下写单操作返回 true

错误码

HTTPcode错误码说明
4001001MISSING_HEADER缺少必要请求头 X-App-Key / X-Timestamp / X-Sign
4011002SIGN_INVALID签名错误或应用不存在
4011003TIMESTAMP_EXPIRED时间戳与服务器时差超过 5 分钟
4011004USER_TOKEN_INVALIDX-User-Token 无效或已过期
4031005IP_FORBIDDEN请求 IP 不在白名单
4031006PERMISSION_DENIED应用无该接口的权限范围
4031007APP_DISABLED应用已停用
4031008TENANT_DISABLED租户已停用
4031009PRODUCT_NOT_AUTHORIZED商品不在租户授权范围
4031010ORDER_NOT_AUTHORIZED订单不属于当前租户
4031011AFTERSALE_NOT_AUTHORIZED售后单不属于当前租户
4091012PHONE_ALREADY_EXISTS手机号已注册
4091013COUPON_ALREADY_CLAIMED优惠券已领取
4001014COUPON_UNAVAILABLE优惠券不可用或已过期
4291015RATE_LIMIT_EXCEEDED调用频率超限(默认 60 次/分钟),或商品读并发已满(全局 20 / 单租户 4)
4031016FINANCE_NOT_AUTHORIZED账单或提现记录不属于当前租户
5022001UPSTREAM_ERROR上游平台调用失败
5009999INTERNAL_ERROR系统内部错误

商品接口

所有商品接口需要 PRODUCT_READ 权限。

!
价格字段必读(取错字段会导致页面价格错乱)
1. 拿货价:salePrice(商品级)与详情里的 skuDetail[].price / skuDetail[].tenantPrice(SKU级)—— 这是贵司的采购结算价,商品级与 SKU 级数值口径一致,下单结算以此为准;贵司终端售价请在此基础上自行加价。
2. 建议零售价:marketPrice(商品级)与详情里的 skuDetail[].reservePrice(SKU级,仅部分渠道如天猫超市返回)—— 上游建议零售价,仅供参考展示,切勿当作拿货价或结算价使用;系统保证其非空时不低于对应拿货价,为空时请勿展示。
3. costPrice:保留字段,OpenAPI 不返回(恒为 null)。
4. 商品列表默认不返回 skuDetail(字段为 null)。规格请调 GET /open/api/v1/products/{productId};若列表必须带 SKU,传 includeSkuDetail=true。
前端展示 SKU 价格时请固定读取 skuDetail[].price,不要读 reservePrice。
GET/open/api/v1/products 商品列表(分页)▶
i
按中台类目筛选(ID / 编码 / 多选 ID,同传时以 saasCategoryId 为准):
· saasCategoryId:中台类目单个 ID(/saas-categories 返回的 id);
· saasCategoryIdList:中台类目多个 ID(可勾选多个节点);
· categoryCode:中台类目编码(/saas-categories 返回的 categoryCode,如 food)。
均按「该类目及其所有子类目」范围过滤;传入未知/停用的编码时返回空列表。
i
skuDetail:列表默认不返回(null)。规格请调详情接口。需要列表带 SKU 时传 includeSkuDetail=true(与历史列表一致,报文更大)。

响应示例

{"code":0,"data":{"total":100,"pageNo":1,"pageSize":20,"products":[{"unifiedProductId":"123456","productName":"京东京造示例商品","brandName":"京东京造","brandTag":"JD_JINGZAO","salePrice":99.00,"mainImage":"https://...","status":"ON_SALE"}]}}
GET/open/api/v1/products/{productId} 商品详情▶

响应示例

{"code":0,"data":{"unifiedProductId":"123456","platformProductId":"SKU001","productName":"示例商品","brandName":"品牌A","category":"食品","salePrice":99.00,"marketPrice":128.00,"costPrice":null,"mainImage":"https://...","status":"ON_SALE","stockStatus":"IN_STOCK","stockNum":500,
"skuDetail":"[{\"skuId\":\"SKU001\",\"specName\":\"规格A\",\"price\":99.00,\"tenantPrice\":99.00,\"reservePrice\":128.00,\"images\":[\"https://...\"]}]",
"skuStockDetail":"[{\"skuId\":\"SKU001\",\"quantity\":\"500\",\"stockTagCode\":\"NORMAL\",\"stockTagName\":\"有货\",\"hasStock\":true,\"specName\":\"规格A\"}]"}}
i
示例中 price/tenantPrice=99.00 是拿货价,reservePrice=128.00 是建议零售价(仅部分渠道返回);页面取价请固定用 price。
x
若商品不在租户授权范围内,返回 403 PRODUCT_NOT_AUTHORIZED
GET/open/api/v1/products/{productId}/price 商品价格▶

Query 参数

参数名类型必填说明
areaCodestring可选区域编码(省_市_区_镇),用于区域差价

响应示例

{"code":0,"data":{"productId":"123456","salePrice":99.00,"costPrice":null,"marketPrice":128.00,"skuPrices":{"SKU001":99.00}}}
i
salePrice 与 skuPrices 均为拿货价;marketPrice 为建议零售价,仅供展示。
GET/open/api/v1/products/{productId}/stock 商品库存▶

Query 参数

参数名类型必填说明
areaCodestring可选区域编码

响应示例

{"code":0,"data":{"productId":"123456","hasStock":true,"stockNum":500,"areaCode":"1_72_2799_0"}}
GET/open/api/v1/saas-categories 中台类目树▶

返回中台全部启用类目(status=ENABLED),按 parentId 组装为树形结构。用于客户端展示中台类目、与商品接口返回的 saasCategoryId 对应。

i
类目为公共基础字典,返回全部启用类目,与租户授权范围无关,也不受租户「对外暴露中台类目」开关影响。需要 PRODUCT_READ 权限。可加 ?flat=true 返回平铺列表(不组树,children 恒为空)。

响应示例

{
  "code": 0,
  "data": [
    {
      "id": 3,
      "categoryCode": "food",
      "categoryName": "食品",
      "parentId": 0,
      "sortOrder": 0,
      "icon": null,
      "children": [
        {"id": 31, "categoryCode": "food_snack", "categoryName": "休闲零食", "parentId": 3, "sortOrder": 0, "icon": null, "children": []}
      ]
    }
  ]
}

订单接口

创建/取消订单需要 ORDER_WRITE 权限;查询订单需要 ORDER_READ 权限。

POST/open/api/v1/orders 创建订单▶

请求示例

{
  "products": [
    {"platformProductId": "1008814639745", "skuId": "6010122344404", "quantity": 1, "price": 28.9}
  ],
  "address": {
    "receiverName": "张三",
    "receiverMobile": "13800138000",
    "province": "北京市",
    "city": "北京市",
    "district": "朝阳区",
    "detailAddress": "xxx路xxx号",
    "fullAddress": "北京市朝阳区xxx路xxx号"
  },
  "areaCode": "110105",
  "paymentType": 4,
  "buyerRemark": "请尽快发货",
  "customerOrderNo": "CUSTOM-20260506-001"
}

响应示例

{"code":0,"data":{"orderSn":"SO20260425143000T001ABC123","orderId":1,"tenantAmount":66.00,"status":"CREATED"},"is_sandbox":false}

// 沙箱模式响应:
{"code":0,"data":{"orderSn":"SANDBOX_D0C35852DB854C05","orderId":1,"status":"CREATED","receiverName":"张三","receiverPhone":"13800138000","receiverAddress":"北京市朝阳区xxx路xxx号"},"is_sandbox":true}
!
沙箱模式下,订单仅在本地落库,不调用上游平台,响应中 is_sandbox=true
POST/open/api/v1/orders/{orderId}/cancel 取消订单▶
i
请求体可选:{ "reason": "取消原因" }。

响应示例

{"code":0,"data":{"orderSn":"SO20260425143000T001ABC123","status":"CANCELLED"}}
GET/open/api/v1/orders/{orderId} 订单详情▶

响应示例

{"code":0,"data":{"order":{"id":1,"orderSn":"SO20260425...","status":"CREATED","tenantAmount":66.00,"freightAmount":0.00,"receiverName":"张三","orderTime":"2026-04-25T14:30:00"},"items":[{"productId":123,"productName":"示例商品","quantity":1,"tenantPrice":66.00}]}}
i
tenantAmount/items[].tenantPrice 为贵司应付结算金额/单价,与商品接口拿货价同口径。
x
若订单不属于当前租户,返回 403 ORDER_NOT_AUTHORIZED
GET/open/api/v1/orders 订单列表▶
!
列表中的 consumerId 为额外查询参数(非 DTO 字段),可按消费者ID筛选;订单状态见 status 枚举。

响应示例

{"code":0,"data":{"total":50,"pageNo":1,"pageSize":20,"orders":[{"unifiedOrderId":"1","platformOrderId":"JD001","totalAmount":60.00,"status":"CREATED","orderTime":"2026-04-25T14:30:00"}]}}
i
订单列表仅返回当前租户下的订单,不会跨租户泄露数据。订单列表项仅填充部分字段(unifiedOrderId/platformOrderId/totalAmount/status/orderTime)。
GET/open/api/v1/orders/{orderId}/logistics 订单物流信息▶

响应示例

{"code":0,"data":{"logisticsCompany":"顺丰速运","waybillNo":"SF1234567890","status":"IN_TRANSIT","tracks":[{"time":"2026-04-25 10:00:00","content":"已揽收"}]}}

售后接口

所有售后接口需要 AFTERSALE 权限。

POST/open/api/v1/aftersales 申请售后▶

售后类型(type)可选值

枚举值说明
REFUND_ONLY仅退款
RETURN_REFUND退货退款
EXCHANGE换货
REPAIR维修

申请原因编码(reasonCode)可选值

编码含义
QUALITY_ISSUE质量问题
NOT_AS_DESCRIBED与描述不符
DAMAGED商品损坏
WRONG_ITEM发错商品
NO_LONGER_NEEDED不再需要
i
沙箱模式响应为 {afterSaleSn, type, status};下方响应字段为生产模式(AfterSaleCreateResult)。

请求示例

{
  "platformOrderId": "SANDBOX_CE3C017EF6FE4FB5",
  "type": "REFUND_ONLY",
  "reasonCode": "NO_LONGER_NEEDED",
  "description": "不想要了,申请退款",
  "refundAmount": 99.00,
  "contactName": "张三",
  "contactPhone": "13800138000",
  "items": [
    {"platformProductId": "123456", "quantity": 1, "productName": "测试商品"}
  ]
}

响应示例

// 沙箱模式
{"code":0,"data":{"afterSaleSn":"SANDBOX_AS_A1B2C3D4E5F6","type":"REFUND_ONLY","status":"CREATED"},"is_sandbox":true}

// 生产模式
{"code":0,"data":{"success":true,"platformAfterSaleId":"AS123456"},"is_sandbox":false}
!
沙箱模式下,售后申请仅在本地落库,不调用上游平台。
GET/open/api/v1/aftersales/{afterSaleId} 售后详情▶

响应示例

{"code":0,"data":{"afterSaleId":"AS001","orderSn":"SO20260425...","afterSaleType":"REFUND_ONLY","reason":"质量问题","status":"PENDING_AUDIT","refundAmount":60.00,"createTime":"2026-04-25T15:00:00"}}
x
若售后单不属于当前租户,返回 403 AFTERSALE_NOT_AUTHORIZED
GET/open/api/v1/aftersales 售后列表▶
i
status 枚举:PENDING_AUDIT / AUDIT_PASSED / AUDIT_REJECTED / PENDING_RETURN / RETURNING / RETURN_RECEIVED / REFUNDING / REFUNDED / EXCHANGING / EXCHANGE_COMPLETED / COMPLETED / CANCELLED / CLOSED;type 枚举:REFUND_ONLY / RETURN_REFUND / EXCHANGE / REPAIR。

响应示例

{"code":0,"data":[{"id":1,"orderSn":"SO20260425...","afterSaleType":"REFUND_ONLY","status":"PENDING_AUDIT","refundAmount":60.00,"createTime":"2026-04-25T15:00:00"}]}
POST/open/api/v1/aftersales/{afterSaleId}/cancel 取消售后申请▶

无需请求体,直接 POST 即可。

响应字段(data)- 生产模式

字段类型说明
databooleantrue 表示取消成功

响应示例

// 生产模式
{"code":0,"data":true}

// 沙箱模式
{"code":0,"data":{"afterSaleSn":"SANDBOX_AS_A1B2C3D4E5F6","type":"REFUND_ONLY","status":"CANCELLED"},"is_sandbox":true}
POST/open/api/v1/aftersales/{afterSaleId}/return-logistics 填写退货物流▶

仅适用于 RETURN_REFUND(退货退款)类型、且售后单已审核通过后,买家寄回商品时回填退货运单号。

请求体字段

字段类型必填说明
logisticsCompanystring是退货快递公司名称,如“中通快递”
logisticsNostring是退货运单号
shipperCodestring否快递公司编码,如 ZTO

请求示例

{
  "logisticsCompany": "中通快递",
  "logisticsNo": "78474409893076",
  "shipperCode": "ZTO"
}

响应示例

{"code":0,"data":true}
x
若售后单不属于当前租户,返回 403 AFTERSALE_NOT_AUTHORIZED

用户接口

所有用户接口需要 USER 权限。注册和登录接口无需 X-User-Token,其余接口需要。

POST/open/api/v1/users/register 注册消费者▶
x
手机号已注册时返回 409 PHONE_ALREADY_EXISTS

响应示例

{"code":0,"data":{"userId":10001,"username":"testuser","mobile":"13800138000","nickName":"testuser","status":"ENABLED","createTime":"2026-04-25T16:00:00"}}
POST/open/api/v1/users/login 用户登录▶

响应示例

{"code":0,"data":{"user_token":"a1b2c3d4e5f6...","user_id":10001}}
i
登录成功后,请保存 user_token,后续需要消费者身份的接口通过 X-User-Token 请求头传递,有效期 7 天。
GET/open/api/v1/users/me 获取当前用户信息(需要 X-User-Token)▶

无需额外参数,从 X-User-Token 识别用户身份。

响应示例

{"code":0,"data":{"userId":10001,"username":"testuser","mobile":"13800138000","nickName":"小明","avatar":"https://...","sex":1,"status":"ENABLED"}}
PUT/open/api/v1/users/me 更新用户信息(需要 X-User-Token)▶
i
请求体为用户对象的部分字段(patch),仅传需要更新的字段即可(常用:nickName / avatar / sex)。

响应示例

{"code":0,"data":{"userId":10001,"username":"testuser","nickName":"新昵称","avatar":"https://new-avatar.png","sex":1}}
GET/open/api/v1/users/me/addresses 收货地址列表(需要 X-User-Token)▶

返回当前用户的所有收货地址。

响应示例

{"code":0,"data":[{"addressId":1,"receiverName":"张三","receiverMobile":"13800138000","province":"浙江省","city":"杭州市","district":"西湖区","detailAddress":"文三路100号","isDefault":true}]}
POST/open/api/v1/users/me/addresses 新增收货地址(需要 X-User-Token)▶

响应示例

{"code":0,"data":{"addressId":2,"receiverName":"李四","receiverMobile":"13900139000","province":"广东省","city":"深圳市","district":"南山区","detailAddress":"科技园路1号","fullAddress":"广东省深圳市南山区科技园路1号","isDefault":false}}

营销接口

所有营销接口需要 PROMOTION 权限。

GET/open/api/v1/promotions/coupons 可用优惠券列表▶

响应示例

{"code":0,"data":{"total":5,"coupons":[{"couponId":1,"couponName":"满100减10","couponType":"PRICE","price":10.00,"consumeThreshold":100.00,"status":"ACTIVE"}]}}
POST/open/api/v1/promotions/coupons/{couponId}/claim 领取优惠券(需要 X-User-Token)▶

无需请求体,通过 X-User-Token 识别领取用户。

响应示例

{"code":0,"data":{"userCouponId":100,"couponId":1,"couponName":"满100减10","status":"UNUSED","startTime":"2026-04-25","endTime":"2026-05-25"}}
错误情况HTTP错误码
优惠券已领完或不在有效期400COUPON_UNAVAILABLE
消费者已领取过同一优惠券409COUPON_ALREADY_CLAIMED

资金流接口

所有资金流接口需要 FINANCE 权限。

GET/open/api/v1/finance/balance 查询账户余额▶

响应字段(data)

字段类型说明
datadecimal账户余额(元)

响应示例

{"code":0,"data":1280.50}
GET/open/api/v1/finance/fund-flows 查询资金流水▶
i
storeId 由系统按当前租户自动覆盖,无需传入。flowType:ORDER_IN / REFUND_OUT / WITHDRAW / COMMISSION / SUBSIDY / ADJUSTMENT。

响应示例

{"code":0,"data":[{"flowId":1,"flowSn":"F20260425...","flowType":"ORDER_IN","amount":60.00,"direction":"IN","balanceBefore":1220.50,"balanceAfter":1280.50,"description":"订单结算","createTime":"2026-04-25T14:30:00"}]}
GET/open/api/v1/finance/bills 查询结算账单列表▶
istoreId 由系统按当前租户自动覆盖,无需传入。

响应示例

{"code":0,"data":{"total":3,"bills":[{"billId":1,"billSn":"B20260425...","orderAmount":1000.00,"commissionAmount":60.00,"finalAmount":940.00,"status":"PENDING","startTime":"2026-04-01","endTime":"2026-04-30"}]}}
GET/open/api/v1/finance/bills/{billId} 账单详情▶

返回指定账单的完整信息。

响应示例

{"code":0,"data":{"billId":1,"billSn":"B20260425...","orderAmount":1000.00,"refundAmount":0,"commissionAmount":60.00,"finalAmount":940.00,"status":"PENDING","startTime":"2026-04-01","endTime":"2026-04-30"}}
GET/open/api/v1/finance/bills/{billId}/details 账单明细▶

返回账单下的所有结算明细条目。

响应示例

{"code":0,"data":[{"detailId":1,"billId":1,"orderSn":"SO20260425...","orderAmount":100.00,"commissionAmount":6.00,"settlementAmount":94.00}]}
POST/open/api/v1/finance/withdrawals 申请提现▶
字段类型必填说明
applyAmountdecimal必填申请提现金额
bankNamestring必填开户银行
bankAccountNamestring必填开户名
bankAccountNumberstring必填银行账号
remarkstring可选备注
!
提现申请提交后需平台审核,审核通过后方可到账。

响应字段(data)

字段类型说明
datalong提现申请ID(withdrawalId)

响应示例

{"code":0,"data":1}
GET/open/api/v1/finance/withdrawals 查询提现列表▶

返回当前租户的提现申请记录列表,支持分页。

响应示例

{"code":0,"data":{"total":3,"withdrawals":[{"withdrawalId":1,"withdrawalSn":"W20260425...","applyAmount":500.00,"status":"PENDING","applyTime":"2026-04-25T16:00:00"}]}}
GET/open/api/v1/finance/withdrawals/{withdrawalId} 提现详情▶

返回指定提现申请的详细信息及当前审核状态。

响应示例

{"code":0,"data":{"withdrawalId":1,"withdrawalSn":"W20260425...","applyAmount":500.00,"actualAmount":500.00,"bankName":"工商银行","bankAccountName":"张三","status":"APPROVED","applyTime":"2026-04-25T16:00:00","approveTime":"2026-04-26T10:00:00"}}

消息推送(平台 → 接入方)

平台在订单发货、售后状态变更时 主动 POST 到您配置的 notifyUrl。商品标题/价格/库存变更 不推送,请继续轮询商品接口,或后续使用 updatedSince。

!
创建订单成功后本地状态已是 SUBMITTED/PAID,没有「确认收货/确认订单」开放接口。请在用户支付完成后再调用 POST /orders。发货与售后不要只靠轮询,接入本模块即可。

v1 事件

event触发时机建议处理
ORDER_SHIPPEDSaaS 订单首次进入 SHIPPED(上游发货同步 / 猫超·京东回调 / 运营补救)按 orderSn 或 customerOrderNo 更新物流,通知终端用户
AFTERSALE_STATUS售后单状态变化(申请 PENDING、上游受理 PROCESSING、待寄回 RETURN_PENDING、退款完成 REFUNDED 等)按 afterSaleId + status 刷新售后;需寄回时引导用户填退货物流

出站报文

平台向 notifyUrl 发送 POST application/json,Body 全部为顶层标量(无嵌套对象,便于与开放 API 同一套签名)。请求头:

请求头说明
X-App-Key与入站相同的 AppKey
X-TimestampUnix 秒级时间戳,与 Body timestamp 一致
X-Sign与 Body sign 一致
X-Event事件名,如 ORDER_SHIPPED
X-Event-Id幂等键,与 Body eventId 一致

签名(与入站算法相同)

  1. 取 Body 全部顶层非空标量,排除 sign
  2. 按 key 字母升序拼接 key1=value1&key2=value2&...
  3. 末尾追加 &app_secret={AppSecret}
  4. UTF-8 MD5 后转大写,与 X-Sign / sign 比对

发货事件示例

POST {notifyUrl}
X-App-Key: a1b2c3d4...
X-Timestamp: 1745548800
X-Sign: A3F2B1C4...
X-Event: ORDER_SHIPPED
X-Event-Id: ORDER_SHIPPED:SO20260902001:7310001234567

{
  "event": "ORDER_SHIPPED",
  "eventId": "ORDER_SHIPPED:SO20260902001:7310001234567",
  "orderSn": "SO20260902001",
  "customerOrderNo": "CRMEB123456",
  "status": "SHIPPED",
  "logisticsCompany": "中通快递",
  "logisticsNo": "7310001234567",
  "shipperCode": "ZTO",
  "shipTime": "2026-09-02T10:15:00",
  "appKey": "a1b2c3d4...",
  "timestamp": "1745548800",
  "sign": "A3F2B1C4..."
}

售后事件示例

{
  "event": "AFTERSALE_STATUS",
  "eventId": "AFTERSALE_STATUS:9001:RETURN_PENDING",
  "afterSaleId": "9001",
  "orderSn": "SO20260902001",
  "status": "RETURN_PENDING",
  "afterSaleType": "RETURN_REFUND",
  "refundAmount": "88.00",
  "appKey": "a1b2c3d4...",
  "timestamp": "1745548900",
  "sign": "..."
}

接收方要求

  • 幂等:以 eventId 去重。发货为 ORDER_SHIPPED:{orderSn}:{logisticsNo}(尚无运单时末段为 _,补物流后再推一条);售后为 AFTERSALE_STATUS:{afterSaleId}:{status}。平台可能因多通道并发重试而重复投递。
  • 成功应答:HTTP 2xx,且 Body 为空,或 JSON code 为 0 / 200 / SUCCESS,或 errcode=0 / success=true,或正文含 SUCCESS。
  • 失败重试:未确认时进程内最多 8 次,间隔约 1s / 2s / 4s / 8s / 16s / 32s / 60s / 120s。仍失败请用订单/售后查询接口补偿。
  • 请在数秒内返回 ACK,业务处理放到异步队列,避免拖垮推送线程。
GET/open/api/v1/notify/config 查询推送配置▶

返回当前 AppKey 对应应用的回调地址与订阅事件。无需额外 PermScope。

响应示例

{"code":0,"data":{"notifyUrl":"https://your.example.com/openapi/notify","events":"ORDER_SHIPPED,AFTERSALE_STATUS"}}
PUT/open/api/v1/notify/config 更新推送配置▶

省略字段表示不改。notifyUrl 传空字符串关闭推送(会写入 NULL)。仅允许指向公网的 http/https,本机与内网地址会被拒绝。生产请用 HTTPS。events 传空则默认订阅全部 v1 事件。生产订单只推给 app_type=PRODUCTION 的应用。

请求示例

{"notifyUrl":"https://your.example.com/openapi/notify","events":"ORDER_SHIPPED,AFTERSALE_STATUS"}
POST/open/api/v1/notify/test 发送测试事件▶

向已配置的 notifyUrl 投递一条测试报文(eventId 以 TEST: 开头,orderSn=TEST_ORDER),用于联调接收端,无需真实发货。

请求体

字段类型必填说明
eventstring可选默认 ORDER_SHIPPED,也可 AFTERSALE_STATUS

响应示例

{"code":0,"data":{"event":"ORDER_SHIPPED","eventId":"TEST:ORDER_SHIPPED:ab12...","queued":true}}
i
queued=true 表示已进入投递队列,不是对方 HTTP 已成功。请在接收端核对 eventId。

字典表

对接的是中台开放接口,不是京东、天猫、好食期、聚水潭等供应商原始接口。各供应商自己的状态码、类目码、原因码都不一样,中台已全部映射成下面这套统一枚举。

请只认本页取值。不要按供应商原始字典做判断,也不要再各自做一层供应商映射。开放接口不会返回供应商原始状态码。

i
状态没有单独的字典查询接口,按下表写死即可。类目动态字典:GET /open/api/v1/saas-categories。

商品状态 status

枚举值说明
ON_SALE在售
OFF_SALE下架
OUT_OF_STOCK缺货
DELETED已删除
AUTH_FAILED未授权(对接入方等同不存在)
UNKNOWN未知

库存状态 stockStatus

枚举值说明
IN_STOCK有货
LOW_STOCK库存紧张
OUT_OF_STOCK无货

价格字段(中台统一口径)

字段口径说明
salePrice、skuDetail[].price、skuDetail[].tenantPrice拿货价 / 结算价三者相同。订单结算以此为准,终端售价请在此基础上加价
marketPrice、skuDetail[].reservePrice建议零售价仅部分渠道返回,仅供展示,禁止当拿货价。为空则不要展示
costPrice保留字段开放接口不返回,恒为 null
!
前端展示 SKU 价请固定读 skuDetail[].price,不要读 reservePrice。

订单状态 status

枚举值说明
CREATED已创建(尚未提交履约)
SUBMITTING提交中
RESULT_UNKNOWN提交结果待核实
FAILED提交失败
SUBMITTED已提交
PAID已支付 / 待发货
SHIPPED已发货
COMPLETED已完成
CANCELLED已取消
i
请在用户支付完成后再调用 POST /open/api/v1/orders。创建成功后状态为 SUBMITTED 或 PAID。发货请接消息推送 ORDER_SHIPPED(payload 中 status=SHIPPED),不要只靠轮询。

售后类型 type

枚举值说明
REFUND_ONLY仅退款
RETURN_REFUND退货退款
EXCHANGE换货
REPAIR维修

售后申请原因 reasonCode

枚举值说明
QUALITY_ISSUE质量问题
NOT_AS_DESCRIBED与描述不符
DAMAGED商品损坏
WRONG_ITEM发错商品
NO_LONGER_NEEDED不再需要

售后状态 status

枚举值说明
PENDING已申请(仅此状态可取消)
APPROVED审核通过(可填写退货物流)
RETURN_PENDING待寄回(可填写退货物流)
PROCESSING处理中
REFUNDED已退款
REJECTED审核拒绝
CANCELLED已取消
COMPLETED已完成(终态)
CLOSED已关闭(终态)
i
售后状态变更会推 AFTERSALE_STATUS,报文里的 status 即上表取值。沙箱申请售后可能返回 CREATED,生产申请成功为 PENDING,请以生产为准。

沙箱环境

新创建的应用默认为沙箱模式(app_type=SANDBOX),用于安全地测试 API 集成,不影响真实业务。

沙箱与生产的区别

操作类型沙箱模式生产模式
创建订单仅本地落库,生成 SANDBOX_ 前缀订单号调用上游平台,真实下单
区域配送预检拒绝真实配送确认,返回 reason=SANDBOX调用上游确认该地址是否可配送
取消订单仅本地更新状态调用上游平台取消
申请售后仅本地落库调用上游平台
查询订单列表查询沙箱订单表,返回 SANDBOX_ 前缀订单查询生产订单表
查询订单详情/物流返回沙箱数据返回真实数据
商品/用户/优惠券与生产一致与生产一致

识别沙箱响应

{"code":0,"data":{"orderSn":"SANDBOX_A1B2C3D4E5F6G7H8","orderId":1,"status":"CREATED","receiverName":"张三","receiverPhone":"13800138000","receiverAddress":"北京市朝阳区xxx路xxx号"},"is_sandbox":true}

升级为生产模式

完成沙箱联调测试后,联系平台运营人员在管理后台审核升级为生产模式。

!
升级为生产模式不可逆,请确保所有接口联调测试通过后再申请升级。

常见问题

Q不想自己做商城前端,只想用中台 H5,还要对接开放API吗?

不需要为托管 H5 申请 AppKey。联系运营开通,按 《H5 接入说明》 提供机构信息即可;渠道码由运营分配。同一租户若还有自建系统,再按本文对接开放API。

Q签名验证失败(SIGN_INVALID)怎么排查?
  1. 确认参数排序是否按 key 字母升序(区分大小写)
  2. 确认空值字段是否已跳过(值为 null 或空字符串的字段不参与签名)
  3. 确认 JSON Body 中只有顶层字段参与签名,嵌套对象(如 address)整体不参与
  4. 确认 MD5 结果是否转为大写
  5. 确认使用的是 UTF-8 编码计算 MD5
Q时间戳过期(TIMESTAMP_EXPIRED)怎么处理?
  • 使用秒级时间戳,不是毫秒(毫秒值会比服务器时间大 1000 倍)
  • 服务器时钟与 NTP 同步,避免时钟漂移
  • 每次请求重新生成时间戳,不要复用
Q调用频率超限(RATE_LIMIT_EXCEEDED)如何处理?
  • 默认限制为 60 次/分钟,收到文案「调用频率超限」后等待 1 分钟再重试
  • 商品列表/详情/价格/库存另有并发闸:全局 20 路、单租户 4 路。文案为「商品查询繁忙」时请降低并发或稍后重试
  • 如业务需要更高频率,联系平台运营调整 rate_limit 配置
QX-User-Token 过期后如何续期?

Token 有效期 7 天,过期后重新调用 POST /users/login 获取新 Token。建议捕获 USER_TOKEN_INVALID 错误后自动跳转登录流程。

Q沙箱订单号和生产订单号如何区分?

沙箱订单号以 SANDBOX_ 为前缀,如 SANDBOX_A1B2C3D4E5F6G7H8。生产模式下为上游平台的真实订单号格式。

Q下单页提示「当前开放应用为沙箱环境」怎么办?

这不是地址或商品配送问题。当前 AppKey 对应的开放应用仍是沙箱(app_type=SANDBOX),中台不会向供应商确认真实配送范围。请在开放应用管理中将该应用「升级生产」后再试。

Q订单发货、售后状态必须一直轮询吗?

不必。配置 消息推送 后,平台会在 ORDER_SHIPPED / AFTERSALE_STATUS 时 POST 到您的 URL。商品变价/库存仍请轮询。推送失败可继续用订单详情、售后详情补偿。

Q如何校验推送签名?收到重复推送怎么办?

算法与入站完全一致(顶层标量排序 + AppSecret + MD5 大写),不要把 sign 本身加入签名原文。用 eventId 做幂等:发货键含运单号,补物流会再推一次。联调可调用 POST /open/api/v1/notify/test(返回 queued 仅表示入队)。