租赁系统开发做到支付环节,支付系统设计就是绕不开的硬骨头,很多团队却默认"接个微信支付就完了"。但机器人租赁的真实客群里,超过六成是 B 端企业客户——他们走财务对公转账,要求开增值税专票,付款流程短则一天长则一周。我们的平台最后落成了"C 端微信通道 + B 端对公结算"的双通道设计,这篇文章讲清楚两条通道各自的实现和对账这套真正的难点。


一、为什么必须双通道:一笔对公订单的启示

平台刚上线时我们只接了微信支付,结果第一个月就遇到尴尬场景:某连锁酒店集团要租 6 台服务机器人,合同、押金条款都谈好了,到付款环节对方财务直接问:"我们对公账户转账,你们收款账号是多少?"当时客服只能临时提供创始团队的个人账户收款码——这个操作在财务合规上的风险今天想想都后怕。
在这里插入图片描述

那之后我们明确了支付架构的分工:

维度C 端通道(微信支付)B 端通道(对公结算)
客户群体个人、小微企业企业、政企单位
支付方式JSAPI / 小程序支付银行对公转账 + 线下认领
到账确认秒级回调,自动确认T+1 内人工/OCR 辅助认领
付款节奏一次性或分期常按合同节点分批付款
对账方式渠道对账单自动核销银行流水 + 内部台账人工核对

两条通道共同的原则是:支付单与订单分离。一笔订单可能对应多笔支付单(分期、押金与租金分开付、补差价),支付系统的职责是把"支付单收妥"这件事可靠地通知订单系统,而不是和订单耦合在一起。

二、C 端通道:微信支付与分期思路

C 端走标准的小程序下单流程,有两点值得说:

1. 支付单状态机要独立。 支付单有自己的状态(CREATED/PAYING/SUCCESS/CLOSED/REFUNDING),微信回调驱动支付单状态机,支付单状态迁移成功后才发布 PAY_SUCCESS 事件给订单系统。这样微信侧的任何异常(重复回调、迟到的回调、退款回调)都被隔离在支付域内。

下单链路的工程细节也要交代:小程序端调统一下单拿 prepay_id,二次签名后拉起收银台;服务端要处理商户证书与平台证书的定期轮换(微信平台证书有有效期,我们用证书下载接口 + 定时刷新任务处理,避免某天证书过期导致回调验签全挂);支付超时的支付单由定时任务主动调"关单"接口关闭,防止订单已取消但用户在收银台还能付款的"迟到支付"问题——迟到支付的兜底策略是自动原路退款并通知用户。这些细节琐碎,但每一项漏掉都是线上事故。

2. 分期付款是对接思路问题,不是支付技术问题。 微信支付本身没有"分期履约扣款"的标准产品(代扣类能力有准入门槛),我们的实现是合同层面分期、支付层面多次普通支付

合同约定:3 期付款(签约付 40%、交付签收付 40%、验收后付 20%)
系统实现:每期生成一张支付单 + 到期提醒 → 用户主动支付
逾期处理:第 N 期超期 7 天 → 推送催缴 + 触发订单状态机 OVERDUE 事件

对应地,订单状态机里我们预留了 PARTIAL_PAID 状态(对接 10 号文章讲的迁移表,PAY_PARTIAL 事件),已付比例由订单下的支付单流水汇总实时计算。

三、B 端通道:对公转账与认领流程

对公结算的难点不在转账本身,在于认领:客户把钱打到对公账户后,银行流水上只有对方户名、金额、附言,系统怎么知道这属于哪张订单?

认领之前要先有应收台账。我们把每笔订单的应付拆解成结构化的应收单,台账模型大致是:

CREATE TABLE t_receivable (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  order_id BIGINT NOT NULL,
  period_no INT NOT NULL COMMENT '期次,一次性付款为1',
  amount DECIMAL(12,2) NOT NULL,
  received_amount DECIMAL(12,2) DEFAULT 0 COMMENT '已认领金额',
  due_date DATE COMMENT '约定付款日',
  status VARCHAR(20) NOT NULL COMMENT 'PENDING/PARTIAL/SETTLED/CLOSED',
  UNIQUE KEY uk_order_period (order_id, period_no)
);

应收单是 B 端结算的"账本脊柱":认领围绕它发生,对账围绕它核对,催收看它的账龄,开票看它的结算进度。有了这张表,"客户到底还差我们多少钱"不再是一个需要翻聊天记录的问题。

我们的认领流程设计:

生成应收单(订单 + 期次 + 金额 + 收款账户)
→ 财务上传/导入银行流水
→ 系统自动匹配(金额 + 户名 + 附言订单号)
→ 匹配成功:自动认领;匹配失败:进入人工认领池

自动匹配的三级规则:

public MatchResult match(BankFlow flow, List<Receivable> candidates) {
    // 一级:附言里有完整应收单号,且金额一致 → 高置信
    var hit = candidates.stream().filter(r ->
        flow.getRemark().contains(r.getReceivableNo())
        && r.getAmount().compareTo(flow.getAmount()) == 0)
        .findFirst();
    if (hit.isPresent()) return MatchResult.auto(hit.get());

    // 二级:金额 + 户名与客户企业实名一致 → 中置信,自动认领但标记复核
    hit = candidates.stream().filter(r ->
        r.getAmount().compareTo(flow.getAmount()) == 0
        && flow.getPayerName().contains(r.getCustomerName()))
        .findFirst();
    if (hit.isPresent()) return MatchResult.autoWithReview(hit.get());

    // 三级:金额部分到账 → 挂起,关联 PARTIAL_PAID
    var partial = candidates.stream().filter(r ->
        r.getCustomerName().contains(flow.getPayerName()))
        .findFirst();
    if (partial.isPresent()) return MatchResult.partial(partial.get(), flow);

    return MatchResult.manual(); // 进人工认领池
}

回单 OCR 是人工认领的加速器。 客户会把电子回单发给销售,销售上传回单图片,OCR 抽取"付款户名、金额、付款时间、附言"四要素,预填进认领表单。上线 OCR 后人工认领的单均处理时长从 6 分钟降到 2 分钟以内,错误率也明显下降。另外附言规范要写进合同和付款指引:“请务必在转账附言中注明应收单号 R2026xxxx”——一句话规范能消灭一半人工认领量。

四、对账系统:渠道对账单、自动核销与差异处理

对账是支付系统的"最后一道防线",我们按天跑三层对账:

第一层:渠道对账。 每天拉取微信支付的交易账单,与本地支付单流水逐笔核销:

public ReconReport reconcileChannel(LocalDate date) {
    List<ChannelBill> bills = wechatPayApi.downloadBill(date);   // 渠道账单
    List<PayFlow> flows = payFlowMapper.listByDate(date);        // 本地流水
    // 以渠道账单为准逐笔匹配本地流水
    for (ChannelBill bill : bills) {
        PayFlow flow = flowIndex.get(bill.getTransactionId());
        if (flow == null)          report.addLong(bill);        // 长款:渠道有本地无
        else if (!flow.getAmount().equals(bill.getAmount()))
                                   report.addAmountDiff(bill, flow); // 金额不一致
        else                       report.addMatched(bill);
        flowIndex.remove(bill.getTransactionId());
    }
    flowIndex.values().forEach(report::addShort);             // 短款:本地有渠道无
    return report;
}

第二层:银行流水对账。 对公账户日终流水与已认领记录核对,重点盯"银行有入账但未认领"的挂账。

第三层:业务对账。 支付单与订单状态的最终一致性:已支付订单是否都有成功支付流水、退款单与原支付单金额是否守恒(退款总额 ≤ 支付总额)。

差异处理的原则是**“先挂起、再排查、忌自动冲正”**。长款可能是掉单(回调丢了),短款可能是重复退款,自动冲正一旦方向判断错误就是资金事故。我们的差异单分四类处置:掉单补确认、重复支付原路退回、金额差异人工核查、疑似异常转风控。每类差异都有 SLA 和告警群机器人推送。

对账系统落地后还有个隐性收益值得分享:它成了支付渠道配置管理的安全网。支付通道的费率调整、商户号切换、接口版本升级,任何配置变更引发的系统性差错(比如某天所有回调签名验不过、某类订单金额记错),都会第一时间以"对账差异激增"的形式暴露,而不是等用户投诉或财务月结时才发现。我们把"渠道日差异率 > 0.5% 自动熔断告警"设为红线,这条规则拦过一次商户号迁移时漏配回调地址的事故——那次如果没有对账兜底,一天的支付回调会全部静默丢失。

五、开票管理与支付联动

B 端客户开票需求是硬性的,我们把开票做成了与支付状态联动的独立模块:

  • 开票申请基于实付金额而非订单金额(分期、部分退款都会改变可开票额),可开票额 = Σ 已支付 - Σ 已开票 - Σ 已退款;
  • 开票申请自动校验支付单是否全部为最终态,避免"付一半就开全票";
  • 红冲与退款联动:退款完成事件自动生成红字通知单待办,提醒财务冲销对应发票;
  • 抬头信息做企业档案化管理,与电子签约的企业实名档案共用数据源,杜绝抬头错别字导致的退票重开。

六、最佳实践清单

  1. 支付单与订单分离,支付域自闭环,事件驱动通知订单域;
  2. B 端对公结算的核心是认领流程:附言规范前置,三级自动匹配兜底,OCR 提效;
  3. 分期做成"合同分期 + 多笔独立支付单",不要对抗支付渠道的能力边界;
  4. 对账三层递进(渠道/银行/业务),差异单先挂起再处置,禁止自动冲正;
  5. 可开票额 = 实付 - 已开 - 已退,红冲与退款事件联动;
  6. 把"附言规范"写进合同模板和付款指引,流程规范比技术手段更省成本。
Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐