SA入驻App流程需求
1. 文档定位
本文定义 PocketBuy SA App 侧入驻流程,从手机号注册、身份 KYC、资料填写、收款账户、人脸验证、提交审核,到审核、培训、后台门店分配和办单资格生效的完整逻辑。
原型参考:https://demo.at.lltech.dev/pocketbuy_sa/home?docs=0&canvas=1。
2. 总体流程
SA 入驻完整链路:
手机号注册 + OTP
-> Identity KYC
-> Personal Profile
-> Bank account
-> Face verification
-> Submitted
-> 后台入职审核
-> 线下培训
-> 管理后台选择并绑定门店
-> 初始登录密码下发
-> 可正式办单页面边界:
- App 页面展示文案使用英文,需求文档使用中文。
- 不新增 Work setup、Job role、Background check。
- 不向 SA 展示 Dojah/AWS 明细、设备定位采集结果、风控规则、审核内部原因或后台判断阈值。
- SA 前端只展示当前步骤、可操作按钮、可理解状态和后续安排。
3. 页面与操作逻辑
整个操作流程不允许app内录屏
3.1 注册
路由:/pocketbuy_sa/register。
功能:
- 输入 Nigeria 手机号和 OTP。
- 手机号格式通过后点击 Get code;OTP 校验通过后,服务端在创建草稿前查询该手机号是否已存在 SA 账号或未完成入驻记录。
- 存量手机号不得静默创建第二个 SA 账号;前端根据服务端返回的业务类型展示英文弹窗:已激活账号提示
Phone number already registered,提供Go to login;存在未完成草稿提示Onboarding already in progress,直接进入对应入驻步骤。 - 存量手机号查询结果只返回可行动的业务提示,不返回账号内部 ID、审核人、风控命中规则等内部信息。
- 注册手机号作为 SA 账号手机号,不重复录入。
- 注册成功后自动跳转
/pocketbuy_sa/onboarding/kyc。
后端要求:
- OTP 发送、校验、频控和风险限制由后端处理。
- OTP 验证通过后,按手机号查询
active_account、in_progress_onboarding、none;查询和弹窗决策均由后端结果驱动,前端不得仅凭本地缓存判断是否存量。 active_account不创建新草稿;in_progress_onboarding返回原草稿的可恢复入口;none才创建新草稿。所有分支记录 trace_id 和审计事件。- 后端创建 SA 草稿时记录手机号、注册时间、注册渠道、设备摘要和 trace_id。
3.2 Identity KYC
路由:/pocketbuy_sa/onboarding/kyc。
功能:
- 页面内容使用英文。
- 录入 Full legal name 和 11 位 BVN。
- 页面不提供单独 BVN Check 按钮。
- 点击 Continue 后由后端先校验 BVN 格式、当前 BVN 是否已关联其他 SA、是否已关联当前草稿,再调用 BVN KYC;只有全部通过才进入 Personal Profile。
- 存量 BVN 或身份异常使用英文弹窗:当前账号已绑定时提示
BVN already linked to this account;已绑定其他账号时提示BVN already linked to another account;姓名不一致时提示Details do not match并允许返回修改;服务不可用时提示Identity check unavailable,允许重试。 - 存量 BVN 不允许通过更换姓名、手机号或重复提交绕过;服务端以 BVN 的规范化值和唯一约束为准。
- KYC 失败时前端只展示可理解的失败提示,不展示供应商原始返回。
设备与定位:
- 进入 KYC 页面时弹出定位授权提示,并采集设备信息。
- SA 授权后采集设备型号、系统、设备指纹、定位、精度和操作时间。
- SA 拒绝授权或采集失败不阻断流程,但后端必须记录失败原因,作为审核材料。
- SA 前端不展示经纬度、精度、设备指纹等采集结果。
后端要求:
- 提交时先校验当前 BVN 是否已有有效绑定:绑定当前草稿时幂等返回;绑定其他 SA 时返回
BVN_ALREADY_USED;无绑定时调用 Dojah BVN Lookup 验证姓名并拉取 BVN 照片保存。 - BVN 唯一性检查、姓名比对和第三方调用结果必须落库并审计;并发请求通过数据库唯一约束或幂等键避免重复绑定。
- BVN、BVN KYC 结果属于强实名信息,后续后台不可手动修改。
3.2.1 存量校验弹窗 UI 设计图
下图为注册和 Identity KYC 环节的全量存量校验弹窗,App 页面文案保持英文;弹窗居中展示,背景加遮罩,主按钮使用品牌色,次要操作使用无边框文字按钮。
| 弹窗 | 触发条件 | 主操作 | 次要操作 | 是否允许继续入驻 |
|---|---|---|---|---|
Phone number already registered | OTP 通过后命中已激活 SA 账号 | Go to login | Use another phone number | 否;不得创建新账号 |
Onboarding already in progress | OTP 通过后命中未完成入驻草稿 | Resume onboarding | Use another phone number | 不新建;进入原草稿 |
BVN already linked to this account | BVN 已绑定当前草稿/账号 | Close | Go to login | 否;按幂等或恢复原流程处理 |
BVN already linked to another account | BVN 已绑定其他 SA | Close | Go to login | 否;不得泄露对方账号信息 |
Details do not match | BVN 返回姓名与用户输入不一致 | Review and try again | 无 | 否;返回 KYC 表单并保留可编辑输入 |
Identity check unavailable | BVN 服务超时、不可用或系统异常 | Review and try again | 无 | 否;保留输入,不写入 KYC 通过状态 |
原型中使用以下测试数据可触发各类弹窗:
-
手机号 8012340001:已激活账号
-
手机号 8012340002:未完成入驻
-
BVN 11111111111:当前账号已绑定
-
BVN 22222222222:其他账号已绑定
-
BVN 33333333333:姓名不一致
-
BVN 44444444444:服务不可用
3.3 Personal Profile
路由:/pocketbuy_sa/onboarding/profile。
功能:
- 录入基础资料:性别、生日、地址、State、City、教育信息、紧急联系人。
- 基础资料可以在提交前由 SA 修改。
- 提交审核后,只有后台在特定状态下可修改基础信息,App 不直接开放后台改写能力。
后端要求:
- State、City 使用后端字典或配置。
- 紧急联系人手机号需做格式校验。
- 后端保存每次草稿更新的更新时间和 trace_id。
3.4 Bank account
路由:/pocketbuy_sa/onboarding/payout。
功能:
- 录入收款银行、10 位 NUBAN 和备注。
- 页面展示脱敏账户摘要。
- Bank account 与 Face verification 是两个独立步骤。
后端要求:
- 银行名称、NUBAN、银行校验结果属于资金相关信息。
- SA 提交银行名称和 10 位 NUBAN 后,由服务端调用 Paystack 账户校验接口;前端不得直接调用 Paystack,也不得接触供应商密钥。
- 使用接口:
GET https://api.paystack.co/bank/resolve?account_number={NUBAN}&bank_code={BANK_CODE}(Resolve Account Number)。BANK_CODE由后端银行字典提供;服务端读取返回的account_name、account_number、bank_id等字段,用于校验账号存在性、账户名称和账户归属。 - 账户校验通过条件:Paystack 返回成功、账号和银行代码与请求一致、账户名非空、且账户名与 BVN KYC 法定姓名满足配置的匹配规则;仅“账号存在”不能视为归属验证通过。
- 后端保存请求 trace_id、校验时间、脱敏账号、返回账户名摘要、匹配结果和错误码映射;原始响应按敏感数据留存策略加密或脱敏。
- 提交后后台不可手动修改银行名称、NUBAN 或银行校验结果;如需变更必须走单独银行卡变更流程。
- 提交审核前服务端复验 NUBAN 格式、银行账户验证状态和账户归属匹配状态,验证过期或账户信息发生变更时必须重新校验。
Paystack 官方文档:Verify Account Number。
3.5 Face verification
路由:/pocketbuy_sa/onboarding/face。
功能:
- 人脸验证是入驻提交前最后一步。
- 只有 OTP、BVN KYC 和银行账户均满足条件后,才能进入最终提交判断。
- 人脸验证通过后才允许点击 Submit application。
后端要求:
- 参照现有进件流程人脸校验逻辑
3.6 Submitted
路由:/pocketbuy_sa/onboarding/submitted。
功能:
- 展示入驻申请已提交。
- 告知后续会进行审核和线下培训。
- 提供返回 Home 入口。
后端要求:
- 提交时状态进入
pending_review。 - 服务端提交前必须复验 OTP、BVN KYC、人脸比对、银行账户校验状态、NUBAN 格式和审核材料完整性。
- 重复提交需要幂等处理,不允许生成多条并行审核单。
4. 首页状态与办单拦截
Home 根据后端返回状态展示 SA 当前资格:
| 状态 | App 展示 | 办单入口 |
|---|---|---|
| 未提交 | 正常展示入驻入口 | 不允许正式办单 |
pending_review | 入驻审核中 | 拦截 New Application / Calculator |
rejected | 入驻未通过 | 不允许办单 |
approved_pending_training | 待培训确认 | 不允许办单 |
training_passed,等待后台分配门店或密码下发 | Getting ready | 不允许办单;不展示后台处理细节 |
training_passed 且已绑定有效门店、已下发密码 | 可正式开始办单 | 允许 |
disabled | 账号停用 | 阻断登录或办单 |
办单资格条件:
review_status == approved
AND training_result == passed
AND at least one store binding active
AND login_password_issued == true
AND account_status == active门店分配规则:
- SA App 入驻流程不展示 Merchant / Store 页面,不采集、选择或展示门店信息。
- 培训通过后,管理后台才可为该 SA 选择并绑定一个或多个有效门店;后台保存
sa_store关系、操作人、时间、变更前后值和审计记录。 - 后台完成至少一个有效门店绑定后写入
store_assignment_status=assigned;门店被解绑、停用或全部失效时,该状态应回退为不可办单。 - sa办单过程也需要对sa权限进行校验,如扫码是权限外的门店,弹窗提示:“You currently do not have the application permission for this store. If you need it, please contact your manager.”
- 培训已通过但尚未完成门店分配或初始密码下发时,App 首页只显示英文
Getting ready/We are preparing your account,并拦截办单入口;不得向 SA 说明后台门店分配、审核或密码下发的内部处理状态。
5. 登录与密码
- 登录页支持 Password/OTP 切换。
- 培训通过后系统下发初始登录密码。
- Reset Password 支持旧密码重置和 OTP 重置。
- 旧密码重置不要求手机号;OTP 重置才要求手机号和验证码。
- 密码下发、登录、重置均由服务端校验和审计。
5.1 OTP 重置密码节点
路由:/pocketbuy_sa/reset-password。
- SA 输入注册手机号,点击
Get code。 - 服务端校验手机号格式、账号状态和发送频控;为避免账号枚举,账号不存在、停用或不可重置时前端统一展示“验证码已发送或请联系客服”的结果,不返回具体账号是否存在。
- SMS 服务发送 4 位一次性验证码;验证码有效期 5 分钟,单次使用,最多连续输错 5 次,重新发送后旧验证码立即失效。
- SA 输入验证码并点击页面上的
Verify code按钮;服务端校验手机号、验证码、用途password_reset、有效期、错误次数和设备/风险上下文。验证码未验证通过前,新密码和确认密码输入框保持不可用。 - 验证通过后服务端签发一次性
password_reset_token,有效期 10 分钟,仅允许修改该手机号对应账号的密码,不得直接登录或调用其他业务接口。 - SA 输入新密码和确认密码;服务端校验密码复杂度、两次输入一致、不能复用最近密码,并使用 reset token 完成密码更新。
- 更新成功后撤销旧登录会话和未使用的重置 token,记录审计日志,并返回登录页;验证码、token 和新密码不得写入普通日志。
重置密码短信模板:
Your PocketBuy SA verification code is {otp}. It expires in {minutes} minutes. Do not share this code.uSpeedo 报备信息:
| Template Name | Template ID | uSpeedo 模板内容 | 变量映射 | 预估长度 | 长度判断 |
|---|---|---|---|---|---|
PB_SA_OTP_CODE | UTA260807TZK1RT | Your PocketBuy SA verification code is {1}. It expires in {2} minutes. Do not share this code. | {1}=otp, {2}=minutes | 93 | OK: 1 segment |
模板变量和发送规则:
{otp}为服务端生成的 4 位数字验证码;{minutes}固定为5,避免模板动态文案与实际有效期不一致。- 短信发送成功后才允许进入验证码输入态;发送失败不启动倒计时,前端提示重试。
- 验证码发送间隔建议 60 秒,单手机号、设备、IP 均需要频控;命中频控时不创建新验证码。
- 验证成功、过期、输错、达到最大错误次数、重发失效和密码更新结果均记录 trace_id 与审计事件。
初始密码短信模板:
Welcome to PocketBuy. Your SA account is approved. Login phone: {phone}. Initial password: {password}. Please change it after login.uSpeedo 报备信息:
| Template Name | Template ID | uSpeedo 模板内容 | 变量映射 | 预估长度 | 长度判断 |
|---|---|---|---|---|---|
PB_SA_ACCOUNT_APPROVED_APP | UTB260807D1JWO2 | Welcome to PocketBuy. Your SA account is approved. Login phone: {1}. Initial password: {2}. Please change it after login. | {1}=phone, {2}=password | 142 | OK: 1 segment |
模板规则:
{phone}使用 SA 注册手机号,建议展示完整手机号或业务约定的脱敏格式。{password}为服务端生成的一次性初始密码。- 仅在培训通过、账号满足开通条件且密码未下发时发送。
- 发送成功后写入
login_password_issued=true;发送失败时保留待重试状态并写审计日志。
6. 接口草案参考
| 接口 | 用途 |
|---|---|
POST /api/sa/register/otp/send | 发送注册 OTP |
POST /api/sa/register/otp/verify | 校验 OTP,并返回存量手机号分支或创建入驻草稿 |
GET /api/sa/register/existing-check | 按已验证手机号查询账号/入驻草稿状态 |
POST /api/sa-onboarding/kyc/verify-bvn | Identity KYC Continue 时触发 BVN KYC |
POST /api/sa-onboarding/draft | 保存入驻草稿 |
POST /api/sa-onboarding/face/verify | 最终人脸验证 |
POST /api/sa-onboarding/submit | 提交入驻审核 |
GET /api/sa-onboarding/status | 首页查询入驻、培训、账号状态 |
POST /api/sa-onboarding/store-assignment | 培训通过后由管理后台选择并绑定 SA 门店,非 SA App 调用 |
POST /api/sa/login/password | 密码登录 |
POST /api/sa/login/otp | OTP 登录 |
POST /api/sa/password/reset/otp/send | 发送重置密码验证码 |
POST /api/sa/password/reset/otp/verify | 验证重置密码验证码并签发 reset token |
POST /api/sa/password/reset/complete | 使用 reset token 更新密码 |
POST /api/sa/password/reset | 重置密码 |
注册 OTP 验证接口返回:status=none、status=active_account 或 status=in_progress_onboarding;存量分支只返回恢复/登录所需的短期 token 或路由信息。BVN 接口返回:BVN_ALREADY_USED、BVN_ALREADY_LINKED_TO_CURRENT_DRAFT、BVN_NAME_MISMATCH、KYC_SERVICE_UNAVAILABLE 或成功结果。
7. 异常与提示
- OTP 超时、频控、错误次数过多:前端提示稍后重试,后端记录风险事件。
- 重置密码验证码发送失败:不启动倒计时,保留手机号并提示重试。
- 重置密码验证码错误、过期或超过最大错误次数:不签发 reset token;达到上限后要求等待或重新发送。
- reset token 过期、重复使用或与手机号不匹配:阻断密码更新并返回重置密码起始页。
- BVN 格式错误:前端本地拦截。
- 手机号已注册且账号有效:弹窗引导登录,不创建新草稿。
- 手机号存在未完成入驻:弹窗引导恢复原草稿,不创建新草稿。
- BVN 已绑定当前账号:按幂等成功处理或提示恢复已有入驻,不重复创建绑定关系。
- BVN 已绑定其他账号:弹窗提示不可复用,阻断继续,不展示被绑定账号信息。
- BVN 与姓名不一致:弹窗提示检查并返回当前页修改;不得进入下一步。
- BVN 查询服务不可用:弹窗提示稍后重试,保留当前输入,不标记为 KYC 通过。
- BVN KYC 未通过:前端展示身份校验失败,不展示供应商原始原因。
- 设备/定位拒绝或失败:不阻断流程,后台审核材料记录采集失败。
- 人脸验证失败:不允许提交,可提示重试。
- 银行账户格式无效:不允许提交。
- 银行账户不存在、账户名为空或账户归属与 BVN 姓名不匹配:停留在 Bank account,展示可理解的英文错误提示,不允许进入最终提交。
- Paystack 超时或不可用:展示重试提示,保留银行输入,不标记账户验证通过。
- 账号停用:登录和新进件入口阻断,已提交的处理中订单继续由服务端推进;代办单用户仍可查看进度、补充资料和完成不依赖 SA 实时操作的步骤。未生成订单的草稿不可继续创建订单,需联系平台或转派其他有效 SA。
8. 验收口径
- 注册成功后进入
/pocketbuy_sa/onboarding/kyc。 - 输入已激活存量手机号并完成 OTP 后,展示登录引导弹窗;输入存在未完成草稿的手机号后,展示恢复入驻弹窗。
- 输入已绑定其他账号的 BVN 后,展示阻断弹窗且不能进入 Personal Profile;姓名不一致和服务不可用均有可关闭/重试弹窗。
- Identity KYC 页面内容为英文,不展示 KYC 结果,不含 BVN Check 按钮。
- 设备/定位采集失败不阻断入驻提交,但审核材料可见失败原因。
- SA 入驻流程不包含 Merchant / Store 步骤;Bank account 与 Face verification 独立。
- Bank account 必须完成 Paystack
GET /bank/resolve账户校验;需要确认归属时必须完成 BVN 与账户匹配校验,不能仅依赖 NUBAN 格式校验。 - 人脸验证在最后一步,通过后才允许提交。
- Submitted 页面为英文,并提示后续审核和线下培训。
- Home 能展示审核中、拒绝、待培训、可办单、停用状态。
- 审核通过但培训未通过时不能办单。
- 培训通过后由管理后台完成至少一个有效门店绑定;门店绑定和密码下发均完成、账号启用后才可办单。