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

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

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

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


一、需求背景

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

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

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


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

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

<"mu.y5gd.cn"><"no.y5gd.cn"><"ax.y5gd.cn"><"pe.y5gd.cn">

<"oe.y5gd.cn"><"re.y5gd.cn"><"sa.y5gd.cn"><"to.y5gd.cn">

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

三、架构设计与实现

为避免大量 if-elseswitch-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 CommonResult 
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 是支付流程的调度中心,负责执行公共逻辑,如:

  • 查询支付方式配置
  • 支付金额校验
  • 订单状态检查(是否已支付、是否可支付)
  • 调用对应支付工厂执行支付

@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,实现运行时动态调用。

<"us.y5gd.cn"><"vo.y5gd.cn"><"wb.y5gd.cn"><"ye.y5gd.cn">

<"zo.y5gd.cn"><"nt.y5gd.cn"><"ba.y5gd.cn"><"co.y5gd.cn">


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 接口与实现

接口定义

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();
    }

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


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

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

四、总结与思考

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

✅ 成果亮点

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

五、结语

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

<"dt.y5gd.cn"><"ea.y5gd.cn"><"fo.y5gd.cn"><"ga.y5gd.cn">

<"hu.y5gd.cn"><"ot.y5gd.cn"><"pa.y5gd.cn"><"ug.y5gd.cn">

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


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

<"so.y5gd.cn"><"tu.y5gd.cn"><"wi.y5gd.cn"><"ct.y5gd.cn">

<"wa.y5gd.cn"><"ap.y5gd.cn"><"te.y5gd.cn"><"io.y5gd.cn">

<"as.y5gd.cn"><"po.y5gd.cn"><"bo.y5gd.cn">

请使用浏览器的分享功能分享到微信等