一、需求背景我们正在为公司自研平台以下简称“平台”构建统一支付能力。平台覆盖多端场景具体需求如下PC 端支持扫码支付展示二维码用户扫码完成付款H5 端支持调起支付宝/微信支付并支持会员卡支付微信小程序端支持一键唤起微信支付同时支持会员卡支付 会员卡支付为平台自研支付方式基于用户储值账户体系实现。本文重点聚焦于第三方支付的统一接入设计故该部分仅作简要提及。二、可行性分析支持的支付方式根据业务需求需集成以下主流支付方式支付渠道支付类型官方文档支付宝H5 支付支付宝 H5 支付文档支付宝订单码支付扫码支付宝订单码支付文档微信支付H5 支付微信 H5 支付文档微信支付二维码支付扫码微信扫码支付文档微信支付小程序支付微信小程序支付文档自研会员卡支付——三、架构设计与实现为避免大量 if-else 或 switch-case 判断带来的代码膨胀与维护困难我们采用 工厂模式 Spring 容器管理 的策略实现支付方式的动态分发与解耦。1. 统一支付入口Controller 层定义统一的 /pay 接口作为所有支付请求的入口屏蔽客户端差异。RestController RequestMapping(/api/pay) Tag(name 支付中心, description 统一支付接口) public class PayController { private final PayService payService; private final RedissonPropertiesConfig redissonPropertiesConfig; private final Redisson redisson; Autowired public PayController(RedissonPropertiesConfig redissonPropertiesConfig, Redisson redisson, PayService payService) { this.redissonPropertiesConfig redissonPropertiesConfig; this.redisson redisson; this.payService payService; } PostMapping(/pay) Operation(summary 统一支付入口) PermitAll public CommonResultPaymentVO corePay(RequestBody Valid PayVO payParam) throws Exception { log.info(【订单 {}】发起支付请求参数{}, payParam.getOrderId(), JSON.toJSONString(payParam)); String lockKey RedisConstant.PAYMENT_LOCK.concat(payParam.getOrderId().toString()); RLock lock redisson.getLock(lockKey); PaymentVO r null; try { lock.lock(redissonPropertiesConfig.getAutomaticReleaseTimeValue(), TimeUnit.SECONDS); // 执行支付 r payService.corePay(payParam); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { if (lock.isHeldByCurrentThread()) { lock.unlock(); } } return CommonResult.success(result); } } 说明使用 Redisson 分布式锁防止订单重复支付锁的持有时间与等待时间可配置提升系统健壮性。2. 支付服务层PayServicePayService 是支付流程的调度中心负责执行公共逻辑如查询支付方式配置支付金额校验订单状态检查是否已支付、是否可支付调用对应支付工厂执行支付Service public class PayServiceImpl implements PayService { Resource private ApplicationContext applicationContext; Override Transactional(rollbackFor Exception.class) public PaymentVO corePay(PayVO payParam) throws Exception { // todo 1. 查询支付方式配置 PayTypeDO payType ... // todo 2. 金额预检 validateAmount(payParam.getAmount()); // todo 3. 检查订单状态是否已支付、是否存在 // 4. 动态获取支付实现类 CorePayService corePayService applicationContext.getBean(payType.getServiceName(), CorePayService.class); // 5. 执行支付 return corePayService.pay(payParam); } } 关键设计通过 PayTypeDO 表中的 serviceName 字段动态映射到 Spring 容器中的 Bean实现运行时动态调用。3. 支付方式配置表PayTypeDO字段描述id主键pay_code支付方式编码如ALI_H5、WX_NATIVE、MEMBER_CARDservice_name对应 Spring Bean 名称如aliH5PayService、wxNativePayServiceconfig_json支付参数JSON如 appId、商户号、密钥等✅ 这种设计使得新增支付方式只需新增配置记录 实现 CorePayService 接口 注册为 Spring Bean无需修改任何已有代码完美符合 开闭原则。4. 支付工厂CorePayService 接口与实现接口定义public interface CorePayService { /** * 执行支付 * param payParam 支付参数 * return 支付结果如二维码链接、跳转 URL 等 */ PaymentVO pay(PayVO payParam) throws Exception; }体验AI代码助手代码解读复制代码public interface CorePayService { /** * 执行支付 * param payParam 支付参数 * return 支付结果如二维码链接、跳转 URL 等 */ PaymentVO pay(PayVO payParam) throws Exception; }实现示例支付宝扫码支付订单码Service(AliPayQRCodeServiceImpl) Slf4j public class AliPayQRCodeServiceImpl implements CorePayService { Resource private AlipayConfig alipayConfig; Resource private AliPayPropertiesConfig aliPayPropertiesConfig; Resource private PayLogMapper logMapper; Resource private OrderRefundApi orderRefundApi; Resource private PayLogMapper payLogMapper; Resource private OrderPayLogApi orderPayLogApi; Resource private StringRedisTemplate stringRedisTemplate; Override public PaymentVO pay(PayVO payParam) throws Exception { // 支付日志 log.info(【{}】订单支付场景信息 {}, payLog.getOrderId(), JSONUtil.toJsonStr(build)); // 支付场景信息保存到redis中避免传入的数据字节数太大导致接口调用失败 stringRedisTemplate.opsForValue().set(PAYMENT_INFO.concat(payLog.getPayPaylog()), JSONUtil.toJsonStr(build), 12, TimeUnit.HOURS); // 初始化SDK AlipayClient alipayClient new DefaultAlipayClient(alipayConfig); // 构造请求参数以调用接口 AlipayTradePrecreateRequest request new AlipayTradePrecreateRequest(); AlipayTradePrecreateModel model new AlipayTradePrecreateModel(); // 设置商户订单号 model.setOutTradeNo(...); // 设置订单总金额(小数点后两位) model.setTotalAmount(...); // 设置订单标题 model.setSubject(...); // 设置产品码 model.setProductCode(QR_CODE_OFFLINE); // 设置订单附加信息 model.setBody(JSON.toJSONString(payParam.getScenarios())); log.info(订单【{}】申请支付宝QR付款(v3)参数 {}, payParam.getOrderId(), JSONUtil.toJsonStr(model)); request.setBizModel(model); request.setNotifyUrl(...); AlipayTradePrecreateResponse response alipayClient.execute(request); JSONObject entries JSONUtil.parseObj(response.getBody()); if (!response.isSuccess()) { log.error(订单【{}】申请支付宝QR付款(v3)失败响应 {}, payParam.getOrderId(), entries); throw exception(PayErrorCode.ZFB_QR_PAY_ERR); } log.info(订单【{}】申请支付宝QR付款(v3)成功响应 {}, payParam.getOrderId(), entries); JSONObject resp entries.getJSONObject(alipay_trade_precreate_response); String qrCode resp.getStr(qr_code); return PaymentVO.builder() .payType(Integer.valueOf(payParam.getPayTypeNo())) .inTradeNo(payParam.getPayLog()) .param(qrCode) .build(); } 同理可实现 AliH5PayService、WxAppletPayService 等各自独立互不影响。5. 组件图统一支付系统架构6. 序列图用户发起支付流程四、总结与思考通过本次统一支付系统的构建我们实现了以下目标✅ 成果亮点高内聚低耦合各支付方式独立实现职责清晰。易扩展新增支付渠道只需新增实现类 配置核心逻辑无侵入。可维护性强避免 if-else 地狱代码结构清晰。安全可靠通过分布式锁防止重复支付金额校验防篡改。符合开闭原则对扩展开放对修改关闭。五、结语支付系统是业务的核心环节安全、稳定、可扩展是基本要求。本文通过工厂模式 Spring IOC的组合拳实现了多支付方式的优雅接入。这不仅是一次技术实践更是对设计模式、系统架构的深入理解。支付无小事细节定成败。愿我们都能在构建高质量系统的过程中不断精进写出更优雅、更可靠的代码。欢迎留言交流如果你也在做支付系统欢迎分享你的架构设计与踩坑经验原文链接https://juejin.cn/post/7550956468586053641
统一支付入口集成六种支付方式
一、需求背景我们正在为公司自研平台以下简称“平台”构建统一支付能力。平台覆盖多端场景具体需求如下PC 端支持扫码支付展示二维码用户扫码完成付款H5 端支持调起支付宝/微信支付并支持会员卡支付微信小程序端支持一键唤起微信支付同时支持会员卡支付 会员卡支付为平台自研支付方式基于用户储值账户体系实现。本文重点聚焦于第三方支付的统一接入设计故该部分仅作简要提及。二、可行性分析支持的支付方式根据业务需求需集成以下主流支付方式支付渠道支付类型官方文档支付宝H5 支付支付宝 H5 支付文档支付宝订单码支付扫码支付宝订单码支付文档微信支付H5 支付微信 H5 支付文档微信支付二维码支付扫码微信扫码支付文档微信支付小程序支付微信小程序支付文档自研会员卡支付——三、架构设计与实现为避免大量 if-else 或 switch-case 判断带来的代码膨胀与维护困难我们采用 工厂模式 Spring 容器管理 的策略实现支付方式的动态分发与解耦。1. 统一支付入口Controller 层定义统一的 /pay 接口作为所有支付请求的入口屏蔽客户端差异。RestController RequestMapping(/api/pay) Tag(name 支付中心, description 统一支付接口) public class PayController { private final PayService payService; private final RedissonPropertiesConfig redissonPropertiesConfig; private final Redisson redisson; Autowired public PayController(RedissonPropertiesConfig redissonPropertiesConfig, Redisson redisson, PayService payService) { this.redissonPropertiesConfig redissonPropertiesConfig; this.redisson redisson; this.payService payService; } PostMapping(/pay) Operation(summary 统一支付入口) PermitAll public CommonResultPaymentVO corePay(RequestBody Valid PayVO payParam) throws Exception { log.info(【订单 {}】发起支付请求参数{}, payParam.getOrderId(), JSON.toJSONString(payParam)); String lockKey RedisConstant.PAYMENT_LOCK.concat(payParam.getOrderId().toString()); RLock lock redisson.getLock(lockKey); PaymentVO r null; try { lock.lock(redissonPropertiesConfig.getAutomaticReleaseTimeValue(), TimeUnit.SECONDS); // 执行支付 r payService.corePay(payParam); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { if (lock.isHeldByCurrentThread()) { lock.unlock(); } } return CommonResult.success(result); } } 说明使用 Redisson 分布式锁防止订单重复支付锁的持有时间与等待时间可配置提升系统健壮性。2. 支付服务层PayServicePayService 是支付流程的调度中心负责执行公共逻辑如查询支付方式配置支付金额校验订单状态检查是否已支付、是否可支付调用对应支付工厂执行支付Service public class PayServiceImpl implements PayService { Resource private ApplicationContext applicationContext; Override Transactional(rollbackFor Exception.class) public PaymentVO corePay(PayVO payParam) throws Exception { // todo 1. 查询支付方式配置 PayTypeDO payType ... // todo 2. 金额预检 validateAmount(payParam.getAmount()); // todo 3. 检查订单状态是否已支付、是否存在 // 4. 动态获取支付实现类 CorePayService corePayService applicationContext.getBean(payType.getServiceName(), CorePayService.class); // 5. 执行支付 return corePayService.pay(payParam); } } 关键设计通过 PayTypeDO 表中的 serviceName 字段动态映射到 Spring 容器中的 Bean实现运行时动态调用。3. 支付方式配置表PayTypeDO字段描述id主键pay_code支付方式编码如ALI_H5、WX_NATIVE、MEMBER_CARDservice_name对应 Spring Bean 名称如aliH5PayService、wxNativePayServiceconfig_json支付参数JSON如 appId、商户号、密钥等✅ 这种设计使得新增支付方式只需新增配置记录 实现 CorePayService 接口 注册为 Spring Bean无需修改任何已有代码完美符合 开闭原则。4. 支付工厂CorePayService 接口与实现接口定义public interface CorePayService { /** * 执行支付 * param payParam 支付参数 * return 支付结果如二维码链接、跳转 URL 等 */ PaymentVO pay(PayVO payParam) throws Exception; }体验AI代码助手代码解读复制代码public interface CorePayService { /** * 执行支付 * param payParam 支付参数 * return 支付结果如二维码链接、跳转 URL 等 */ PaymentVO pay(PayVO payParam) throws Exception; }实现示例支付宝扫码支付订单码Service(AliPayQRCodeServiceImpl) Slf4j public class AliPayQRCodeServiceImpl implements CorePayService { Resource private AlipayConfig alipayConfig; Resource private AliPayPropertiesConfig aliPayPropertiesConfig; Resource private PayLogMapper logMapper; Resource private OrderRefundApi orderRefundApi; Resource private PayLogMapper payLogMapper; Resource private OrderPayLogApi orderPayLogApi; Resource private StringRedisTemplate stringRedisTemplate; Override public PaymentVO pay(PayVO payParam) throws Exception { // 支付日志 log.info(【{}】订单支付场景信息 {}, payLog.getOrderId(), JSONUtil.toJsonStr(build)); // 支付场景信息保存到redis中避免传入的数据字节数太大导致接口调用失败 stringRedisTemplate.opsForValue().set(PAYMENT_INFO.concat(payLog.getPayPaylog()), JSONUtil.toJsonStr(build), 12, TimeUnit.HOURS); // 初始化SDK AlipayClient alipayClient new DefaultAlipayClient(alipayConfig); // 构造请求参数以调用接口 AlipayTradePrecreateRequest request new AlipayTradePrecreateRequest(); AlipayTradePrecreateModel model new AlipayTradePrecreateModel(); // 设置商户订单号 model.setOutTradeNo(...); // 设置订单总金额(小数点后两位) model.setTotalAmount(...); // 设置订单标题 model.setSubject(...); // 设置产品码 model.setProductCode(QR_CODE_OFFLINE); // 设置订单附加信息 model.setBody(JSON.toJSONString(payParam.getScenarios())); log.info(订单【{}】申请支付宝QR付款(v3)参数 {}, payParam.getOrderId(), JSONUtil.toJsonStr(model)); request.setBizModel(model); request.setNotifyUrl(...); AlipayTradePrecreateResponse response alipayClient.execute(request); JSONObject entries JSONUtil.parseObj(response.getBody()); if (!response.isSuccess()) { log.error(订单【{}】申请支付宝QR付款(v3)失败响应 {}, payParam.getOrderId(), entries); throw exception(PayErrorCode.ZFB_QR_PAY_ERR); } log.info(订单【{}】申请支付宝QR付款(v3)成功响应 {}, payParam.getOrderId(), entries); JSONObject resp entries.getJSONObject(alipay_trade_precreate_response); String qrCode resp.getStr(qr_code); return PaymentVO.builder() .payType(Integer.valueOf(payParam.getPayTypeNo())) .inTradeNo(payParam.getPayLog()) .param(qrCode) .build(); } 同理可实现 AliH5PayService、WxAppletPayService 等各自独立互不影响。5. 组件图统一支付系统架构6. 序列图用户发起支付流程四、总结与思考通过本次统一支付系统的构建我们实现了以下目标✅ 成果亮点高内聚低耦合各支付方式独立实现职责清晰。易扩展新增支付渠道只需新增实现类 配置核心逻辑无侵入。可维护性强避免 if-else 地狱代码结构清晰。安全可靠通过分布式锁防止重复支付金额校验防篡改。符合开闭原则对扩展开放对修改关闭。五、结语支付系统是业务的核心环节安全、稳定、可扩展是基本要求。本文通过工厂模式 Spring IOC的组合拳实现了多支付方式的优雅接入。这不仅是一次技术实践更是对设计模式、系统架构的深入理解。支付无小事细节定成败。愿我们都能在构建高质量系统的过程中不断精进写出更优雅、更可靠的代码。欢迎留言交流如果你也在做支付系统欢迎分享你的架构设计与踩坑经验原文链接https://juejin.cn/post/7550956468586053641