• 微信小程序支付服务端.net core实现,简单直接


    做小程序的支付时,在翻阅了大量的别人分享的代码后,感觉写的简直就是一堆垃圾,不敢苟同,要是代码都那么写,维护性简直了,于是才有了这篇文章。

    首先流程是很清楚的,就是先统一下单拼一个xml,然后把有值的参数排序后做计算一个签名,把签名也写到xml中,提交给微信,返回发起支付需要的参数,紧接着进行二次签名,将结果返回给小程序,小程序去调微信api发起支付

    1,将需要拼接程xml的参数都写到一个类里边

     [Serializable]
        [XmlRoot(ElementName = "xml", Namespace = "",IsNullable =true,DataType ="")]
        public class WxUnifiedOrder
        {
            [XmlElement(ElementName = "appid")]
            public string AppId { get; set; }
            [XmlElement(ElementName = "mch_id")]
            public string Mch_Id { get; set; }
            [XmlElement(ElementName = "device_info")]
            public string Device_Info { get; set; }
            [XmlElement(ElementName = "nonce_str")]
            public string Nonce_Str { get; set; }
    
            [XmlElement(ElementName = "sign")]
            public string Sign { get; set; }
            /// <summary>
            /// 商品描述
            /// </summary>
            [XmlElement(ElementName = "body")]
            public string Body { get; set; }
            /// <summary>
            /// 商户订单号
            /// </summary>
            [XmlElement(ElementName = "out_trade_no")]
            public string Out_Trade_No { get; set; }
            /// <summary>
            /// 订单金额
            /// </summary>
            [XmlElement(ElementName = "total_fee")]
            public int Total_Fee { get; set; }
            /// <summary>
            /// 终端IP
            /// </summary>
            [XmlElement(ElementName = "spbill_create_ip")]
            public string Spbill_Create_Ip { get; set; }
            /// <summary>
            /// 通知地址
            /// </summary>
            [XmlElement(ElementName = "notify_url")]
            public string Notify_Url { get; set; }
            /// <summary>
            /// 交易类型
            /// </summary>
            [XmlElement(ElementName = "trade_type")]
            public string Trade_Type { get; set; }
    
            [XmlElement(ElementName = "openid")]
            public string OpenId { get; set; }

    加上特性,标明最终生成xml的文档结构。

    2,写个xml简单的操作类,就是个序列化和反序列化的过程

     public static class XMLOption
        {
            public static string ToXml<T>(this T obj, Encoding encoding)
            {
                string result = string.Empty;
                try
                {
                    using (MemoryStream memoryStream = new MemoryStream())
                    {
                        XmlSerializer xmlSerializer = new XmlSerializer(obj.GetType());
                        //序列化对象
                        XmlSerializerNamespaces namespaes = new XmlSerializerNamespaces();
                        namespaes.Add("", "");
                        XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, encoding);
                        xmlTextWriter.Formatting = System.Xml.Formatting.None;
                        xmlSerializer.Serialize(xmlTextWriter, obj, namespaes);
                        xmlTextWriter.Flush();
                        xmlTextWriter.Close();
                        result = encoding.GetString(memoryStream.ToArray());
                    }
                }
                catch (Exception ex)
                {
    
                }
                return result;
            }
    
            public static T ToXmlObject<T>(this string str, Encoding encoding)
            {
                try
                {
                    using (MemoryStream memoryStream = new MemoryStream())
                    {
                       
                        var buffer = encoding.GetBytes(str);
                        memoryStream.Write(buffer, 0, buffer.Length);
                        memoryStream.Position = 0;
                        XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
                        var result = xmlSerializer.Deserialize(memoryStream);
                        return (T)result;
                    }
                }
                catch (Exception ex)
                {
                    return default;
                }
            }
        }
    View Code

    3.一个模拟post请求的公用方法

     /// <summary>  
            /// 模拟POST提交  
            /// </summary>  
            /// <param name="url">请求地址</param>  
            /// <param name="xmlParam">xml参数</param>  
            /// <returns>返回结果</returns>  
            public string PostHttpResponse(string url, string xmlParam)
            {
                HttpWebRequest myHttpWebRequest = (HttpWebRequest)HttpWebRequest.Create(url);
                myHttpWebRequest.Method = "POST";
                myHttpWebRequest.ContentType = "application/x-www-form-urlencoded;charset=utf-8";
    
                // Encode the data  
                byte[] encodedBytes = Encoding.UTF8.GetBytes(xmlParam);
                myHttpWebRequest.ContentLength = encodedBytes.Length;
    
                // Write encoded data into request stream  
                Stream requestStream = myHttpWebRequest.GetRequestStream();
                requestStream.Write(encodedBytes, 0, encodedBytes.Length);
                requestStream.Close();
    
                HttpWebResponse result;
    
                try
                {
                    result = (HttpWebResponse)myHttpWebRequest.GetResponse();
                }
                catch
                {
                    return string.Empty;
                }
    
                if (result.StatusCode == HttpStatusCode.OK)
                {
                    using (Stream mystream = result.GetResponseStream())
                    {
                        using (StreamReader reader = new StreamReader(mystream))
                        {
                            return reader.ReadToEnd();
                        }
                    }
                }
                return null;
            }
    View Code

    4.再来一个扩展方法,计算string字符串的md5值

     /// <summary>
            /// 对字符串进行MD5加密
            /// </summary>
            /// <param name="strIN">需要加密的字符串</param>
            /// <returns>密文</returns>
            public static string MD5(this string source)
            {
                if (source == null)
                {
                    return null;
                }
                using (MD5 md5 = System.Security.Cryptography.MD5.Create())
                {
                    byte[] result = md5.ComputeHash(Encoding.UTF8.GetBytes(source));
                    string strResult = BitConverter.ToString(result);
                    return strResult.Replace("-", "");
                }
            }
    View Code

    5.填充需要提交的信息,并计算签名

     var wxUnifiedOrder = new WxUnifiedOrder()
                {
                    AppId = wxsetting.Appid,
                    Body = product.Name,
                    Device_Info = "WEB",
                    Mch_Id = wxsetting.Mch_id,
                    Nonce_Str = Guid.NewGuid().ToString().Replace("-", "").ToUpper(),
                    Notify_Url = _configuration["WechatPaied:Notify_Url"],
                    Out_Trade_No = orderinfo.OrderCode,
                    Sign = null,
                    Spbill_Create_Ip = _configuration["WechatPaied:Spbill_Create_Ip"],
                    Total_Fee = 1,// Convert.ToInt32(orderinfo.TotalPrice * 100)
                    Trade_Type = "JSAPI",
                    OpenId = uinfo.OpenId
                };
                var type = wxUnifiedOrder.GetType();
                SortedDictionary<string, string> param = new SortedDictionary<string, string>();
                foreach (var item in type.GetProperties())
                {
                    if (item.GetValue(wxUnifiedOrder) != null && item.Name != "Sign")
                    {
                        param.Add(item.Name.ToLower(), item.GetValue(wxUnifiedOrder).ToString());
                    }
                }
                List<string> lstparams = new List<string>();
                foreach (var item in param)
                {
                    lstparams.Add(string.Concat(item.Key, "=", item.Value));
                }
                lstparams.Add(string.Concat("key", "=", _configuration["WechatPaied:Api_Key"]));
                string param_Sign = string.Join("&", lstparams);
                wxUnifiedOrder.Sign =param_Sign.MD5().ToUpper();//计算签名

    这里的 SortedDictionary提供的就是一个排序后的参数列表,紧接着把他们按照排列好的顺序,拼起来,最后把key加上,调用.MD5这个扩展方法计算签名,把model填充起来

    6.模拟请求一下微信提供的接口,执行统一下单,拿到返回值

    string xmldoc = wxUnifiedOrder.ToXml(Encoding.UTF8);
    var result = PostHttpResponse("https://api.mch.weixin.qq.com/pay/unifiedorder", xmldoc);
    var resultData = result.ToXmlObject<WxUnifiedOrderResponse>(Encoding.UTF8);
    PostHttpResponse 方法上文已提供 .ToXml .ToXmlObject上文也有提供,就是操作xml序列化的那两
    6.1:WxUnifiedOrderResponse model内容:
     [System.SerializableAttribute()]
        [System.ComponentModel.DesignerCategoryAttribute("code")]
        [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
        [System.Xml.Serialization.XmlRootAttribute(ElementName = "xml", Namespace = "", IsNullable = false)]
        public class WxUnifiedOrderResponse
        {
            /// <summary>
            /// 返回状态码
            /// </summary>
            [XmlElement(ElementName = "return_code")]
            public string Return_Code { get; set; }
    
            /// <summary>
            /// 返回信息
            /// </summary>
            [XmlElement(ElementName = "return_msg")]
            public string Return_Msg { get; set; }
    
            /// <summary>
            /// 小程序id
            /// </summary>
            [XmlElement(ElementName = "appid")]
            public string Appid { get; set; }
    
            /// <summary>
            /// 商户号
            /// </summary>
            [XmlElement(ElementName = "mch_id")]
            public string Mch_Id{get;set;}
            /// <summary>
            /// 随机字符串
            /// </summary>
            [XmlElement(ElementName = "nonce_str")]
            public string Nonce_Str { get; set; }
            /// <summary>
            /// 
            /// </summary>
            [XmlElement(ElementName = "openid")]
            public string Openid { get; set; }
            /// <summary>
            /// 微信返回的签名
            /// </summary>
            [XmlElement(ElementName = "sign")]
            public string Sign { get; set; }
            /// <summary>
            /// 业务结果
            /// </summary>
            [XmlElement(ElementName = "result_code")]
            public string Result_Code { get; set; }
    
            /// <summary>
            /// 预支付交易回话标识
            /// </summary>
            [XmlElement(ElementName = "prepay_id")]
            public string Prepay_Id { get; set; }
            /// <summary>
            /// 交易类型
            /// </summary>
            [XmlElement(ElementName = "trade_type")]
            public string Trade_Type { get; set; }
        }
    View Code

    7,再次签名

    TimeSpan ts = DateTime.Now - new DateTime(1970, 1, 1, 0, 0, 0, 0);
                var wxPaidParams = new WxPaidParams()
                {
                    appId = unifiedOrder.Data.Appid,
                    nonceStr = unifiedOrder.Data.Nonce_Str,
                    package = string.Concat("prepay_id=", unifiedOrder.Data.Prepay_Id),
                    signType = "MD5",
                    timeStamp = Convert.ToInt64(ts.TotalSeconds).ToString()
                };
    
                var type = wxPaidParams.GetType();
                SortedDictionary<string, string> param = new SortedDictionary<string, string>();
                foreach (var item in type.GetProperties())
                {
                    if (item.GetValue(wxPaidParams) != null && item.Name != "paySign")
                    {
                        param.Add(item.Name, item.GetValue(wxPaidParams).ToString());
                    }
                }
                List<string> lstparams = new List<string>();
                foreach (var item in param)
                {
                    lstparams.Add(string.Concat(item.Key, "=", item.Value));
                }
                lstparams.Add(string.Concat("key", "=", _configuration["WechatPaied:Api_Key"]));
                string param_paySign = string.Join("&", lstparams);
                wxPaidParams.paySign = param_paySign.MD5().ToUpper();

    7.1  WxPaidParams

      public class WxPaidParams
        {
            /// <summary>
            /// appid
            /// </summary>
            public string appId { get; set; }
            /// <summary>
            /// 时间戳
            /// </summary>
            public string timeStamp { get; set; }
            /// <summary>
            /// 随机串
            /// </summary>
            public string nonceStr { get; set; }
            /// <summary>
            /// 数据包
            /// </summary>
            public string package { get; set; }
            /// <summary>
            /// 签名方式
            /// </summary>
            public string signType { get; set; }
            /// <summary>
            /// 签名
            /// </summary>
            public string paySign { get; set; }
        }
    View Code
    一毛一样,的道理,主逻辑写下来不到100行代码,将来统一下单地方的参数有变化,仅仅需要增加字段,赋值就可以,其他都不动,
    再把下边那坨加密的东西再稍微封装一下,用到生产环境妥妥的。

  • 相关阅读:
    软考之操作系统
    牛腩javascript(二)之正则表达式
    牛腩javascript(一)
    软考之算法
    软考之数据结构
    软考之路之刷屏开始
    XML中的几种比较
    北大青鸟ASP.NET之总结篇
    Webassembly 学习2 -- Js 与C 数据交互
    nginx-proxy_redirect
  • 原文地址:https://www.cnblogs.com/zzfstudy/p/13519051.html
Copyright © 2020-2023  润新知