支付域 — 开发设计(开发就绪样板)

本文是支付域改造点的实现级设计,目标是开发照此即可动手:改什么、怎么改、内外依赖、配置、异常。规划级范围见 03-支付域
标注:✓ 代码实证(payment-middle 源码)/ ⚠ 待确认(需后端架构讲解人确认,不臆造)。

1. 现状关键结构(实证)

实现说明
入口契约IInnerPaymentService(Feign HTTP,/pmt/innerPaymentpaymentOrderPush(放款)、paymentResultrepayOrderPush/repayOrderBatchPush(还款)、repayResultrepayOrderForH5/repayTypeForH5commonRepayGatewayvitualAccountRepay
通道路由ThirdPaymentFactory.getThirdPayment(PaymentChannel)switch 路由;已启用 Paystack / PaystackWebpay / Monnify / MonnifyMandate / NibssEasypay
通道抽象IThirdPaymentpayment / transfer(放款) / repayment / batchRepayment / queryPayment / queryRepayment / queryTransfer / queryBalance
通道出口PaystackOuting / MonnifyOutgoing调三方 HTTP,URL + 密钥从 Apollo 注入
回调PaystackCallbackBusiness(transfer/charge)、MonnifyPayCallbackBusiness(disburse)、MonnifyRepayCallbackBusiness(collection)
数据PmtPaymentOrder / PmtPaymentFlow / PmtOrderOriginalMonnifyMandateRecord/Result;账务侧 fcs_order_txn(含 merchant_name)
异步对账/补偿 JobQueryTransferResult / QueryChargeResult / TransferComplement / PendingOverTimeSolve / ReverseNotify / RefCompensation / RetrieveMandateStatus
关键入参PaymentOrderPushReq:orderId / custId / amount / bankCode / accountNumber / bankAcctName(收款账户)/ isAcceptOtherAccount(放非本人账户)/ paymentChannel / txnType / loanIdPayDto:payer / beneficiary(收款方) / recipientCode / paymentMerchant(我方通道商户)

2. 改造点①:放款到客户 / 商户

目标

现网放款到客户;手机分期需放款到商户银行账户。

关键判断(✓ 实证)

PaymentOrderPushReq 已有收款账户字段(bankCode/accountNumber/bankAcctName)+ isAcceptOtherAccount(放到非本人账户);PayDto.beneficiary 为收款方。pmt 不区分客户/商户,只把钱转到给定收款账户——放款到商户对 pmt 基本透明。

怎么改(时序)

  1. 编排/账务经 IInnerPaymentService.paymentOrderPush 发放款指令,收款账户填商户账户 + isAcceptOtherAccount=Y + paymentChannel
  2. pmt 建 PmtPaymentOrder/FlowThirdPaymentFactory.getThirdPayment(channel).transfer(PayDto)
  3. PaystackOuting:账户名核验 → 创建 recipient(paystack.pay.recipient.url) → 放款(paystack.pay.url);Monnify 同理(monnify.pay.url)。
  4. 异步 transfer/disburse 回调 → 更新 PmtPaymentOrder 状态。
  5. pmt 通知账务放款结果 → 账务建据。⚠ 通知机制(paymentResult 接口回调 vs MQ zooPaymentResult/afterPaymentInput)待确认。

改动落点

  • pmt 侧:基本零改(收款方字段、isAcceptOtherAccount、beneficiary、recipient 创建均已支持)。
  • 账务/编排域(主要改动):发放款指令时填商户收款账户 + isAcceptOtherAccount + 选通道。商户账户来源见依赖。

内部依赖

  • 账务域:放款指令入参(PaymentOrderPushReq 契约)+ 建据回调(结果通知)。
  • ✓ 渠道商品域:门店表收款三件套 payee_bvn / payee_bank_account / payee_account_name(门店即 POS 点);SA 办单时按 store_id 取并随放款指令传入。见 07-渠道商品域

外部依赖

  • Paystack:transfer recipient API + transfer + callback;Monnify:disburse + callback。
  • Provider:商户号、callback/webhook 注册。

配置(Apollo,✓ 实证键名)

paystack_transfer_authorization_secret_key(Map,加 MVP 商户条目)、paystack.pay.recipient.urlpaystack.pay.urlpaystack.pay.query.urlmonnify_secrity_key_json / monnify_api_key(Map)、monnify.pay.urlmonnify.pay.query.urlmonnify.auth.login.url。URL 切生产。

异常 / 回滚(✓ 现网 Job)

失败/超时 → QueryTransferResult / TransferComplement / PendingOverTimeSolve;冲正 → ReverseNotify;代偿 → RefCompensation

待确认

  • isAcceptOtherAccount 现网语义与取值(是否即”放到非客户本人账户”,能否直接用于商户放款)。
  • paymentChannel 由谁决定(入参指定 / 路由配置 / 按产品)。
  • ⚠ 放款结果通知账务的机制(接口回调 vs MQ)。

3. 改造点②:手机分期多期代扣调度

目标

按每期还款日代扣(现网单期到期代扣)。

现状(✓ 实证)

IThirdPayment.repayment / batchRepayment 已实现;卡扣(Paystack recurring)+ Mandate(Monnify)已有;账务 Job NormalWithholding / OverdueWithholding 调度代扣;入口 repayOrderPush / repayOrderBatchPush

怎么改

  1. 账务(还款中心)按每期 due_date 生成代扣任务(主改动在账务域,多期还款计划/排程表层已支持)。
  2. IInnerPaymentService.repayOrderPush(单笔)/ repayOrderBatchPush(批量)发扣款。
  3. pmt repayment(PayDto) 走 Mandate / 卡扣(monnify.repay.url / paystack.repay.url)。
  4. collection/charge 回调 → repayResult 通知账务销账。

改动落点

  • 账务域:按期调度(多期)。pmt 侧复用 repayOrderPush,基本零改。

待确认

  • ⚠ 多期场景 repayOrderPush 入参是否需带期次(term)。
  • ⚠ 批量代扣 repayOrderBatchPush 的触发方与批次组织。

4. 改造点③:代扣授权 Mandate(手机分期还款)

现状(✓ 实证)

MonnifyMandatePayment + MonnifyMandateRecord/Result + 创建/debit/status(MonnifyCreateMandateReq / MonnifyDebitMandateReqDto / MonnifyMandateStatusReqDto);RetrieveMandateStatus Job 轮询;contractCode 配置。

怎么改 / 流程

签约或首次绑定阶段创建 mandate → 还款时 debit → status 轮询。

配置

contractCode(Monnify Mandate)、mandate 相关 URL(⚠ 具体键名待确认)。

待确认

  • ⚠ mandate 创建在哪个业务环节触发(签约 / 首次还款 / 绑卡)。
  • ⚠ NIBSS direct debit 是经 Monnify 还是直连。

5. 改造点④:通道接入(配置 + Provider 对接)

Apollo 配置项清单(✓ 实证 @Value / @ApolloJsonValue)

  • Paystackpc_paystack_authorization_secret_key(Map)、paystack_transfer_authorization_secret_key(Map)、paystack.pay.recipient.urlpaystack.pay.urlpaystack.pay.query.urlpaystack.repay.urlpaystack.partial.repay.urlpaystack.batch.repay.urlpaystack.repay.query.urlpaystack.balance.query.urlpaystack_dispute_*
  • Monnifymonnify_secrity_key_json(Map)、monnify_api_key(Map)、monnify.auth.login.urlmonnify.pay.urlmonnify.pay.query.urlmonnify.repay.urlmonnify.repay.query.urlmonnify.balance.query.urlmonnify.merchant.bindAccount.config.mappingcontractCode

Provider 对接(非代码)

开 Paystack/Monnify 商户号;注册 callback/webhook URL;Paystack transfer recipient;Monnify reserved account / NIBSS mandate contractCode。

待确认

  • ⚠ 通道路由配置(哪个产品/业务线走哪家)存哪:Apollo 还是 PayChannelAvailableInfo 表。
  • ⚠ merchant/channel 命名规则(现网 PC/NC;MVP 现金贷/手机分期如何映射到密钥 Map 的 key)。

6. zoo / MQ 链路(待确认重点)

账务 fcs 配置中发 plutus-pay-chargezooPaymentProcess,消费 zooPaymentResult;而 pmt 入口是 IInnerPaymentService(HTTP)。

  • ⚠ 放款/还款是走 IInnerPaymentService(HTTP) 还是 MQ topic(plutus-pay-charge / zooPaymentProcess)?
  • ⚠ “zoo” 的角色:是放款编排中间层(独立服务)还是同一服务的 MQ 侧?是否在本地代码内。

7. 待确认清单(汇总给后端架构讲解人)

  1. 放款结果通知账务的机制:接口回调 / MQ。
  2. isAcceptOtherAccount 语义与商户放款适用性。
  3. paymentChannel 选择来源(入参 / 配置 / 按产品)。
  4. 通道路由配置存放位置(Apollo / DB 表)。
  5. merchant/channel 命名与 MVP 业务线映射。
  6. 多期 repayOrderPush 是否需带期次;批量代扣触发方。
  7. Mandate 创建触发环节;NIBSS 直连 vs 经 Monnify。
  8. zoo / MQ 在放款还款链路中的角色与是否在本地代码。
  9. 商户银行账户来源 ✓ 已确认:渠道商品域门店表收款三件套(payee_bvn / payee_bank_account / payee_account_name);SA 办单时按 store_id 取并随放款指令传入。见 07-渠道商品域

8. 验收

  • 放款到客户(现金贷)、放款到商户(手机分期)经 transfer + 通道成功,回调更新状态、通知账务建据。
  • 多期借据按每期 due_date 自动代扣(Mandate / 卡扣)+ 主动还款,repayResult 触发销账。
  • 失败/超时/冲正经现网 Job 兜底,幂等不重复。