CRMEB多商户商城「余额+微信组合支付」开发记录

   2026-09-01 50
核心提示:本文记录 CRMEB 多商户商城新增「余额 + 微信」组合支付功能的实现方案:余额最多抵扣订单金额 n%(后台可配)、剩余由微信兜底,覆盖前端支付流程、微信回调链路与订单状态更新。

CRMEB 多商户商城「余额 + 微信组合支付」开发记录

功能概述:订单支付新增「余额 + 微信」组合支付——余额最多支付订单金额的 n%(比例后台可配置),余额不足或超出上限的部分由微信支付兜底。

一、需求说明

1.1 现状

现有支付弹窗支持 5 种单一渠道支付:消费积分(balance)、微信(weixin)、支付宝(alipay)、补贴券(xiaofeiquan)、线下(offline)。用户余额不足全额时,无法部分使用余额,只能全额走微信/支付宝,导致用户余额使用率低、大额订单全额外付。

1.2 目标与规则

规则说明
余额上限余额最多支付订单金额的 n%(n 为后台系统配置项,建议键名 pay_balance_ratio,范围 0–100)
兜底渠道剩余部分由微信支付兜底(支付宝不参与组合)
金额拆分balance_amount = min(用户可用余额, 订单金额 × n%);wechat_amount = 订单金额 − balance_amount

1.3 边界条件

场景处理
用户可用余额 = 0隐藏组合入口,走单一微信支付
用户可用余额 ≥ 订单金额不进入组合,走原有「余额全款」单一支付
余额足够 n% 但不足以支付剩余按 min(余额, 金额×n%) 拆分,剩余全走微信
微信支付失败 / 超时不扣余额,订单保持待支付,可重新发起
支付宝不参与组合,保持原逻辑(H5/小程序走 order_pay_back 中转)

二、总体方案

采用「微信只支付兜底金额」的单笔支付拆分方案(非全额微信 + 余额退款方案):

  1. 前端在支付弹窗展示「余额 + 微信」组合项,显示「余额抵扣 X 元 + 微信支付 Y 元」。

  2. 用户选组合支付并确认 → 前端携带 balance_amount / wechat_amount 调下单接口(金额建议由后端按 n% 规则计算返回,前端只展示)。

  3. 后端下单时仅对 wechat_amount 发起微信支付——微信侧看到的订单金额、回调金额都是 wechat_amount,订单总金额仍为原金额。

  4. 余额部分在微信回调确认支付成功后,与订单状态更新放同一事务扣减。

  5. 微信回调链路保持 CRMEB 原有结构不变:wechatNotify → WechatService::handleNotify → event('pay_success_{attach}') → 事件监听器 → 订单状态更新。监听器内兼容组合支付订单:识别已扣余额部分,仅登记微信流水 trade_no 并置 paid=1。

三、前端修改记录(uni-app)

3.1 支付方式列表 getPayTypeList()

在 payMode 数组中新增「组合支付」项:

{
  name: '余额+微信支付',
  icon: 'icon-a-ic_wechatpay',
  value: 'combine_weixin',
  title: '余额抵扣 {balance_amount} 元,微信支付 {wechat_amount} 元',
  payStatus: res.data.yue_pay_status && res.data.pay_weixin_open
}

展示条件:余额开关与微信开关同时开启、now_money > 0、now_money < pay_price。比例 n% 与拆分金额建议由后端计算并随接口返回,前端只做展示。

3.2 watch orderPayList / getOrderConfig()

现有链路保留:Vuex orderPayList 更新 → deep watch 触发 → payMode = nVal → getOrderConfig()。getOrderConfig() 拉取订单配置后,若为组合支付订单,把后端返回的 balance_amount / wechat_amount 存入 data 用于展示。

3.3 goPay() 组合支付分支

} else if (paytype == 'combine_weixin') {
  type = 'combine_weixin';
  // #ifdef H5
  type = this.$wechat.isWeixin() ? 'combine_weixin' : 'combine_weixin_h5';
  // #endif
}

前置校验:balance_amount > 0、wechat_amount > 0、余额足以支付本次抵扣部分。下单时携带 balance_amount / wechat_amount 参数,支付成功后跳转沿用原微信成功路径(/pages/order_pay_status/index?status=1)。

四、后端修改记录(ThinkPHP / CRMEB 多商户)

4.1 下单接口(orderPay / PayServices::pay)

  • 接收组合支付参数 balance_amount / wechat_amount(或后端按 n% 规则自行计算);

  • 校验:balance_amount <= min(now_money, pay_price × n%);wechat_amount = pay_price − balance_amount >= 0;

  • 微信支付下单金额 = wechat_amount(注意以「分」为单位),订单总金额仍为 pay_price;

  • 订单落库记录组合拆分信息(建议订单表新增字段 balance_amount、wechat_amount);

  • attach 沿用业务标识(如 order),回调事件名 pay_success_order 不变。

4.2 余额扣减

时机:微信回调确认支付成功后,与订单状态更新同一事务扣减,避免「微信未付成功、余额已扣」无法回滚。实现:调用余额变动服务(user_bill)扣减 balance_amount,扣减前校验余额充足。

4.3 微信回调链路(结构不变,仅监听器兼容)

event('pay_success_' . $notify['attach'], [
    'order_sn'   => $notify['out_trade_no'],
    'data'       => $notify,
    'is_combine' => 0,
]);

注意:组合支付订单的微信回调 total_fee 是兜底部分(wechat_amount),不是订单全款——监听器必须识别组合订单,不能用回调金额覆盖订单应付款,否则对账/金额校验会出错。

4.4 事件注册与监听器

'pay_success_order' => [\crmeb\listens\pay\OrderPaySuccessListen::class],

OrderPaySuccessListen::handle() 处理流程:① 用 order_sn(= out_trade_no)查订单;② 幂等:paid == 1 直接返回(防微信重复回调);③ 若是组合支付订单:校验并扣减余额 balance_amount → 更新订单 paid = 1、pay_time、trade_no = 微信流水(对应 wechat_amount);④ 触发后续业务:分佣、库存、短信/模板消息、订单状态记录。

五、组合支付时序

用户选择「余额 + 微信」组合支付
  → 前端展示抵扣金额(balance_amount / wechat_amount)
  → 点击确认 goPay() → 调下单接口
  → 后端校验 n% 规则 → 生成订单并记录拆分 → 仅对 wechat_amount 发起微信支付
  → 用户在微信完成支付(金额 = wechat_amount)
  → 微信异步回调 wechatNotify → handleNotify 验签
  → event('pay_success_order') → OrderPaySuccessListen
     ① 查订单 ② 幂等 ③ 扣减余额 ④ 更新订单已支付
  → 返回 SUCCESS → 前端跳转 order_pay_status?status=1

六、涉及文件清单

文件 / 模块改动
前端支付弹窗组件(order_pay)新增组合支付项、展示抵扣金额、watch 兼容
前端goPay()新增组合支付渠道分支与校验
前端api/order.js下单接口增加组合支付参数
后端PayServices / OrderPayServices支持组合支付下单、金额拆分与校验
后端订单模型 / 数据表新增 balance_amount / wechat_amount 字段
后端Notify 控制器 / WechatService基本不变(验签 + 协议分发)
后端app/event.phppay_success_order 注册(已有,确认存在)
后端OrderPaySuccessListen兼容组合订单(余额扣减 + 状态更新)
后端StoreOrderSuccessServices::paySuccess组合支付订单落库处理
后台系统配置新增 n% 比例配置项 pay_balance_ratio

七、已知问题与注意事项

  1. 前端 goPages URL 拼接 bug:goPages + 'status=1'(缺 &)、goPages + '?status=1'(误用 ?)需统一改为 goPages + '&status=' + (成功 ? 1 : 0);

  2. getPayList / getPayTypeList 命名歧义:getPayList() 调用的是 $util.getPayTypeList,组件内又定义了同名方法,需核对 util 实现是否内部转发;

  3. 小程序 routine 分支 complete 回调里 if (res.errMsg == 'requestPayment:cancel') 的 res 是外层下单接口响应(非支付回调),判断恒不成立,可删除;

  4. 回调金额口径:组合支付回调金额 = 微信部分,监听器与对账须区分;

  5. 余额扣减时机:推荐支付成功后同事务扣减,若下单即扣需处理失败回滚;

  6. n% 配置:后台新增系统配置 pay_balance_ratio,前端展示值以后端计算为准;

  7. 支付宝不参与组合,避免引入支付宝组合分支增加复杂度;

  8. 微信下单金额为「分」,后端拆分计算须注意单位换算,避免精度问题。

八、验收清单

  • 支付弹窗在「余额 > 0 且 余额 < 订单金额」时展示组合项,金额拆分显示正确;

  • 组合支付下单后微信侧支付金额 = wechat_amount(非全款);

  • 微信支付成功后订单置为已支付,余额扣减 balance_amount,流水记录 trade_no;

  • 微信重复回调不重复扣余额、不重复改状态(幂等);

  • 微信支付失败时余额未被扣减,订单仍可重新支付;

  • 余额占比超过 n% 时被限制(金额校验生效);

  • 后端配置 pay_balance_ratio 调整后无需改代码即时生效;

  • H5 / 小程序 / APP 三端支付成功后均正确跳转 order_pay_status;

  • 原单一渠道支付回归通过。

记录说明:本文基于对现有支付弹窗(uni-app)与微信回调链路(ThinkPHP / CRMEB 多商户)的代码梳理整理;「n%」为待配置项(建议键名 pay_balance_ratio),具体默认值由运营确认。

 
举报收藏 0评论 0
更多>相关评论
暂时没有评论,来说点什么吧
更多>同类资讯
  • admin
    加关注0
  • 没有留下签名~~
推荐图文
推荐资讯
点击排行
网站首页  |  关于我们  |  联系方式  |  用户协议  |  隐私政策  |  版权声明  |  排名推广  |  广告服务  |  积分换礼  |  网站留言  |  帮助中心  |  网站地图  |  违规举报