Files
as-daycard-openapi/第三方合作伙伴接口流程.md
zhaogd 221decc070 更新第三方合作伙伴接口流程文档
- 阶段二补充 /list 多栏目查询、/list-all 全量聚合、/episodes 分集查询接口说明
- 阶段七补充电视剧分集 contentcode 与 programCode 播放地址的衔接说明
2026-07-29 12:17:27 +08:00

8.4 KiB
Raw Permalink Blame History

第三方合作伙伴接口完整流程

阶段一:管理后台准备(平台方操作)

1. 创建合作伙伴
   POST /manage/partner/save  (JWT鉴权)
   -> 自动生成 partnerId / appKey / appSecret,交给第三方

2. 采购天卡到合作伙伴池
   POST /manage/partner-card/purchase?partnerId=xxx&quantity=N  (JWT鉴权)
   -> 平台天卡池 UNUSED -> RESERVED (FOR UPDATE SKIP LOCKED)
   -> 写入 partner 天卡池 status=AVAILABLE

3. 配置内容包授权范围(可选)
   POST /manage/content-package/...  (JWT鉴权)
   -> 三级授权:CONTENT(单部) > CATALOG(栏目) > FULL(全量)

阶段二:第三方EPG界面展示与支付(第三方完成)

第三方自行开发EPG界面和支付流程

可选:第三方调媒资接口展示内容
   GET /openapi/partner/media/catalog-tree   (appKey签名鉴权)
   GET /openapi/partner/media/list?catalogid=xxx         # 单栏目查询
   GET /openapi/partner/media/list?catalogid=472,492,494 # 多栏目聚合查询(逗号分隔)
   GET /openapi/partner/media/list-all?parentCatalogid=471  # 全量聚合(按一级栏目,不分页,适合按天同步)
   GET /openapi/partner/media/episodes?contentcode=xxx   # 电视剧分集列表(分集contentcode即programCode)
   GET /openapi/partner/media/search?keyword=xxx
   -> 按内容包授权范围过滤返回

支付完成后进入阶段三

阶段三:第三方调消耗接口发卡(核心)

POST /openapi/partner/card/consume  (appKey签名鉴权)
Headers: appKey / timestamp / nonce / sign
Body: { mac, wifiMac, deviceUid, deviceSn, channelOrgId }
  (mac/wifiMac/deviceUid/deviceSn 至少传一个)

处理流程:
  ① PartnerSignFilter 校验签名: MD5(appKey+timestamp+nonce+appSecret).toUpperCase()
     - timestamp 5分钟有效期 + nonce 5分钟防重放(Redis)

  ② Redis 防重锁: daycard:partner:consume:{设备标识}, TTL 30s

  ③ 查找/创建设备 (优先级: mac > wifiMac > deviceUid > deviceSn)

  ④ 幂等检查: 设备已有有效天卡(status=USED且未过期) -> 直接返回, 不重复消耗

  ⑤ FOR UPDATE SKIP LOCKED 取partner池AVAILABLE天卡 (防并发超卖)

  ⑥ 绑定天卡到设备:
     - day_card_pool: RESERVED -> USED, device_id, activate_status=PENDING, expire_at=24h兜底
     - partner_card_pool: AVAILABLE -> CONSUMED

  ⑦ @Transactional 事务保护 ②~⑥ 全部数据库写操作

  ⑧ 异步激活天卡 (线程池 thirdPartyActivateExecutor)

返回: { deviceId, expireAt, status:"ACTIVATING" }
      thirdCardNo 不暴露给第三方, App 通过 checkBenefits 自行获取

阶段四:异步激活天卡(调橘视厅,自动完成)

activateCardAsync(poolId, deviceId):

  ① 激活锁: api:thirdparty:activate:lock:{poolId}, TTL 3600s
     防止同一张卡重复激活

  ② 获取Token (优先缓存):
     - 缓存key: api:thirdparty:activate:{thirdCardNo}, TTL 5分钟
     - 命中 -> 直接用 token + memberCode
     - 未命中 -> 调橘视厅免密登录:
       POST {baseUrl}/ums_api/api/v1/third/login (RSA加密签名)
       返回 token + memberCode -> 写入缓存

  ③ 卡密激活:
     POST {baseUrl}/ccms_api/hswj/api/card/activate?sign={MD5签名}
     - E0000003 + "已激活,重复使用激活码" 视为幂等成功

  ④ 确定过期时间:
     优先 activateCard返回的expireTime -> 其次queryVipExpireTime -> 兜底24h

  ⑤ 更新 day_card_pool: activate_status=SUCCESS, expire_at=真实过期时间

  ⑥ finally 释放激活锁

  异常: activate_status=FAILED + 错误信息

阶段五:EPG传参启动App

EPG通过app传参启动App, App使用同一设备标识初始化:

POST /api/jst/init  (无需sign)
Body: { mac, wifiMac, deviceSn, deviceUid, ...(设备信息) }
  不传 activationCode -> 代销模式 doInitWithoutActivationCode

  ① 按优先级查找已注册设备: mac > wifiMac > deviceSn > deviceUid
     (设备由阶段三 consume 接口创建)
  ② 找不到 -> "设备未注册,请先通过合作伙伴天卡消耗接口激活设备"
  ③ 找到 -> 更新心跳, 生成 sign
     sign = Base64URL(deviceId:timestamp:orgCode:HMAC-SHA256), TTL 24h

返回: { device: {deviceId, roomNo}, sign }

阶段六:App查询权益拿播放凭证

POST /api/jst/checkBenefits  (需sign)

  ① ApiDeviceSignFilter 校验sign, 解析deviceId
  ② 查询 day_card_pool: device_id + status=USED + expire_at > NOW()

激活完成返回:
  { status:"ACTIVE", thirdCardNo:"xxx", cardNo:"xxx", expireAt:"..." }
  -> thirdCardNo 即播放凭证

激活未完成返回:
  { status:"UNBOUND" }
  -> App 轮询直到 status 变为 ACTIVE

辅助接口:
  POST /api/jst/heartBeat    -> 续期sign
  POST /api/jst/refreshSign  -> 换新sign

阶段七:App播放

App使用 thirdCardNo 作为播放凭证
-> 请求橘视厅SDK初始化
-> 换取播放地址(programCode = 内容contentcode,电视剧取分集contentcode)
-> 播放内容

说明:
  - 电影:contentcode 直接作为 programCode
  - 电视剧:先调 /openapi/partner/media/episodes?contentcode=父级code 取分集列表
           分集的 contentcode 作为 programCode 传入SDK

完整时序图

管理后台          第三方EPG           本系统              橘视厅           App
   |                 |                  |                  |               |
   |--采购天卡------->|                  |                  |               |
   |                 |                  |                  |               |
   |            [用户浏览付费]           |                  |               |
   |                 |--consume-------->|                  |               |
   |                 |                  |--取卡+绑卡       |               |
   |                 |                  |--异步激活-------->|               |
   |                 |<--deviceId-------|                  |               |
   |                 |     expireAt     |                  |               |
   |                 |     ACTIVATING   |                  |               |
   |                 |                  |<--激活成功-------|               |
   |                 |                  |                  |               |
   |                 |--传参启动App---->|                  |               |
   |                 |   (设备标识)     |                  |               |
   |                 |                  |                  |<--init--------|
   |                 |                  |                  |   (免激活码)   |
   |                 |                  |                  |--sign-------->|
   |                 |                  |                  |               |
   |                 |                  |                  |<--checkBenefits|
   |                 |                  |                  |   (携带sign)   |
   |                 |                  |                  |--thirdCardNo->|
   |                 |                  |                  |               |
   |                 |                  |                  |          [SDK初始化]
   |                 |                  |                  |          [播放]  |

关键设计说明

安全设计

  • thirdCardNo 不暴露给第三方:播放凭证仅通过 App 的 init -> checkBenefits 链路获取,第三方无法绕过 App 直接播放
  • 签名鉴权:第三方接口通过 MD5(appKey+timestamp+nonce+appSecret) 签名,timestamp 5分钟有效期 + nonce 防重放
  • 设备 sign 鉴权:App 接口通过 HMAC-SHA256 sign 鉴权,24小时有效,Redis 保持最新 sign

并发控制

  • consume 防重锁:Redis daycard:partner:consume:{设备标识},TTL 30s,防止并发重复消耗
  • 取卡行级锁:FOR UPDATE SKIP LOCKED,防止并发超卖
  • 激活锁:Redis api:thirdparty:activate:lock:{poolId},TTL 3600s,防止重复激活

幂等设计

  • consume 幂等:设备已有有效天卡时直接返回,不重复消耗
  • 卡密激活幂等:橘视厅返回 E0000003 + "已激活,重复使用激活码" 视为成功

性能优化

  • Token 缓存:Redis api:thirdparty:activate:{thirdCardNo},TTL 5分钟,减少橘视厅登录调用
  • 异步激活:consume 接口立即返回 ACTIVATING,激活在后台线程池完成

事务保障

  • consume 方法 @Transactional(rollbackFor = Exception.class),保证设备创建、卡池状态更新、partner池更新的原子性