using System; using System.Collections.Generic; using System.Net.Http; using System.Text; using System.Text.RegularExpressions; using System.Web; using LitJson; using ProtoCSStruct; using SimpleHttpServer; using Sog; using Sog.Crypto; using System.Text.Json; using System.Security.Cryptography; using Google.Protobuf.WellKnownTypes; using Org.BouncyCastle.Bcpg; using Newtonsoft.Json.Linq; using TencentCloud.Ecm.V20190719.Models; namespace HttpProxyPay { // 英雄支付回调状态类型 public enum HeroPayCallbackStatus { order = 1, // 下单 paySucc = 2, // 支付成功 refund = 3, // 退款 } // 英雄支付回调结构 public class HeroPayCallbackArgs { public string orderId; public HeroPayCallbackStatus status; public string pcode; } public class HeroWebPayData { public string roleId { get; set; } public string cpOrderNo { get; set; } public string cpNotifyUrl { get; set; } public string cpCustomMsg { get; set; } public string cpSku { get; set; } } public class HeroWebPayGoogleRes { public int code { get; set; } public HeroWebPayData data { get; set; } public string msg { get; set; } } public static class HttpParamsUtils { // 解析k1=v1&k2=v2&k3=v3...格式的字符串 public static Dictionary ParseByKeyValue(string str) { str = str.Trim(); if (string.IsNullOrEmpty(str)) { return null; } int idx = str.IndexOf('?'); if (idx != -1) { str = str.Substring(idx + 1); } var map = new Dictionary(); string[] allParams = str.Split('&'); foreach (var param in allParams) { string[] namevalue = param.Split('='); if (namevalue != null && namevalue.Length == 2) { map[namevalue[0]] = namevalue[1]; } } return map; } } public static class HttpContextCache { private static object httpCtxCacheLocker = new object(); private static Dictionary httpCtxCache = new Dictionary(); private static List waitDeleteCtx = new List(); public static bool AddContext(HttpContext httpCtx) { lock (httpCtxCacheLocker) { if (httpCtxCache.ContainsKey(httpCtx.id)) { TraceLog.Error("HeroPayHttpHandler.AddContext fail, id {0} already exist", httpCtx.id); return false; } httpCtx.CreateTime = HttpProxyPayServerUtils.GetTimeSecond(); httpCtxCache.Add(httpCtx.id, httpCtx); } return true; } public static bool RemoveContext(HttpContext httpCtx) { lock (httpCtxCacheLocker) { waitDeleteCtx.Add(httpCtx); return httpCtxCache.Remove(httpCtx.id); } } public static HttpContext GetContext(uint id) { lock (httpCtxCacheLocker) { if (httpCtxCache.TryGetValue(id, out HttpContext ctx)) { return ctx; } } return null; } public static void TickTimeoutClient(long nowSec) { //30秒超时 lock (httpCtxCacheLocker) { List timeoutList = new List(); foreach(var hc in httpCtxCache) { if(nowSec - hc.Value.CreateTime > 30) { timeoutList.Add(hc.Value); } } // 等待删除的队列 for (int i = waitDeleteCtx.Count - 1; i >= 0; i--) { if (nowSec - waitDeleteCtx[i].CreateTime > 30) { timeoutList.Add(waitDeleteCtx[i]); waitDeleteCtx.RemoveAt(i); } } foreach (var hc in timeoutList) { try { hc.client.Close(); } catch(Exception ex) { } httpCtxCache.Remove(hc.id); } timeoutList.Clear(); } } } public static class HeroPayHttpHandler { // 网页支付查询使用的key private static string _WebPayKey = "4os5dyf7z39b66i9fxz7"; public static void Init(HttpProxyPayServerConfig config) { if (! string.IsNullOrEmpty(config.heroWebPayKey)) { _WebPayKey = config.heroWebPayKey; } TraceLog.Trace("HeroPayHttpHandler.Init web pay key {0}", _WebPayKey); } public static void HeroPayCallback_SEA(HttpContext httpCtx) { TraceLog.Error("HeroPayHttpHandler.HeroPayCallback_SEA {0}", httpCtx.id); HeroPayCallback(httpCtx, HeroUSDKSvc._CALLBACK_KEY[(int)HeroUSDKProj.SEA], HeroUSDKProj.SEA); } public static void HeroPayCallback_JP(HttpContext httpCtx) { TraceLog.Trace("HeroPayHttpHandler.HeroPayCallback_JP {0}", httpCtx.id); HeroPayCallback(httpCtx, HeroUSDKSvc._CALLBACK_KEY[(int)HeroUSDKProj.JP], HeroUSDKProj.JP); } public static void HeroPayCallback_KR(HttpContext httpCtx) { TraceLog.Trace("HeroPayHttpHandler.HeroPayCallback_KR {0}", httpCtx.id); HeroPayCallback(httpCtx, HeroUSDKSvc._CALLBACK_KEY[(int)HeroUSDKProj.KR], HeroUSDKProj.KR); } public static void HeroPayCallback_ZH(HttpContext httpCtx) { TraceLog.Trace("HeroPayHttpHandler.HeroPayCallback_ZH {0}", httpCtx.id); HeroPayCallback(httpCtx, HeroUSDKSvc._CALLBACK_KEY[(int)HeroUSDKProj.ZH], HeroUSDKProj.ZH); } public static void HeroPayCallback_JP2(HttpContext httpCtx) { TraceLog.Trace("HeroPayHttpHandler.HeroPayCallback_JP2 {0}", httpCtx.id); HeroPayCallback(httpCtx, HeroUSDKSvc._CALLBACK_KEY[(int)HeroUSDKProj.JP2], HeroUSDKProj.JP2); } public static void HeroPayCallback_EN(HttpContext httpCtx) { TraceLog.Trace("HeroPayHttpHandler.HeroPayCallback_EN {0}", httpCtx.id); HeroPayCallback(httpCtx, HeroUSDKSvc._CALLBACK_KEY[(int)HeroUSDKProj.EN], HeroUSDKProj.EN); } /* 可以用postman 发送http post请求来处理出错的订单,前提是日志里有之前callback的记录 POST url 为 http://ip:31006/heropay/ 注意不能在url里多填内容,只能是上面那个,参数填在body里 参数类型为x-www-form-urlencoded,2个参数,一个data,一个sign data里注意转义,日志里打印的 %3D 要改成 = */ public static void HeroPayCallback(HttpContext httpCtx, string cbKey, HeroUSDKProj proj) { if (httpCtx.httpRequest == null) { TraceLog.Trace("HeroPayHttpHandler.HeroPayCallback ctx id {0} httpRequest is null", httpCtx.id); return; } TraceLog.Trace("HeroPayHttpHandler.HeroPayCallback ctx id {0} content {1}", httpCtx.id, httpCtx.httpRequest.Content); var postParams = HttpParamsUtils.ParseByKeyValue(httpCtx.httpRequest.Content); if (! postParams.TryGetValue("data", out string recvPayData)) { TraceLog.Error("HeroPayHttpHandler.HeroPayCallback 'data' is null"); return; } if (! postParams.TryGetValue("sign", out string sign)) { TraceLog.Error("HeroPayHttpHandler.HeroPayCallback 'sign' is null"); return; } // url解码 string payData = HttpUtility.UrlDecode(recvPayData); // 校验签名 var allParams = new Dictionary(); allParams["data"] = payData; string calcSign = HeroUSDKSecurity.CalcSign(cbKey, allParams); if (calcSign != sign) { TraceLog.Error("HeroPayHttpHandler.HeroPayCallback sign not same, calc {0} req {1}", calcSign, sign); return; } // base64解码 byte[] tmp = Convert.FromBase64String(payData); var payStr = Encoding.UTF8.GetString(tmp); TraceLog.Trace("HeroPayHttpHandler.HeroPayCallback {0}", payStr); JsonData payDataJson = JsonMapper.ToObject(payStr); string orderId = payDataJson["gameOrder"].ToString(); string[] split = orderId.Split('-'); // uid-time-orderIdSeq-serverId-payitemId-payParam1-payParam2-paymentId-payUrlId if (split.Length < 8) { TraceLog.Error("HeroPayHttpHandler.HeroPayCallback invalid orderId {0}, need >= 8 params", orderId); return; } int payUrlId = 0; if (split.Length >= 9) { string strpayUrlId = split[8]; if (!string.IsNullOrEmpty(strpayUrlId)) { if (!int.TryParse(strpayUrlId, out payUrlId)) { TraceLog.Error("HeroPayHttpHandler.HeroPayCallback invalid payUrlId {0}", strpayUrlId); return; } } } // 打印错误log方便查询所有请求 string orderId3rd = payDataJson["orderNo"].ToString(); TraceLog.Error("HeroPayHttpHandler.HeroPayCallback orderId {0} order3rd {1}", orderId, orderId3rd); var svrData = HttpProxyPayServerUtils.GetHttpProxyServerData(); if (payUrlId > 0 && payUrlId != svrData.selfPayId) { if (payUrlId < svrData.payUrlCfg.Length && svrData.payUrlCfg[payUrlId] != null) { string url = svrData.payUrlCfg[payUrlId].url; if (proj == HeroUSDKProj.JP) { url = url.Replace("heropay_sea", "heropay_jp"); }else if (proj == HeroUSDKProj.KR) { url = url.Replace("heropay_sea", "heropay_kr"); } // 支付转发是正常情况, 打印错误log方便查询转发到内网的请求 TraceLog.Error("HeroPayHttpHandler.HeroPayCallback orderId {0} order3rd {1} trans to {2}", orderId, payDataJson["orderNo"].ToString(), url); var trans = new Dictionary(); // 这里要使用urldecode之后的数据, 否则转发的时候又会再次被urlencode trans["data"] = payData; trans["sign"] = sign; var content = new FormUrlEncodedContent(trans); string ret = HttpUtils.HttpPost(url, content, out bool exception); if (string.IsNullOrEmpty(ret)) { TraceLog.Error("HeroPayHttpHandler.HeroPayCallback trans fail, HttpPost return null"); } else if (ret == "SUCCESS") { TraceLog.Error("HeroPayHttpHandler.HeroPayCallback trans succ, remote paysvr res 'SUCCESS'", ret); // 转发给英雄的支付服务器 httpCtx.httpResponse = new HttpResponse { StatusCode = "200", Content = Encoding.UTF8.GetBytes("SUCCESS") }; HttpServer.SendResponse(httpCtx); } else { TraceLog.Error("HeroPayHttpHandler.HeroPayCallback remote paysvr res fail {0}", ret); } } else { TraceLog.Error("HeroPayHttpHandler.HeroPayCallback orderId {0} payUrlId {1} no url", orderId, payUrlId); } return; } if (! long.TryParse(split[0], out long uid)) { TraceLog.Error("HeroPayHttpHandler.HeroPayCallback invalid uid {0}", split[0]); return; } if (! HttpContextCache.AddContext(httpCtx)) { TraceLog.Error("HeroPayHttpHandler.HeroPayCallback add httpContext failed, ctx id {0}", httpCtx.id); return; } if (! int.TryParse(split[5], out int payParam1)) { TraceLog.Error("HeroPayHttpHandler.HeroPayCallback invalid payParam1 {0}", split[5]); payParam1 = (int)PayUtils.GetParam1FromOrderId(orderId); if (payParam1 >= 0) { TraceLog.Error("HeroPayHttpHandler.HeroPayCallback invalid payParam1 {0} {1}", split[5], payParam1); return; } } if ( !int.TryParse(split[6], out int payParam2)) { TraceLog.Error("HeroPayHttpHandler.HeroPayCallback invalid payParam2 {0}", split[6]); return; } // 英雄支付的子类型 int subPayType = 0; if (payDataJson.ContainsKey("payType")) { int.TryParse(payDataJson["payType"].ToString(), out subPayType); } string currency = null; // 英雄不保证回调中的币种currency字段是否存在 if (payDataJson.ContainsKey("currency")) { currency = payDataJson["currency"].ToString(); } if (string.IsNullOrEmpty(currency)) { currency = ((Currency)((int)proj)).ToString(); // 对方空就填0, 后面好算账 } string am = payDataJson["amount"].ToString(); if (!double.TryParse(am, out double amount)) { TraceLog.Error("HeroPayHttpHandler.HeroPayCallback invalid amount {0}", am); return; } // 英雄支付的子类型 int sandbox = 0; if (payDataJson.ContainsKey("sandbox")) { int.TryParse(payDataJson["sandbox"].ToString(), out sandbox); } TraceLog.Trace("HeroPayHttpHandler.HeroPayCallback amount str {0} double {1}", am, amount); // 支付id不一致, 说明订单请求金额跟实际支付的金额不一致 string goodsId = payDataJson["goodsId"].ToString(); if (! goodsId.Equals(split[7])) { TraceLog.Error("HeroPayHttpHandler.HeroPayCallback invalid paymentId, req {0} res {1}", goodsId, split[7]); SSPayGoogleSuccessRes res = new SSPayGoogleSuccessRes {Uid = uid, PayType = (int)PayType.Hero }; res.Ret = (int) CSErrCode.PayMoneyNotEqual; res.OrderId.SetString(orderId); res.OrderId3rd.SetString(orderId3rd); res.ItemID = int.Parse(split[4]); res.PayParam1 = payParam1; res.PayParam2 = payParam2; res.PayTime3rd = ConfigStringTimeParse.ParseConfigTime(payDataJson["payTime"].ToString()); res.GmTestPay = payDataJson["channelUid"].ToString().Equals("0"); res.Currency.SetString(currency); res.Amount = Convert.ToUInt32(amount * 100); if (!uint.TryParse(split[3], out uint serverId)) { TraceLog.Error("HeroPayHttpHandler.HeroPayCallback invalid uid {0}", split[0]); return; } // 发给world处理, 注意serverId是gamesvr的id var worldId = ServerIDUtils.GetLevel1ServerIDByType(serverId, (int)ServerType.World, 1); HttpProxyPayServerUtils.GetPacketSender().SendToServerByID(worldId, (int)SSGameMsgID.PayGoogleSuccessRes, ref res, uid); return; } // 保存回调上下文 httpCtx.callbackArgs = new HeroPayCallbackArgs {orderId = orderId, status = HeroPayCallbackStatus.paySucc}; var req = new SSPayGoogleSuccessReq {Uid = uid, PayType = (int) PayType.Hero, CheckSuccess3rd = 1}; req.HttpCtxId = httpCtx.id; req.ItemID = int.Parse(split[4]); req.PurchaseData.SetString(payData); req.Signature.SetString(sign); req.OrderId.SetString(orderId); req.OrderId3rd.SetString(payDataJson["orderNo"].ToString()); req.PayParam1 = payParam1; req.PayParam2 = payParam2; req.GmTestPay = payDataJson["channelUid"].ToString().Equals("0"); req.Amount = Convert.ToUInt32(amount * 100); req.Currency.SetString(currency); // 币种 req.SubPayType = subPayType; req.PayTime3rd = ConfigStringTimeParse.ParseConfigTime(payDataJson["payTime"].ToString()); req.Sandbox = sandbox; // 如果第三方时间解析不出来就用服务器当前时间 if (req.PayTime3rd == 0) { req.PayTime3rd = HttpProxyPayServerUtils.GetTimeSecond(); } var desc = PayDiamondDescMgr.Instance.GetConfig(int.Parse(split[4])); uint dbServerID = DBServerSelect.GetDBServerID(uid); HttpProxyPayServerUtils.GetPacketSender().SendToServerByID(dbServerID, (int)SSGameMsgID.PayGoogleSuccessReq, ref req, uid); } public static void HeroRefundCallback(HttpContext httpCtx, string cbKey) { TraceLog.Trace("HeroPayHttpHandler.HeroRefundCallback content {0}", httpCtx.httpRequest.Content); var postParams = HttpParamsUtils.ParseByKeyValue(httpCtx.httpRequest.Content); if (!postParams.TryGetValue("data", out string payData)) { TraceLog.Error("HeroPayHttpHandler.HeroRefundCallback 'data' is null"); return; } if (!postParams.TryGetValue("sign", out string sign)) { TraceLog.Error("HeroPayHttpHandler.HeroRefundCallback 'sign' is null"); return; } // url解码 payData = HttpUtility.UrlDecode(payData); // 校验签名 var allParams = new Dictionary(); allParams["data"] = payData; string calcSign = HeroUSDKSecurity.CalcSign(cbKey, allParams); if (calcSign != sign) { TraceLog.Error("HeroPayHttpHandler.HeroRefundCallback sign not same, calc {0} req {1}", calcSign, sign); return; } // base64解码 byte[] tmp = Convert.FromBase64String(payData); JsonData payDataJson = JsonMapper.ToObject(Encoding.UTF8.GetString(tmp)); string orderId = payDataJson["gameOrder"].ToString(); string[] split = orderId.Split('-'); // uid-time-orderIdSeq-serverId-payitemId-payParam1-payParam2 if (split.Length != 7) { TraceLog.Error("HeroPayHttpHandler.HeroRefundCallback invalid orderId {0}", orderId); return; } if (!long.TryParse(split[0], out long uid)) { TraceLog.Error("HeroPayHttpHandler.HeroRefundCallback invalid uid {0}", split[0]); return; } if (! HttpContextCache.AddContext(httpCtx)) { TraceLog.Error("HeroPayHttpHandler.HeroRefundCallback add httpContext failed, ctx id {0}", httpCtx.id); return; } // 保存订单号 httpCtx.callbackArgs = new HeroPayCallbackArgs { orderId = orderId, status = HeroPayCallbackStatus.refund }; var req = new SSPaySendDBRefundReq { Uid = uid, HttpCtxId = httpCtx.id}; req.OrderId.SetString(orderId); req.OrderId3rd.SetString(payDataJson["orderNo"].ToString()); req.RefundTime = ConfigStringTimeParse.ParseConfigTime(payDataJson["refund_time"].ToString()); uint dbServerID = DBServerSelect.GetDBServerID(uid); HttpProxyPayServerUtils.GetPacketSender().SendToServerByID(dbServerID, (int)SSGameMsgID.PaySendDbRefundReq, ref req, uid); } public static string HeroPayDecode(string str, byte[] keys) { if (string.IsNullOrEmpty(str) || keys == null) { return null; } // 需要url_decode if (str[0] == '%') { str = HttpUtility.UrlDecode(str, Encoding.UTF8); } List data_enc = new List(); Match m = Regex.Match(str, "\\d+"); while (m.Success) { data_enc.Add(Convert.ToByte(m.Value)); m = m.NextMatch(); } byte[] data = new byte[data_enc.Count]; for (int i = 0; i < data_enc.Count; i++) { data[i] = (byte) (data_enc[i] - (0xff & keys[i % keys.Length])); } return Encoding.UTF8.GetString(data); } public static void HomeAdCallBack(HttpContext httpCtx) { if (httpCtx.httpRequest == null) { TraceLog.Error("HeroPayHttpHandler.HomeAdCallBack ctx id {0} httpRequest is null", httpCtx.id); return; } TraceLog.Trace("HeroPayHttpHandler.HomeAdCallBack Url:{0} Content:{1}", httpCtx.httpRequest.Url, httpCtx.httpRequest.Content); Dictionary allParams = null; if (httpCtx.httpRequest.Url.IndexOf('?') >= 0) { allParams = HttpParamsUtils.ParseByKeyValue(httpCtx.httpRequest.Url); } else { allParams = HttpParamsUtils.ParseByKeyValue(httpCtx.httpRequest.Content); } // url解码 foreach (var kv in allParams) { allParams[kv.Key] = HttpUtility.UrlDecode(kv.Value); } var errorFlag = false; //获取玩家uid long userId = 0; if (!allParams.TryGetValue("user_id", out string user_id)) { errorFlag = true; } else { userId = Convert.ToInt64(user_id); if (userId <= 0) errorFlag = true; } var player = HttpProxyPayServerUtils.GetPlayerTableOp().GetPlayer(userId); if (player == null) { TraceLog.Error("HeroPayHttpHandler.HomeAdCallBack get player is null! player:{0}", userId); httpCtx.httpResponse = new HttpResponse { StatusCode = ((int)HttpErrorStatusCode.OtherError).ToString(), Content = Encoding.UTF8.GetBytes("ERROR") }; HttpServer.SendResponse(httpCtx); return; } if (!allParams.TryGetValue("trans_id", out string trans_id)) errorFlag = true; if (!allParams.TryGetValue("extra_data", out string extra_data)) errorFlag = true; if (!allParams.TryGetValue("placement_id", out string placement_id)) errorFlag = true; if (!allParams.TryGetValue("adsource_id", out string adsource_id)) errorFlag = true; if (!allParams.TryGetValue("reward_amount", out string reward_amount)) errorFlag = true; if (!allParams.TryGetValue("reward_name", out string reward_name)) errorFlag = true; if (!allParams.TryGetValue("sign", out string sign)) errorFlag = true; allParams.TryGetValue("ilrd", out string ilrd); if (errorFlag) { TraceLog.Error("HeroPayHttpHandler.HomeAdCallBack info error!"); httpCtx.httpResponse = new HttpResponse { StatusCode = ((int)HttpErrorStatusCode.OtherError).ToString(), Content = Encoding.UTF8.GetBytes("ERROR")}; HttpServer.SendResponse(httpCtx); return; } string sec_key = HttpProxyPayServerUtils.GetServerConfig().adkey; string signStr; if (string.IsNullOrEmpty(ilrd)) { signStr = "trans_id=" + trans_id + "&placement_id=" + placement_id + "&adsource_id=" + adsource_id + "&reward_amount=" + reward_amount + "&reward_name=" + reward_name + "&sec_key=" + sec_key; } else { signStr = "trans_id=" + trans_id + "&placement_id=" + placement_id + "&adsource_id=" + adsource_id + "&reward_amount=" + reward_amount + "&reward_name=" + reward_name + "&sec_key=" + sec_key + "&ilrd=" + ilrd; } var md5sign = HashHelper.MD5Utf8String(signStr); TraceLog.Trace("HeroPayHttpHandler.HomeAdCallBack sign key:{0} sign:{1} md5Sign:{2}", sec_key, sign, md5sign); if (sign != md5sign) { TraceLog.Error("HeroPayHttpHandler.HomeAdCallBack sign error sign:{0} md5Sign:{1}", sign, md5sign); httpCtx.httpResponse = new HttpResponse { StatusCode = ((int)HttpErrorStatusCode.SignError).ToString(), Content = Encoding.UTF8.GetBytes("ERROR") }; HttpServer.SendResponse(httpCtx); return; } httpCtx.httpResponse = new HttpResponse { StatusCode = ((int)HttpErrorStatusCode.Success).ToString(), Content = Encoding.UTF8.GetBytes("SUCCESS") }; HttpServer.SendResponse(httpCtx); //将信息发送给所有的逻辑服 SSHomeAdSendAwardSyn syn = new SSHomeAdSendAwardSyn(); HttpProxyPayServerUtils.GetPacketSender().SendToServerByID(player.worldSvrId, (int)SSGameMsgID.HomeAdSendAwardSyn, ref syn, userId); } private static void SendWebPayErrorMsg(HttpContext httpCtx, string errorMsg) { var httpRes = new HeroWebPayGoogleRes{code = -1, msg = errorMsg}; string ret = JsonSerializer.Serialize(httpRes); TraceLog.Trace("HeroPayHttpHandler.SendWebPayErrorMsg httpContext {0} msg {1}", httpCtx.id, errorMsg); httpCtx.httpResponse = new HttpResponse { StatusCode = "200", Content = Encoding.UTF8.GetBytes(ret) }; HttpServer.SendResponse(httpCtx); } public static void HomeMiniMsgBack(HttpContext httpCtx) { if (httpCtx.httpRequest == null) { TraceLog.Trace("HeroPayHttpHandler.HomeMiniMsgBack ctx id {0} httpRequest is null", httpCtx.id); return; } httpCtx.httpResponse = new HttpResponse { StatusCode = ((int)HttpErrorStatusCode.Success).ToString(), Content = Encoding.UTF8.GetBytes("SUCCESS") }; HttpServer.SendResponse(httpCtx); } /// /// 小程序支付的单独接口 /// /// public static void HomeMiniCallBack(HttpContext httpCtx) { if (httpCtx.httpRequest == null) { TraceLog.Trace("HeroPayHttpHandler.HomeMiniCallBack ctx id {0} httpRequest is null", httpCtx.id); return; } TraceLog.Trace("HeroPayHttpHandler.HomeMiniCallBack ctx id {0} content {1}, url {2}, path={3}, header{4}", httpCtx.id, httpCtx.httpRequest.Content, httpCtx.httpRequest.Url, httpCtx.httpRequest.Path, httpCtx.httpRequest.Headers); var postParams = HttpParamsUtils.ParseByKeyValue(httpCtx.httpRequest.Path); if (!postParams.TryGetValue("signature", out string signature)) { //TraceLog.Error("HeroPayHttpHandler.HomeMiniMsgBack 'signature' is null"); //jsapi回调 WXHandler.HomeMiniJSAPICallBack(httpCtx); return; } if (!postParams.TryGetValue("msg_signature", out string msg_signature)) { TraceLog.Error("HeroPayHttpHandler.HomeMiniCallBack 'msg_signature' is null"); //return; } if (!postParams.TryGetValue("timestamp", out string timestamp)) { TraceLog.Error("HeroPayHttpHandler.HomeMiniCallBack 'timestamp' is null"); return; } if (!postParams.TryGetValue("nonce", out string nonce)) { TraceLog.Error("HeroPayHttpHandler.HomeMiniCallBack 'nonce' is null"); return; } if (httpCtx.httpRequest.Method == "GET") { var getParams = HttpParamsUtils.ParseByKeyValue(httpCtx.httpRequest.Path); getParams.TryGetValue("echostr", out string echostr); var list = new List(); list.Add(timestamp); list.Add(nonce); list.Add(HttpProxyPayServerUtils.GetServerConfig().wxPayToken); list.Sort((x, y) => string.Compare(x, y)); var str = string.Join("", list); var sig = HashHelper.SHA1String(str); TraceLog.Trace("HeroPayHttpHandler.HomeMiniCallBack signature {0} sig {1}", signature, sig); if(signature == sig) { httpCtx.httpResponse = new HttpResponse { StatusCode = ((int)HttpErrorStatusCode.Success).ToString(), Content = Encoding.UTF8.GetBytes(echostr) }; HttpServer.SendResponse(httpCtx); return; } httpCtx.httpResponse = new HttpResponse { StatusCode = ((int)HttpErrorStatusCode.Success).ToString(), Content = Encoding.UTF8.GetBytes("Verify fail") }; HttpServer.SendResponse(httpCtx); return; } Tencent.WXBizMsgCrypt wxcpt = new Tencent.WXBizMsgCrypt(HttpProxyPayServerUtils.GetServerConfig().wxPayToken, HttpProxyPayServerUtils.GetServerConfig().wxPayAesKey, HttpProxyPayServerUtils.GetServerConfig().wxAppId); string sMsg = ""; //解析之后的明文 int wxret = 0; JsonData reqJson = JsonMapper.ToObject(httpCtx.httpRequest.Content); wxret = wxcpt.DecryptMsg(msg_signature, timestamp, nonce, reqJson["Encrypt"].ToString(), ref sMsg); if (wxret != 0) { TraceLog.Error("HeroPayHttpHandler.HomeMiniCallBack: Decrypt fail, ret: " + wxret); return; } TraceLog.Trace("{0}", sMsg); JsonData jsonData = JsonMapper.ToObject(sMsg); JsonData MiniGame = jsonData["MiniGame"]; long CreateTime = long.Parse(jsonData["CreateTime"].ToString()); JsonData Payload = MiniGame["Payload"]; string PayEventSig = MiniGame["PayEventSig"].ToString(); bool IsMock = bool.Parse(MiniGame["IsMock"].ToString()); if (IsMock) { httpCtx.httpResponse = new HttpResponse { StatusCode = ((int)HttpErrorStatusCode.Success).ToString(), Content = Encoding.UTF8.GetBytes("{\"ErrCode\":0,\"ErrMsg\":\"Success\"}") }; HttpServer.SendResponse(httpCtx); return; } //小程序解码验证 //var paySig = HashHelper.GetHMACSHA256(HttpProxyPayServerUtils.GetServerConfig().appkey, "event" + "&" + Payload); //if (paySig != PayEventSig) //{ // TraceLog.Error("HeroPayHttpHandler.HeroPayCallback sign error paySig:{0} PayEventSig:{1}", paySig, PayEventSig); // httpCtx.httpResponse = new HttpResponse { StatusCode = ((int)HttpErrorStatusCode.MiniPayError).ToString(), Content = Encoding.UTF8.GetBytes("internal error") }; // HttpServer.SendResponse(httpCtx); // return; //} JObject PayJson = JObject.Parse(Payload.ToString()); TraceLog.Trace("GoodsInfo {0}", PayJson.ToString()); Payload = JsonMapper.ToObject(PayJson.ToString()); JsonData GoodsInfo = Payload["GoodsInfo"]; JsonData WeChatPayInfo = Payload["WeChatPayInfo"]; string orderId = Payload["OutTradeNo"].ToString(); string orderId3rd = WeChatPayInfo["TransactionId"].ToString(); string[] split = orderId.Split('-'); // uid-time-orderIdSeq-serverId-payitemId-payParam1-payParam2-paymentId-payUrlId if (split.Length < 8) { TraceLog.Error("HeroPayHttpHandler.HomeMiniCallBack invalid orderId {0}, need >= 8 params", orderId); return; } int payUrlId = 0; if (split.Length >= 9) { string strpayUrlId = split[8]; if (!string.IsNullOrEmpty(strpayUrlId)) { if (!int.TryParse(strpayUrlId, out payUrlId)) { TraceLog.Error("HeroPayHttpHandler.HomeMiniCallBack invalid payUrlId {0}", strpayUrlId); return; } } } //WeChatPayInfo 中是微信第三方的订单号码 // 打印错误log方便查询所有请求 TraceLog.Trace("HeroPayHttpHandler.HomeMiniCallBack orderId {0}", orderId); var svrData = HttpProxyPayServerUtils.GetHttpProxyServerData(); if (payUrlId > 0 && payUrlId != svrData.selfPayId) { if (payUrlId < svrData.payUrlCfg.Length && svrData.payUrlCfg[payUrlId] != null) { string url = svrData.payUrlCfg[payUrlId].url; url = url +"?"+ "signature=" + signature+"×tamp="+timestamp +"&nonce="+nonce+"&encrypt_type=aes&msg_signature=" + msg_signature; // 支付转发是正常情况, TraceLog.Trace("HeroPayHttpHandler.HomeMiniCallBack orderId {0} trans to {1}", orderId, url); HttpContent content = new StringContent(httpCtx.httpRequest.Content); ; string ret = HttpUtils.HttpPost(url, content, out bool exception); if (string.IsNullOrEmpty(ret)) { TraceLog.Trace("HeroPayHttpHandler.HomeMiniCallBack trans fail, HttpPost return null"); } else if (ret == "{\"ErrCode\":0,\"ErrMsg\":\"Success\"}") { TraceLog.Trace("HeroPayHttpHandler.HomeMiniCallBack trans succ, remote paysvr res 'SUCCESS'", ret); // 转发给英雄的支付服务器 httpCtx.httpResponse = new HttpResponse { StatusCode = "0", Content = Encoding.UTF8.GetBytes("{\"ErrCode\":0,\"ErrMsg\":\"Success\"}") }; HttpServer.SendResponse(httpCtx); } else { TraceLog.Trace("HeroPayHttpHandler.HomeMiniCallBack remote paysvr res fail {0}", ret); } } else { TraceLog.Trace("HeroPayHttpHandler.HomeMiniCallBack orderId {0} payUrlId {1} no url", orderId, payUrlId); } return; } if (!long.TryParse(split[0], out long uid)) { TraceLog.Error("HeroPayHttpHandler.HomeMiniCallBack invalid uid {0}", split[0]); return; } if (!HttpContextCache.AddContext(httpCtx)) { TraceLog.Error("HeroPayHttpHandler.HomeMiniCallBack add httpContext failed, ctx id {0}", httpCtx.id); return; } //if (!int.TryParse(split[5], out int payParam1)) //{ // TraceLog.Error("HeroPayHttpHandler.HomeMiniCallBack invalid payParam1 {0}", split[5]); // payParam1 = (int)PayUtils.GetParam1FromOrderId(orderId); // if (payParam1 >= 0) // { // TraceLog.Error("HeroPayHttpHandler.HomeMiniCallBack invalid payParam1 {0} {1}", split[5], payParam1); // return; // } //} //if (!int.TryParse(split[6], out int payParam2)) //{ // TraceLog.Error("HeroPayHttpHandler.HomeMiniCallBack invalid payParam2 {0}", split[6]); // return; //} // int subPayType = (int)PayType.MiniPay; string currency = null; // if (string.IsNullOrEmpty(currency)) { currency = ((int)Currency.CNY).ToString(); // 对方空就填0, 后面好算账 } // 环境 int sandbox = 0; if (Payload.ContainsKey("Env")) { sandbox = int.Parse(Payload["Env"].ToString()); } string amount = GoodsInfo["ActualPrice"].ToString(); /* 没有相关参数 只能在穿透里加 // 支付id不一致, 说明订单请求金额跟实际支付的金额不一致 string goodsId = GoodsInfoMap["ProductId"].ToString(); if (!goodsId.Equals(split[7])) { TraceLog.Error("HeroPayHttpHandler.HeroPayCallback invalid paymentId, req {0} res {1}", goodsId, split[7]); SSPayGoogleSuccessRes res = new SSPayGoogleSuccessRes { Uid = uid, PayType = (int)PayType.Hero }; res.Ret = (int)CSErrCode.PayMoneyNotEqual; res.OrderId.SetString(orderId); res.OrderId3rd.SetString(orderId3rd); res.ItemID = int.Parse(split[4]); res.PayParam1 = payParam1; res.PayParam2 = payParam2; res.PayTime3rd = Convert.ToInt64(CreateTime); res.GmTestPay = false; res.Currency.SetString(currency); res.Amount = Convert.ToUInt32(amount * 100); if (!uint.TryParse(split[3], out uint serverId)) { TraceLog.Error("HeroPayHttpHandler.HeroPayCallback invalid uid {0}", split[0]); return; } // 发给world处理, 注意serverId是gamesvr的id var worldId = ServerIDUtils.GetLevel1ServerIDByType(serverId, (int)ServerType.World, 1); HttpProxyPayServerUtils.GetPacketSender().SendToServerByID(worldId, (int)SSGameMsgID.PayGoogleSuccessRes, ref res, uid); return; } */ // 保存回调上下文 httpCtx.callbackArgs = new HeroPayCallbackArgs { orderId = orderId, status = HeroPayCallbackStatus.paySucc }; var req = new SSPayGoogleSuccessReq { Uid = uid, PayType = (int)PayType.MiniPay, CheckSuccess3rd = 1 }; req.HttpCtxId = httpCtx.id; req.ItemID = int.Parse(split[4]); req.PurchaseData.SetString(Payload.ToJson()); req.Signature.SetString(PayEventSig); req.OrderId.SetString(orderId); req.OrderId3rd.SetString(orderId3rd); req.PayParam1 = 0; req.PayParam2 = 0; req.GmTestPay = false; req.Amount = Convert.ToUInt32(amount);//分 req.Currency.SetString("CNY"); // 币种 req.SubPayType = subPayType; req.PayTime3rd = Convert.ToInt64(CreateTime); req.Sandbox = sandbox; // 如果第三方时间解析不出来就用服务器当前时间 if (req.PayTime3rd == 0) { req.PayTime3rd = HttpProxyPayServerUtils.GetTimeSecond(); } uint dbServerID = DBServerSelect.GetDBServerID(uid); HttpProxyPayServerUtils.GetPacketSender().SendToServerByID(dbServerID, (int)SSGameMsgID.PayGoogleSuccessReq, ref req, uid); } } }