using System; using System.Collections.Generic; using System.IO; using System.Security.Cryptography; using System.Text; using UnityEngine; using UnityEngine.Networking; using simplejson; public class YouLoftServices : MonoBehaviour { public static string URL_ROOT = "https://chinaapi.youloft.com"; /* channelPay */ /** * 渠道下单接口,根据不同渠道需要的数据不同 * 返回的数据结构不同,前端需要根据情况解析 */ public static string ChannelpayOrder = "/api/Channelpay/Order"; /** * 获取当前游戏,渠道的最新版本 * 返回结果无加密 */ public static string ChannelpayGetAppVersion = "/api/Channelpay/GetAppVersion"; /* Config */ /** * 获取服务器配置 * appver:如果不需要根据版本判断可以串0 * lastver:每次全新获取传0 */ public static string ConfigSync = "/api/Config/Sync"; /* Dedeem */ /** * 兑换码兑换接口 */ public static string DedeemConsume = "/api/Dedeem/Consume"; /* user */ /** * 登录接口微信-游客 * 登录之后返回token,每次操作验证token * 请求需要签名,返回数据无加密 */ public static string UserLogin = "/api/User/Login"; /** * 获取(提交)存档到服务器 * 1.每次启动游戏优先获取存档 * 2.当有需要更新的存档时提交服务器 * 提交存档时服务器会默认覆盖以前的存档不会做其他判断 * 请求需要签名,返回数据有加密 */ public static string UserSync = "/api/User/Sync"; /** * 获取(提交)存档到服务器 * 1.每次启动游戏优先获取存档 * 2.当有需要更新的存档时提交服务器 * 3.文件流不加入签名 * 4.返回字符串编码为ascii * 提交存档时服务器会默认覆盖以前的存档不会做其他判断 * 请求需要签名,返回数据有加密 */ public static string UserSyncStream = "/api/User/SyncStream"; /** * 心跳检测,判断当前客户端登陆是否有效 * 请求需要签名,返回数据无加密 */ public static string UserHeartbeat = "/api/User/Heartbeat"; /** * 用户下订接口,下单成功之后返回支付链接 * -跳转支付页面 */ public static string UserOrder = "/api/User/Order"; /** * 恢复购买,查看已完成的所有订单,以及消耗状态 * 请求需要签名,返回结果有加密 */ public static string UserResumepurchase = "/api/User/Resumepurchase"; /** * 订单消耗 * 请求需要签名,返回结果有加密 */ public static string UserConsume = "/api/User/Consume"; /** * 查询单个订单的状态 * 用来处理非消耗性商品,判断订单是否支付完成 * 返回结果有加密 */ public static string UserQueryOrder = "/api/User/QueryOrder"; /** * 获取排行榜 * 用户信息 */ public static string UserLeaderboard = "/api/User/Leaderboard"; /** * 提交用户分数 * 返回用户排行榜信息,返回结果无加密 */ public static string UserUploadScore = "/api/User/UploadScore"; /** * 获取用户自己的排名 */ public static string UserGetUserRank = "/api/User/GetUserRank"; /** * 提交反馈信息 */ public static string feedback = "/api/Feedback/Submit"; /** * 获取活动及对应物品接口(带渠道),返回结果有加密 */ public static string getActivitiesWithChannel = "/api/Activity/GetActivitiesWithChannel"; /** * 获取公告及补偿物品列表接口(带渠道),返回结果有加密 */ public static string gameAnoncementsWithChannel = "/api/Anoncement/GameAnoncementsWithChannel"; /** * 带渠道的兑换 */ public static string RedeemConsume = "/api/Redeem/Consume"; public static string RedeemUserConsume = "/api/Redeem/UserConsume"; /** * 上报付费,匹配点击记录,回调到广告方 */ public static string PayrecordSubmit = "/api/Payrecord/Submit"; #region 初始化 private static readonly YouLoftServices instance = null; static YouLoftServices() { if (string.IsNullOrEmpty(apiUrl) || string.IsNullOrEmpty(gameId) || string.IsNullOrEmpty(gameKey)) { Debug.LogError("没有配置YouLoftService配置"); } else { Debug.Log("YouLoftService: " + string.Join("=", apiUrl, gameId, gameKey)); var go = new GameObject(nameof(YouLoftServices)); instance = go.AddComponent(); DontDestroyOnLoad(go); } } //初始化 public static void Init() { } private static string apiUrl = "https://gameapi.pottingmob.com"; // private static string gameId = "80388d1f-8901-414c-b7f8-62d125e60200"; private static string gameId = PottingMobileContent.GameId; private static string gameKey = PottingMobileContent.GameSecret; // private static string gameKey = "12345678123456781234567812345678"; private static string tokenid = ""; private static string userid = ""; private static int GameServerArchiveversion { get; set; } #endregion /// /// 服务器配置 /// public static void ServerConfig(Action callback) { var jsonData = new JSONObject { ["appVer"] = "0", ["lastVer"] = 0, ["nonce"] = UnityEngine.Random.Range(0, 100000000).ToString(), ["timestamp"] = GetUnixTime(), ["gameid"] = gameId }; var signStr = GetSign(jsonData); jsonData["sign"] = signStr; PostJson(ConfigSync, jsonData, (i, s) => { if (i == 1) { var jsn = JSON.Parse(s); var data = DecryptDES(jsn["data"], gameKey.Substring(0, 8)); callback?.Invoke(1, data); } else callback?.Invoke(2, s); }); } /// /// 兑换码 /// public static void RedemptionCode(string code, Action callback) { var jsonData = new JSONObject { ["redeemcode"] = code, ["nonce"] = UnityEngine.Random.Range(0, 100000000).ToString(), ["timestamp"] = GetUnixTime(), ["gameid"] = gameId }; var signStr = GetSign(jsonData); jsonData["sign"] = signStr; PostJson(DedeemConsume, jsonData, (i, s) => { if (i == 1) { var jsn = JSON.Parse(s); var data = DecryptDES(jsn["data"], gameKey.Substring(0, 8)); callback?.Invoke(1, data); } else callback?.Invoke(2, s); }); } public static void _redeemConsume(string code,string channel, Action callback) { var jsonData = new JSONObject { ["redeemcode"] = code, ["channel"] = channel, ["nonce"] = UnityEngine.Random.Range(0, 100000000).ToString(), ["timestamp"] = GetUnixTime(), ["gameid"] = gameId }; var signStr = GetSign(jsonData); jsonData["sign"] = signStr; PostJson(RedeemConsume, jsonData, (i, s) => { if (i == 1) { var jsn = JSON.Parse(s); var data = DecryptDES(jsn["data"], gameKey.Substring(0, 8)); callback?.Invoke(1, data); } else callback?.Invoke(2, s); }); } public static void _redeemUserConsume(string code,string channel, Action callback) { var jsonData = new JSONObject { ["redeemcode"] = code, ["channel"] = channel, ["clientid"] = userid, ["nonce"] = UnityEngine.Random.Range(0, 100000000).ToString(), ["timestamp"] = GetUnixTime(), ["gameid"] = gameId }; var signStr = GetSign(jsonData); jsonData["sign"] = signStr; PostJson(RedeemUserConsume, jsonData, (i, s) => { if (i == 1) { var jsn = JSON.Parse(s); var data = DecryptDES(jsn["data"], gameKey.Substring(0, 8)); callback?.Invoke(1, data); } else callback?.Invoke(2, s); }); } /// ///服务器游戏版本 /// public static void GameServerVersion(int channel, Action callback) { var jsonData = new JSONObject { ["channel"] = channel, ["nonce"] = UnityEngine.Random.Range(0, 100000000).ToString(), ["timestamp"] = GetUnixTime(), ["gameid"] = gameId }; //0: 官方包 3: vivo 4: oppo 5: huawei 6: 4399 7: 应用包 var signStr = GetSign(jsonData); jsonData["sign"] = signStr; PostJson(ChannelpayGetAppVersion, jsonData, (i, s) => { if (i == 1) { var jsn = JSON.Parse(s); var data = DecryptDES(jsn["data"], gameKey.Substring(0, 8)); ShowLog("GameServerVersion:" + data); callback?.Invoke(1, jsn); } else callback?.Invoke(2, s); }); } /// /// 活动 /// /// /// /// public static void ActivityBy(string channel,string language, Action callback) { var jsonData = new JSONObject { ["channel"] = channel, ["language"] = language, ["nonce"] = UnityEngine.Random.Range(0, 100000000).ToString(), ["timestamp"] = GetUnixTime(), ["gameid"] = gameId }; var signStr = GetSign(jsonData); jsonData["sign"] = signStr; PostJson(getActivitiesWithChannel, jsonData, (i, s) => { if (i == 1) { var jsn = JSON.Parse(s); var data = DecryptDES(jsn["data"], gameKey.Substring(0, 8)); callback?.Invoke(1, data); } else callback?.Invoke(2, s); }); } /// /// 公告 /// /// /// /// /// public static void AnnouncementBy(string channel, string version, string language, Action callback) { var jsonData = new JSONObject { ["channel"] = channel, ["version"] = version, ["language"] = language, ["nonce"] = UnityEngine.Random.Range(0, 100000000).ToString(), ["timestamp"] = GetUnixTime(), ["gameid"] = gameId }; var signStr = GetSign(jsonData); jsonData["sign"] = signStr; PostJson(gameAnoncementsWithChannel, jsonData, (i, s) => { var jsn = JSON.Parse(s); var data = DecryptDES(jsn["data"], gameKey.Substring(0, 8)); callback?.Invoke(1, data); }); } // private static void MoniLogin() // { // Login(new UserGraph // { // userID = SystemInfo.deviceUniqueIdentifier, // nickName = SystemInfo.deviceUniqueIdentifier, // platform = 0 // }, (i, s) => { }); // } /// /// 登录 /// public static void Login(string id, int type, string name, string icon, Action callback) { var jsonData = new JSONObject { ["markid"] = id, ["type"] = type, ["name"] = name, ["icon"] = icon, ["nonce"] = UnityEngine.Random.Range(0, 100000000).ToString(), ["timestamp"] = GetUnixTime(), ["gameid"] = gameId }; var signStr = GetSign(jsonData); jsonData["sign"] = signStr; PostJson(UserLogin, jsonData, (i, s) => { if (i == 1) { var jsn = JSON.Parse(s); var data = jsn["data"]; tokenid = data["tokenid"]; userid = data["userid"]; callback?.Invoke(1, userid); } else callback?.Invoke(2, s); }); } /// /// 保存 or 提交存档 /// public static void CommitArchive(string archiveStr, long archiveVersion, Action callback) { ShowLog("CommitArchive_version = " + archiveVersion); var jsonData = GetBaseJsonData(); jsonData["archive"] = archiveStr; jsonData["archiveVersion"] = archiveVersion; var signStr = GetSign(jsonData); jsonData["sign"] = signStr; PostJson(UserSync, jsonData, (i, s) => { if (i == 1) { var jsn = JSON.Parse(s); var archive = GetDecryptDESDecodeMsg(jsn["data"]); var archiveJson = JSON.Parse(archive); var archiveData = archiveJson["Archive"]; int.TryParse(archiveJson["Archiveversion"], out var version); GameServerArchiveversion = version; callback?.Invoke(1, archiveData); } else callback?.Invoke(2, s); }); } /// /// 请求支付 (starUrl = resultData["data"] endUrl = resultData["returnAppLink"] orderid = resultData["orderid"]) /// public static void RequestOrder(string goodsid,string activityid, Action callback) { var jsonData = GetBaseJsonData(); jsonData["goodsid"] = goodsid; jsonData["activityid"] = activityid; var signStr = GetSign(jsonData); jsonData["sign"] = signStr; PostJson(UserOrder, jsonData, (i, s) => { if (i == 1) { var resultData = JSON.Parse(s); var starUrl = resultData["data"]; var endUrl = resultData["returnAppLink"]; var orderid = resultData["orderid"]; callback?.Invoke(1, starUrl, endUrl, orderid); } else callback?.Invoke(2, s, s, s); }); } public static void _channelPayOrder(int channel,string goodsid, Action callback) { var jsonData = GetBaseJsonData(); jsonData["channel"] = channel; jsonData["goodsid"] = goodsid; var signStr = GetSign(jsonData); jsonData["sign"] = signStr; PostJson(ChannelpayOrder, jsonData, (i, s) => { if (i == 1) { callback?.Invoke(1, s); } else callback?.Invoke(2,s); }); } public static void _userQueryOrder(string goodsid, string orderId, Action callback) { var jsonData = GetBaseJsonData(); jsonData["goodsid"] = goodsid; jsonData["orderid"] = orderId; var signStr = GetSign(jsonData); jsonData["sign"] = signStr; PostJson(UserQueryOrder, jsonData, (i, s) => { if (i == 1) { callback?.Invoke(1, s); } else callback?.Invoke(2,s); }); } public static void _userHeartbeat(Action callback) { var jsonData = GetBaseJsonData(); var signStr = GetSign(jsonData); jsonData["sign"] = signStr; PostJson(UserHeartbeat, jsonData, (i, s) => { if (i == 1) { callback?.Invoke(1, s); } else callback?.Invoke(2,s); }); } /// /// 消耗订单 /// public static void ConsumeOrder(string goodsid, string orderid, Action callback) { var jsonData = GetBaseJsonData(); jsonData["goodsid"] = goodsid; jsonData["orderid"] = orderid; var signStr = GetSign(jsonData); jsonData["sign"] = signStr; PostJson(UserConsume, jsonData, (i, s) => { if (i == 1) { var resultData = JSON.Parse(s); string orderData = GetDecryptDESDecodeMsg(resultData["data"]); var orderJson = JSON.Parse(orderData); var orderStatus = int.Parse(orderJson["Orderstatus"]); //0:not pay 1:pay 2:pay and use ShowLog($"订单状态数据:{orderJson},{orderStatus}"); if (orderStatus == 2) callback?.Invoke(1, orderid); else callback?.Invoke(2, "错误:订单状态=" + orderStatus.ToString()); } else callback?.Invoke(2, s); }); } //查询订单 public static void ResumeOrder(Actioncallback) { var jsonData = GetBaseJsonData(); var signStr = GetSign(jsonData); jsonData["sign"] = signStr; PostJson(UserResumepurchase, jsonData, (code, s) => { if (code == 1) { var resultData = JSON.Parse(s); string orderData = GetDecryptDESDecodeMsg(resultData["data"]); var orderJson = JSON.Parse(orderData); ShowLog("Resumepurchase data : " + orderJson); // List orderList = new List(); // for (int i = 0; i < orderJson.Count; i++) // { // var orderInfo = new OrderInfo(); // orderInfo.orderID = orderJson[i]["Orderid"]; // orderInfo.orderStatus = int.Parse(orderJson[i]["Orderstatus"]); // orderInfo.goodsID = orderJson[i]["Goodsid"]; // orderInfo.goodsInfo = orderJson[i]["Goodsinfo"]; // if (orderInfo.orderStatus == 1) orderList.Add(orderInfo); // else ShowLog("Resumepurchase orderStatus = " + orderInfo.orderStatus); // } callback?.Invoke(1, orderJson); } else callback?.Invoke(2, null); }); } public static void Feedback(string imsg, string contact, string savedata, string appVer, string deviceid, string countrycode, Action callback) { var jsonData = new JSONObject(); jsonData["msg"] = imsg; //用户提交的信息 jsonData["contact"] = contact; //联系方式 邮箱 qq 电话,微信 jsonData["documentinfo"] = savedata; //存档 jsonData["appVer"] = appVer; //应用版本 jsonData["device"] = deviceid; //设备信息 jsonData["countryCode"] = countrycode; //国家代码 jsonData["nonce"] = UnityEngine.Random.Range(0, 100000000).ToString(); jsonData["timestamp"] = GetUnixTime(); jsonData["gameid"] = gameId; var signStr = GetSign(jsonData); jsonData["sign"] = signStr; PostJson(feedback, jsonData, (i, s) => { callback?.Invoke(i == 1 ? 1 : 2, s); }); } //上传分数 public static void UploadScore(string rankingid, int score, string info, System.Action callback = null) { var jsonData = GetBaseJsonData(); jsonData["rankingid"] = rankingid; jsonData["score"] = score; jsonData["info"] = info; var signStr = GetSign(jsonData); jsonData["sign"] = signStr; PostJson(UserUploadScore, jsonData, (i, s) => { callback?.Invoke(i == 1); }); } public static void _userGetUserRank(string rankingid,System.Action callback) { var jsonData = GetBaseJsonData(); jsonData["rankingid"] = rankingid; var signStr = GetSign(jsonData); jsonData["sign"] = signStr; PostJson(UserGetUserRank, jsonData, (i, s) => { callback?.Invoke(i,s); }); } //获取排行榜链接 public static string GetLeaderboardLink(string rankId = "") { if (userid == null) return null; return "https://chinaapi.youloft.com/ranking/index?" + "gameid=" + gameId + "&userid=" + userid + "&rankid=" + rankId; } //服务基础数据 private static JSONObject GetBaseJsonData() { var jsonData = new JSONObject(); jsonData["userid"] = userid; jsonData["token"] = tokenid; jsonData["gameid"] = gameId; jsonData["nonce"] = UnityEngine.Random.Range(0, 100000000).ToString(); jsonData["timestamp"] = GetUnixTime(); return jsonData; } private static string CheckError(UnityWebRequest request) { int code = 0; string log = ""; if (request.error != null) code = -1; if (code == 0 && request.downloadHandler.text != null) { var resultData = JSON.Parse(request.downloadHandler.text); code = int.Parse(resultData["code"]); } switch (code) { case 200: case 2000: case 2100: case 2200: log = ""; break; case -1: log = request.error; break; case 4001: log = "错误的随机数"; break; case 4002: log = "错误的时间"; break; case 4003: log = "错误的签名"; break; case 4004: log = "错误的游戏"; break; case 4005: log = "错误的授权"; break; case 4006: log = "错误的订单"; break; case 4007: log = "错误的标识"; break; case 4008: log = "用户登录异常"; break; case 4009: log = "服务器维护中"; break; default: log = "其他"; break; } if (!string.IsNullOrEmpty(log)) Debug.LogError($"CheckErrcode : code = {code}, Msg = {log}"); return log; } private static string GetSign(JSONObject jsonData) { List keyList = new List(); foreach (var pair in jsonData) { var value = pair.Value; keyList.Add(value); } keyList.Add(gameKey); return EncrpytParam(keyList); } #region 加密解密 /// /// 对list ascii 排序,然后md5加密 /// /// /// public static string EncrpytParam(List paramList) { if (paramList == null) { throw new ArgumentNullException(nameof(paramList)); } paramList.Sort((a, b) => { return string.CompareOrdinal(a, b); }); var tempStr = string.Join("&", paramList); return MD5Hash(tempStr); } /// /// md5加密,32位大写字母,不包含短横线 /// /// /// public static string MD5Hash(string input) { using (var md5 = MD5.Create()) { var result = md5.ComputeHash(Encoding.UTF8.GetBytes(input)); var strResult = BitConverter.ToString(result); return strResult.Replace("-", ""); } } /// /// 获取解密秘钥(登录) /// /// public static string GetDescryptKey() { var key = tokenid.Replace("-", "").Substring(0, 4) + gameKey.Substring(0, 4); return key; } /// /// DES解密字符串 /// /// 待解密的字符串 /// 解密密钥,要求为8位,和加密密钥相同 /// 解密成功返回解密后的字符串,失败返源串 public static string DecryptDES(string decryptString, string decryptKey) { try { byte[] rgbKey = Encoding.UTF8.GetBytes(decryptKey); byte[] rgbIV = { 0x12, 0x34, 0x56, 0x45, 0x31, 0x16, 0x78, 0x67 }; byte[] inputByteArray = Convert.FromBase64String(decryptString); DESCryptoServiceProvider DCSP = new DESCryptoServiceProvider(); MemoryStream mStream = new MemoryStream(); CryptoStream cStream = new CryptoStream(mStream, DCSP.CreateDecryptor(rgbKey, rgbIV), CryptoStreamMode.Write); cStream.Write(inputByteArray, 0, inputByteArray.Length); cStream.FlushFinalBlock(); return Encoding.UTF8.GetString(mStream.ToArray()); } catch (Exception e) { Debug.LogError("DES解密字符串错误 : " + e); return decryptString; } } public static string GetGetDecryptDESWithoutToken(string decryptString) { return DecryptDES(decryptString, gameKey.Substring(0, 8)); } public static string GetDecryptDESDecodeMsg(string decryptString) { string descyptKey = GetDescryptKey(); string dataStr = DecryptDES(decryptString, descyptKey); return dataStr; } #endregion #region http interface private static void PostJson(string switcher, JSONObject jsonObject, Action onCompleted) { var jsn = jsonObject.ToString(); ShowLog($"PostJson:Url={switcher},data={jsn}"); HttpPost(instance, switcher, Encoding.Default.GetBytes(jsn), request => { var str = CheckError(request); if (string.IsNullOrEmpty(str)) { onCompleted?.Invoke(1, request.downloadHandler.text); } else onCompleted?.Invoke(2, str); }); } private static void HttpPost(MonoBehaviour holder, string switcher, byte[] body, Action onCompleted, int timeoutSeconds = -1) { holder.StartCoroutine(HttpPostHelper(holder, switcher, body, onCompleted, timeoutSeconds)); } private static IEnumerator HttpPostHelper(MonoBehaviour holder, string switcher, byte[] body, Action onCompleted, int timeoutSeconds) { using (UnityWebRequest request = new UnityWebRequest(apiUrl + switcher, "POST")) { request.uploadHandler = new UploadHandlerRaw(body); request.SetRequestHeader("Content-Type", "application/json;charset=utf-8"); request.downloadHandler = new DownloadHandlerBuffer(); if (timeoutSeconds > 0) request.timeout = timeoutSeconds; yield return request.SendWebRequest(); onCompleted?.Invoke(request); } } #endregion public class OrderInfo { public string orderID; public int orderStatus; public string goodsID; public string goodsInfo; } private static readonly DateTime _date = new DateTime(1970, 1, 1, 0, 0, 0, 0); public static int GetUnixTime() { TimeSpan ts = DateTime.UtcNow - _date; return Convert.ToInt32(ts.TotalSeconds); } public static string ShowLog(string msg) { Debug.Log(msg); return msg; } } public class UserGraph { public string userID; public int platform; //99:游客登录 0:自注册用户 1:微信登录 2:qq用户 3: vivo 4: oppo 5: huawei 6: 4399 7: 应用包 public string nickName; public string imageUrl; }