EIOTCLUB Web Staging 验证清单
范围:AOVIS / NEXA 独立站的前端 staging 验证
目标:从浏览器侧确认 EIOTCLUB 连通性、履约、Webhook、退款,以及用户可见状态更新
0. 前置条件
- staging 站点已部署且健康
- VM 的
.env.production已配置 EIOTCLUB 环境变量 - EIOTCLUB 出网 IP 白名单包含
8.229.42.246 - EIOTCLUB webhook 地址已配置为
https://aovis.app/api/webhooks/eiotclub - 有可用的测试 ICCID
- 管理员账号可正常登录
- Stripe test mode 可用于购买验证
1. 只读连通性检查
1.1 管理后台 SIM 卡同步
目标页面:
/admin/sim-cards/admin/sim-cards/[iccid]
清单:
- 打开
/admin/sim-cards - 对
Sync all from EIOTCLUB执行Dry run - 确认结果里有
scanned、updated、skipped - 确认 dry run 不会写入数据
- 打开一张测试 ICCID 的详情页
- 执行
Sync from EIOTCLUB - 确认卡片记录被真实 EIOTCLUB 字段填充
imsipackageCodepackageNamepackageTypenetworkplanExpiry- 流量 / 用量相关数值
预期:
- 页面展示真实卡数据,而不是占位值
- 用户不会看到原始 EIOTCLUB 错误信息
1.2 数据套餐刷新
目标页面:
/admin/data-plans
清单:
- 打开
/admin/data-plans - 对带有
packageCode的行点击Refresh from EIOTCLUB - 确认本地
priceUsd不会被改动 - 确认页面会给出成功 / 失败反馈
预期:
- EIOTCLUB 相关的套餐元数据会在本地刷新
- 本地价格仍然是事实来源
2. 购买与履约检查
2.1 创建一个测试数据套餐购买
目标页面:
/services/cellular-data/data-plans- Stripe checkout 成功返回页
清单:
- 发起一次 Stripe 测试购买
- 使用测试卡完成支付
- 确认后台生成
DataPlanPurchase - 确认该购买有预期的
dataPlanId - 确认购买起始状态为
pending/ordering
预期:
- Stripe webhook 会写入购买记录
- 后续可以基于购买记录走 EIOTCLUB 履约
2.2 履约状态推进
目标页面:
/admin/data-plan-purchases/[id]
清单:
- 打开这笔购买的详情页
- 确认履约开始后出现
eiotclubOrderId - 确认状态从
pending变为ordering - 等待 reconcile 或 webhook 处理
- 确认状态变为
active - 确认
activatedAt已设置 - 确认
eiotclubPackageEndDate已设置
预期:
- 履约状态变化可以在后台详情页中看到
- 审计时间线会记录变化过程
3. Webhook 检查
目标页面 / 脚本:
/api/webhooks/eiotclubscripts/eiotclub-webhook-replay.ts
清单:
- 重放一条
PkgEffective样例 payload - 确认 webhook 返回
SUCCESS - 确认相关购买保持
active - 再重放一次同样的 payload
- 确认第二次会被当作重复投递处理
- 重放一条
Refund样例 payload - 确认购买变为
refunded - 重放一条
CardIMEILocked样例 payload - 确认 SIM 状态变为
locked
预期:
- Webhook 处理具备幂等性
- 重复 payload 不会重复改变状态
- webhook 成功响应为
{ "status": "SUCCESS" }
4. 退款检查
目标页面:
/admin/data-plan-purchases/[id]
清单:
- 点击
Preview refund - 确认会返回一个金额
- 填入可选退款原因
- 点击
Refund via EIOTCLUB - 确认购买变为
refunded - 确认没有自动触发 Stripe refund
预期:
- EIOTCLUB 退款和 Stripe 退款仍是两个独立动作
- 页面会给出友好的成功 / 失败反馈
5. 用户可见状态检查
目标页面:
/account/services/account/devices
清单:
- 打开
/account/services - 确认购买显示可读的履约状态徽标
ProcessingActivatingActiveExpiredRefundedAction needed - contact support
- 确认
Active行显示到期日期 - 如果是
pending_assignment,确认 UI 展示 SIM 分配占位 - 打开
/account/devices - 确认 SIM 行显示用量、
carrier、以及Expires {date} - 确认
offline和locked状态有明显标记
预期:
- 用户不用进后台也能看到履约状态变化
6. 失败路径检查
6.1 非法签名
清单:
- 使用错误签名重放 webhook
- 确认接口返回
401 - 确认响应体里有
Invalid signature
6.2 过期时间戳
清单:
- 使用过期 timestamp 重放 webhook
- 确认接口返回
401
6.3 本地记录缺失
清单:
- 为本地不存在的卡片 / 订单重放 webhook
- 确认接口返回
200 - 确认 handler 报告
not_local或等价状态
预期:
- 认证问题要 fail closed,未知但合法的回调要 fail open
7. 运维健康检查
-
npm run eiotclub:connection-test能打印签名自检和真实卡 JSON -
npm run eiotclub:smoke -- account-balance能返回真实余额 -
npm run eiotclub:smoke -- cards --size 5能返回列表 - 管理页面没有原始 stack trace
- staging 站点仍能正常打开首页和主要产品页
8. 给 staging 运维的备注
- 只使用测试 ICCID
- 不要期望每个 EIOTCLUB
cardStatus都能在第一天完美映射到本地标签 - 如果浏览器流程通过,但 webhook 重放失败,优先相信 webhook 结果
- EIOTCLUB 退款和 Stripe 退款要分开处理
- 如果购买是
pending_assignment,它仍是运维任务,不是用户自助流程