支付宝单品优惠券交易中如何传递goods_detail字段?
结论
goods_detail 不是创建单品优惠券后由支付宝自动生成或返回的数据。它记录本次交易实际购买的商品明细,应由商户的收银系统、订单系统或商品系统根据当前订单生成,并随支付请求传递。
- 直连商户:调用支付宝交易接口时,将
goods_detail放入biz_content。 - 间连商户:将商品明细提交给银行或收单机构,由其支付接口映射并透传给支付宝。如果代运营方不参与交易请求,通常无法单独补传该字段。
- 商品标识、名称、价格和数量等信息应与真实订单一致,商品标识规则也要与配置单品优惠券时使用的规则相同,否则可能无法匹配优惠资格。
goods_detail 的数据从哪里获取
数据通常来自商户自己的业务系统,例如:
- POS 收银系统
- 商城订单系统
- 商品中心或 ERP
- 外卖、点餐等交易系统
如果一笔订单包含多个商品,需要为每个商品分别生成一条明细。常见信息如下:
| 字段 | 含义 | 常见来源 |
|---|---|---|
goods_id | 商户侧商品编号 | 商品中心、SKU 或 POS 商品编码 |
alipay_goods_id | 支付宝侧商品编号 | 仅在已经建立支付宝商品标识时填写 |
goods_name | 商品名称 | 商品中心或订单快照 |
quantity | 购买数量 | 订单明细 |
price | 商品单价 | 订单成交价 |
goods_category | 商品类目 | 商户商品分类 |
categories_tree | 商品类目树 | 按接入接口及活动要求填写 |
body | 商品描述 | 可选的订单商品说明 |
show_url | 商品展示地址 | 可选的商品详情页地址 |
各交易接口、产品能力和接入方案支持的子字段可能不同,具体应以实际调用接口的参数文档为准。
支付宝无法根据交易总金额判断订单中包含哪些商品,因此不能自动生成可靠的 goods_detail。如果收银系统目前只上传订单号和总金额,就需要改造从订单系统到支付请求的数据链路。
直连商户如何传递
直连支付宝的商户需要在创建支付交易时,将商品明细作为 goods_detail 数组放入 biz_content。以调用 alipay.trade.pay 为例:
{
"out_trade_no": "ORDER_202609090001",
"scene": "bar_code",
"auth_code": "用户付款码",
"subject": "门店消费",
"total_amount": "25.80",
"goods_detail": [
{
"goods_id": "SKU_10001",
"goods_name": "拿铁咖啡",
"quantity": 1,
"price": "18.00",
"goods_category": "DRINK"
},
{
"goods_id": "SKU_20003",
"goods_name": "原味可颂",
"quantity": 1,
"price": "7.80",
"goods_category": "FOOD"
}
]
}
使用 Java SDK 时,核心逻辑如下:
AlipayTradePayRequest request = new AlipayTradePayRequest();
AlipayTradePayModel model = new AlipayTradePayModel();
model.setOutTradeNo("ORDER_202609090001");
model.setScene("bar_code");
model.setAuthCode("用户付款码");
model.setSubject("门店消费");
model.setTotalAmount("25.80");
List<GoodsDetail> goodsDetailList = new ArrayList<>();
GoodsDetail coffee = new GoodsDetail();
coffee.setGoodsId("SKU_10001");
coffee.setGoodsName("拿铁咖啡");
coffee.setQuantity(1L);
coffee.setPrice("18.00");
coffee.setGoodsCategory("DRINK");
goodsDetailList.add(coffee);
GoodsDetail croissant = new GoodsDetail();
croissant.setGoodsId("SKU_20003");
croissant.setGoodsName("原味可颂");
croissant.setQuantity(1L);
croissant.setPrice("7.80");
croissant.setGoodsCategory("FOOD");
goodsDetailList.add(croissant);
model.setGoodsDetail(goodsDetailList);
request.setBizModel(model);
AlipayTradePayResponse response = alipayClient.execute(request);
这段示例只用于说明 goods_detail 的组织方式。SDK 类名、字段类型和可用的交易接口,应以项目实际使用的支付宝 SDK 版本为准。
如果服务商应用代商户调用支付宝接口,还要按照对应的开放平台授权模式传入商户授权信息,例如在适用场景中传入 app_auth_token。授权关系用于确定以谁的身份调用接口,不能代替商品明细。
间连商户如何传递
间连商户通常采用以下交易链路:
商户收银系统 → 银行或收单机构 → 支付宝
在这种情况下,支付宝接收的是银行或收单机构发起的请求。处理流程如下:
- 商户收银系统根据订单生成商品明细。
- 商户调用银行或收单机构接口时提交这些明细。
- 银行或收单机构将自己的接口字段映射为支付宝的
goods_detail。 - 收单机构在发送给支付宝的交易请求中透传这些数据。
- 使用测试订单核对优惠是否按预期匹配。
商户提交给银行的请求可能采用下面的形式。字段名称仅作示意,不能作为任何一家银行的正式接口定义:
{
"merchant_order_no": "ORDER_202609090001",
"amount": "25.80",
"goods_list": [
{
"goods_id": "SKU_10001",
"goods_name": "拿铁咖啡",
"quantity": 1,
"unit_price": "18.00"
},
{
"goods_id": "SKU_20003",
"goods_name": "原味可颂",
"quantity": 1,
"unit_price": "7.80"
}
]
}
接入前需要向银行或收单机构确认:
- 当前使用的支付宝交易接口是否支持商品明细;
- 商品明细对应的字段名称和数据格式;
- 相关字段是否会映射到支付宝的
goods_detail; goods_id、单价和数量是否会原样透传;- 是否需要申请或开通单品优惠相关能力;
- 是否支持多商品、退款和部分退款场景。
如果银行通道不支持传递商品明细,单靠代运营方创建优惠券无法完成交易匹配。支付宝收到交易请求后,代运营方也不能再为该笔交易追加 goods_detail。此时需要由收单机构改造接口,或调整支付接入链路。
商品标识必须统一
单品优惠能否匹配,不仅取决于交易请求是否包含 goods_detail,还取决于商品标识是否一致。
例如,创建活动时配置的商品编号是:
SKU_10001
交易时传递的却是:
10001-L
即使两个编号实际指向同一商品,系统也可能把它们视为不同商品。建议统一以下规则:
- 明确使用 SKU、SPU、条码还是 POS 商品编码;
- 同一商品在活动配置和交易请求中使用相同的
goods_id; - 不要临时拼接门店号、规格或促销标记;
- 商品改码后及时更新活动配置;
- 保存活动商品标识与商户商品标识之间的映射关系。
如果活动要求使用 alipay_goods_id 或指定的商品编码,应遵循该活动的接入规则,不能自行改用 goods_id。
金额与数量注意事项
商品明细必须与真实成交内容一致:
商品明细金额 = Σ(price × quantity)
实际交易还可能涉及整单优惠、商品折扣、运费和抹零,因此明细合计与 total_amount 之间应满足所调用交易接口及活动的校验规则。不要为了触发优惠而伪造商品、价格或数量。
还需注意:
- 金额通常使用十进制字符串表示,避免直接使用浮点数计算;
- 商品单价应采用活动要求的原价或成交价口径;
- 数量必须与订单明细一致;
- 一笔订单中有多个符合条件的商品时,需要分别传递;
- 发生退款,尤其是部分退款时,应保留原始商品明细,以便退款商品正确对应;
- 日志可以记录订单号和商品编码,但应避免记录付款码等敏感信息。
直连场景中,商户或服务商需要在支付宝交易请求中填写 goods_detail。间连场景中,商户需要先把商品数据提交给银行通道,再由银行或收单机构透传。能否自动完成这一过程,取决于订单系统是否保存商品明细,以及支付通道是否支持字段映射,并不是支付宝在创建优惠券后自动提供这些数据。
备注:内容仅供参考。
