# 权益平台 SDK v2

## 定位

面向合作商户的权益商城 SDK。终端用户看到的是商户自己的品牌与权益商城体验，平台方能力在后端完成产品、锁价、订单和履约支撑。

## 当前能力

- 自动城市识别
- 产品准入分层：可购买 / 可查看 / 即将开放 / 隐藏
- Flow Router：充值、门店、券码、电影、景区、加油、充电、票务等流程分流
- 后端 quote 锁价
- 创建待支付订单
- 商户会员身份透传：merchantMemberId / memberOpenId / memberMobile
- 我的订单按商户会员维度查询，不依赖浏览器本地缓存
- 订单详情与订单列表支持售后/退款状态展示与申请入口
- 跳转商户收银台：订单创建返回短期 token 化 `cashierUrl`，浏览器 URL 不暴露订单号、金额和签名
- 支持沙箱模式与正式模式开关

## 接入示例

```html
<div id="merchant-benefit-sdk"></div>
<link rel="stylesheet" href="/sdk/commercial-v2/commercial-v2.css">
<script src="/sdk/commercial-v2/commercial-v2-sdk.js"></script>
<script>
ChinaDaoCommercialV2.init({
  merchantId: '1',
  container: '#merchant-benefit-sdk',
  merchantName: '商户权益商城',
  apiBase: '/prod-api/openapi/v1',
  liveMode: true,
  merchantMemberId: '商户侧会员ID',
  memberOpenId: '微信openId/unionId，可选',
  memberMobile: '会员手机号，可选',
  memberSource: 'h5'
});
</script>
```


## H5订单页获取接口

H5类权益由上游页面完成选品/下单后，商户侧仍需要能获取订单列表页和订单详情页，方便把“我的订单”“订单详情”嵌入自己的会员中心。SDK 已暴露两个 helper，底层对应开放接口：

```js
// 获取H5订单列表页
const orderPageUrl = await ChinaDaoCommercialV2.getH5OrderPageUrl({
  tenantCode: '租户编码',
  userId: '商户会员ID或手机号',
  interfaceTypeCode: 'H5对应的interfaceTypeCode',
  phone: '18988888888'
});

// 获取H5订单详情页
const orderInfoUrl = await ChinaDaoCommercialV2.getH5OrderInfoUrl({
  tenantCode: '租户编码',
  userId: '商户会员ID或手机号',
  interfaceTypeCode: 'H5对应的interfaceTypeCode',
  phone: '18988888888',
  channelOrderId: 'H5订单号'
});
```

对应服务端开放接口为 `POST /prod-api/biz/v1/getOrderPageUrl` 与 `POST /prod-api/biz/v1/getOrderInfoUrl`。`timestamp`、`nonceStr`、`sign` 由服务端按租户密钥生成；前端 SDK 不暴露密钥、不拼签名。

## 正式商用前必须完成

1. 配置商户品牌、Logo、主题色。
2. 配置商户域名白名单。
3. 配置商户收银台地址 cashierUrl；实际订单返回的 cashierUrl 只包含短期 `token=cst_xxx`，商户前端直接跳转，不解析、不拼接订单号/金额/签名参数。
4. 配置支付成功回调 notifyUrl；正式支付成功必须由商户服务端调用 `/sdk/payment/notify` 或 `/sdk/order-pay-notify` 签名确认，浏览器前端不能直接确认支付成功。
5. 关闭 sandboxMode。
6. 选定试点品类并完成真实履约验收。

## 收银台 URL 安全机制

`/sdk/order-create` 返回的 `cashierUrl` 已采用短期 token 模式，示例：

```text
/sdk/commercial-v2/merchant-cashier.html?token=cst_xxx
```

商户前端只需要跳转该地址；不要解析、保存或自行拼接 `merchantId/orderNo/quoteId/amount/timestamp/nonce/sign`。token 仅用于加载收银台展示信息，不代表支付成功。正式支付成功必须由商户服务端签名通知平台。

## 产品开放清单与准入规则

SDK 用户端只开放经过正式接口准入的权益。产品状态以 `product-admission.json` 为准，展示口径如下：

| 状态 | 用户端表现 | 开放条件 |
|---|---|---|
| 可购买 | 进入正式购买/预约/领取流程 | 已验证后端 query/quote/order-create 链路，金额以后端锁价快照为准 |
| 可查资源，暂不开放支付 | 可查看接入说明或资源状态，不进入支付 | 已有正式接口字段或资源查询，但上游授权、价格日历、订单履约或回调未完成 |
| 即将开放 | 仅展示能力规划，不进入购买弹窗 | 产品规划中，未进入正式联调 |
| 暂不可接入 | 展示不可接入原因，不提供临时跳转 | 缺少正式开放文档、签名/回调说明或属于非用户购买型能力 |

### 当前可购买产品

点餐外卖+到店、微信立减金、话费充值、移动充值、话费小额充值、影音会员、虚拟会员(无票版)、洗车、干洗、家政、洁牙、跑腿、途虎养车、快递、问诊、京东家政、蛋糕外卖、蛋糕、鲜花、大润发、叮咚买菜、京东、永辉超市、盒马、山姆会员商店、自营商超、自营商城2期、1688采购批发、天猫API、怡亚通、短剧、药品、积分焕彩、支付宝红包、景区门票、电影、酒店、银联支付、国际机票、团油加油。

### 当前可查/联调但暂不开放支付的产品

点餐、打车、打车V2、饿了么H5、图书、点餐(无票)、电影(无票)、电影V3、演出、丽呈酒店、锦江酒店、国际酒店、飞机票、InternationalAirTicket、火车票、TrainTicket、火车票标准版、汽车票、贵宾厅、柴油加油、永辉彩食鲜、ChargingStationOfficial、油站(中石油中石化)、油站V2、加油站、充电站、充电桩V2、充电桩API版、团油充电。

这些产品不得进入真实支付，原因包括上游未授权、沙箱可联调、选座/出票/履约链路待验收、价格日历或订单回调未完成。

### 即将开放产品

水电燃气、LifePayment、ShopMall、DiscountMall、MedicalCare。

### 暂不可接入产品

海底捞、美团外卖H5、美团团购H5、沃尔玛展码付、三方支付、实名、锅圈展码付、饿了么(开票版)、山姆展码付、中石化中石油展码付、夏商展码付、支付宝企业码、核销、大润发到店、小象超市H5。

这些产品缺少正式开放文档或不属于用户购买型权益，用户端不使用非正式地址、临时参数或前端伪跳转。



## 会员身份与我的订单

SDK 初始化时建议传入商户自有会员身份：

| 字段 | 说明 | 优先级 |
|---|---|---|
| `merchantMemberId` | 商户侧会员ID/用户ID | 最高 |
| `memberOpenId` | 微信 openId/unionId | 第二 |
| `memberMobile` | 会员手机号 | 兜底 |
| `memberName` | 会员昵称/姓名 | 可选 |
| `memberSource` | 来源，如 h5/wechat/miniapp/app | 可选 |

订单归属优先级：`merchantId + merchantMemberId` → `merchantId + memberOpenId` → `merchantId + memberMobile`。浏览器 `localStorage` 仅作为历史兜底，不作为正式订单归属依据。

我的订单接口示例：

```text
GET /prod-api/openapi/v1/sdk/order-list?merchantId=11&merchantMemberId=U10001
GET /prod-api/openapi/v1/sdk/order-list?merchantId=11&memberOpenId=oXxxx
GET /prod-api/openapi/v1/sdk/order-list?merchantId=11&memberMobile=13800138000
```

## 售后/退款

SDK v2 订单详情展示售后状态；符合条件的订单展示“申请退款/售后”按钮，并在提交前进行二次确认。退款申请只代表创建售后请求，实际退款需平台/商户按支付渠道、履约状态和风控规则审核处理。

## 异步通知与失败退款

平台会向商户 `notifyUrl` 推送 `ORDER_SUCCESS`、`ORDER_FAIL`、`ORDER_REFUND` 等事件。商户按 `orderNo + event` 幂等处理，重复通知不得重复发货或重复退款。

- `ORDER_FAIL + orderStatus=CLOSED + upstreamStatus=FAILED` 表示上游履约失败；若用户已在商户侧支付，商户需按原支付渠道退款给用户。
- `ORDER_REFUND + refundStatus=SUCCESS + refundAmount>0` 才表示退款完成。
- 单独 `orderStatus=CLOSED` 不等于退款成功，不得只凭 closed 自动退款。
- 商户返回 `SUCCESS` 或 HTTP 2xx 只代表接收通知成功，不代表用户侧退款完成。
