折扣卡CPS多平台API统一对接方案
折扣卡CPS业务需要对接外卖、加油、充值、电影等众多第三方联盟平台,不同平台的签名规则、入参结构、回调格式、状态码定义、报表接口差异巨大。如果每个渠道单独写业务逻辑,会出现代码臃肿、新增渠道改主流程、回调处理混乱、对账逻辑重复、版本频繁发布等问题。本文介绍适配器模式的统一对接架构、标准化模型、网关路由、签名抽象、回调处理、报表拉取以及代码实现方案。
痛点分析
第一,渠道代码硬耦合,新增一个CPS平台就要修改核心分佣、回调代码,上线风险高。 第二,签名、加密、参数组装逻辑分散,有的渠道MD5签名,有的HMAC-SHA256,还有AES加密,维护成本高。 第三,订单状态定义不统一,A平台的“已结算”在B平台叫“有效订单”,退款、失效字段各不一样,上层分佣引擎需要大量if判断。 第四,回调接口分散,每个渠道单独提供接口,网关无法统一限流、幂等、风控拦截。 第五,对账报表接口格式不同,有的是JSON分页、有的返回CSV文件,报表解析逻辑重复开发。 第六,异常处理独立,渠道超时、限流、变更接口时,没有统一重试、降级、告警机制。 第七,渠道PID、密钥配置散落在代码中,无法后台动态配置,切换渠道参数需要改配置文件重启服务。
整体设计思路
基于适配器+工厂模式构建CPS渠道接入中台,上层分佣、归因、对账模块只依赖一套标准模型,不感知底层第三方平台差异。整体划分为配置层、渠道工厂层、适配器抽象层、统一网关层、标准化消息层、任务调度层。 所有第三方渠道都实现统一的适配器接口,对外暴露转链生成、回调解析、签名校验、订单查询、报表拉取五个能力。新增渠道只新增适配器实现类,不改动上层业务引擎。 链路流程:领卡页请求生成CPS链接 → 网关根据渠道编码路由到对应适配器 → 适配器组装渠道参数、签名、生成追踪链接 → 用户下单后第三方回调到统一回调网关 → 适配器解析报文、映射成标准订单对象 → 投递MQ给分佣引擎;离线对账任务调用适配器拉取渠道报表,统一解析对账。
核心模块设计
渠道动态配置中心
后台维护渠道配置,一条渠道记录包含渠道编码、渠道名称、接口地址、回调地址、密钥、签名算法、PID/推广位、超时时间、是否启用、限流阈值。配置信息加载到Redis缓存,渠道密钥、接口地址修改后自动刷新,不需要重启应用。 支持租户维度子配置,同一个渠道,不同租户使用不同的推广PID,隔离归因数据。
适配器抽象接口定义
定义顶层接口,所有CPS渠道适配器必须实现这套方法,屏蔽底层差异。 核心能力:
- 生成转链链接:携带追踪参数、推广PID;
- 回调签名校验:校验第三方回调合法性;
- 回调报文解析:原始报文转为标准CPS订单DTO;
- 主动查询订单:根据外部订单号查询订单实时状态;
- 拉取对账报表:按日期拉取结算报表,返回标准对账行数据。
渠道工厂路由
工厂类根据传入的channelCode,从Spring容器中获取对应的适配器Bean。工厂增加缓存,避免每次都扫描容器,找不到渠道或者渠道禁用时直接抛出业务异常。
统一回调网关
只对外暴露一个回调地址,第三方平台回调时在路径携带channelCode。网关层统一做前置处理:幂等判断、限流、日志全量落盘,再交给对应适配器解析。 网关层不处理分佣、归因,只做报文转换,保证回调快速应答,避免第三方平台重复推送。
标准领域模型统一映射
定义标准CPS订单对象,所有渠道适配器解析完成后,都映射到这个对象。统一字段:渠道编码、外部订单号、用户追踪标识、交易金额、预估佣金、实际佣金、订单状态、退款标记、下单时间、结算时间。 内部枚举统一状态:待确认、有效可结算、已退款、已失效、风控拦截。适配器内部把第三方平台五花八门的状态码翻译成这套枚举,上层分佣引擎只判断枚举值,不需要关心渠道原始状态。
离线报表统一拉取任务
对账调度任务按渠道、日期批量拉取报表,适配器内部处理CSV、JSON分页、文件下载等不同形式的返回结果,输出统一的对账行对象,写入对账中间表,供对账引擎比对本地订单。
统一重试、降级、告警封装
封装统一调用工具,渠道接口调用失败支持可配置重试次数、重试间隔;渠道接口持续异常时触发熔断降级,暂停该渠道的主动查询任务,推送告警通知,避免大量无效请求打垮第三方平台。
代码实现示例
顶层适配器接口
public interface CpsChannelAdapter { /** * 生成CPS跳转链接 */ String buildLink(CpsLinkBuildDTO dto); /** * 校验回调签名 */ boolean verifyCallbackSign(Map<String, String> paramMap, String body); /** * 解析回调原始数据为标准订单 */ CpsStandardOrder parseCallbackData(Map<String, String> params, String body); /** * 主动查询订单信息 */ CpsStandardOrder queryOrder(String outOrderNo); /** * 拉取对账报表 */ List<CpsReportItem> fetchReport(String date); }渠道工厂
@Component public class CpsChannelFactory { @Autowired private Map<String, CpsChannelAdapter> adapterMap; @Autowired private CpsChannelConfigService configService; public CpsChannelAdapter getAdapter(String channelCode) { CpsChannelConfig config = configService.getByChannelCode(channelCode); if (config == null || !config.getEnable()) { throw new BusinessException("该渠道未启用"); } CpsChannelAdapter adapter = adapterMap.get(channelCode); if (adapter == null) { throw new BusinessException("渠道适配器不存在"); } return adapter; } }统一回调网关
@RestController @RequestMapping("/cps/unified/callback") public class CpsUnifiedCallbackController { @Autowired private CpsChannelFactory channelFactory; @Autowired private RocketMQTemplate rocketMQTemplate; @Autowired private RedisTemplate<String,Object> redisTemplate; private static final String IDEM_PREFIX = "cps:unified:idem:"; @PostMapping("/{channelCode}") public String callback(@PathVariable String channelCode, HttpServletRequest request) throws IOException { CpsChannelAdapter adapter = channelFactory.getAdapter(channelCode); String body = StreamUtils.copyToString(request.getInputStream(), StandardCharsets.UTF_8); Map<String,String> paramMap = request.getParameterMap().entrySet().stream() .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue()[0])); boolean signPass = adapter.verifyCallbackSign(paramMap, body); if (!signPass) { return "fail"; } CpsStandardOrder standardOrder = adapter.parseCallbackData(paramMap, body); String idemKey = IDEM_PREFIX + channelCode + ":" + standardOrder.getOutOrderNo(); if (Boolean.TRUE.equals(redisTemplate.hasKey(idemKey))) { return "success"; } rocketMQTemplate.syncSend("cps-order-topic", MessageBuilder.withPayload(standardOrder).build()); redisTemplate.opsForValue().set(idemKey, "1", 7, TimeUnit.DAYS); return "success"; } }落地关键要点
- 签名逻辑封装在适配器内部,不同渠道的MD5、HMAC、AES加密在各自适配器实现,上层完全隔离;
- 回调网关尽量快速返回success,耗时逻辑全部交给MQ异步消费,防止第三方平台超时重推;
- 所有原始回调报文完整入库保存,用于渠道规则变更、排查对账差异;
- 渠道PID按租户隔离,适配器读取上下文租户信息,自动匹配对应推广位;
- 适配器只做转换,不写业务分佣逻辑,保持职责单一;
- 新增渠道只新增适配器实现类,在配置后台开启渠道即可接入,不需要修改分佣、对账主流程;
- 增加渠道版本管理,第三方升级接口时,可以并行维护新旧适配器,灰度切换;
- 监控每个渠道的回调量、失败率、接口耗时、对账差异率,渠道异常及时告警。
总结
折扣卡CPS多平台API统一对接方案的核心是适配器隔离渠道差异、标准化模型统一上层、统一网关收口回调。 用工厂模式路由不同渠道实现,把签名、报文解析、转链、报表拉取这些差异化逻辑收敛到适配器层,分佣引擎、归因模块、对账模块只依赖一套标准数据结构。 这套方案可以和之前的卡密联动、多租户、分佣结算体系无缝集成,后续接入加油、外卖、影音等新CPS渠道时,开发工作量大幅降低,整体架构稳定、可扩展,适合持续接入各类本地生活联盟。