410 lines
15 KiB
C#
410 lines
15 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Security.Cryptography;
|
||
using System.Text;
|
||
using UnityEngine;
|
||
|
||
namespace HC
|
||
{
|
||
public class HCServer : HCSingleton<HCServer>
|
||
{
|
||
// #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<int, string, MiniAppConfig> callback)
|
||
{
|
||
var req = new Dictionary<string, object>
|
||
{
|
||
["iv"] = iv,
|
||
["signature"] = signature,
|
||
["encrypted_data"] = encryptedData
|
||
};
|
||
|
||
Post(URL_WX_DATA_DECRYPT, req, callback);
|
||
}
|
||
|
||
public void AppConfig(Action<int, string, MiniAppConfig> callback)
|
||
{
|
||
var req = new Dictionary<string, object>();
|
||
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<int, string, MiniPayResult> callback)
|
||
{
|
||
Post(URL_MINI_APP_PAY, new Dictionary<string, object>()
|
||
{
|
||
["bill_no"] = billNo,
|
||
["product_name"] = productName,
|
||
["amt"] = amt,
|
||
}, callback);
|
||
}
|
||
|
||
|
||
public void CheckSensitiveWords(int type, string text, Action<int, string, CensorTextResult> callback)
|
||
{
|
||
Post(URL_CENSOR_TEXT, new Dictionary<string, object>()
|
||
{
|
||
["biz_type"] = type,
|
||
["text"] = text
|
||
}, callback);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 支付下单接口
|
||
/// </summary>
|
||
/// <param name="productName">商品名称</param>
|
||
/// <param name="productId">商品id</param>
|
||
/// <param name="price">价格 (分)</param>
|
||
/// <param name="quantity">数量</param>
|
||
/// <param name="orderType">支付类型 1 = wechat</param>
|
||
/// <param name="currency">货币单位</param>
|
||
/// <param name="gameExtraParam"></param>
|
||
/// <param name="callback">回调</param>
|
||
public void OrderCreate(string productName, string productId, string price, int quantity, int orderType = 1, string currency = "CNY", string gameExtraParam = "", Action<int, string, OrderCreateResult> callback = null)
|
||
{
|
||
Post(URL_ORDER_CREATE, new Dictionary<string, object>
|
||
{
|
||
["product_name"] = productName,
|
||
["product_id"] = productId,
|
||
["price"] = price,
|
||
["quantity"] = quantity,
|
||
["order_type"] = orderType,
|
||
["currency"] = currency,
|
||
["game_extra_param"] = gameExtraParam,
|
||
}, callback);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 订单查询
|
||
/// </summary>
|
||
/// <param name="orderNo">订单号</param>
|
||
/// <param name="callback">回调</param>
|
||
public void OrderQuery(string orderNo, Action<int, string, OrderQueryResult> callback = null)
|
||
{
|
||
Post(URL_ORDER_QUERY, new Dictionary<string, object>
|
||
{
|
||
["order_no"] = orderNo
|
||
}, callback);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 订单奖励下发上报接口
|
||
/// </summary>
|
||
/// <param name="orderNo">订单号</param>
|
||
/// <param name="callback">回调</param>
|
||
public void OrderAwarded(string orderNo, Action<int, string, OrderQueryResult> callback = null)
|
||
{
|
||
Post(URL_ORDER_AWARDED, new Dictionary<string, object>
|
||
{
|
||
["order_no"] = orderNo
|
||
}, callback);
|
||
}
|
||
|
||
|
||
public void VerifyPurchase(Dictionary<string, object> args, Action<int, string, DataInfo> callback)
|
||
{
|
||
Post(URL_VERIFY_PURCHASE, args, callback);
|
||
}
|
||
|
||
public void Post<T>(string url, Dictionary<string, object> args, Action<int, string, T> callback,
|
||
Dictionary<string, string> headers = null) where T : new()
|
||
{
|
||
args ??= new Dictionary<string, object>();
|
||
|
||
headers ??= new Dictionary<string, string>();
|
||
|
||
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<T> resp = null;
|
||
var errorMsg = "";
|
||
|
||
try
|
||
{
|
||
res = DecryptResponseBody(res);
|
||
resp = JsonUtility.FromJson<Response<T>>(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<string, object> 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<string, object> 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<string, object> dictionary)
|
||
{
|
||
StringBuilder jsonBuilder = new StringBuilder();
|
||
jsonBuilder.Append("{");
|
||
|
||
int count = 0;
|
||
|
||
foreach (KeyValuePair<string, object> 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<string, object> nestedDictionary = value as Dictionary<string, object>;
|
||
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<T>
|
||
{
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 下单结果
|
||
/// </summary>
|
||
[Serializable]
|
||
public class OrderCreateResult
|
||
{
|
||
/// <summary>
|
||
/// 订单号
|
||
/// </summary>
|
||
public string order_no;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 查询结果
|
||
/// </summary>
|
||
[Serializable]
|
||
public class OrderQueryResult
|
||
{
|
||
public const int PayStatusNotPaid = 0;
|
||
public const int PayStatusPaid = 1;
|
||
public const int PayStatusPaymentFailed = 2;
|
||
|
||
/// <summary>
|
||
/// 订单状态 0:未支付, 1:已支付,2:支付失败(等其它原因)
|
||
/// </summary>
|
||
public int pay_status;
|
||
|
||
public string transaction_id;
|
||
public string pay_success_time;
|
||
public string game_extra_param;
|
||
}
|
||
|
||
[Serializable]
|
||
public class OrderAwardedResult
|
||
{
|
||
}
|
||
}
|
||
} |