签约首付与放款到商户

1. 目标

审批通过后,客户完成签约并支付首付。首付到账后,bns 判断首付已足额并通知 fcs 发起商户放款;fcs 按商品金额 S 调用 pmt 放款到商户/门店,商户收到商品全款后才允许交机。

这是手机分期区别于现金贷的核心资金链路。

2. 金额口径

金额公式 / 来源说明
商品售价 SSA 选品和试算确认最终成交价
首付 D审批方案 / 客户选择客户自有资金
借据本金 PP = S - Dfcs 借据本金只记 P
商户实收 MM = S = P + Dfcs 按商品金额调用 pmt 放款给商户

首付不进入借据本金、还款余额和逾期本金。首付应收、实收、状态和放行判断记录在 bns;pmt 只记录通道收款流水并将到账结果通知 bns;fcs 只在 bns 确认首付足额后处理借据本金 P 和商户放款。

3. 签约

步骤说明
查询签约信息bns 返回审批方案、合同要素、首付金额和还款计划摘要
客户确认方案正常通过或降级通过均需客户确认
SA 与客户合照必须现场拍摄,作为线下场景证据;禁止从相册上传
客户签署合同签署完成后订单进入 Signed
生成首付单bns 创建首付单并记录首付应收、状态,pmt 准备虚拟账户

签约失败或客户放弃时,订单不能进入首付阶段。签约流程中断后重新进入签约,必须重新采集客户人脸并重新拍摄 SA 与客户合照,不能复用中断前的人脸和合照结果。

3.1 合照、人脸有效期与照片来源

MVP 口径
客户人脸有效期当天有效;跨日签约或签约中断后重进,必须重新采集客户人脸
合照有效期与本次签约会话绑定;签约流程中断后必须重新拍摄
照片来源全流程禁止从相册上传照片,只允许现场拍摄或扫码采集
合照人数合照中必须检测到 2 张人脸,分别对应 SA 和客户
前端检测PocketBuy SA Android 端使用 ML Kit 人脸检测能力做本地检测,参考 Google ML Kit Face Detection for Android
前端产物前端需从合照中裁剪/导出 2 张人脸图片,分别作为 sa_face_refcustomer_face_ref 传给后端
后端比对后端对 customer_face_ref 与客户本次有效人脸/证件照结果做比对,对 sa_face_ref 与登录 SA 档案照或本次 SA 人脸结果做比对
失败处理未检测到 2 张人脸、检测到超过 2 张人脸、人脸不完整、任一比对失败,均阻断签约并提示重新现场拍摄

ML Kit 在本场景只用于前端人脸检测、检测框定位和人脸裁剪,不作为身份识别或最终人脸比对能力;最终身份比对结果以后端/crs 结果为准。

4. 首付收款

4.1 账户方案

MVP 采用订单级首付虚拟账户:

口径
账户绑定一个手机分期订单绑定一个首付单,一个首付单绑定一个虚拟账户
账户复用不做客户级长期复用
页面展示PocketBuy SA 展示金额、银行名、账号、户名和转账指引
匹配依据首付单号、订单号、accountReference、金额、币种、通道流水
重复进入页面查询已有首付单和账户,不重复开户

账户绑定:按订单维度绑定,待 Paystack 和 Monnify 进行验证;Monnify 文档中的 accountReference 可用于实现订单级唯一关联。

Monnify 使用口径:

  • 本期收款供应商优先使用 Monnify Reserved Account(虚拟账户)能力。
  • 一个手机分期订单只生成一个首付收款账户,accountReference 绑定首付单号,首付单号绑定订单号。
  • 如果客户退出页面后再进入,bns 查询已有首付单和虚拟账户并原样返回,不重复开户。
  • 如果订单取消、拒绝、超时或签约失效,首付单关闭;关闭后到账进入异常挂账池,不能自动推进订单。
  • 创建虚拟账户时需要提供客户 BVN 或 NIN。若开户资料暂缺,应在 SA 进件/KYC 阶段补齐后再生成收款账户。
  • 可通过 getAllAvailableBanks=true 生成多个合作银行虚拟账户;MVP 默认先展示 Monnify 默认银行账户,除非业务明确要求多银行选项。

推荐字段:

accountReference = DP-{downpayment_order_id}
accountName = PocketBuy-{customer_name 或脱敏姓名}
customerEmail = downpayment_order_id 维度的系统邮箱或客户邮箱
customerName = 客户姓名
customerBvn / customerNin = KYC 采集结果
currencyCode = NGN
contractCode = Monnify 合同号

不采用客户级虚拟账户复用,原因是客户级复用会把匹配逻辑变成“客户 + 金额 + 时间窗口”。同一客户多笔订单、少付、多付或重复支付时容易误匹配,不适合首付作为放款硬前置的场景。

上线前必须确认 Monnify 是否允许同一自然客户基于不同 accountReference 创建多笔订单级 reserved account。如果 Monnify 合同限制同一客户只能有一个 active reserved account,优先切换为 Monnify invoice reserved account / dynamic invoice 的订单级收款能力;若该能力也不可用,则产品上限制同一客户同一时间只能存在一笔待支付首付单,并将该限制作为 MVP 约束。

4.2 到账处理

场景处理
满足放行bns 根据 pmt 到账通知确认实收金额大于等于应收首付,推进 Signed -> Paid
少付首付单为 PARTIAL_PAID,不允许放款,提示补足
多付MVP 按满足首付处理,记录超额金额,后续对账或人工退款
重复支付多余金额进入异常挂账,不自动抵扣借据
冲正首付状态回退或异常;若已放款需人工风险处理
账户创建失败保持 Signed,允许重试

SA 端的“确认收款”只能触发后端查询或校验,不能由 SA 主观确认直接推进状态。

Monnify webhook 处理要求:

  • 支付域提供 Monnify webhook URL,并在 Monnify 后台配置 Transaction Completion、Disbursement 等通知地址。
  • 支付域必须校验 webhook 签名,按 Monnify 要求使用 monnify-signature 验签。
  • webhook 必须幂等处理,按 transactionReferencepaymentReferenceaccountReference 去重。
  • 首付到账处理前必须校验订单、金额、币种、支付状态。
  • webhook 失败或延迟时,支付域支持按虚拟账户 accountReference 拉取交易列表,或按交易引用查询交易详情做补偿。

4.3 首付款流程编排

首付款流程从合同签署完成后开始,到订单进入 Paid 结束。该流程只处理客户首付 D,不创建借据本金,也不触发商户放款;商户放款在 Paid 后由 bns 通知 fcs 发起。

步骤触发方处理方关键动作状态/数据结果
1. 签约完成PocketBuy SA / 客户bns校验订单处于 Approved,记录合同签署结果订单进入 Signed
2. 创建首付单bnsbns按审批方案写入首付应收金额 D、币种、订单号、客户号、方案版本bns 生成首付业务记录,状态为 INIT
3. 申请收款账户bnspmt携带订单号、首付单号、客户信息、应收金额 D 申请订单级虚拟账户pmt 返回银行名、账号、户名、accountReference
4. 返回 SA 页面bnsPocketBuy SA聚合首付金额和虚拟账户信息SA 页面展示转账指引,首付单进入等待支付
5. 客户转账客户Monnify / Paystack客户向订单级虚拟账户转账通道产生收款流水
6. 到账通知Monnify / Paystackpmtpmt 校验 webhook、金额、币种、账户引用和重复流水pmt 登记通道流水
7. 同步首付结果pmtbns将到账金额、通道流水、到账时间、匹配结果通知 bnsbns 更新实收和首付状态
8. 推进订单bnsbnsbns 确认首付状态为 PAID 或可放行状态订单从 Signed 推进到 Paid

首付款流程中的数据归属如下:

数据归属用途
首付应收 Dbns判断是否满足首付放行条件、后续对账
首付实收bns驱动订单是否可进入 Paid
虚拟账户pmt提供客户转账账户和通道匹配依据
通道流水pmt资金对账、重复支付识别、冲正处理
订单主状态bns控制是否可进入商户放款和交机
accountReferencepmt + bns绑定首付单和订单,避免客户级账户复用导致错配

首付放行由 bns 按 paid_amount >= expected_amount 判断。少付、重复支付、冲正不会直接推进订单;多付在 MVP 阶段允许推进,超额部分记录为对账差异或人工退款事项。只有 bns 确认首付满足放行条件后,才能将订单推进到 Paid 并通知 fcs 发起商户放款。

4.4 首付到账通知契约

内容
调用方向pmt -> bns
触发时点Monnify / Paystack webhook 验签通过
前置状态订单 Signed,首付单未关闭
幂等键provider_transaction_idpayment_event_id

首付单字段:

字段说明
downpayment_order_id首付单号
installment_order_id手机分期订单号
customer_id客户号
expected_amount应收首付
currencyNGN
pricing_version签约报价版本
status初始为 INIT

bns 调 pmt 申请订单级虚拟账户时,必须携带 downpayment_order_id,pmt 用 accountReference 绑定该首付单。

到账通知字段:

字段必填说明
payment_event_idpmt 内部事件 ID
provider_transaction_id通道交易号
providerMONNIFY / PAYSTACK
account_reference绑定首付单
downpayment_order_id首付单号
installment_order_id订单号
expected_amount应收金额
paid_amount实收金额
currency币种
paid_at到账时间
payer_name付款人名称
raw_payload_ref原始报文存储引用
signature_verifiedwebhook 验签结果

金额状态:

金额匹配首付单状态订单状态处理
paid_amount >= expected_amountPAIDSigned -> Paidbns 记实收并推进;如多付则记录超额金额
paid_amount < expected_amountPARTIAL_PAIDSigned提示补足,不放款
重复流水不变不变记录重复事件,不重复推进
冲正REVERSED视是否已放款人工处理若未放款则禁止推进

顺序要求:

  1. pmt 校验通道 webhook。
  2. pmt 登记通道流水和原始报文。
  3. pmt 通知 bns 记首付实收、通道流水和到账时间。
  4. bns 按首付单应收、累计实收、币种和订单状态判断是否足额。
  5. bns 只在首付状态满足放行条件后推进 Signed -> Paid
  6. bns 通知 fcs 发起商户放款。

5. 放款到商户

5.1 发起前置

前置条件说明
订单状态为 Paid首付实收金额大于等于应收首付
bns 首付状态为 PAID业务状态已确认
借据本金 P 已准备借据本金只等于商品售价减首付;注意异常情况用户多转的情况下,仍然以实际所需支付首付金额为准。
门店/商户账户有效渠道商品域提供账户号、银行代码、户名和快照
合同签署有效签约未超时、未撤销

5.2 编排流程

  1. bns 确认订单处于 Paid
  2. bns 查询渠道商品域,获取商户/门店收款账户快照。
  3. bns 通知 fcs 发起商户放款,并传入订单、商品金额 S、首付 D、借据本金 P、门店账户快照和首付单引用。
  4. fcs 准备借据和还款计划,借据本金为 P
  5. fcs 按商品金额 S 调用 pmt 发起商户打款。
  6. pmt 调支付通道执行 transfer。
  7. pmt 回流放款结果给 fcs,fcs 确认放款结果后通知 bns。
  8. bns 标记商户打款成功。
  9. PocketBuy SA 才允许进入交机确认和 IMEI 回填;最终完成交机还需通过 IMEI 反查机型一致性校验和客户还款 App 登录确认,详见 05-交机与IMEI回填.md

6. 异常处理

异常MVP 处理
商户账户缺银行代码阻断放款,渠道商品域补齐账户资料
账户名校验失败阻断放款,人工确认后更新账户快照
支付通道失败放款单 FAILED,订单保持 Paid,允许重试
通道结果未知放款单 UNKNOWN,查询或人工对账,不允许交机
放款成功后冲正进入人工风险处理,订单加异常标记
已收首付但客户取消暂不自动退款,进入人工处理

7. 验收口径

验收点通过标准
首付到账前订单不能进入 Paid,不能发起商户放款
首付到账后bns 有首付业务状态,pmt 有通道流水
商户放款fcs 按商品金额 S 调用 pmt 放款,并保存商户账户快照
放款失败不允许交机,状态可定位、可重试
放款成功SA 可进入交机流程并回填 IMEI;完成交机仍需通过机型一致性和还款 App 登录校验

8. 商户放款接口契约

8.1 商户放款发起

内容
调用方向bns -> fcs -> pmt
触发时点订单进入 Paid
前置bns 首付状态满足放行条件,门店账户有效,合同未失效
幂等键merchant_disbursement_order_id

放款前校验:

校验来源阻断条件
订单状态bnsPaid
首付状态bnsPAID,即实收金额未达到应收首付
报价有效fcs/pfs报价版本不匹配或过期
门店账户渠道商品域缺账户号、银行代码、户名、账户未核验
合同bns/fcs未签署、已撤销、已过期
重复放款pmt/fcs已存在成功放款单

请求字段:

字段必填说明
merchant_disbursement_order_id商户放款单号
installment_order_id手机分期订单
loan_order_idfcs 借据/放款单引用
downpayment_order_id首付单号
principal_amount本金 P
downpayment_amount首付 D
goods_amount商品金额 S
transfer_amount商品金额 S,等于 P + D
currencyNGN
store_account_snapshot门店收款账户快照
channel_snapshot门店、商户、SA 快照
pricing_snapshot_id签约报价快照
purposePHONE_INSTALLMENT_MERCHANT_SETTLEMENT

门店账户快照至少包含 store_idmerchant_idpayee_bank_accountpayee_bank_codepayee_account_namepayee_bvnaccount_verify_statusaccount_snapshot_versionsnapshot_at

8.2 商户放款结果回流

内容
调用方向pmt -> fcs -> bns
幂等键merchant_disbursement_order_id + provider_transfer_id + result_version
结果订单处理说明
PROCESSING保持 Paid等待最终结果
SUCCESS保持 Paid,允许进入交机流程商户已收款,交机接口可继续做 IMEI 机型一致性和还款 App 登录校验
FAILED保持 Paid允许重试或人工处理
UNKNOWN保持 Paid查询通道或人工对账,禁止交机
REVERSED异常标记成功后冲正,进入人工风险处理

回流字段至少包含 merchant_disbursement_order_idprovider_transfer_idresulttransfer_amountpaid_atfailure_codefailure_messageraw_payload_ref

成功回流后,fcs 确认放款结果并通知 bns,bns 记录“商户放款成功”标记。主订单仍为 Paid,直到 SA 完成交机才进入 Completed

8.3 Monnify 打款状态映射

Monnify 状态系统处理
SUCCESS / COMPLETED商户打款成功;pmt 通知 fcs 确认放款,fcs 通知 bns 记录商户放款成功
PENDING / AWAITING_PROCESSING / IN_PROGRESS保持处理中;定时查询补偿
PENDING_AUTHORIZATION说明 MFA 未关闭或需要 OTP;MVP 自动放款不可接受,进入配置异常
FAILED放款失败;订单保持 Paid,允许重试或人工处理
REVERSED款项冲正;若 fcs 未确认放款则阻断,若已确认需进入人工风险处理
EXPIRED重新发起新打款单,原单关闭

打款前必须确认 Monnify Disbursement 已开通、生产出口 IP 已白名单、MFA/OTP 已关闭或有可执行授权流程,并完成门店收款账户校验。

9. 前端交互要求

9.1 PocketBuy SA 首付页

现有页面:/pocketbuy_sa/application/pay?orderId=preview,页面标题为 Collect Down Payment。页面交互模型是客户向 PocketBuy 虚拟收款账户银行转账,SA 查看账号并引导客户完成转账。

MVP 要求:

  • 页面只展示 bns 返回的首付金额、Account Number、Bank、Account Name,不提供手工录入或切换收款账户。
  • 如果进入页面时还没有虚拟账户,前端展示加载态并等待 bns 完成开户。
  • 开户失败时展示重试,不展示空账号。
  • SA 点击 Confirm Payment Received 只触发服务端状态刷新或人工确认记录,不得绕过 webhook/交易查询直接推进 Paid
  • SA 端至少能看到“等待首付到账 / 首付已收,等待商户放款 / 可交机 / 放款失败待处理”。

10. 数据、安全与对账

10.1 首付收款记录

bns 建议新增或扩展首付支付记录,至少保存:

  • 首付单号、订单号、客户 ID。
  • Monnify accountReference、账号、银行、户名。
  • 应收金额、实收金额、币种。
  • Monnify transactionReferencepaymentReference
  • 支付状态、到账时间、通知时间。
  • 付款方账户信息。
  • webhook 原文摘要或原文落库位置。
  • 幂等键和处理次数。

10.2 商户打款记录

至少保存:

  • 放款单号、订单号、借据号。
  • 商户/门店 ID。
  • 首付金额 D、本金 P、商品金额/打款金额 S
  • 门店收款账户快照。
  • Monnify 交易号和最终状态。
  • 失败原因、重试次数、冲正标记。

10.3 安全与对账要求

  • webhook 必须验签。
  • 所有 Monnify 通知必须幂等。
  • 所有金额使用最小货币单位或 Decimal,禁止浮点。
  • 首付到账和商户打款必须有支付流水。
  • 支付域每日输出 Monnify 收款、打款、冲正对账文件或报表。
  • MVP 允许人工对账,但必须能定位到订单、首付单、虚拟账户、Monnify 交易号和商户打款单。

11. 上线前待确认

  1. Monnify 合同是否允许同一自然客户创建多个 active reserved account;若不允许,优先改用 invoice reserved account / dynamic invoice 的订单级收款能力,不退回客户级复用。
  2. Monnify reserved account 创建时,BVN/NIN 是否二选一即可满足本期交易限额。
  3. 是否启用 getAllAvailableBanks,还是只展示默认 Moniepoint 虚拟账户。
  4. Monnify Disbursement 是否已开通,生产出口 IP 是否已白名单。
  5. Monnify 打款 MFA/OTP 是否可关闭;若不可关闭,需要运营授权流程。
  6. pmt 现有 vitualAccountRepay 能否直接承载 Monnify reserved account;若不能,需要新增 Reserved Account 适配。
  7. paymentResult 走 HTTP 回调还是 MQ/zoo 链路。
  8. isAcceptOtherAccount 的现网取值语义,是否可直接用于商户打款。
  9. 门店账户名核验失败时,是阻断办单、阻断放款,还是允许运营人工放行。

12. 参考资料