折扣卡CPS卡密与CPS渠道联动开发方案

发布时间:2026/10/21 0:29:53
折扣卡CPS卡密与CPS渠道联动开发方案
折扣卡CPS卡密与CPS渠道联动开发方案很多本地生活折扣卡业务中卡密是地推、线下摆摊、代理商分发的重要载体用户输入卡密完成绑卡之后才具备CPS渠道的返利资格。如果卡密系统和CPS返利系统各自独立会出现卡密绑定状态无法同步、非持卡用户也能拿到返利、卡密作废后CPS归因不失效、代理商业绩和CPS收益统计脱节等问题。本文围绕卡密生命周期、归因联动、权限控制、逆向失效、对账统计讲解整套联动开发方案以及后端示例代码。痛点分析第一卡密和CPS追踪链路互不关联用户直接打开CPS链接下单不需要绑卡就能产生返利违背“持卡才可返利”的业务规则。 第二卡密过期、作废、核销之后CPS归因记录没有同步失效用户后续下单依旧可以拿到佣金造成资金损失。 第三代理商分发卡密无法统计该批次卡密带来的CPS订单和收益分不清地推卡密渠道的变现效果。 第四用户换设备、清除缓存后CPS追踪标识丢失无法通过已绑定的卡密重新恢复归属。 第五卡密批次冻结之后已经发放的追踪链接还在对外流转没有拦截机制。 第六两套系统事件不同步卡密绑定事件丢失CPS归因匹配失败佣金归属到平台而不是对应代理商、推广员。 第七缺少联合对账卡密发放量、绑卡量、CPS订单量分开统计无法核算单批次卡密投入产出。整体设计思路以卡密批次作为联动核心卡密绑定成功后生成持久化持卡身份标记打通CPS归因链路。整体遵循事件驱动模式卡密服务和CPS服务独立部署依靠MQ做事件通信。 完整业务链路平台生成CPS类型卡密批次 → 代理商领取、线下分发 → 用户扫码进入领卡页输入卡密 → 后端校验卡密有效并完成绑定发送【卡密绑定成功事件】 → CPS服务消费事件记录持卡归属信息绑定tenantId、batchId、agentId、promoterId → 用户跳转CPS渠道转链下单 → CPS回调到达后优先匹配持卡身份做归因卡密作废、过期、解绑时发送逆向事件CPS侧标记该用户持卡资格失效新订单不再走该卡密的分佣规则。 同时支持两种归因模式短链接临时追踪兜底 卡密绑定持久化归因解决缓存失效、换设备导致归因丢失的问题。核心联动模块设计卡密批次扩展属性配置原有卡密批次表增加CPS联动配置字段用来控制这个批次的卡密是否开启CPS返利、分佣模板ID、可访问CPS渠道白名单、失效策略。 配置项包括是否开启CPS返利、关联分佣模板、渠道白名单、失效后是否停止新订单分佣、是否保留历史已结算佣金。 只有开启CPS联动的批次用户绑卡后才会生成持卡身份记录普通权益卡密不会进入CPS归因池。代理商在创建批次的时候可以选择是否开启联动选择对应的分佣规则不同卡密批次可以配置不同返利比例。卡密绑定正向事件联动用户提交卡密卡密服务校验卡密状态未绑定、在有效期、批次未冻结。绑定成功之后更新卡密状态为已绑定写入用户-卡密绑定关系表并且投递MQ事件。 事件体携带核心字段租户ID、卡密编号、批次ID、代理商ID、用户openId、绑定时间、分佣模板编号。 CPS服务消费该事件在持卡关系表写入一条持久记录标记该用户当前有效的持卡来源。同个用户支持多张卡密但业务上可以配置优先级优先取最新绑定的有效卡密作为归因来源。CPS转链归因逻辑改造用户从领卡页面跳转CPS渠道时转链接口优先查询该用户是否存在有效持卡记录。 如果存在有效卡密绑定记录在CPS追踪标识中写入batchId、agentId即使Redis里面临时追踪key过期回调到达时也可以通过用户ID反查持卡关系重新匹配归属。 如果用户没有有效卡密记录即使点击了CPS链接也标记为普通访客订单不触发推广员、代理商分佣仅可配置平台基础收益或者直接不计返利。 这个机制可以防止外部流量绕过卡密直接薅返利。卡密逆向生命周期联动卡密存在多种逆向状态过期作废、后台手动作废、批量冻结、用户解绑。 只要状态变更卡密服务发送逆向事件事件类型区分过期、作废、冻结、解绑。 CPS服务收到逆向事件之后更新持卡关系状态为失效设置失效时间。规则区分失效之后新产生的CPS订单不再使用这个卡密批次的分佣模板失效前已经下单、联盟已经结算的历史佣金正常结算不受影响失效前下单但后续退款的订单依旧走原分佣链路逆向扣佣保证资金闭环。 不会直接删除历史归因数据只做状态标记用于对账追溯。联合统计与批次看板新增统计维度卡密批次维度CPS数据。每一条CPS订单关联batchId后台看板可以展示单批次卡密的发放数量、绑卡数量、CPS下单量、GMV、预估佣金、退款金额、实际收益。 代理商可以查看自己名下所有卡密批次对应的CPS变现数据方便评估地推获客质量平台可以统计获客成本和CPS收益核算ROI。权限隔离沿用多租户权限体系代理商只能查看自己名下卡密批次对应的CPS订单无法查看其他代理商批次数据门店店员不具备查看CPS佣金的权限。核心代码示例卡密绑定成功发送事件Service public class CardSecretBindServiceImpl implements CardSecretBindService { Autowired private CardSecretMapper cardSecretMapper; Autowired private RocketMQTemplate rocketMQTemplate; Override Transactional(rollbackFor Exception.class) public ResultDTO bindSecret(BindSecretDTO dto) { CardSecretEntity secret cardSecretMapper.selectBySecretNo(dto.getSecretNo()); if(secret null || !WAIT_BIND.equals(secret.getStatus())){ return ResultDTO.fail(卡密无效或已绑定); } // 判断批次是否开启CPS联动 if(secret.getCpsEnable()){ CardBindEvent event new CardBindEvent(); event.setTenantId(secret.getTenantId()); event.setSecretNo(secret.getSecretNo()); event.setBatchId(secret.getBatchId()); event.setAgentId(secret.getAgentId()); event.setUserId(dto.getUserId()); event.setTemplateId(secret.getCpsTemplateId()); event.setBindTime(LocalDateTime.now()); rocketMQTemplate.syncSend(card-secret-bind-event, MessageBuilder.withPayload(event).build()); } // 更新卡密状态 secret.setStatus(BIND); secret.setBindUserId(dto.getUserId()); secret.setBindTime(LocalDateTime.now()); cardSecretMapper.updateById(secret); return ResultDTO.success(绑卡成功); } }CPS消费端维护持卡关系转链时读取持卡信息Component RocketMQMessageListener(topic card-secret-bind-event, consumerGroup cps-card-bind-consumer) public class CardBindEventConsumer implements RocketMQListenerCardBindEvent { Autowired private UserCardRelMapper userCardRelMapper; Override Transactional(rollbackFor Exception.class) public void onMessage(CardBindEvent event) { LambdaQueryWrapperUserCardRel wrapper Wrappers.lambdaQuery(); wrapper.eq(UserCardRel::getUserId, event.getUserId()); wrapper.eq(UserCardRel::getBatchId, event.getBatchId()); Long count userCardRelMapper.selectCount(wrapper); if(count 0){ return; } UserCardRel rel new UserCardRel(); rel.setTenantId(event.getTenantId()); rel.setUserId(event.getUserId()); rel.setBatchId(event.getBatchId()); rel.setAgentId(event.getAgentId()); rel.setTemplateId(event.getTemplateId()); rel.setStatus(VALID); rel.setBindTime(event.getBindTime()); userCardRelMapper.insert(rel); } }CPS转链接口校验持卡资格RestController RequestMapping(/cps/link) public class CpsLinkController { Autowired private UserCardRelMapper userCardRelMapper; Autowired private CpsLinkService cpsLinkService; GetMapping(/generate) public ResultDTOString generateLink(RequestParam String userId, RequestParam String channelCode){ LambdaQueryWrapperUserCardRel wrapper Wrappers.lambdaQuery(); wrapper.eq(UserCardRel::getUserId, userId); wrapper.eq(UserCardRel::getStatus, VALID); wrapper.orderByDesc(UserCardRel::getBindTime); UserCardRel rel userCardRelMapper.selectOne(wrapper); if(rel null){ // 无有效持卡记录返回普通链接不分配代理商分佣 String normalLink cpsLinkService.buildNormalLink(channelCode, userId); return ResultDTO.success(normalLink); } // 携带卡密批次信息的追踪链接 String link cpsLinkService.buildCardCpsLink(channelCode, rel); return ResultDTO.success(link); } }落地关键要点事件必须保证可靠性MQ开启事务消息卡密绑定入库成功之后再投递事件避免消息发送失败导致CPS侧没有持卡记录持卡关系不要无限叠加业务可配置单用户有效卡密上限避免多批次卡密冲突追踪缓存只是辅助归因优先走持久化持卡关系解决换设备、缓存丢失的问题卡密作废只影响新订单已经产生的CPS订单和佣金历史数据保留对账链路不断开渠道白名单控制同一个卡密批次可以限制只能跳转指定CPS渠道防止跨渠道违规引流联合对账定时任务每日比对卡密绑卡表和CPS订单表统计绑卡用户下单转化率发现数据断层告警缓存设计用户有效持卡信息缓存到Rediskey带上tenantId和userId卡密状态变更时主动清理对应缓存风控结合可以针对卡密批次配置风控阈值某一批次短时间CPS退款率异常自动预警甚至临时冻结该批次CPS返利资格。总结折扣卡CPS卡密与CPS渠道联动开发方案的核心是卡密生命周期驱动CPS归因资格临时追踪持久持卡关系双保险正向绑定、逆向失效闭环。 把卡密批次作为CPS收益的统计单元通过事件打通卡密系统和CPS中台实现只有持卡用户才能参与代理商分佣同时支持卡密作废之后自动停止新增返利历史订单不受破坏。 该方案和权益包、多租户、CPS聚合返利、推广员权限模块无缝对接适合地推、代理商批量发卡的业务模式既保障CPS归因准确又可以量化每一批线下卡密的变现收益。