等级决定会员能做哪些业务。结论由我方给,资料由会员在我方托管的屏上填。
KYC 概览与等级
三个等级
| 等级 | 怎么算出来的 | 大致解锁 |
|---|---|---|
| 0 | 什么都没过 | 收款、看余额、站内转账(视配置) |
| 1 | L1 档案审核通过 | 发卡、扫码付、理财、兑换、提现等大部分线 |
| 2 | 在 L1 之上,L2 档案也通过 | 汇款一类需要更完整资料的业务 |
GET /v1/kyc 返回的字段名是 level,不是 kyc_level。
⚠ 照
kyc_level取会拿到undefined,而undefined >= 1在 JS 里是
false—— 表现是「所有人都没实名」,不报错。(
kyc_level是会员对象上的字段名,两处不同名。)
⚠ 取严:L2 通过而 L1 没通过(数据异常)时算 0 —— 不是 1,也不是 2。
判据是「L1 没过就直接 0」。反过来会让一个 L1 都没过的人被你当成实名用户。
「哪条线要几级」不要硬编码
GET /v1/kyc/requirements
返回各条业务线当前要求的等级。它会变(我方或你的配置调整),所以:
⚠ 在你这边写死一张「发卡要 L1、汇款要 L2」的表,会在门槛调整的当天失效 ——
而失效的表现是你的用户被你自己的前端挡住,或者反过来,走完六屏才在最后
一步拿到
kyc_required。
覆盖九条线
remittance.express · remittance.pobo · card · exchange · earn · withdraw · transfer.send · transfer.receive · qrpay
⚠ 转账收发是两行。 合成一行取严会把只收不发的会员判成不够格。
⚠
card按你看得见的卡产品取最低值,不是恒为 1 —— 卡产品可以要求 L2。
扫码付那一条要特别看
它的闸不是等级,是一条累计触发线:未过 L1 的会员可以一直付,直到累计消费超过 kyc_trigger_amount 才要求 L1。
所以它的 required_level 是 0,另带两个字段:
| 字段 | 意思 |
|---|---|
kyc_trigger_amount | 累计超过它之后升级(没有触发线时这个字段不出现) |
kyc_trigger_level | 升到几级(1) |
⚠ 只看
required_level: 0会漏判 —— 用户某一笔会突然被拒。把它当成 1 又是高报 —— 现在完全能付的用户被你挡住。两个数一起用。
这张表不回答「这条线能不能用」
它只回答要几级。可用性(开没开通、有没有被停售)走GET /v1/merchant/lines —— 一行配置都没有时这里照样给 0。
拿到 kyc_required 之后怎么办
错误码不告诉你差的是 L1 还是 L2 —— 六个内部原因(缺 L1 / 缺 L2 ×汇款 / 发卡 / 兑换 / 理财)全部折成同一个对外码 kyc_required。
恢复路径固定是三步:
GET /v1/kyc→ 拿当前levelGET /v1/kyc/requirements→ 拿这条线要几级(注意上面那条「只有五条」)- 差 L1 →
POST /v1/kyc/sessions;差 L2 → 目前没有通路,见 L2 那一页
⚠ 少了第 3 步的分叉,你的接入代码会对一个缺 L2 的用户
反复发 L1 托管屏链接 —— 而他每次都能填完、每次都通过,
level却永远停在 1。
典型接入序列
POST /v1/members → 建会员
↓ 会员去下单
拿到 kyc_required
↓
GET /v1/kyc/requirements → 得知这条线要 level 1
↓
POST /v1/kyc/sessions → 换一条托管屏链接,转给终端用户
↓ 他在我方的页面上填表 + 拍照
kyc.result.updated 事件 → 通过 / 驳回 / 待补件
这条序列的泳道图(谁在什么时候做什么、失败往哪走)在创建会员与 KYC 全流程。
会员的 KYC 结论不跨商户继承
一个自然人在别的商户下已经完成实名,不会让他在你这里的 level 变成 1。
判据是你这个商户下的这一行会员:新建的会员没有指向任何 L1 档案,所以 POST /v1/members 之后 level 恒为 0,无一例外。
⚠ 别写「新建会员后先读
level,≥1 就直接放行」—— 那个分支永远不会命中。
但身份是跨商户共享的,这带来一个真实的坏路径
我方按邮箱(不区分大小写)认「同一个自然人」。于是:
| 情况 | 结果 |
|---|---|
| 同一个人、同一个邮箱在两个商户下建会员 | 同一个身份。他在你这里做 L1 不会被判重复 |
| 同一个人、换了一个邮箱 | 两个身份。他在你这里做 L1 时,证件号撞上他自己在别处那份档案 → kyc_identity_taken |
⚠⚠
kyc_identity_taken没有任何人工通道。 终端用户填完 15 个字段、传完三张证件照,在提交那一刻拿到一行红字,然后这条路对他永久关闭。
而你这一侧收不到任何信号:没有事件,
GET /v1/kyc仍然是none。你会一直等一个不会来的结论。
能做的预防只有一件:建会员时用这个人真实、长期在用的邮箱,
别用你自己拼出来的别名(
u88123@yourapp.com这种)。
同样的规则也适用于邮箱本身:该邮箱已被另一个身份的档案用过 → kyc_email_taken,同样是提交期 400、同样没有人工通道。
被驳回时把理由给出来
GET /v1/kyc 在 status: "rejected" 时带 reject_reason —— 审核员写的人话,不是错误码。L2 那一级是 l2_reject_reason,两级各一条不合并(L1 通过而 L2 被驳是常态,合成一个字段就分不出该让用户补哪一级的材料)。
没被驳回时两者恒为空串 —— 一条「上次被驳回的理由」挂在已通过的档案上,会让你的界面在用户已经过了之后还催他整改。
⚠ 这是自由文本,直接展示给终端用户,别拿它做分支判断 ——
审核员换一种说法你的分支就失效了。空串也可能只是老数据没写过理由,
那时按「未说明原因」处理。
资料要改:profile-sessions
POST /v1/kyc/profile-sessions → { "hosted_url": "…", "expires_at": … }
会员改名、换了证件、搬了家。这是唯一的路 —— POST /v1/kyc/sessions那一屏在已有档案时拒绝(每账户只许一份实名档案),而给他重新建一个会员会造出第二个余额为 0 的账号,旧账号里的钱与卡都留在原地。
托管屏会预填现有资料,用户只改要改的那几项;证件影像没重传的沿用原来那一份。
⚠⚠ 改完一律退回待审核,包括已通过的档案。 这个会员的
level会从 1掉回 0,复审通过之前发卡 / 汇款 / 高额转账都会停。
请在给出链接之前把这件事告诉用户 —— 否则他会在半路上发现自己的卡不能用了。
理由:资料改了就不再是审核员看过的那一份。要么拒绝编辑、要么重审,
没有第三种。
⚠ 该会员还没建过档时回
404,不会兜底签一张建档的票。两条路不互相回落。
影像与档案一个字节都不经过你
这是这条线的设计前提,也是为什么开放 API 上没有「提交实名资料」那种入参:证件影像落到你的服务器上,你就承担了它的合规义务 ——而你并不需要那份义务。