對接八大應用渠道

背景

? ? ? ?最近公司想把游戲包上到各個渠道上,因此需要對接各種渠道,渠道如下,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的設計,你去看就會發現里面有很多類似這樣的設計

后續補充

補充一

? ? ? ? 就像之前說的doDeliveryCallback方法并不是每個渠道都需要的,所以并不需要弄為抽血方法,可以提供默認實現,子類根據需要實現即可,以上是改動的代碼

改之前的結構protected abstract CallBackResultBo doDeliveryCallback() throws Exception每個子類都得強制實現改之后的結構protected CallBackResultBo doDeliveryCallback() throws Exception {return CallBackResultBo.buildDefaultSuccess();
}默認提供成功的實現這樣一來,子類就可以根據所需來選擇是否需要覆蓋了,減少了不少代碼

? ?補充二?

? ? ? ? 之前的登錄接口是設計為多個來給安卓調用的,比如以上兩個接口

oppo調用: https://zxc.com/oppo/login

vivo調用: https://zxc.com/vivo/login

但是安卓說不好區分,他想統一調一個接口,如果是以外就麻煩了,但是在設計模式的加持下,現在實現的功能就非常簡單了,只需要做幾個事

1.在抽象類?AbstractChannelStrategy添加 方法

2.在子類提供實現

3.在ChannelStrategyComponent提供獲取方式

最后在接收的通用參數定義 channelSource來源,然后子類跟它綁定起來就可以,代碼如下

抽象類添加//sdk端定義的渠道來源public abstract String getSdkChannelSource();子類實現@Overridepublic String getSdkChannelSource() {return "oppo";}上下文中添加獲取即可,省略了這里

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/917168.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/917168.shtml
英文地址,請注明出處:http://en.pswp.cn/news/917168.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

學習:入門uniapp Vue3組合式API版本(17)

42.打包發行微信小程序的上線全流程 域名 配置 發行 綁定手機號 上傳 提交后等待&#xff0c;上傳 43.打包H5并發布上線到unicloud的前端頁面托管 完善配置 unicloud 手機號實名信息不一致&#xff1a;請確保手機號的實名信息與開發者姓名、身份證號一致&#xff0c;請前往開…

SOLIDWORKS材料明細表設置,屬于自己的BOM表模板

上一期我們了解了如何在SOLIDWORKS工程圖中添加材料明細表?接下來&#xff0c;我們將進行對SOLIDWORKS材料明細表的設置、查看縮略圖、模板保存的深度講解。01 材料明細表設置菜單欄生成表格后左側菜單欄會顯示關于材料明細表的相關設置信息。我們先了解一下菜單欄設置詳情&am…

全棧:Maven的作用是什么?本地倉庫,私服還有中央倉庫的區別?Maven和pom.xml配置文件的關系是什么?

Maven和pom.xml配置文件的關系是什么&#xff1a; Maven是一個構建工具和依賴管理工具&#xff0c;而pom.xml&#xff08;Project Object Model&#xff09;是Maven的核心配置文件。 SSM 框架的項目不一定是 Maven 項目&#xff0c;但推薦使用 Maven進行管理。 SSM 框架的項目可…

超越 ChatGPT:智能體崛起,開啟全自主 AI 時代

引言 短短三年,生成式 AI 已從對話助手跨越到能自主規劃并完成任務的“智能體(Agentic AI)”時代。這場演進不僅體現在模型規模的提升,更在于系統架構、交互范式與安全治理的全面革新。本文按時間線梳理關鍵階段與核心技術,為您呈現 AI 智能體革命的脈絡與未來趨勢。 1. …

一杯就夠:讓大腦瞬間在線、讓肌肉滿電的 “Kick-out Drink” 全解析

一杯就夠&#xff1a;讓大腦瞬間在線、讓肌肉滿電的 “Kick-out Drink” 全解析“每天清晨&#xff0c;當鬧鐘還在哀嚎&#xff0c;你舉杯一飲&#xff0c;睡意像被扔出擂臺——這&#xff0c;就是 Kick-out Drink 的全部浪漫。”清晨 30 分鐘后&#xff0c;250 mL 常溫水里溶解…

系統開機時自動執行指令

使用 systemd 創建一個服務單元可以讓系統開機時自動執行指令&#xff0c;假設需要執行的指令如下&#xff0c;運行可執行文件&#xff08;/home/demo/可執行文件&#xff09;&#xff0c;并輸入參數&#xff08;–input/home/config/demo.yaml&#xff09;&#xff1a; /home/…

Docker 初學者需要了解的幾個知識點 (七):php.ini

這段配置是 php.ini 文件中針對 PHP 擴展和 Xdebug 調試工具的設置&#xff0c;主要用于讓 PHP 支持數據庫連接和代碼調試&#xff08;尤其在 Docker 環境中&#xff09;&#xff0c;具體解釋如下&#xff1a;[PHP] extensionpdo_mysql extensionmysqli xdebug.modedebug xdebu…

【高階版】R語言空間分析、模擬預測與可視化高級應用

隨著地理信息系統&#xff08;GIS&#xff09;和大尺度研究的發展&#xff0c;空間數據的管理、統計與制圖變得越來越重要。R語言在數據分析、挖掘和可視化中發揮著重要的作用&#xff0c;其中在空間分析方面扮演著重要角色&#xff0c;與空間相關的包的數量也達到130多個。在本…

dolphinscheduler中一個腳本用于從列定義中提取列名列表

dolphinscheduler中&#xff0c;我們從一個mysql表導出數據&#xff0c;上傳到hdfs, 再創建一個臨時表&#xff0c;所以需要用到列名定義和列名列表。 原來定義兩個變量&#xff0c;不僅繁鎖&#xff0c;還容易出現差錯&#xff0c;比如兩者列序不對。 所以考慮只定義列定義變量…

JavaWeb(蒼穹外賣)--學習筆記16(定時任務工具Spring Task,Cron表達式)

前言 本篇文章是學習B站黑馬程序員蒼穹外賣的學習筆記&#x1f4d1;。我的學習路線是Java基礎語法-JavaWeb-做項目&#xff0c;管理端的功能學習完之后&#xff0c;就進入到了用戶端微信小程序的開發&#xff0c;用戶端開發的流程大致為用戶登錄—商品瀏覽&#xff08;其中涉及…

靈敏度,精度,精確度,精密度,精準度,準確度,分辨率,分辨力——概念

文章目錄前提總結前提 我最近在整理一份數據指標要求的時候&#xff0c;總是混淆這幾個概念&#xff1a;靈敏度&#xff0c;精度&#xff0c;精確度&#xff0c;精密度&#xff0c;精準度&#xff0c;準確度&#xff0c;分辨率&#xff0c;分辨力&#xff0c;搜了一些文章&…

python-異常(筆記)

#后續代碼可以正常運行 try:f open("xxx.txt","r",encodingutf-8)except:print("except error")#捕獲指定異常&#xff0c;其他異常報錯程序中止&#xff0c;管不到 try:print(name) except NameError as you_call:print("name error"…

[lvgl_player] 用戶界面(LVGL) | 播放器核心設計

docs&#xff1a;基于LVGL的音樂播放器 本項目是為嵌入式設備設計的音樂播放系統&#xff0c;采用LVGL圖形庫構建用戶界面。 系統支持播放WAV格式音頻文件&#xff0c;具備播放列表管理功能&#xff0c;可實現播放/暫停控制、曲目切換等核心操作。 用戶可通過交互界面實時調…

數據賦能(354)——數據分析——多角度分析原則

概述重要性如下&#xff1a;獲得全面理解&#xff1a;多角度分析原則避免僅從單一角度解讀數據&#xff0c;從不同角度、不同維度對數據進行分析&#xff0c;以獲得更全面的理解。發現潛在規律&#xff1a;通過多角度分析&#xff0c;發現數據中的潛在規律和趨勢&#xff0c;為…

【華為機試】127. 單詞接龍

文章目錄127. 單詞接龍描述示例 1&#xff1a;示例 2&#xff1a;提示&#xff1a;解題思路算法分析問題本質分析單向BFS算法詳解雙向BFS算法詳解鄰居單詞生成過程算法流程圖邊界情況分析各種解法對比時間復雜度分析空間復雜度分析關鍵優化點實際應用場景圖構建策略雙向BFS優化…

仿艾莫迅MODBUS調試工具寫一個上位機

公司采購了一個夾具&#xff0c;項目負責人想要試探這個夾具的性能&#xff0c;于是想要我這邊寫一個烤機的程序&#xff0c;小編結合官網資料 https://wiki.amsamotion.com/?title196&doc222查看其pdf說明文檔和調試工具并按照其工具寫一個烤機上位機根據項目負責人的要求…

云展廳:開啟數字化展示新時代

在科技飛速發展的今天&#xff0c;數字化浪潮正席卷各個行業&#xff0c;展覽展示領域也不例外。云展廳作為一種全新的展覽形式&#xff0c;正逐漸嶄露頭角&#xff0c;以其獨特的優勢和創新的技術應用&#xff0c;為觀眾帶來前所未有的觀展體驗&#xff0c;也為企業和機構提供…

硬件電路基礎學習

一、基礎元器件學習 1、電阻 1.1 作用 電阻的工作原理是基于歐姆定律&#xff0c;即電阻的阻值取決于其材料、長度和橫截面積。電阻的主要作用是限制電流&#xff0c;調節電壓和電流&#xff0c;以及保護電路。1.2 數值計算 歐姆定律 通過歐姆定律計算所需保護電阻的大小注意…

基于C++和人工智能(DeepSeek)實踐

基于C++和人工智能(如DeepSeek)實踐 以下是基于C++和人工智能(如DeepSeek或其他AI框架)的實際應用示例,涵蓋不同領域和技術方向,供參考: 基于C++和人工智能(如DeepSeek或其他AI框架)的實際應用示例 圖像識別與處理 人臉檢測:使用OpenCV和DNN模塊加載預訓練的Caffe…

書生浦語第五期L0G1000

完成 視頻課程學習&#xff0c;并在 https://chat.intern-ai.org.cn/ 平臺中實踐提示詞技巧&#xff0c;與 InternLM 和 InternVL 各完成 10 次對話記錄在飛書文檔中。 參加 浦語提示詞工程論文分類打榜賽&#xff0c;分數超過 40 分 InternLM InternVL 浦語提示詞工程論文分…