前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >微信小程序支付服务端.net core实现,简单直接

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

作者头像
FreeTimeWorker
发布2020-08-31 13:25:27
8290
发布2020-08-31 13:25:27
举报
文章被收录于专栏:C#开发点点滴滴C#开发点点滴滴

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

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

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

代码语言:javascript
复制
 [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简单的操作类,就是个序列化和反序列化的过程

代码语言:javascript
复制
 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;
            }
        }
    }

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

代码语言:javascript
复制
 /// <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;
        }

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

代码语言:javascript
复制
 /// <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("-", "");
            }
        }

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

代码语言:javascript
复制
 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.模拟请求一下微信提供的接口,执行统一下单,拿到返回值

代码语言:javascript
复制
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);
代码语言:javascript
复制
PostHttpResponse 方法上文已提供 .ToXml .ToXmlObject上文也有提供,就是操作xml序列化的那两
6.1:WxUnifiedOrderResponse model内容:
代码语言:javascript
复制
 [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; }
    }

7,再次签名

代码语言:javascript
复制
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

代码语言:javascript
复制
  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; }
    }

一毛一样,的道理,主逻辑写下来不到100行代码,将来统一下单地方的参数有变化,仅仅需要增加字段,赋值就可以,其他都不动,再把下边那坨加密的东西再稍微封装一下,用到生产环境妥妥的。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-08-17 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档