03 — 客户身份验证与订单创建
来源:
api/verify.ts+api/order.ts createOrder
涉及页面:Verify PRD
共 3 个接口
业务上下文
本步骤是 draft → 订单落库 的临界点:
- SA 在 Calculate 完成试算后跳 Verify 页
- 客户口述手机号 → SA 录入 → 发送 OTP(SMS / Voice)
- 客户读 OTP → SA 录入 → 校验
- 校验通过的瞬间 = 订单正式创建(status = Drafting),从此本单有
order_id,可中途暂停后通过 Applications 继续
关键业务规则:
- 同号活跃订单(30 天窗口)→ 校验通过时一并返回,前端弹层让 SA 选「查看现有单」或「继续新单」
- SMS / Voice 双通道独立倒计时(应对 NG SMS 不稳定)
- OTP 通过即创建订单 — 用一次接口请求完成(不分两步)
1. 发送 OTP
业务描述:给客户手机发 6 位验证码;支持 SMS / Voice 双通道。
触发场景:
- Verify 页 SMS 通道:点击「Get Code」按钮
- Verify 页 Voice 通道:点击「Get code via voice call」链接
入参
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| phone | string | ✓ | 10 位 NG 本地号(不含 +234) |
| channel | enum | ✓ | sms(默认)/ voice(应对 SMS 不稳定) |
出参
| 字段 | 类型 | 说明 |
|---|---|---|
| expires_at | string(ISO 8601) | 验证码过期时间(一般 5 分钟) |
| resend_after_seconds | int | 重发冷却秒数(前端按此值启动倒计时)— 建议 SMS 60s / Voice 90s |
关键异常
| 异常码 | 含义 | 前端处理 |
|---|---|---|
| OTP_PHONE_INVALID | 手机号格式不对(应该前端拦下,兜底) | inline 错误 |
| OTP_RATE_LIMITED | 该号码短时间发码次数过多 | toast「Too many requests, please try later」 |
| OTP_CHANNEL_UNAVAILABLE | 该通道暂不可用(SMS 网关挂 / Voice 网关挂) | toast「Couldn’t send code, please retry」,建议 SA 切到另一通道 |
注:
- SMS 与 Voice 各自独立冷却(防滥用电话成本)
- Voice 通道接通方式:客户接电话后听到机器人读 6 位码
- 通道选择由 SA / 客户即时决定,无需 lock-in(同号可来回切)
2. 校验 OTP + 活跃订单查询 + 创建订单
业务描述:原子操作 — 校验通过则同时创建新订单(status = Drafting)+ 返回该号 30 天窗口内的活跃订单列表(供 SA 决定开新单 or 接老单)。
触发场景:Verify 页点击「Verify & Continue」按钮(前置:OTP 6 位 + 隐私政策已勾选 + Calculate Draft 已存在)。
入参
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| phone | string | ✓ | 10 位 NG 本地号 |
| code | string | ✓ | 6 位 OTP |
| calculate | object | ✓ | Calculate Draft 数据(pre-OTP 本地草稿,结构见下) |
| merchant_id | string | ✓ | POS 反查得到的 merchant ID(防欺诈:必须有现场办单上下文) |
calculate 子结构(来源:Calculate 页 draft)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| category | enum | ✓ | mobile / tablet |
| brand | string | ✓ | 品牌 |
| model | string | (IMEI 路径必填) | 设备型号(IMEI 反查路径填) |
| product_id | string | (catalog 路径必填) | catalog 选品路径填 |
| price | number(NGN) | ✓ | 售价 |
| down_payment | number(NGN) | ✓ | 首付 |
| frequency | enum | ✓ | weekly / monthly |
| term | int | ✓ | 分期期数 |
| pos_id | string | ✓ | POS ID(防欺诈现场办单) |
| pos_name / pos_address / merchant_name | string | 冗余 | 由前端从 POS 反查时回写,便于审计 |
出参
| 字段 | 类型 | 说明 |
|---|---|---|
| new_order_id | string | 新创建订单 ID |
| active_orders | array<ActiveOrderSummary> | 该号 30 天窗口内的活跃订单(不含 new_order_id 本身) |
ActiveOrderSummary 结构
| 字段 | 类型 | 说明 |
|---|---|---|
| order_id | string | 订单 ID |
| short_code | string | 短码(UI 紧凑展示,如 #8A2F1C) |
| product | string | 商品描述(品牌 + 型号 + 配置) |
| amount | number(NGN) | 订单金额(成交价) |
| created_at | string(ISO 8601) | 创建时间 |
| status | enum | Drafting / FixRequired / Approved / Signed / Paid / Reviewing(不含终态 Completed/Closed) |
前端处理 active_orders:
- 数组为空 → 直接跳 Identity 步骤
- 数组非空 → 弹层让 SA 选「View Existing」(跳详情,丢弃 new_order_id)或「Continue New」(保留 new_order_id 跳 Identity)
关键异常
| 异常码 | 含义 | 前端处理 |
|---|---|---|
| OTP_INVALID | 验证码错误或过期 | inline 错误 Incorrect code |
| OTP_EXPIRED | 验证码过期 | 同上 + 暗示重新发码 |
| ORDER_CREATE_FAILED | 订单创建失败(merchant 失效 / SA 授权变更等) | toast 显示具体原因(按 code 解析) |
| MERCHANT_NOT_AUTHORIZED | merchant 不在 SA 授权列表(运营刚撤销) | toast Merchant authorization changed, please contact admin |
待业务方确认事项
| # | 议题 | 待确认 |
|---|---|---|
| 1 | 同号活跃订单查询的时间窗口 | 当前 30 天;超期是否允许复借同号开新单(预案:允许) |
| 2 | 隐私政策版本号是否传入 | 用于审计客户看到的是哪版条款;预案:加 privacy_policy_version 字段 |