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

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 的组合拳,实现了多支付方式的优雅接入。这不仅是一次技术实践,更是对设计模式、系统架构的深入理解。

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


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

相关推荐
Pr Young4 小时前
服务优雅停止和服务优雅启动
后端
嘟嘟MD6 小时前
程序员副业 | 2025年9月复盘
后端·aigc
尘觉6 小时前
中秋节与 Spring Boot 的思考:一场开箱即用的团圆盛宴
java·spring boot·后端
间彧7 小时前
Seata分布式事务框架详解与项目实战
后端
zhuyasen7 小时前
单机已达上限?PerfTest 分布式压测登场,轻松模拟百万用户洪峰
后端·性能优化·测试
勇哥java实战分享7 小时前
sensitive-word:一个简单易用的敏感词过滤框架
后端
popoxf7 小时前
spring容器启动流程(反射视角)
java·后端·spring
Funcy8 小时前
XxlJob 源码08:任务执行流程(三)之执行器揭秘
后端
AAA修煤气灶刘哥8 小时前
监控摄像头?不,我们管这个叫优雅的埋点艺术!
java·后端·spring cloud
2301_772093568 小时前
tuchuang_后端_前端_注册登录
数据库·后端·网络协议·mysql·wireshark