微信支付V2 统一下单 .NET CORE


微信支付V2 统一下单 .NET CORE

一个小功能总是拖拖拖。。唉。。。今天2024年01月15日终于调成功了,以下是代码,能取到预支付订单号就行了。。

2024年7月25日 再测试,好像之前的不行,还得再加上一步,还需要二次签名。。。



        /// <summary>
        /// 微信支付-微信小程序
        /// </summary>
        /// <returns></returns>
        [HttpGet("Wxpay_app")]
        public async Task<ApiResult> Wxpay_app(string wxappopenid)
        {
            try
            {
                if (string.IsNullOrEmpty(wxappopenid))
                {
                    throw new Exception("wxappopenid is null.");
                }

                //微信支付V2-统一下单文档 https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_1
                //1.准备提交数据
                Hashtable ht = new Hashtable();
                ht.Add("appid", "wxd64fd8b6ce123486"); 
                ht.Add("mch_id", "1628000000"); 
                ht.Add("nonce_str", Guid.NewGuid().ToString().Replace("-", ""));
                ht.Add("sign_type", "MD5");
                ht.Add("body", "腾讯充值中心-QQ会员充值");
                ht.Add("out_trade_no", DateTime.Now.ToString("yyyyMMddHHmmss") + Guid.NewGuid().ToString().Substring(0, 4));
                ht.Add("total_fee", 1);
                ht.Add("spbill_create_ip", HttpContext.UserHostAddress().ToString());
                ht.Add("notify_url", "https://bbb.sss.net/api/wx/notify");
                ht.Add("trade_type", "JSAPI");
                ht.Add("openid", wxappopenid); 
                //2.签名: 排序、拼接、MD5, 把生成的签名加入到提交数据的sign中
                string pinjie = ""; 
                string key = "keykeykey0123456789jccy"; 
                SortedDictionary<string, string> sortedDictionary = new SortedDictionary<string, string>();
                foreach (DictionaryEntry entry in ht)
                {
                    sortedDictionary.Add(entry.Key.ToString(), entry.Value.ToString());
                }
                foreach (KeyValuePair<string, string> pair in sortedDictionary)
                {
                    if (string.IsNullOrEmpty(pair.Value)) { continue; }
                    pinjie += $"{pair.Key}={pair.Value}&";
                }
                pinjie += "key=" + key;
                _logger.LogInformation("拼接好的串:\r\n" + pinjie + "\r\n");
                string sign = Tool.MD5Hash(pinjie).ToUpper();
                ht.Add("sign", sign);
                //3.把提交的数据转为XML
                XmlDocument xmlDoc = new XmlDocument();
                XmlElement rootElement = xmlDoc.CreateElement("xml");
                xmlDoc.AppendChild(rootElement);
                foreach (DictionaryEntry entry in ht)
                {
                    XmlElement element = xmlDoc.CreateElement(entry.Key.ToString());
                    element.InnerText = entry.Value.ToString();
                    rootElement.AppendChild(element);
                }
                string postdata = xmlDoc.OuterXml;
                _logger.LogInformation("提交的XML:\r\n" + postdata + "\r\n");
                //4.把XML数据POST提交到微信支付V2的接口地址,得到返回的预支付订单号
                string aaa = "";
                string package = "prepay_id=";
                using (var client = new HttpClient())
                {
                    var content = new StringContent(postdata, Encoding.UTF8, "application/xml");
                    var response = await client.PostAsync("https://api.mch.weixin.qq.com/pay/unifiedorder", content);

                    if (response.IsSuccessStatusCode)
                    {
                        aaa = await response.Content.ReadAsStringAsync();
                        _logger.LogInformation("远程返回:\r\n" + aaa + "\r\n");

                        if (aaa.Contains("FAIL")) {
                            //失败 <result_code><![CDATA[FAIL]]></result_code>
                            throw new Exception(aaa);
                        }

                        XDocument xdoc = XDocument.Parse(aaa);

                        var ad = from a in xdoc.Descendants("xml")

                                 select new

                                 {

                                     return_code = a.Element("return_code").Value,

                                     return_msg = a.Element("return_msg").Value,

                                     appid = a.Element("appid").Value,
                                     mch_id = a.Element("mch_id").Value,
                                     nonce_str = a.Element("nonce_str").Value,
                                     sign = a.Element("sign").Value,
                                     result_code = a.Element("result_code").Value,
                                     prepay_id = a.Element("prepay_id").Value,
                                     trade_type = a.Element("trade_type").Value,

                                 };



                        var a1 = ad.FirstOrDefault();
                        if (a1.result_code == "SUCCESS" && a1.return_code == "SUCCESS")
                        {
                            package += a1.prepay_id;
                        }
                        else
                        {
                            throw new Exception(a1.return_msg);
                        }
                    }
                    else
                    {
                        throw new Exception($"StatusCode: {response.StatusCode}");
                    }
                }
                //5.小程序调起微信支付还需要再进行二次签名 https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_7&index=5
                Hashtable ht_return = new Hashtable();
                ht_return.Add("appId", ht["appid"]);
                ht_return.Add("timeStamp", DateTimeOffset.UtcNow.ToUnixTimeSeconds());
                ht_return.Add("nonceStr", ht["nonce_str"]);
                ht_return.Add("package", package);
                ht_return.Add("signType", ht["sign_type"]);

                string sign2 = Tool.MD5Hash($"appId={ht_return["appId"]}&nonceStr={ht_return["nonceStr"]}&package={ht_return["package"]}&signType={ht_return["signType"]}&timeStamp={ht_return["timeStamp"]}&key={key}").ToUpper();
                ht_return.Add("paySign", sign2);

                return new ApiResult()
                {
                    code = 0,
                    msg = aaa,
                    data = ht_return,
                };
            }
            catch (Exception ex)
            {
                return new ApiResult() { code = 1, msg = "出错:" + ex.Message };
            }
        }

注意MD5加密那里得用utf8,原来是ascii的总是签名错误



        /// <summary>
        /// netcore下的实现MD5加密
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static string MD5Hash(string input)
        {
            using (var md5 = MD5.Create())
            {
                var result = md5.ComputeHash(Encoding.UTF8.GetBytes(input)); //用Encoding.ASCII的话在微信支付V2那里的签名不匹配
                var strResult = BitConverter.ToString(result);
                return strResult.Replace("-", "");
            }

        }