Skip to main content

EIOTCLUB Integration Handoff

接入概览

EIOTCLUB 接入已经从基础设施、数据库扩展、只读同步、履约闭环、退款预览与执行、回调处理到用户侧可见性全部串起来了。现在本地系统可以从 EIOTCLUB 拉取卡片、套餐、余额和订单状态,也可以接收 EIOTCLUB webhook 推进 DataPlanPurchaseSimCard 状态,并在后台和用户侧看到同步后的结果。

环境变量

Variable用途缺失行为
EIOTCLUB_APP_KEYEIOTCLUB API 请求签名所需的 appkey未配置时 postSigned() 返回 null,脚本打印 EIOTCLUB not configured
EIOTCLUB_SECRETEIOTCLUB API 请求签名所需 secret同上
EIOTCLUB_BASE_URLEIOTCLUB API Base URL,默认 https://oapi.eiotclub.com未配置时使用默认值
EIOTCLUB_WEBHOOK_SECRETEIOTCLUB 回调验签 secret未配置时 webhook 验签失败;本地 replay 脚本会提示 EIOTCLUB not configured
CRON_SECRET/api/cron/eiotclub-reconcile 鉴权 secret未配置时 cron 路由返回 cron_not_configured

关键文件地图

状态机

stateDiagram-v2
[*] --> pending
pending --> pending_assignment: webhook / retry fulfillment
pending --> ordering: Stripe webhook / retry fulfillment / order_detail
pending_assignment --> ordering: assign SIM + fulfill
ordering --> active: package_activated webhook / reconcile / cron
ordering --> failed: webhook error / fulfillment error
active --> expired: usage_exhausted webhook / reconcile
active --> refunded: refund webhook / admin refund
ordering --> refunded: refund webhook / admin refund
expired --> refunded: refund webhook / admin refund
active --> active: sync / reconcile / usage refresh

9 类回调映射表

EIOTCLUB 原始事件名本地 EiotclubEventType业务动作
FlowAlert / CloudESimFlowAlertflow_warning更新 SimCard.remainFlowMb,不发邮件
SubPkgList / CloudESimSubPkgListorder_detaileiotclubOrderId 找购买记录,推进到 ORDERING,落 eiotclubPackageEndDate
PkgEffective / CloudESimPkgActivatepackage_activated购买记录推进 ACTIVE,写 activatedAteiotclubPackageEndDateexpiresAt
Refund / CloudESimRefundrefund购买记录推进 REFUNDED,幂等处理重复投递
PkgQuantityList / CloudESimPkgDeactivateusage_exhausted购买记录推进 EXPIRED,同步 SimCard.planExpiry
CardStopped / CloudESimCardStoppedcard_offlineSimCard.status = "offline"
SwitchProduct / CloudESimSwitchProductproduct_switched更新 SimCard.packageCode / packageName / packageType
CardIMEILockedcard_lockedSimCard.status = "locked"
IMEIUnLockcard_unlockedSimCard.status = "active"

dedupKey 规则

优先使用 EIOTCLUB 回调自带的唯一标识 id;如果没有 id,则使用 sha256(eventType + iccid + orderId/packageCode + timestamp) 作为本地幂等键,并写入 SimEvent.payload.dedupKey

签名规则要点

  • API 请求签名包含 appkey
  • 回调验签不包含 appkey
  • 过滤 null / undefined / ""
  • 参数按 ASCII 升序排序
  • 每项拼成 key=value&
  • 最后直接追加 secret=xxx
  • 对字符串做 SHA1 后再 toUpperCase()

实现细节见 lib/eiotclub.ts

手动运维操作

  • 后台单卡同步:/admin/sim-cards/[iccid]Sync from EIOTCLUB
  • 后台全量同步:/admin/sim-cardsSync all from EIOTCLUB / Dry run
  • 后台履约重试:/admin/data-plan-purchases/[id]Retry fulfillment
  • 后台状态回收:/admin/data-plan-purchases/[id]Reconcile status
  • 后台用量刷新:/admin/data-plan-purchases/[id]Refresh usage
  • 后台 EIOTCLUB 退款:/admin/data-plan-purchases/[id]Refund via EIOTCLUB
  • 后台取消会话:/admin/sim-cards/[iccid]Cancel session / reconnect
  • Stripe 退款仍然在 /admin/payments 里单独处理,EIOTCLUB 退款不自动联动 Stripe
  • cron 建议:/api/cron/eiotclub-reconcile 每 10 到 15 分钟跑一次

联调脚本

scripts/eiotclub-smoke.ts

用途:本地 / staging 只读探测 EIOTCLUB。

scripts/eiotclub-webhook-replay.ts

用途:把本地 JSON 样例重新签名后 POST 到本地 webhook,验证回调链路。

已知边界 / 留待后续

  • eSIM / cloud eSIM 目前只接了客户端壳,业务侧只保留了兼容入口
  • 运营商切换、IMEI 池、短信、分润未接
  • pending_assignment 的用户侧 assign 流程仍未实现
  • EIOTCLUB 与 Stripe 的退款联动仍需人工决定,建议按订单号 / 支付意图 / EIOTCLUB orderId 做对账
  • CloudESIM 某些回调只带 eid,本地 SimCard 目前没有 eid 字段时只能落 not_local

端到端验证清单

这里保留完整清单供联调时使用,按环境变量、只读探测、后台同步、支付履约、回调重放和退款验证的顺序逐项确认。