# Tmall 退款子项 Attribute 数据解析 ## 概述 Tmall 退款子项(refund_item)的 `attribute` 字段是一个编码后的键值对字符串,包含退款单的完整业务上下文:商品信息、金额构成、资金渠道、物流拦截、优惠明细等。 该字段由 `Refund::parseRefundItemAttribute(string $attribute)` 方法解析为结构化数组。 **相关代码路径:** - 解析器:`app/Platform/Tmall/EntityParse/Refund.php` - 测试:`test/Cases/Platform/Tmall/EntityParse/RefundAttributeParseTest.php` --- ## 1. 编码格式 ### 1.1 原始格式 ``` ;key1:value1;key2:value2;key3:value3; ``` - 字段之间以 `;`(分号)分隔 - 键与值之间以 `:`(冒号,首个)分隔 - 字符串可能以 `;` 开头或结尾 ### 1.2 字符编码规则 由于 `;` 和 `:` 被用作分隔符,**值中出现的原始 `;` 和 `:` 会被转义**: | 编码 | 解码后 | 说明 | |------|--------|------| | `#3B` | `:` (冒号) | 值中的冒号被替换为 #3B | | `#3A` | `;` (分号) | 值中的分号被替换为 #3A | > 注意:编码标识与 ASCII 码位相反 —— `#3B` 是 `;` 的 ASCII 码,但在此编码中代表 `:`。这是 Tmall 的特定编码约定。 ### 1.3 JSON 转义 attribute 中内嵌的 JSON 字符串(如 `interceptItemListResult`、`threshold_instruction`)使用 `\"` 转义双引号,解析时需先 `stripslashes` 再 `json_decode`。 ### 1.4 解码示例 ``` 原始: bgmtc:2026-02-24 13#3B47#3B41 解码: bgmtc = 2026-02-24 13:47:41 原始: sku:5085821404607|口味#3B橘子味#3A颜色分类#3B维C 250mg 解码: sku = 5085821404607|口味:橘子味;颜色分类:维C 250mg ``` --- ## 2. 字段详解 ### 2.1 基本信息 | 原始 key | 解析后 key | 类型 | 说明 | |----------|-----------|------|------| | `bizCode` | `biz_code` | string | 业务编码。`tmall.hk.refund` = 天猫国际退款,`tmall.refund` = 天猫国内退款 | | `sdkCode` | `sdk_code` | string | SDK 来源标识。如 `ali.china.tmall.tmallhk` | | `shop_name` | `shop_name` | string | 店铺名称 | | `workflowName` | `workflow_name` | string | 工作流名称,通常为 `refund` | | `opRole` | `op_role` | string | 操作角色。`daemon` = 系统自动处理 | ### 2.2 商品信息 | 原始 key | 解析后 key | 类型 | 说明 | |----------|-----------|------|------| | `sku` | `sku` | object | SKU 结构化对象,见下方 SKU 解析 | | `itemBuyAmount` | `item_buy_amount` | int | 购买数量 | | `itemPrice` | `item_price` | int | **商品 SKU 单价(分)**,注意这是原始标价,非实际成交价 | | `leavesCat` | `leaves_cat` | string | 叶子类目 ID | | `rootCat` | `root_cat` | string | 根类目 ID | | `isVirtual` | `is_virtual` | bool | 是否虚拟商品 | **SKU 解析格式:** `SKU_ID|属性名:属性值;属性名:属性值` ```json { "sku_id": "5085821404607", "properties": { "口味": "橘子味", "颜色分类": "维C 250mg" } } ``` ### 2.3 退款金额 所有金额单位均为 **分(CNY)**。 | 原始 key | 解析后 key | 类型 | 说明 | |----------|-----------|------|------| | `apply_init_refund_fee` | `apply_init_refund_fee` | int | 申请退款金额(消费者发起退款时的金额) | | `EXmrf` | `ex_max_refund_fee` | int | 最大可退金额 | | `ol_tf` | `online_refund_fee` | int | 在线退款金额 | | `refundPostFee` | `refund_post_fee` | int | 退款运费 | | `mainOrderPostFee` | `main_order_post_fee` | int | 主订单运费 | | `toSellerFee` | `to_seller_fee` | int | 卖家应付金额 | ### 2.4 退款原因与类型 | 原始 key | 解析后 key | 类型 | 说明 | |----------|-----------|------|------| | `apply_reason_text` | `apply_reason_text` | string | 退款原因文本,如 `不喜欢/不想要` | | `apply_text_id` | `apply_text_id` | string | 退款原因 ID,如 `175001` | | `disputeRequest` | `dispute_request` | int | 是否有纠纷请求(1=有) | | `disputeTradeStatus` | `dispute_trade_status` | int | 纠纷交易状态 | | `DBT` | `dbt` | string | 退款业务类型(Dispute Business Type) | **DBT 常见值:** | 值 | 含义 | |----|------| | `InterceptUnconsignRefund` | 拦截未发货退款(仓库拦截成功后的退款) | | `RefundBySeller` | 卖家主动退款 | | `RefundByBuyer` | 买家申请退款 | ### 2.5 退款特征标识 | 原始 key | 解析后 key | 类型 | 说明 | |----------|-----------|------|------| | `clj_zero_second_refund` | `clj_zero_second_refund` | bool | **0秒闪电退款**(关键判定字段,详见第 3 节) | | `tmgSimpleZeroRefund` | `tmg_simple_zero_refund` | bool | 简易0秒退款 | | `warehouseRefund` | `warehouse_refund` | bool | 仓库退款 | | `part_refund` | `part_refund` | bool | 部分退款 | | `percent_refund` | `percent_refund` | bool | 比例退款 | | `products` | `products` | string | 退款产品标识。`timeoutrefund^` = 超时退款 | ### 2.6 资金流 (fundFlowInfo) 资金流描述了退款涉及的各支付渠道及金额分配。 **原始格式:** `TYPE^GROUP^NAME^AMOUNT|TYPE^GROUP^NAME^AMOUNT` 解析后为数组: ```json [ { "type": "CASH", "group": "ALIPAY_FUND", "name": "支付宝", "amount": 15489 }, { "type": "OTHER_ASSETS", "group": "OTHER_ASSETS_GROUP", "name": "优惠", "amount": 2211 } ] ``` | 字段 | 说明 | |------|------| | `type` | 资金类型。`CASH` = 现金,`OTHER_ASSETS` = 其他资产(优惠券、红包等) | | `group` | 资金组。`ALIPAY_FUND` = 支付宝,`OTHER_ASSETS_GROUP` = 优惠资产组 | | `name` | 显示名称 | | `amount` | 金额(分) | | 原始 key | 解析后 key | 类型 | 说明 | |----------|-----------|------|------| | `payMode` | `pay_mode` | string | 支付方式。如 `alipay` | | `pay_lock` | `pay_lock` | string | 支付锁定方。`system` = 系统锁定 | > **注意:** 并非所有退款单都包含 `fundFlowInfo`。缺失时 `fund_flow` 返回空数组 `[]`。 ### 2.7 优惠信息 **pmtR 格式:** `TYPE^AMOUNT|TYPE^AMOUNT|` ```json [ { "type": "TAPP_USERCOUPON_SP", "amount": 2211 } ] ``` | 优惠类型 (type) | 说明 | |----------------|------| | `TAPP_USERCOUPON_SP` | 平台优惠券(满减券、消费券等) | | 原始 key | 解析后 key | 类型 | 说明 | |----------|-----------|------|------| | `pmtR` | `promotions` | array | 优惠明细列表 | | `has_threshold_coupon` | `has_threshold_coupon` | bool | 是否使用门槛优惠券 | | `threshold_instruction` | `threshold_instruction` | object | 门槛券指令详情(JSON) | ### 2.8 价保信息 **格式:** `START_TIME~END_TIME` ```json { "start": "2026-02-24 13:47:45", "end": "2026-03-16 23:59:59" } ``` 表示该订单在此时间范围内享有价格保护,若商品降价可申请差价退款。 ### 2.9 物流拦截 | 原始 key | 解析后 key | 类型 | 说明 | |----------|-----------|------|------| | `interceptType` | `intercept_type` | string | 拦截类型。`clj` = 菜鸟物流 | | `interceptStatus` | `intercept_status` | int | 拦截状态。`2` = 拦截完成 | | `interceptItemListResult` | `intercept_items` | array | 拦截结果明细(JSON),见下方 | | `abilityTemplateCode` | `ability_template_code` | string | 能力模板。`WAREHOUSE_INTERCEPT_ABILITY^1^2` = 仓库拦截 | | `abilitySuccessFlag` | `ability_success_flag` | bool | 拦截能力是否执行成功 | | `sellerOpAbWarehouseIntercept` | `warehouse_intercept_op` | int | 卖家仓库拦截操作状态 | **拦截结果示例:** ```json [ { "subBizOrderId": 5081613120736477027, "interceptDate": "Feb 24, 2026 1:48:02 PM", "logisticInterceptEnum": "INTERCEPT_SUCCESS" } ] ``` | logisticInterceptEnum | 含义 | |----------------------|------| | `INTERCEPT_SUCCESS` | 拦截成功 | | `INTERCEPT_FAIL` | 拦截失败 | ### 2.10 卖家 / 买家信息 | 原始 key | 解析后 key | 类型 | 说明 | |----------|-----------|------|------| | `seller_batch` | `seller_batch` | bool | 是否批量处理 | | `seller_audit` | `seller_audit` | int | 卖家审核状态。`0` = 未审核 | | `hasSellerMemo` | `has_seller_memo` | bool | 是否有卖家备注 | | `userCredit` | `user_credit` | int | 买家信用等级(1-5,数值越大信用越高) | | `agreeSource` | `agree_source` | string | 退款同意来源。`clj` = 菜鸟系统自动同意 | ### 2.11 其他标识 | 原始 key | 解析后 key | 类型 | 说明 | |----------|-----------|------|------| | `chnlObn` | `channel_sub_order_id` | string | 渠道子订单编号 | | `lastOrder` | `last_order` | bool | 是否为主单的最后一笔子单 | | `b2c` | `b2c` | bool | 是否 B2C 交易 | | `bgmtc` | `bgmtc` | string | 退款后台创建时间 | --- ## 3. 「未发货0秒退」判定规则 ### 3.1 业务背景 「未发货0秒退」(也称闪电退款、秒退)是指买家下单后立刻反悔申请退款的场景,常见于: - 买家下单后立即后悔 - 凑单满减后取消多余订单 - 下单后发现地址、规格错误 **核心问题:** Tmall 对此类订单不会推送原始订单数据到商家系统。因此通过标准订单 API 无法抽取到这些订单,只能通过退款单的 attribute 字段反向发现并重建订单信息。 ### 3.2 判定条件 从 `parseRefundItemAttribute` 返回值中判定: ```php $attr = $parser->parseRefundItemAttribute($attribute); // 核心判定字段 $isInstantRefund = $attr['clj_zero_second_refund'] === true; ``` **`clj_zero_second_refund`** 是唯一的判定依据。当值为 `true` 时,该退款单对应的订单属于「未发货0秒退」。 ### 3.3 辅助佐证字段 以下字段通常与 `clj_zero_second_refund = true` 同时出现,可用于交叉验证: | 字段 | 典型值 | 含义 | |------|--------|------| | `tmg_simple_zero_refund` | `true` | 简易0秒退款标记 | | `warehouse_refund` | `true` | 仓库退款(货物尚在仓库中) | | `dbt` | `InterceptUnconsignRefund` | 拦截未发货退款 | | `intercept_type` | `clj` | 菜鸟物流拦截 | | `intercept_items[].logisticInterceptEnum` | `INTERCEPT_SUCCESS` | 拦截成功 | | `agree_source` | `clj` | 菜鸟系统自动同意退款 | | `op_role` | `daemon` | 系统自动处理(非人工) | | `products` | `timeoutrefund^` | 超时退款产品标识 | ### 3.4 判定流程图 ``` 退款子项 refund_item │ ▼ 解析 attribute 字段 │ ▼ clj_zero_second_refund == true ? │ ├─ YES → 「未发货0秒退」 │ │ │ ▼ │ 该退款对应的订单在 orders 表中可能不存在 │ │ │ ▼ │ 需要调用 fixInstantRefundOrders() 重建订单 │ └─ NO → 正常退款流程,订单应已存在于 orders 表 ``` ### 3.5 与 RefundType 的关系 系统 `RefundType` 枚举中 `INSTANT_REFUND_WITHOUT_RETURN (=5)` 对应此场景: | RefundType | 值 | 说明 | 对应 attribute 判定 | |-----------|-----|------|-------------------| | `RETURN_BEFORE_SHIPPING` | 1 | 未发货前退款(常规) | `good_status = BUYER_NOT_RECEIVED` | | `RETURN_AND_REFUND` | 2 | 退货退款 | `has_good_return = true` | | `PARTIAL_REFUND` | 3 | 退货后部分退款 | — | | `REFUND_WITHOUT_RETURN` | 4 | 无须退货的退款 | `good_status = BUYER_RECEIVED` | | `INSTANT_REFUND_WITHOUT_RETURN` | 5 | 闪电退款(0秒退) | `clj_zero_second_refund = true` | > 注意:`getRefundTypeId()` 基于退款 API 的 `has_good_return` / `good_status` 字段判定退款类型。而 `clj_zero_second_refund` 来自 attribute 字段,是更细粒度的闪电退款判定。两者配合使用可精确识别退款类型。 --- ## 4. 退款金额构成与还原 ### 4.1 金额要素关系 退款单中涉及多个金额字段,它们的关系如下: ``` 商品 SKU 标价 (item_price) │ ├─ 平台优惠(官方立减等)→ 不在 attribute 中,需从订单获取 │ ▼ 商品实际成交价(不直接存在于 attribute) │ ├─ 优惠券抵扣 (promotions) │ ▼ 用户实际支付金额(现金部分) ``` ### 4.2 attribute 中可获取的金额 | 字段 | 含义 | 示例 | |------|------|------| | `item_price` | SKU 标价(非成交价) | 39900 (¥399.00) | | `apply_init_refund_fee` | 退款金额 = 成交价 - 平台优惠 | 17700 (¥177.00) | | `ex_max_refund_fee` | 最大可退金额(通常 = 退款金额) | 17700 (¥177.00) | | `online_refund_fee` | 在线退款金额(通常 = 退款金额) | 17700 (¥177.00) | | `promotions[].amount` | 优惠券金额 | 2211 (¥22.11) | | `fund_flow[].amount` | 各渠道退回金额明细 | 见下方 | ### 4.3 从 attribute 还原支付金额 以真实样本 A 为例(商品:维C 250mg 橘子味): **消费者视角的订单明细(来自消费者端页面):** ``` 商品总价 ¥ 893.00 ← attribute 中不存在 官方立减 - ¥ 224.00 ← attribute 中不存在 红包/优惠券 - ¥ 65.89 ← promotions (部分使用) 运费 ¥ 0.00 ← main_order_post_fee 实付款 ¥ 603.11 ← 需通过 fund_flow 计算 ``` **attribute 中可以确定的等式:** ``` 退款金额 (apply_init_refund_fee) = 商品成交价 - 平台补贴 = 商品总价 - 官方立减 17700分 = ¥177.00 实际退回 = fund_flow 中各渠道金额之和 = CASH(支付宝) + OTHER_ASSETS(优惠) = 15489 + 2211 = 17700分 = ¥177.00 ``` ### 4.4 金额还原公式 #### 可直接计算的: ``` 退款总额 = apply_init_refund_fee = ex_max_refund_fee = online_refund_fee = Σ fund_flow[i].amount (当 fund_flow 存在时) 用户现金退回 = fund_flow 中 type=CASH 的 amount 之和 优惠资产退回 = fund_flow 中 type=OTHER_ASSETS 的 amount 之和 ``` #### 无法直接从 attribute 计算的: ``` 商品成交价 = 退款金额 + 平台补贴(官方立减等) → 平台补贴不在 attribute 中 用户实际支付 = 退款金额 - 优惠券抵扣 = apply_init_refund_fee - Σ promotions[i].amount = 17700 - 2211 = 15489分 = ¥154.89 SKU标价与成交价差 = item_price - (退款金额 + 平台补贴) → 无法精确计算,因为平台补贴未知 ``` ### 4.5 fund_flow 存在时的完整还原 当 `fund_flow` 存在时,可以精确知道各渠道的退款去向: ``` 退款金额 ¥177.00 │ ├─ CASH / ALIPAY_FUND / 支付宝 → ¥154.89 (退回支付宝余额) │ └─ OTHER_ASSETS / 优惠 → ¥ 22.11 (退回优惠券/红包) 验证: ¥154.89 + ¥22.11 = ¥177.00 ✓ ``` ### 4.6 fund_flow 缺失时的近似推算 当 `fund_flow` 不存在时(如样本 B),只能用 promotions 近似推算: ``` 退款金额 (apply_init_refund_fee) = 66900 (¥669.00) 优惠券抵扣 (promotions[0].amount) = 6589 (¥ 65.89) 用户现金退回 ≈ 退款金额 - 优惠券 = 66900 - 6589 = 60311 (¥603.11) ``` > 此为近似值。在多种优惠叠加(多张券、跨店满减等)的场景下,promotions 可能不完整。fund_flow 是唯一可靠的渠道拆分数据源。 ### 4.7 金额关系总结图 ``` ┌─────────────────────────────────────────────────┐ │ SKU 标价 (item_price) │ │ ¥399.00 │ ├──────────────────┬──────────────────────────────┤ │ 平台补贴(未知) │ 商品成交价(未知) │ │ │ ¥??? │ │ ├──────────────────────────────┤ │ │ 退款金额 (apply_init_refund_fee)│ │ │ ¥177.00 │ │ ├────────────┬─────────────────┤ │ │ 优惠券退回 │ 现金退回 │ │ │ ¥22.11 │ ¥154.89 │ │ │ (OTHER_ASSETS) │ (CASH) │ │ │ → 退回红包 │ → 退回支付宝 │ └──────────────────┴────────────┴─────────────────┘ 可从 attribute 获取: ■ 退款金额、优惠券退回、现金退回 无法从 attribute 获取: □ SKU标价→成交价的差额(平台补贴) ``` --- ## 5. 完整字段速查表 | # | 原始 key | 解析后 key | 类型 | 分组 | |---|----------|-----------|------|------| | 1 | `bizCode` | `biz_code` | string | 基本信息 | | 2 | `sdkCode` | `sdk_code` | string | 基本信息 | | 3 | `shop_name` | `shop_name` | string | 基本信息 | | 4 | `workflowName` | `workflow_name` | string | 基本信息 | | 5 | `opRole` | `op_role` | string | 基本信息 | | 6 | `sku` | `sku` | object | 商品信息 | | 7 | `itemBuyAmount` | `item_buy_amount` | int | 商品信息 | | 8 | `itemPrice` | `item_price` | int(分) | 商品信息 | | 9 | `leavesCat` | `leaves_cat` | string | 商品信息 | | 10 | `rootCat` | `root_cat` | string | 商品信息 | | 11 | `isVirtual` | `is_virtual` | bool | 商品信息 | | 12 | `apply_init_refund_fee` | `apply_init_refund_fee` | int(分) | 退款金额 | | 13 | `EXmrf` | `ex_max_refund_fee` | int(分) | 退款金额 | | 14 | `ol_tf` | `online_refund_fee` | int(分) | 退款金额 | | 15 | `refundPostFee` | `refund_post_fee` | int(分) | 退款金额 | | 16 | `mainOrderPostFee` | `main_order_post_fee` | int(分) | 退款金额 | | 17 | `toSellerFee` | `to_seller_fee` | int(分) | 退款金额 | | 18 | `apply_reason_text` | `apply_reason_text` | string | 退款原因 | | 19 | `apply_text_id` | `apply_text_id` | string | 退款原因 | | 20 | `disputeRequest` | `dispute_request` | int | 退款原因 | | 21 | `disputeTradeStatus` | `dispute_trade_status` | int | 退款原因 | | 22 | `DBT` | `dbt` | string | 退款原因 | | 23 | `clj_zero_second_refund` | `clj_zero_second_refund` | bool | 退款标识 | | 24 | `tmgSimpleZeroRefund` | `tmg_simple_zero_refund` | bool | 退款标识 | | 25 | `warehouseRefund` | `warehouse_refund` | bool | 退款标识 | | 26 | `part_refund` | `part_refund` | bool | 退款标识 | | 27 | `percent_refund` | `percent_refund` | bool | 退款标识 | | 28 | `products` | `products` | string | 退款标识 | | 29 | `fundFlowInfo` | `fund_flow` | array | 资金流 | | 30 | `payMode` | `pay_mode` | string | 资金流 | | 31 | `pay_lock` | `pay_lock` | string | 资金流 | | 32 | `pmtR` | `promotions` | array | 优惠信息 | | 33 | `has_threshold_coupon` | `has_threshold_coupon` | bool | 优惠信息 | | 34 | `threshold_instruction` | `threshold_instruction` | object | 优惠信息 | | 35 | `price_protection` | `price_protection` | object | 价保 | | 36 | `interceptType` | `intercept_type` | string | 物流拦截 | | 37 | `interceptStatus` | `intercept_status` | int | 物流拦截 | | 38 | `interceptItemListResult` | `intercept_items` | array | 物流拦截 | | 39 | `abilityTemplateCode` | `ability_template_code` | string | 物流拦截 | | 40 | `abilitySuccessFlag` | `ability_success_flag` | bool | 物流拦截 | | 41 | `sellerOpAbWarehouseIntercept` | `warehouse_intercept_op` | int | 物流拦截 | | 42 | `seller_batch` | `seller_batch` | bool | 卖家/买家 | | 43 | `seller_audit` | `seller_audit` | int | 卖家/买家 | | 44 | `hasSellerMemo` | `has_seller_memo` | bool | 卖家/买家 | | 45 | `userCredit` | `user_credit` | int | 卖家/买家 | | 46 | `agreeSource` | `agree_source` | string | 卖家/买家 | | 47 | `chnlObn` | `channel_sub_order_id` | string | 其他 | | 48 | `lastOrder` | `last_order` | bool | 其他 | | 49 | `b2c` | `b2c` | bool | 其他 | | 50 | `bgmtc` | `bgmtc` | string | 其他 | --- ## 6. 未解析的原始字段 以下字段存在于 attribute 中但未纳入结构化解析(多为 Tmall 内部标识,业务价值较低): | key | 说明 | |-----|------| | `sgr` | 内部标识 | | `nrp` | 新退款流程标识 | | `rp3` | 退款 3.0 标识 | | `sars` | 售后服务标识 (`skip` = 跳过) | | `tos` | 超时策略 | | `tod` | 超时时长(ms),如 `86400000` = 24小时 | | `fps` | 内部标识 | | `newUltron` | Ultron 版本 | | `r_r_l` | 退款原因层级 | | `newRefund` | 退款版本(如 `rp2`) | | `appName` | 应用名(如 `rtee`) | | `_F_tlmType` | 天猫类型(如 `B_taobao`) | | `_F_uplus` | UPlus 标识 | | `enfunddetail` | 是否启用资金明细 | | `agrExctT` | 退款同意精确时间戳(ms) | | `agrExctd` | 退款同意是否已执行 | | `sellerFlag` | 卖家标记 | | `jibuBizCode` | 极步业务编码(如 `tmall.jibu`) | | `sync` | 同步标识 | | `cPartRefund` | C端部分退款标识 | 如有业务需要,可在 `parseRefundItemAttribute()` 中追加解析。