using System; using System.Collections.Generic; using System.Security.Cryptography; using System.Text; using UnityEngine; namespace HC { public class HCServer : HCSingleton { // #if !UNITY_EDITOR // public const string BASE_URL = "https://sandc.gameztsvc.com/ztp"; // // #else // public const string BASE_URL = "https://tk.dgtverse.cn/ztp"; // #endif public const string BASE_URL = "https://sandc.gameztsvc.com/ztp"; public const string URL_VERIFY_PURCHASE = "/game/report/iap"; public const string URL_LOGIN = "/game/user/login"; public const string URL_BIND = "/game/user/bind"; public const string URL_DELETE = "/game/user/delete"; public const string URL_MINI_APP_CONFIG = "/app/parameter"; public const string URL_MINI_APP_PAY = "/wechat/mini-app-pay"; public const string URL_ORDER_CREATE = "/order/create"; public const string URL_ORDER_QUERY = "/order/query"; public const string URL_ORDER_AWARDED = "/order/awarded"; public const string URL_CENSOR_TEXT = "/user/censor-text"; public const string URL_WX_DATA_DECRYPT = "/user/game-data-decrypt"; private const string XXTEA_KEY = "tkbff&(gBUjX#$s0710"; private const string secretKey = "tk~!@#$%^&*()_+0708"; public void WxDataDecrypt(string iv, string encryptedData, string signature, Action callback) { var req = new Dictionary { ["iv"] = iv, ["signature"] = signature, ["encrypted_data"] = encryptedData }; Post(URL_WX_DATA_DECRYPT, req, callback); } public void AppConfig(Action callback) { var req = new Dictionary(); int appType = 0; #if WEBGL_WX appType = 1; #elif WEBGL_BYTEDANCE appType = 2; #endif req["app_type"] = appType; Post(URL_MINI_APP_CONFIG, req, callback); } public void MiniAppPay(string billNo, string productName, int amt, Action callback) { Post(URL_MINI_APP_PAY, new Dictionary() { ["bill_no"] = billNo, ["product_name"] = productName, ["amt"] = amt, }, callback); } public void CheckSensitiveWords(int type, string text, Action callback) { Post(URL_CENSOR_TEXT, new Dictionary() { ["biz_type"] = type, ["text"] = text }, callback); } /// /// 支付下单接口 /// /// 商品名称 /// 商品id /// 价格 (分) /// 数量 /// 支付类型 1 = wechat /// 货币单位 /// /// 回调 public void OrderCreate(string productName, string productId, string price, int quantity, int orderType = 1, string currency = "CNY", string gameExtraParam = "", Action callback = null) { Post(URL_ORDER_CREATE, new Dictionary { ["product_name"] = productName, ["product_id"] = productId, ["price"] = price, ["quantity"] = quantity, ["order_type"] = orderType, ["currency"] = currency, ["game_extra_param"] = gameExtraParam, }, callback); } /// /// 订单查询 /// /// 订单号 /// 回调 public void OrderQuery(string orderNo, Action callback = null) { Post(URL_ORDER_QUERY, new Dictionary { ["order_no"] = orderNo }, callback); } /// /// 订单奖励下发上报接口 /// /// 订单号 /// 回调 public void OrderAwarded(string orderNo, Action callback = null) { Post(URL_ORDER_AWARDED, new Dictionary { ["order_no"] = orderNo }, callback); } public void VerifyPurchase(Dictionary args, Action callback) { Post(URL_VERIFY_PURCHASE, args, callback); } public void Post(string url, Dictionary args, Action callback, Dictionary headers = null) where T : new() { args ??= new Dictionary(); headers ??= new Dictionary(); AddBaseParameters(args); EncryptionParameters(args); var requestBody = ConvertDictionaryToJson(args); HCDebugger.LogDebug("[server] url=> " + url); HCDebugger.LogDebug("[server] requestBody=> " + requestBody); var encryptBody = EncryptRequestBody(requestBody); if (!string.IsNullOrEmpty(encryptBody)) { headers["params"] = encryptBody; headers["encrypt"] = true.ToString().ToLower(); requestBody = ""; } HCRequestHandler.Instance.SendPostRequest(BASE_URL + url, requestBody, headers, (code, res) => { HCDebugger.LogDebug($"[server] resp ====> code : {code} res : {res}"); if (code == 0) { Response resp = null; var errorMsg = ""; try { res = DecryptResponseBody(res); resp = JsonUtility.FromJson>(res); } catch (Exception e) { errorMsg = $"[server] Data conversion exception。{e.Message} {res}"; HCDebugger.LogError(errorMsg); } if (resp != null) { callback(resp.code, resp.msg, resp.data); } else { callback.Invoke(-1, errorMsg, new T()); } } else { callback.Invoke(code, res, new T()); } HCDebugger.LogDebug("[server]" + "[res] " + res); }); } private static string EncryptRequestBody(string requestBody) { var encryptRequestBodyBytes = HCXXTEA.Encrypt(Encoding.UTF8.GetBytes(requestBody), Encoding.UTF8.GetBytes(XXTEA_KEY)); var encryptRequestBody = HCConvertUtils.BytesToHexString(encryptRequestBodyBytes, false); HCDebugger.LogDebug($"[HCServer] [EncryptRequestBody] requestBody = {requestBody} encryptRequestBody = {encryptRequestBody}"); return encryptRequestBody; } private static string DecryptResponseBody(string responseBody) { var decryptResponseBodyBytes = HCXXTEA.Decrypt(HCConvertUtils.HexStringToBytes(responseBody), Encoding.UTF8.GetBytes(XXTEA_KEY)); var decryptResponseBody = Encoding.UTF8.GetString(decryptResponseBodyBytes); HCDebugger.LogDebug($"[HCServer] [DecryptResponseBody] responseBody = {responseBody} decryptResponseBody = {decryptResponseBody}"); return decryptResponseBody; } private static string GetMD5Hash(string input) { using (var md5 = MD5.Create()) { var inputBytes = Encoding.ASCII.GetBytes(input); var hashBytes = md5.ComputeHash(inputBytes); var builder = new StringBuilder(); foreach (var t in hashBytes) { builder.Append(t.ToString("x2")); // Convert byte to hexadecimal string } return builder.ToString(); } } private static void EncryptionParameters(Dictionary args) { var signString = $"{secretKey}platform={args["platform"]}packagename={args["package_name"]}channel={args["channel"]}appversion={args["app_version"]}appversioncode={args["app_version_code"]}language={args["language"]}ip={args["ip"]}ts={args["ts"]}"; var sign = GetMD5Hash(signString); args["sign"] = sign; } private static void AddBaseParameters(IDictionary args) { args.AddIfNotExists("unity_sdk_version", HCNativeInterface.SDKVersion); args.AddIfNotExists("package_name", HCInfoUtils.GetBundleID()); args.AddIfNotExists("platform", HCInfoUtils.GetPlatform()); args.AddIfNotExists("channel", HCInfoUtils.GetChannel()); args.AddIfNotExists("device_type", HCInfoUtils.GetDeviceType()); args.AddIfNotExists("app_version", HCInfoUtils.GetAppVersion()); args.AddIfNotExists("app_version_code", HCInfoUtils.GetAppVersionCode()); args.AddIfNotExists("language", HCInfoUtils.GetLanguage()); args.AddIfNotExists("ip", ""); args.AddIfNotExists("device_id", HCTools.GetPlayerPrefsString(HCAccountManager.Instance.staticSaveKey_DeviceID)); args.AddIfNotExists("app_u8id", ""); args.AddIfNotExists("app_name", Application.productName); args.AddIfNotExists("model", HCInfoUtils.GetModel()); args.AddIfNotExists("brand", HCInfoUtils.GetBrand()); args.AddIfNotExists("system_name", HCInfoUtils.GetSystemName()); args.AddIfNotExists("screen_size", ""); args.AddIfNotExists("network_type", ""); args.AddIfNotExists("ua", ""); args.AddIfNotExists("idfa", ""); args.AddIfNotExists("idfv", ""); args.AddIfNotExists("gaid", ""); args.AddIfNotExists("oaid", ""); args.AddIfNotExists("android_id", ""); args.AddIfNotExists("adid", ""); args.AddIfNotExists("fire_adid", ""); args.AddIfNotExists("ad_network", ""); args.AddIfNotExists("campaign", ""); args.AddIfNotExists("adgroup", ""); args.AddIfNotExists("creative", ""); args.AddIfNotExists("referrer", ""); args.AddIfNotExists("memory", ""); args.AddIfNotExists("memory_usage", ""); args.AddIfNotExists("country", ""); args.AddIfNotExists("user_id", HCTools.GetPlayerPrefsString(HCAccountManager.staticSaveKey_UserID)); args.AddIfNotExists("user_type", HCTools.GetPlayerPrefsInt(HCAccountManager.staticSaveKey_UserType)); args.AddIfNotExists("ss_distinct_id", HCTools.GetPlayerPrefsString(HCTools.SS_DISTINCT_ID)); args.AddIfNotExists("ss_account_id", HCTools.GetPlayerPrefsString(HCAccountManager.staticSaveKey_UserID)); args.AddIfNotExists("ts", "" + HCTimeTools.GetCurrentTimestamp()); foreach (var (key, value) in HCInfoUtils.GetBaseInfo()) { args.AddIfNotExists(key, value); } } public static string ConvertDictionaryToJson(Dictionary dictionary) { StringBuilder jsonBuilder = new StringBuilder(); jsonBuilder.Append("{"); int count = 0; foreach (KeyValuePair kvp in dictionary) { string key = kvp.Key; object value = kvp.Value; jsonBuilder.AppendFormat("\"{0}\":", key); if (value is int || value is float || value is bool) { jsonBuilder.Append(value.ToString().ToLower()); } else if (value == null) { jsonBuilder.Append("\"\""); } else if (value is string) { jsonBuilder.AppendFormat("\"{0}\"", value); } else { // Handle other types or nested dictionaries recursively Dictionary nestedDictionary = value as Dictionary; if (nestedDictionary != null) { string nestedJson = ConvertDictionaryToJson(nestedDictionary); jsonBuilder.Append(nestedJson); } } count++; if (count < dictionary.Count) { jsonBuilder.Append(","); } } jsonBuilder.Append("}"); return jsonBuilder.ToString(); } [Serializable] public class Response { public int code = -1; public string msg; public T data; public int ts; } [Serializable] public class DataInfo { public int illegal_order = -1; } [Serializable] public class MiniAppConfig { public string content; } [Serializable] public class MiniPayResult { public string order_no; public int balance; } [Serializable] public class CensorTextResult { public string has_sensitive; public string content; } /// /// 下单结果 /// [Serializable] public class OrderCreateResult { /// /// 订单号 /// public string order_no; } /// /// 查询结果 /// [Serializable] public class OrderQueryResult { public const int PayStatusNotPaid = 0; public const int PayStatusPaid = 1; public const int PayStatusPaymentFailed = 2; /// /// 订单状态 0:未支付, 1:已支付,2:支付失败(等其它原因) /// public int pay_status; public string transaction_id; public string pay_success_time; public string game_extra_param; } [Serializable] public class OrderAwardedResult { } } }