当前位置: 首页 > news >正文

对接八大应用渠道

背景

       最近公司想把游戏包上到各个渠道上,因此需要对接各种渠道,渠道如下,oppo、vivo、华为、小米、应用宝、taptap、荣耀、三星等应用渠道               

         主要就是对接登录、支付接口(后续不知道会不会有其他的),由于登录、支付前置都是一些通用的逻辑处理,所以一开始我就想到了要用设计模式来做,下面我画了一个图,大家可以参考看一下        

这里值得一提的是目前oppo比常规的渠道多了一个发货通知的接口,我把几个接口都说说明下        

登录:用户通过oppo、vivo等进行登录并绑定我们内部账号中        

支付回调:用户使用oppo支付成功后要通知我们用户下单成功,我们需要进行处理发货        

发货通知:我们发货成功后通知oppo发货成功了        

前面两个接口其实很好理解,主要是发货通知,官方的文档如下,看了也许你就明白了,如下

     如果 OPPO 服务端超过2 个小时仍未收到游戏服务端的发货结果请求,则代表该笔订单发货失败,将进入可退款状态,用户可自助选择是否退款,如果用户选择退款,将进入OPPO 退款流程        

对了没错,用户可以退款,我认为就是在对标苹果吧,目前看小米和vivo都不需要有这个接口

流程图

代码逻辑

        上面的就是设计图,这里再简单的说下代码的结构逻辑是咋样的,其实做过挺多次类似的处理了,这两种模式的设计还是挺有意义的,以下只是稍微说下大概的方式

AbstractChannelStrategy

@Slf4j
public abstract class AbstractChannelStrategy{@Resourceprotected ZXCUserParentDao userParentDao;@Resourceprotected  ZXCUsermpl userRepo;@Resourceprivate  ZXCOrderoDao orderInfoDao;@Resourceprotected ApplicationContext applicationContext;@Resourceprotected  ZXCAppRepoImpl gameAppRepo;//获取用户注册类型public abstract UserTypeEnum getUserRegisterTypeEnum();//获取回调Url地址protected abstract String getCallBackUrlSuffix();//真正的支付成功处理public abstract void doNotifyOrderHandle(ChannelNotifyBaseBo notifyBaseBo, ChannelNotifyOrderResultBO notifyOrderResultBO, OrderInfo orderInfo) throws Exception;//游戏发货成功后的处理protected abstract DeliveryCallBackResultBo doDeliveryCallback(OrderInfo orderInfo, GameApp gameApp) throws Exception;//允许子类校验订单的其他信息protected void checkOrderOtherInfo(OrderInfo orderInfo){}public boolean notifyOrderHandle(ChannelNotifyBaseBo notifyBaseBo) {try {if(notifyBaseBo == null) {return false;}//检查订单基础信息OrderInfo orderInfo = commonOrderCheck(notifyBaseBo.getOrderNumber());checkOrderOtherInfo(orderInfo);ChannelNotifyOrderResultBO notifyOrderResultBO = new ChannelNotifyOrderResultBO();doNotifyOrderHandle(notifyBaseBo, notifyOrderResultBO, orderInfo);boolean success = notifyOrderResultBO.isSuccess();//成功后处理后续if(success) {log.info("notifyOrderHandle-订单号{}处理成功,进行后续处理", orderInfo.getOrderNumber());successOrder(orderInfo, notifyOrderResultBO.getTradeNo());}return success;} catch (Exception e) {log.error("doNotifyOrderHandle出现异常", e);throw new AppException(ErrorCode.SYS_OPERATOR_ERROR.code(), "doNotifyOrderHandle调用时出现异常");}}public boolean deliveryCallback(String orderNumber) {OrderInfo orderInfo = assertOrderInfo(orderNumber);GameApp gameApp = gameAppRepo.getByAppNumber(orderInfo.getAppNumber());if(!Objects.equals(channelOrderRel.getRequestStatus(), 0)) {log.info("AbstractChannelAccountStrategy-deliveryCallback订单号为{}的请求状态不为未处理,无须处理,此时状态为{},来源为{}", orderNumber, channelOrderRel.getRequestStatus(), getLogSourceName());return true;}try {DeliveryCallBackResultBo deliveryCallBackResultBo = doDeliveryCallback(orderInfo, gameApp);boolean success = deliveryCallBackResultBo.isSuccess();//更新状态和次数channelOrderRel.setHttpContent(deliveryCallBackResultBo.getHttpContent());channelOrderRel.setRequestStatus(success ? 1 : null);int update = channelOrderRelService.update(channelOrderRel);log.info("deliveryCallback-处理完成,更新的订单号为{},关联的channelOrderRel主键id为{},影响行数为{}", orderNumber, channelOrderRel.getId(), update);return success;} catch (Exception e) {log.error("AbstractChannelAccountStrategy-deliveryCallback订单号为{}的请求处理出现异常,来源为{}", orderNumber, getLogSourceName(), e);return false;}}protected OrderInfo commonOrderCheck(String orderNumber) {OrderInfo orderInfo = assertOrderInfo(orderNumber);if(orderInfo.getPayWay() != null && !Objects.equals(getOrderPayWay().getCode(), orderInfo.getPayWay())) {throw new ZXCException(ZXCCode.OPERATOR_ERROR.code(), "AbstractChannelAccountStrategy-订单号支付方式不对,此时订单号为" + orderNumber);}return orderInfo;}private OrderInfo assertOrderInfo(String orderNumber) {OrderInfo orderInfo = orderInfoDao.getByOrderNumber(orderNumber);if(orderInfo == null) {throw new AppException(ErrorCode.NOT_FOUND_DATA.code(), "AbstractChannelAccountStrategy-找不到关联的订单号数据,此时订单号为" + orderNumber);}return orderInfo;}private void successOrder(OrderInfo orderInfo, String tradeNo) {boolean updated = discountsService.updateOrderStatusAndDisCount(orderInfo, tradeNo);if(updated){// 发送事件applicationContext.publishEvent(new PaySuccessEvent(this, new PaySuccessEvent.PaySuccessEventData(orderInfo.getOrderNumber())));}}public String buildCallBackUrl() {//校验及简单处理一下数据String callBackUrlSuffix = getCallBackUrlSuffix();if(StringUtil.isBlank(callBackUrlSuffix)) {throw new AppException("未配置回调地址");}if(!Objects.equals("/", callBackUrlSuffix.substring(0, 1))) {callBackUrlSuffix = "/" + callBackUrlSuffix;}String callBackUrlPrefix = "https://test.cn/v1/pay/callback";if(isLine()) {callBackUrlPrefix = "https://prod.cn/v1/pay/callback";}return callBackUrlPrefix + callBackUrlSuffix;}

虽然上面那个看起来很复杂,但是主要是子类方便,这里提供其中一个实现类

OppoStrategyImpl

@Slf4j
@Component
public class OppoStrategyImpl extends AbstractChannelAccountStrategy {@Overridepublic UserTypeEnum getUserRegisterTypeEnum() {return UserTypeEnum.OPPO_USER;}@Overrideprotected String getCallBackUrlSuffix() {return "/oppo/callback";}@Overridepublic void doNotifyOrderHandle(ChannelNotifyBaseBo notifyBaseBo, ChannelNotifyOrderResultBO notifyOrderResultBO, OrderInfo orderInfo) throws Exception {OppoOrderNotifyBo oppoOrderNotifyBo = (OppoOrderNotifyBo) notifyBaseBo; //强制对象,几乎是不可能报错的,除非调用端出了问题//验证签名,这里是用oppo的公钥验签,因为私钥只有oppo有,所以别人无法伪造String baseString = getBaseString(oppoOrderNotifyBo);boolean check = OppoUtils.check(baseString, oppoOrderNotifyBo.getSign());if(!check) {log.info("OppoChannelAccountStrategyImpl-验签失败,此时订单号为{}", oppoOrderNotifyBo.getPartnerOrder());return;}//代表成功了,对数据进行填充notifyOrderResultBO.setSuccess(true);notifyOrderResultBO.setTradeNo(oppoOrderNotifyBo.getNotifyId()); //可能没有}// 生成 baseStringprivate static String getBaseString(OppoOrderNotifyBo ne) {StringBuilder sb = new StringBuilder();sb.append("notifyId=").append(ne.getNotifyId());sb.append("&partnerOrder=").append(ne.getPartnerOrder());sb.append("&productName=").append(ne.getProductName());sb.append("&productDesc=").append(ne.getProductDesc());sb.append("&price=").append(ne.getPrice());sb.append("&count=").append(ne.getCount());sb.append("&attach=").append(ne.getAttach());return sb.toString();}@Overrideprotected OppoDeliveryResult doDeliveryCallback(OrderInfo orderInfo, GameApp gameApp) throws Exception {OppoDeliveryResult oppoDeliveryCallbackResult = OppoUtils.postDeliveryOppo(orderInfo, gameApp);DeliveryCallBackResultBo deliveryCallBackResultBo = new DeliveryCallBackResultBo();deliveryCallBackResultBo.setHttpContent(JsonUtils.Object2Json(oppoDeliveryCallbackResult));deliveryCallBackResultBo.setSuccess(oppoDeliveryCallbackResult.isSuccess());return deliveryCallBackResultBo;}
}

        看起来是不是简单多了,当然第一个意义是不大,主要是后面的vivo、小米、华为等只需要提供对应的实现类即可

        而且其他的可能都不需要doDeliveryCallback这个接口的实现,因此其实还可以改进一下,在底级类上面直接提供个成功的回调实现,这里我就暂时不改了,可能改为在底层提供个propertect方法,然后直接调用也是可以的,就当保存一下所有的交互数据得了,而且可能有其他用途,就是用来主动查询订单的结果       

        至于引用就更简单了,通常都是有个类似于算门面的东西,如下

ChannelStrategyComponent

@Slf4j
@Component
public class ChannelStrategyComponent {@Resourceprivate List<AbstractChannelStrategy> channelStrategyList;@Resourceprivate ZXCUserParentDao userParentDao;@Resourceprivate ZXCOrderDao orderInfoDao;public AbstractChannelStrategy getChannelAccountStrategy(UserTypeEnum userTypeEnum) {AbstractChannelStrategy channelAccountStrategy = checkChannelAccountStrategy(userTypeEnum);if(channelAccountStrategy == null) {throw new AppException(ErrorCode.SYS_ERROR.code(), "找不到关联的AbstractChannelAccountStrategy对象");}return channelAccountStrategy;}public AbstractChannelStrategy checkChannelAccountStrategyByOrderNumber(String orderNumber) {Order  order = orderDao.getByOrderNumber(orderNumber);//用支付方式直接去找for (AbstractChannelStrategy channelStrategy : channelStrategyList) {if(Objects.equals(orderInfo.getPayWay(), channelStrategy.getOrderPayWay().getCode())) {log.info("channelStrategy-从订单支付方式中找到处理器");return channelStrategy;}}//支付方式找不到再从用户注册类型去找,节省一部数据库查询if(orderInfo != null) {return checkChannelAccountStrategy(orderInfo.getUserName());}return null;}
}

           怎么样,看起来是不是很简单,顺带一提,上面的OppoUtils就是跟oppo对接的工具包,这个就不提供了,这部分肯定每个都不一样,需要单独写

         这篇文章主要是想说明一种封装思路,而不是要说代码具体是怎么写的这个事

总结

        其实这个东西并不算很复杂,可能跟我设计多次也有关系,这种思路其实我是多少有点借助spring的设计,你去看就会发现里面有很多类似这样的设计

        

http://www.dtcms.com/a/308199.html

相关文章:

  • Tomcat,WebLogic等中间件漏洞实战解析
  • 大模型流式长链接场景下 k8s 优雅退出 JAVA
  • 用 MyBatis + MySQL 实现高效的批量 Upsert
  • 关于tresos Studio(EB)的MCAL配置之GtmCfg
  • 性能测试篇 :Jmeter监控服务器性能
  • Golang 语言的编程技巧之类型
  • 基础组件(六):网络缓冲区设计 和 定时器方案
  • TTS语音合成|GPT-SoVITS语音合成服务器部署,实现http访问
  • Vue3+Vite项目如何简单使用tsx
  • nl2sql grpo强化学习训练,加大数据量和轮数后,准确率没提升,反而下降了,如何调整
  • PostgreSQL dblink 与 Spring Boot @Transactional 的事务整合
  • Text2SQL 智能问答系统开发-预定义模板(二)
  • docker离线安装mysql镜像
  • 记录几个SystemVerilog的语法——覆盖率
  • 基于MATLAB的GUI来对不同的(彩色或灰色)图像进行图像增强
  • 【国内电子数据取证厂商龙信科技】内存取证
  • 法式基因音响品牌SK(SINGKING AUDIO)如何以硬核科技重塑专业音频版图
  • 防御保护第一次作业
  • AI Gateway 分析:OpenRouter vs Higress
  • python基础语法3,组合数据类型(简单易上手的python语法教学)(课后习题)
  • BFT平台:打造科研教育“最强机器人矩阵”
  • 自动驾驶控制算法——PID算法
  • 蓝桥杯----DS18B20温度传感器
  • vue3+arcgisAPI4示例:轨迹点模拟移动(附源码下载)
  • InfluxDB 与 Python 框架结合:Django 应用案例(二)
  • ASIC芯片简介
  • LangGraph认知篇-Send机制
  • TypeScript 基础介绍(二)
  • QT6 Python UI文件转换PY文件的方法
  • 如何为C#加入EPPlus 包