using Google.Protobuf.WellKnownTypes; using LitJson; using Newtonsoft.Json.Linq; using Org.BouncyCastle.Asn1.Ocsp; using ProtoCSStruct; using SimpleHttpServer; using Sog; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Text; using System.Threading.Tasks; namespace HttpProxyPay { public static class WXHandler { private static ConcurrentDictionary order_cache = new ConcurrentDictionary(); private static string accessToken = null; private static long expireTimeSec = 0; private static string jstiket = null; private static long tiketExpire = 0; public static void CheckFreshAccessToken(bool force = false) { long now = HttpProxyPayServerUtils.GetTimeSecond(); if (now > expireTimeSec || force) { //https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET string url = "https://api.weixin.qq.com/cgi-bin/stable_token"; LitJson.JsonData j = new JsonData(); j["grant_type"] = "client_credential"; j["appid"] = HttpProxyPayServerUtils.GetServerConfig().wxAppId; j["secret"] = HttpProxyPayServerUtils.GetServerConfig().wxSec; j["force_refresh"] = false; var cont = j.ToJson(); HttpContent content = new StringContent(cont, Encoding.UTF8, "application/json"); string ret = HttpUtils.HttpPost(url, content, out bool exception); TraceLog.Debug("WXHandler.checkFreshAccessToken, get ret:{0}", ret); if (ret == null) { return; } LitJson.JsonData jsonData = JsonMapper.ToObject(ret); accessToken = jsonData["access_token"].ToString(); expireTimeSec = now + ((long)jsonData["expires_in"]); } } public static void MiniJSAPICall(HttpContext httpCtx) { TraceLog.Trace("WXHandler.MiniJSAPICall 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); postParams.TryGetValue("callback", out var jsoncallback); postParams.TryGetValue("orderId", out var orderId); postParams.TryGetValue("url", out var url); postParams.TryGetValue("uid", out var uid); url += "?uid="+uid+"&orderId=" + orderId; if(order_cache.TryGetValue(orderId, out var jsonr)) { // CheckFreshJsTiket(); // string text = // "jsapi_ticket="+jstiket+ "&noncestr=" + jsonr["nonceStr"].ToString() // +"×tamp=" + jsonr["timeStamp"].ToString() + "&url="+url+ ""; // var sign = HashHelper.SHA1String(text); string sign = ""; string text = ""; string ret = jsoncallback + "([0," + "'" + jsonr["appId"].ToString() + "'," + "'" + jsonr["timeStamp"].ToString() + "'," + "'" + jsonr["nonceStr"].ToString() + "'," + "'" + jsonr["package"].ToString() + "'," + "'" + jsonr["signType"].ToString() + "'," + "'" + jsonr["paySign"].ToString() + "'," + "'" + sign + "'" + "])" ; httpCtx.httpResponse = new HttpResponse { StatusCode = ((int)HttpErrorStatusCode.Success).ToString(), Content = Encoding.UTF8.GetBytes(ret) }; HttpServer.SendResponse(httpCtx); //order_cache.Remove(orderId, out var _); TraceLog.Trace("ret {0}, sign {1}, text {2}", ret, sign, text); } else { string ret = jsoncallback+ "([-1, \"订单已经过期,请重新下单\"])"; httpCtx.httpResponse = new HttpResponse { StatusCode = ((int)HttpErrorStatusCode.Success).ToString(), Content = Encoding.UTF8.GetBytes(ret) }; HttpServer.SendResponse(httpCtx); } } public static void PayWsjsapiReq(uint remoteAppID, StructPacket packet) { ref var req = ref packet.GetMessage(); TraceLog.Trace("HttpProxyPayMsgHandler.PayWsjsapiReq uid {0}", req.Uid); JObject jsonData = JObject.Parse(req.DataJson.GetString()); //JsonData jsonData = JsonMapper.ToObject("{}"); string out_trade_no = jsonData["out_trade_no"].ToString(); jsonData["appid"] = HttpProxyPayServerUtils.GetServerConfig().wxAppId; jsonData["mchid"] = HttpProxyPayServerUtils.GetServerConfig().mchid; HttpProxyPayServerUtils.GetServerConfig().payUrls.ForEach((payUrl) => { if (payUrl.payId == HttpProxyPayServerUtils.GetServerConfig().selfPayId) { jsonData["notify_url"] = payUrl.url; } }); //jsonData["notify_url"] = HttpProxyPayServerUtils.GetServerConfig().wxJsPayBack; string body = jsonData.ToString(); var ri = HttpProxyPayServerUtils.GetApp().Rand.Next(100000, 999999); var now = HttpProxyPayServerUtils.GetTimeSecond(); string text = "POST\n/v3/pay/transactions/jsapi\n" + now + "\n" + ri + "\n" + body + "\n"; var sign = SHARSASign.SignWithSHA256RSA(text, HttpProxyPayServerUtils.GetServerConfig().wxPayPem); string auth = "mchid=\"" + HttpProxyPayServerUtils.GetServerConfig().mchid + "\",nonce_str=\"" + ri + "\",signature=\"" + sign + "\",timestamp=\"" + now + "\",serial_no=\"" + HttpProxyPayServerUtils.GetServerConfig().wxPayPemNo + "\""; TraceLog.Trace("sign={0}, text={1}", sign, text); //prepay string url = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi"; StringContent content = new StringContent(body, Encoding.UTF8, "application/json"); List> headerList = new List>(); headerList.Add(new KeyValuePair("Authorization", "WECHATPAY2-SHA256-RSA2048 " + auth)); headerList.Add(new KeyValuePair("Accept", "application/json")); headerList.Add(new KeyValuePair("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36")); //headerList.Add(new KeyValuePair("Content-Type", "application/json")); string ret = HttpUtils.HttpPost(url, content, headerList, out var ex); if (ret != "") { TraceLog.Trace("ret ={0}", ret); JsonData jsonr = JsonMapper.ToObject(ret); if (jsonr.ContainsKey("prepay_id")) { string prepayid = jsonr["prepay_id"].ToString(); //var res = new CSWXJSPayReS(); //res.PrepayId.SetString(prepayid); jsonr["appId"] = HttpProxyPayServerUtils.GetServerConfig().wxAppId; jsonr["timeStamp"] = now; jsonr["nonceStr"] = ri; jsonr["package"] = "prepay_id=" + prepayid; jsonr["signType"] = "RSA"; string jtext = ""+ HttpProxyPayServerUtils.GetServerConfig().wxAppId + "\n" + now + "\n"+ri+ "\nprepay_id=" + prepayid+"\n"; string jsign = SHARSASign.SignWithSHA256RSA(jtext, HttpProxyPayServerUtils.GetServerConfig().wxPayPem); jsonr["paySign"] = jsign; //res.JsApiData.SetString(jsonr.ToJson()); //HttpProxyPayServerUtils.GetPacketSender().SendToServerByID(remoteAppID, (int)CSGameMsgID.PayWsjsapiRes, // ref res, packet.ObjectID); //放入缓存等待拉取 order_cache.TryAdd(out_trade_no, jsonr); TraceLog.Trace("add order_cache add {0} = {1}, jtext={2}", out_trade_no, jsonr.ToJson(), jtext); } } } private static void CheckFreshJsTiket(bool force = false) { CheckFreshAccessToken(); long now = HttpProxyPayServerUtils.GetTimeSecond(); if (now > tiketExpire || force) { string url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="+accessToken+"&type=jsapi"; string ret = HttpUtils.HttpGet(url, out bool exception,null ); TraceLog.Debug("WXHandler.CheckFreshJsTiket, get ret:{0}", ret); if (ret == null) { return; } LitJson.JsonData jsonData = JsonMapper.ToObject(ret); jstiket = jsonData["ticket"].ToString(); tiketExpire = now + ((long)jsonData["expires_in"]); } } public static void HomeMiniJSAPICallBack(HttpContext httpCtx) { TraceLog.Trace("HeroPayHttpHandler.HomeMiniJSAPICallBack 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); foreach (var e in httpCtx.httpRequest.Headers) { TraceLog.Trace("HeroPayHttpHandler.HomeMiniJSAPICallBack header {0} {1}", e.Key, e.Value); } var Wechatpay_Timestamp = ""; if (httpCtx.httpRequest.Headers.ContainsKey("Wechatpay-Timestamp")) { Wechatpay_Timestamp = httpCtx.httpRequest.Headers["Wechatpay-Timestamp"]; } else { Wechatpay_Timestamp = httpCtx.httpRequest.Headers["wechatpay-timestamp"]; } Wechatpay_Timestamp = Wechatpay_Timestamp.Trim(); var Wechatpay_Nonce = ""; if (httpCtx.httpRequest.Headers.ContainsKey("Wechatpay_Nonce")) { Wechatpay_Nonce = httpCtx.httpRequest.Headers["Wechatpay_Nonce"]; } else { Wechatpay_Nonce = httpCtx.httpRequest.Headers["wechatpay-nonce"]; } Wechatpay_Nonce = Wechatpay_Nonce.Trim(); var Wechatpay_Signature = ""; if (httpCtx.httpRequest.Headers.ContainsKey("Wechatpay_Signature")) { Wechatpay_Signature = httpCtx.httpRequest.Headers["Wechatpay_Signature"]; } else { Wechatpay_Signature = httpCtx.httpRequest.Headers["wechatpay-signature"]; } Wechatpay_Signature = Wechatpay_Signature.Trim(); var Wechatpay_Serial = ""; if (httpCtx.httpRequest.Headers.ContainsKey("Wechatpay_Serial")) { Wechatpay_Serial = httpCtx.httpRequest.Headers["Wechatpay_Serial"]; } else { Wechatpay_Serial = httpCtx.httpRequest.Headers["wechatpay-serial"]; } string text = Wechatpay_Timestamp + "\n" + Wechatpay_Nonce + "\n" + httpCtx.httpRequest.Content + "\n"; if (!SHARSASign.VerifyWithSHA256RSA(text, HttpProxyPayServerUtils.GetServerConfig().wxPayPubPem, Wechatpay_Signature)) { TraceLog.Error("HeroPayHttpHandler.HomeMiniJSAPICallBack VerifyWithSHA256RSA fail text={0}, sig={1}", text, Wechatpay_Signature); return; } JsonData jsonData = JsonMapper.ToObject(httpCtx.httpRequest.Content); if (jsonData.ContainsKey("event_type") && jsonData["event_type"].ToString() == "TRANSACTION.SUCCESS") { JsonData resource = jsonData["resource"]; string ciphertext = resource["ciphertext"].ToString(); string associated_data = resource["associated_data"].ToString(); string nonce = resource["nonce"].ToString(); string sMsg = SHARSASign.AesGcmDecrypt(HttpProxyPayServerUtils.GetServerConfig().wxV3Key, associated_data, nonce, ciphertext); TraceLog.Trace("HeroPayHttpHandler.HomeMiniJSAPICallBack sMsg {0}", sMsg); resource = JsonMapper.ToObject(sMsg); JsonData amount = resource["amount"]; string out_trade_no = resource["out_trade_no"].ToString(); string transaction_id = resource["transaction_id"].ToString(); string trade_state = resource["trade_state"].ToString(); string payer_total = amount["payer_total"].ToString(); string[] split = out_trade_no.Split('-'); int payUrlId = 0; if (split.Length >= 9) { string strpayUrlId = split[8]; if (!string.IsNullOrEmpty(strpayUrlId)) { if (!int.TryParse(strpayUrlId, out payUrlId)) { TraceLog.Error("HeroPayHttpHandler.HomeMiniJSAPICallBack invalid payUrlId {0}", strpayUrlId); return; } } } var svrData = HttpProxyPayServerUtils.GetHttpProxyServerData(); if (payUrlId > 0 && payUrlId != svrData.selfPayId) {//jsapi不应该需要中转 TraceLog.Trace("HeroPayHttpHandler.HomeMiniJSAPICallBack orderId {0} payUrlId {1} no url", out_trade_no, payUrlId); return; } //if (!int.TryParse(split[5], out int payParam1)) //{ // TraceLog.Error("HeroPayHttpHandler.HomeMiniJSAPICallBack invalid payParam1 {0}", split[5]); // payParam1 = (int)PayUtils.GetParam1FromOrderId(out_trade_no); // if (payParam1 >= 0) // { // TraceLog.Error("HeroPayHttpHandler.HomeMiniJSAPICallBack invalid payParam1 {0} {1}", split[5], payParam1); // return; // } //} //if (!int.TryParse(split[6], out int payParam2)) //{ // TraceLog.Error("HeroPayHttpHandler.HomeMiniJSAPICallBack invalid payParam2 {0}", split[6]); // return; //} if (!long.TryParse(split[0], out long uid)) { TraceLog.Error("HeroPayHttpHandler.HomeMiniJSAPICallBack invalid uid {0}", split[0]); return; } if (!HttpContextCache.AddContext(httpCtx)) { TraceLog.Error("HeroPayHttpHandler.HomeMiniJSAPICallBack add httpContext failed, ctx id {0}", httpCtx.id); return; } order_cache.TryRemove(out_trade_no, out var _ ); DateTimeOffset dateTimeOffset = DateTimeOffset.Parse(resource["success_time"].ToString()); // 如果你想将其转换为 UTC 时间,可以调用 .UtcDateTime 属性 DateTime utcTime = dateTimeOffset.DateTime; var CreateTime = AppTime.ConvertDateTimeToUnixTime(utcTime)/1000; httpCtx.callbackArgs = new HeroPayCallbackArgs() { orderId = out_trade_no, status = HeroPayCallbackStatus.paySucc }; int subPayType = (int)PayType.MiniPay; 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(sMsg); req.Signature.SetString(""); req.OrderId.SetString(out_trade_no); req.OrderId3rd.SetString(transaction_id); req.PayParam1 = 0; req.PayParam2 = 0; req.GmTestPay = false; req.Amount = Convert.ToUInt32(payer_total) ;//分-》元 req.Currency.SetString("CNY"); // 币种 req.SubPayType = subPayType; req.PayTime3rd = CreateTime; req.Sandbox = 0; // 如果第三方时间解析不出来就用服务器当前时间 if (req.PayTime3rd == 0) { req.PayTime3rd = HttpProxyPayServerUtils.GetTimeSecond(); } uint dbServerID = DBServerSelect.GetDBServerID(uid); HttpProxyPayServerUtils.GetPacketSender().SendToServerByID(dbServerID, (int)SSGameMsgID.PayGoogleSuccessReq, ref req, uid); } } } }