一篇文章通关聚合支付设计模式详细设计

该文章已生成可运行项目,

聚合支付系统:基于设计模式的详细设计与代码示例

目标:实现一个可扩展、可维护、易于接入新支付渠道的聚合支付系统,使用多种经典设计模式(策略、工厂、模板方法、责任链、适配器等)。


##记得点赞加收藏哦😁😁😁

一、业务场景与核心需求

  1. 支持多种支付渠道:微信、支付宝、银联、某三方支付等。
  2. 支持多种支付方式:扫码支付、H5 支付、App 支付等。
  3. 上层业务只关注“聚合支付”的统一接口,不关心具体渠道细节。
  4. 后续新增渠道时尽量 不改老代码,只加新代码(开闭原则)。
  5. 支持扩展“风控、限流、风控、风控”等前置/后置处理逻辑。

抽象一句话:对外一个“统一收单接口”,对内多渠道策略 + 可插拔的前置/后置处理。


二、总体设计模式选型

场景使用的设计模式作用
根据渠道编码选择不同实现策略模式 Strategy运行时选择不同支付实现
创建具体渠道处理器工厂模式 + 简单工厂统一创建渠道实现,方便扩展
统一下单流程(校验、落库、请求渠道、更新状态……)模板方法 Template Method抽取通用流程,保留渠道差异点
风控、参数校验、限流等可插拔前置逻辑责任链 Chain of Responsibility可自由组合与扩展
封装第三方支付 SDK适配器 Adapter把第三方 SDK 适配到统一接口
支付结果回调通知业务观察者 / 发布订阅解耦业务与支付模块

核心思想:Controller -> 统一 Service -> 选择策略 -> 调用模板方法 -> 内部走责任链 & 适配器


三、核心领域模型设计

1. 核心枚举

public enum PayChannelEnum {
    WECHAT("WECHAT", "微信支付"),
    ALIPAY("ALIPAY", "支付宝"),
    UNIONPAY("UNIONPAY", "银联");

    private final String code;
    private final String desc;

    PayChannelEnum(String code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    public String getCode() { return code; }
    public String getDesc() { return desc; }

    public static PayChannelEnum of(String code) {
        for (PayChannelEnum value : values()) {
            if (value.code.equalsIgnoreCase(code)) {
                return value;
            }
        }
        throw new IllegalArgumentException("不支持的支付渠道: " + code);
    }
}

2. 统一下单请求 / 响应对象

@Data
public class UnifiedPayRequest {
    /** 商户订单号 */
    private String bizOrderNo;
    /** 支付金额(单位:分) */
    private Long amount;
    /** 支付渠道编码 */
    private String channelCode;
    /** 支付标题 */
    private String title;
    /** 支付成功回调地址 */
    private String notifyUrl;
    /** 前端跳转地址 */
    private String returnUrl;
    /** 额外扩展参数(不同渠道个性化参数) */
    private Map<String, String> extParams;
}
@Data
public class UnifiedPayResponse {
    /** 聚合支付平台订单号 */
    private String payOrderNo;
    /** 支付二维码链接 / H5 链接 / 跳转链接等 */
    private String payUrl;
    /** 渠道返回的原始内容 */
    private String rawBody;
}

四、策略模式:不同渠道不同实现

1. 渠道统一接口

public interface PayChannelService {

    /**
     * 统一下单
     */
    UnifiedPayResponse unifiedPay(UnifiedPayRequest request);

    /**
     * 查询订单
     */
    PayQueryResponse query(String payOrderNo);

    /**
     * 关闭订单
     */
    void close(String payOrderNo);
}

2. 微信渠道实现(示例)

@Service
public class WechatPayChannelService implements PayChannelService {

    @Override
    public UnifiedPayResponse unifiedPay(UnifiedPayRequest request) {
        // 真实场景:这里会调用微信的下单接口
        UnifiedPayResponse resp = new UnifiedPayResponse();
        resp.setPayOrderNo("WECHAT_" + System.currentTimeMillis());
        resp.setPayUrl("weixin://wxpay/bizpayurl?pr=XXXXXX");
        resp.setRawBody("{\"mock\": \"wechat unified order\"}");
        return resp;
    }

    @Override
    public PayQueryResponse query(String payOrderNo) {
        // 真实场景:调用微信订单查询接口
        return new PayQueryResponse();
    }

    @Override
    public void close(String payOrderNo) {
        // 真实场景:调用微信关单接口
    }
}

3. 支付宝渠道实现(示例)

@Service
public class AlipayChannelService implements PayChannelService {

    @Override
    public UnifiedPayResponse unifiedPay(UnifiedPayRequest request) {
        UnifiedPayResponse resp = new UnifiedPayResponse();
        resp.setPayOrderNo("ALIPAY_" + System.currentTimeMillis());
        resp.setPayUrl("https://openapi.alipay.com/gateway.do?order=XXXX");
        resp.setRawBody("{\"mock\": \"alipay unified order\"}");
        return resp;
    }

    @Override
    public PayQueryResponse query(String payOrderNo) {
        return new PayQueryResponse();
    }

    @Override
    public void close(String payOrderNo) {
    }
}

五、工厂 + 策略:根据渠道选择实现

1. 工厂类

@Component
public class PayChannelFactory {

    private final Map<String, PayChannelService> channelServiceMap = new HashMap<>();

    public PayChannelFactory(List<PayChannelService> channelServices) {
        // 假设每个实现类上都使用 @PayChannelType 注解标记渠道
        for (PayChannelService service : channelServices) {
            PayChannelType annotation = service.getClass().getAnnotation(PayChannelType.class);
            if (annotation != null) {
                channelServiceMap.put(annotation.value().getCode(), service);
            }
        }
    }

    public PayChannelService getChannel(String channelCode) {
        PayChannelService service = channelServiceMap.get(channelCode);
        if (service == null) {
            throw new IllegalArgumentException("不支持的支付渠道: " + channelCode);
        }
        return service;
    }
}

2. 自定义注解标记渠道类型

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface PayChannelType {
    PayChannelEnum value();
}

在具体实现类上标注:

@Service
@PayChannelType(PayChannelEnum.WECHAT)
public class WechatPayChannelService implements PayChannelService {
    // ...
}
@Service
@PayChannelType(PayChannelEnum.ALIPAY)
public class AlipayChannelService implements PayChannelService {
    // ...
}

这样新增渠道 = 新增一个实现类 + 加一个注解即可。


六、模板方法:统一收单流程

上面是“针对某个渠道”的实现。再往外一层,我们需要一个 统一入口服务

  1. 做参数校验、风控等前置逻辑;
  2. 生成本地支付订单并落库;
  3. 调用渠道策略;
  4. 更新本地订单状态;
  5. 返回统一响应。

这些步骤在所有支付渠道中基本一致,只是 “第 3 步调用哪个渠道” 不同。因此适合用模板方法。

1. 抽象模板类

public abstract class AbstractPayTemplate {

    @Resource
    private PayOrderRepository payOrderRepository;

    @Resource
    private PayPreHandlerChain payPreHandlerChain;

    public UnifiedPayResponse doUnifiedPay(UnifiedPayRequest request) {
        // 1. 前置处理:参数校验、风控、限流等
        payPreHandlerChain.handle(request);

        // 2. 构建并落库支付订单
        PayOrder payOrder = buildAndSavePayOrder(request);

        try {
            // 3. 调用渠道统一下单(交给子类实现)
            UnifiedPayResponse resp = doChannelPay(request, payOrder);

            // 4. 更新订单渠道返回信息
            updatePayOrderAfterChannel(payOrder, resp);

            // 5. 返回响应
            resp.setPayOrderNo(payOrder.getPayOrderNo());
            return resp;
        } catch (Exception e) {
            // 失败时更新订单状态
            markPayOrderFail(payOrder, e);
            throw e;
        }
    }

    protected abstract UnifiedPayResponse doChannelPay(UnifiedPayRequest request, PayOrder payOrder);

    protected PayOrder buildAndSavePayOrder(UnifiedPayRequest request) {
        PayOrder order = new PayOrder();
        order.setPayOrderNo("PAY_" + System.currentTimeMillis());
        order.setBizOrderNo(request.getBizOrderNo());
        order.setAmount(request.getAmount());
        order.setChannelCode(request.getChannelCode());
        order.setStatus(PayStatusEnum.INIT);
        payOrderRepository.save(order);
        return order;
    }

    protected void updatePayOrderAfterChannel(PayOrder payOrder, UnifiedPayResponse resp) {
        payOrder.setStatus(PayStatusEnum.PROCESSING);
        payOrder.setChannelResp(resp.getRawBody());
        payOrderRepository.update(payOrder);
    }

    protected void markPayOrderFail(PayOrder payOrder, Exception e) {
        payOrder.setStatus(PayStatusEnum.FAIL);
        payOrder.setErrorMsg(e.getMessage());
        payOrderRepository.update(payOrder);
    }
}

2. 具体实现:聚合支付统一入口

@Service
public class AggregatedPayService extends AbstractPayTemplate {

    @Resource
    private PayChannelFactory payChannelFactory;

    @Override
    protected UnifiedPayResponse doChannelPay(UnifiedPayRequest request, PayOrder payOrder) {
        PayChannelService channelService = payChannelFactory.getChannel(request.getChannelCode());
        // 实际调用具体渠道
        return channelService.unifiedPay(request);
    }
}

Controller 中就非常简洁:

@RestController
@RequestMapping("/pay")
public class PayController {

    @Resource
    private AggregatedPayService aggregatedPayService;

    @PostMapping("/unified")
    public UnifiedPayResponse unifiedPay(@RequestBody UnifiedPayRequest request) {
        return aggregatedPayService.doUnifiedPay(request);
    }
}

七、责任链:参数校验、风控、限流等前置处理

1. 抽象处理器

public interface PayPreHandler {

    /**
     * 返回 true 表示继续后续责任链;false 表示中断
     */
    boolean handle(UnifiedPayRequest request);
}

2. 责任链实现类

@Component
public class PayPreHandlerChain {

    private final List<PayPreHandler> handlers;

    public PayPreHandlerChain(List<PayPreHandler> handlers) {
        // 可根据 order 排序
        this.handlers = handlers.stream()
                .sorted(Comparator.comparingInt(PayPreHandlerChain::getOrder))
                .collect(Collectors.toList());
    }

    public void handle(UnifiedPayRequest request) {
        for (PayPreHandler handler : handlers) {
            boolean goOn = handler.handle(request);
            if (!goOn) {
                break;
            }
        }
    }

    private static int getOrder(PayPreHandler handler) {
        Order order = handler.getClass().getAnnotation(Order.class);
        return order == null ? 0 : order.value();
    }
}

3. 示例:参数校验处理器

@Component
@Order(1)
public class ParamValidateHandler implements PayPreHandler {

    @Override
    public boolean handle(UnifiedPayRequest request) {
        if (request.getAmount() == null || request.getAmount() <= 0) {
            throw new IllegalArgumentException("金额必须大于 0");
        }
        if (StringUtils.isBlank(request.getBizOrderNo())) {
            throw new IllegalArgumentException("商户订单号不能为空");
        }
        return true;
    }
}

4. 示例:简单风控处理器

@Component
@Order(2)
public class SimpleRiskHandler implements PayPreHandler {

    @Override
    public boolean handle(UnifiedPayRequest request) {
        // Demo:单笔金额超过 10w 暂停支付
        if (request.getAmount() != null && request.getAmount() > 100_000_00L) {
            throw new IllegalStateException("风控拦截:金额过大");
        }
        return true;
    }
}

后续需要新增任何前置逻辑,只需要实现 PayPreHandler + @Component 即可,责任链会自动组装。


八、适配器模式:对接第三方 SDK

很多三方支付 SDK 的接口风格和我们系统不一致:

  • 入参是乱七八糟的 Map 或者 XML;
  • 出参也是复杂对象或者字符串。

我们可以使用适配器模式,将 SDK 封装成统一接口:

1. 第三方 SDK 示例(伪代码)

// 假设三方 SDK 提供的接口是这样的:
public class ThirdPartyWechatSdk {

    public String pay(Map<String, String> params) {
        // 调用微信
        return "{...}"; // 返回 JSON
    }
}

2. 适配器接口

public interface ThirdPayClient {

    /**
     * 统一下单
     */
    ThirdPayResponse pay(ThirdPayRequest request);
}

3. 微信适配器实现

public class WechatPayClientAdapter implements ThirdPayClient {

    private final ThirdPartyWechatSdk sdk;

    public WechatPayClientAdapter(ThirdPartyWechatSdk sdk) {
        this.sdk = sdk;
    }

    @Override
    public ThirdPayResponse pay(ThirdPayRequest request) {
        Map<String, String> params = new HashMap<>();
        params.put("amount", String.valueOf(request.getAmount()));
        params.put("orderNo", request.getOrderNo());
        // ... 组装微信需要的参数

        String raw = sdk.pay(params);
        // 解析微信返回
        ThirdPayResponse resp = new ThirdPayResponse();
        resp.setRaw(raw);
        // 解析出 payUrl 等
        return resp;
    }
}

4. 在渠道实现中使用适配器

@Service
@PayChannelType(PayChannelEnum.WECHAT)
public class WechatPayChannelService implements PayChannelService {

    private final ThirdPayClient wechatPayClient;

    public WechatPayChannelService(ThirdPayClient wechatPayClient) {
        this.wechatPayClient = wechatPayClient;
    }

    @Override
    public UnifiedPayResponse unifiedPay(UnifiedPayRequest request) {
        ThirdPayRequest thirdReq = new ThirdPayRequest();
        thirdReq.setAmount(request.getAmount());
        thirdReq.setOrderNo(request.getBizOrderNo());
        // ...

        ThirdPayResponse thirdResp = wechatPayClient.pay(thirdReq);

        UnifiedPayResponse resp = new UnifiedPayResponse();
        resp.setPayUrl(thirdResp.getPayUrl());
        resp.setRawBody(thirdResp.getRaw());
        return resp;
    }

    // 其他方法省略
}

这样未来如果 SDK 升级或更换供应商,我们只改适配器,不动上层聚合逻辑。


九、观察者模式:异步通知业务系统

支付成功后,往往需要:

  • 通知订单系统更新状态;
  • 发短信、发站内信、发优惠券等。

如果这些逻辑都写在支付模块里,会非常臃肿且耦合。可以使用 观察者模式 / 发布订阅 解耦。

1. 领域事件对象

@Data
@AllArgsConstructor
public class PaySuccessEvent {
    private String payOrderNo;
    private String bizOrderNo;
    private Long amount;
    private String channelCode;
}

2. 发布事件(可以用 Spring 事件 / MQ 等)

@Component
public class PayEventPublisher {

    @Resource
    private ApplicationEventPublisher publisher;

    public void publishPaySuccess(PayOrder payOrder) {
        PaySuccessEvent event = new PaySuccessEvent(
                payOrder.getPayOrderNo(),
                payOrder.getBizOrderNo(),
                payOrder.getAmount(),
                payOrder.getChannelCode()
        );
        publisher.publishEvent(event);
    }
}

在支付回调处理逻辑里:

public void handlePayNotify(PayNotifyRequest notifyRequest) {
    // 1. 校验签名、校验金额…
    // 2. 更新支付订单状态为成功
    payOrderRepository.markSuccess(payOrder);

    // 3. 发布支付成功事件
    payEventPublisher.publishPaySuccess(payOrder);
}

3. 业务侧订阅事件

@Component
public class OrderModuleListener {

    @EventListener
    public void onPaySuccess(PaySuccessEvent event) {
        // 更新业务订单状态
        // 发送短信 / 消息等
    }
}

这样支付模块和业务模块之间通过“事件”解耦。


十、状态模式(可选):支付订单状态流转

支付订单的一生:INIT -> PROCESSING -> SUCCESS / FAIL / CLOSED
如果状态流转复杂(多种中间态、重试、部分退款等),可以引入 状态模式 来管理。

这里给出一个简单骨架:

public interface PayOrderState {

    void handle(PayOrderContext ctx);
}
public class InitState implements PayOrderState {
    @Override
    public void handle(PayOrderContext ctx) {
        // 触发下单,更新为 PROCESSING
    }
}
public class ProcessingState implements PayOrderState {
    @Override
    public void handle(PayOrderContext ctx) {
        // 根据渠道回调,更新为 SUCCESS / FAIL
    }
}

通过上下文 + 状态对象管理复杂状态机,这里不展开细节。


十一、整体调用流程串起来

  1. 上层业务调用 /pay/unified 接口;
  2. PayController 接收到 UnifiedPayRequest,调用 AggregatedPayService.doUnifiedPay
  3. AbstractPayTemplate
    • 调用 PayPreHandlerChain.handle 走责任链(参数校验、风控等);
    • 构建并持久化 PayOrder
    • 调用抽象方法 doChannelPay
  4. AggregatedPayService.doChannelPay
    • 使用 PayChannelFactory 根据 channelCode 获取对应 PayChannelService 策略实现;
    • 调用 PayChannelService.unifiedPay
  5. 某个渠道实现(如 WechatPayChannelService):
    • 内部调用 ThirdPayClient 适配器,对接三方 SDK;
    • 返回统一的 UnifiedPayResponse
  6. 模板方法内部更新订单渠道响应信息,并返回给 Controller;
  7. 前端根据 payUrl 等信息拉起支付。

支付回调:

  1. 渠道异步通知回调接口;
  2. 校验签名、金额等;
  3. 更新 PayOrder 状态为 SUCCESS;
  4. 发布 PaySuccessEvent 事件,业务模块监听并处理。

十二、如何落地到真实项目中的步骤建议

  1. 先抽象统一请求 / 响应模型,把所有渠道的公共字段梳理清楚;
  2. 先实现最小闭环:一个渠道 + 一种支付方式,代码跑通;
  3. 抽出:
    • PayChannelService 接口 & 实现
    • PayChannelFactory + @PayChannelType
    • AbstractPayTemplate 统一流程
  4. 把原来散落在 Controller/Service 里的校验逻辑迁移到 PayPreHandlerChain
  5. 把第三方 SDK 的调用封装到 ThirdPayClient 适配器中;
  6. 引入事件机制,将“支付成功后”的业务逻辑挪出支付模块;
  7. 当新增渠道时:
    • 新建一个 XXXPayChannelService + @PayChannelType
    • 如果 SDK 接口不一样,再加一个适配器实现即可。

做到这一步,你就有了一个:结构清晰、易扩展、设计模式运用合理的聚合支付框架


你可以直接把这个 Markdown 文件放到项目的 docs/ 目录里,作为“聚合支付设计说明文档”,或者继续在此基础上补充类图、时序图、表结构说明等内容。

本文章已经生成可运行项目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值