统一支付入口集成六种支付方式

Java 开发统一支付入口:集成支付宝、微信、自研支付的实战设计与实现

作者简介:三年 Java 开发经验,近期主导公司自研平台支付系统的统一接入工作。本文总结实战经验,分享架构设计思路,并欢迎同行指正交流。

📌 声明:文中涉及的第三方支付 API 均基于其 v3 版本文档。


一、需求背景

我们正在为公司自研平台(以下简称"平台")构建统一支付能力。平台覆盖多端场景,具体需求如下:

  • PC 端:支持扫码支付(展示二维码,用户扫码完成付款)
  • H5 端:支持调起支付宝/微信支付,并支持会员卡支付
  • 微信小程序端:支持一键唤起微信支付,同时支持会员卡支付

💡 会员卡支付为平台自研支付方式,基于用户储值账户体系实现。本文重点聚焦于第三方支付的统一接入设计,故该部分仅作简要提及。


二、可行性分析:支持的支付方式

根据业务需求,需集成以下主流支付方式:

支付渠道 支付类型 官方文档
支付宝 H5 支付 支付宝 H5 支付文档
支付宝 订单码支付(扫码) 支付宝订单码支付文档
微信支付 H5 支付 微信 H5 支付文档
微信支付 二维码支付(扫码) 微信扫码支付文档
微信支付 小程序支付 微信小程序支付文档
自研 会员卡支付 ------

三、架构设计与实现

为避免大量 if-elseswitch-case 判断带来的代码膨胀与维护困难,我们采用 工厂模式 + Spring 容器管理 的策略,实现支付方式的动态分发与解耦。

1. 统一支付入口:Controller 层

定义统一的 /pay 接口作为所有支付请求的入口,屏蔽客户端差异。

java 复制代码
@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 CommonResult<PaymentVO> 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. 支付服务层:PayService

PayService 是支付流程的调度中心,负责执行公共逻辑,如:

  • 查询支付方式配置
  • 支付金额校验
  • 订单状态检查(是否已支付、是否可支付)
  • 调用对应支付工厂执行支付
java 复制代码
@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_CARD)
service_name 对应 Spring Bean 名称(如:aliH5PayService、wxNativePayService)
config_json 支付参数(JSON,如 appId、商户号、密钥等)

✅ 这种设计使得新增支付方式只需:

  1. 新增配置记录
  2. 实现 CorePayService 接口
  3. 注册为 Spring Bean

无需修改任何已有代码,完美符合 开闭原则


4. 支付工厂:CorePayService 接口与实现

接口定义
java 复制代码
public interface CorePayService {
    /**
     * 执行支付
     * @param payParam 支付参数
     * @return 支付结果(如:二维码链接、跳转 URL 等)
     */
    PaymentVO pay(PayVO payParam) throws Exception;
}
实现示例:支付宝扫码支付(订单码)
java 复制代码
@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();
    }

🌟 同理可实现 AliH5PayServiceWxAppletPayService 等,各自独立,互不影响。


5. 组件图:统一支付系统架构

6. 序列图:用户发起支付流程

四、总结与思考

通过本次统一支付系统的构建,我们实现了以下目标:

✅ 成果亮点

  1. 高内聚低耦合:各支付方式独立实现,职责清晰。
  2. 易扩展:新增支付渠道只需新增实现类 + 配置,核心逻辑无侵入。
  3. 可维护性强 :避免 if-else 地狱,代码结构清晰。
  4. 安全可靠:通过分布式锁防止重复支付,金额校验防篡改。
  5. 符合开闭原则:对扩展开放,对修改关闭。

🚀 未来优化方向

  • 支付配置动态化 :将 PayTypeDO 配置接入 Nacos 或 Apollo,支持热更新。
  • 异步通知统一处理 :设计统一的 NotifyHandler 工厂,处理微信/支付宝异步回调。
  • 支付结果轮询机制:对于扫码支付,前端可轮询支付状态。
  • 日志与监控:接入链路追踪(如 SkyWalking),便于排查问题。
  • 幂等性保障 :在 PaymentVO 中引入 requestId,防止重复下单。

五、结语

支付系统是业务的核心环节,安全、稳定、可扩展 是基本要求。本文通过 工厂模式 + Spring IOC 的组合拳,实现了多支付方式的优雅接入。这不仅是一次技术实践,更是对设计模式、系统架构的深入理解。

支付无小事,细节定成败。愿我们都能在构建高质量系统的过程中不断精进,写出更优雅、更可靠的代码。


📌 欢迎留言交流:如果你也在做支付系统,欢迎分享你的架构设计与踩坑经验!

相关推荐
无双_Joney2 小时前
[更新迭代 - 1] Nestjs 在24年底更新了啥?(功能篇)
前端·后端·nestjs
泉城老铁2 小时前
idea 优化卡顿
前端·后端·敏捷开发
福大大架构师每日一题2 小时前
RustDesk 1.4.2 版本发布:新增增量文件传输与光标显示功能
后端
LH_R2 小时前
OneTerm开源堡垒机实战(四):访问授权与安全管控
运维·后端·安全
poemyang2 小时前
技术圈的“绯闻女孩”:Gossip是如何把八卦秘密传遍全网的?
后端·面试·架构
BingoGo2 小时前
PHP 如何利用 Opcache 来实现保护源码
后端·php
拳打南山敬老院2 小时前
漫谈 MCP 构建之Resources篇
前端·后端·ai编程
我不是混子2 小时前
Java中关于Integer的使用
后端
Raven100862 小时前
DataWhale共学-向量数据库task01
后端