跳到主要内容

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
  • 确认结果里有 scannedupdatedskipped
  • 确认 dry run 不会写入数据
  • 打开一张测试 ICCID 的详情页
  • 执行 Sync from EIOTCLUB
  • 确认卡片记录被真实 EIOTCLUB 字段填充
    • imsi
    • packageCode
    • packageName
    • packageType
    • network
    • planExpiry
    • 流量 / 用量相关数值

预期:

  • 页面展示真实卡数据,而不是占位值
  • 用户不会看到原始 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/eiotclub
  • scripts/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
  • 确认购买显示可读的履约状态徽标
    • Processing
    • Activating
    • Active
    • Expired
    • Refunded
    • Action needed - contact support
  • 确认 Active 行显示到期日期
  • 如果是 pending_assignment,确认 UI 展示 SIM 分配占位
  • 打开 /account/devices
  • 确认 SIM 行显示用量、carrier、以及 Expires {date}
  • 确认 offlinelocked 状态有明显标记

预期:

  • 用户不用进后台也能看到履约状态变化

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,它仍是运维任务,不是用户自助流程