C#微信支付(一)—— 基础帮助类

本文档记录了使用C#进行微信支付的实践经验,详细介绍了配置类、接口数据处理、底层通讯、回调处理(包括H5支付的IP兼容)以及随机数生成等关键步骤。在实现过程中遇到了诸多坑点,但最终成功完成,并提供了如何根据项目需求调整微信源码的建议。
摘要由CSDN通过智能技术生成

最近做了下微信支付,坑好多了,最终还是做完了,避免下次再走坑,在此总结一下

配置类

public class Config
    {
        //=======【基本信息设置】=====================================
        /* 微信公众号信息配置
        * APPID:绑定支付的APPID(必须配置)
        * MCHID:商户号(必须配置)
        * KEY:商户支付密钥,参考开户邮件设置(必须配置),请妥善保管,避免密钥泄露
        * APPSECRET:公众帐号secert(仅JSAPI支付的时候需要配置),请妥善保管,避免密钥泄露
        */

        public static string AppID
        {
            get { return ConfigurationManager.AppSettings["AppID"]; }
        }

        public static string MchID
        {
            get { return ConfigurationManager.AppSettings["MchID"]; }
        }

        public static string Key
        {
            get { return ConfigurationManager.AppSettings["Key"]; }
        }

        public static string AppSecret
        {
            get { return ConfigurationManager.AppSettings["AppSecret"]; }
        }

        //=======【证书路径设置】===================================== 
        /* 证书路径,注意应该填写绝对路径(仅退款、撤销订单时需要)
         * 1.证书文件不能放在web服务器虚拟目录,应放在有访问权限控制的目录中,防止被他人下载;
         * 2.建议将证书文件名改为复杂且不容易猜测的文件
         * 3.商户服务器要做好病毒和木马防护工作,不被非法侵入者窃取证书文件。
        */
        public static string SSlCertPath
        {
            get { return ConfigurationManager.AppSettings["SSlCertPath"]; }
        }

        public static string SSlCertPassword
        {
            get { return ConfigurationManager.AppSettings["SSlCertPassword"]; }
        }

        //=======【支付结果通知url】===================================== 
        /* 支付结果通知回调url,用于商户接收支付结果
        */
        public static string NotifyUrl
        {
            get { return ConfigurationManager.AppSettings["NotifyUrl"]; }
        }

        //=======【商户系统后台机器IP】===================================== 
        /* 此参数可手动配置也可在程序中自动获取
        */
        public static string Ip
        {
            get { return ConfigurationManager.AppSettings["Ip"]; }
        }


        //=======【代理服务器设置】===================================
        /* 默认IP和端口号分别为0.0.0.0和0,此时不开启代理(如有需要才设置)
        */
        public static string ProxyUrl
        {
            get { return ConfigurationManager.AppSettings["ProxyUrl"]; }
        }

        //=======【上报信息配置】===================================
        /* 测速上报等级,0.关闭上报; 1.仅错误时上报; 2.全量上报
        */
        public static int ReportLevel
        {
            get
            {
                int rl = 0;
                int.TryParse(ConfigurationManager.AppSettings["ReportLevel"], out rl);
                return rl;
            }
        }
    }

接口数据处理帮助类

    /// <summary>
    /// 微信支付协议接口数据类,所有的API接口通信都依赖这个数据结构,
    /// 在调用接口之前先填充各个字段的值,然后进行接口通信,
    /// 这样设计的好处是可扩展性强,用户可随意对协议进行更改而不用重新设计数据结构,
    /// 还可以随意组合出不同的协议数据包,不用为每个协议设计一个数据包结构
    /// </summary>
    public class WxPayData
    {
   
        public  const string SIGN_TYPE_MD5 = "MD5";
        public  const string SIGN_TYPE_HMAC_SHA256 = "HMAC-SHA256";
        public WxPayData()
        {

        }

        //采用排序的Dictionary的好处是方便对数据包进行签名,不用再签名之前再做一次排序
        private SortedDictionary<string, object> m_values = new SortedDictionary<string, object>();

        /**
        * 设置某个字段的值
        * @param key 字段名
         * @param value 字段值
        */
        public void SetValue(string key, object value)
        {
            m_values[key] = value;
        }

        /**
        * 根据字段名获取某个字段的值
        * @param key 字段名
         * @return key对应的字段值
        */
        public object GetValue(string key)
        {
            object o = null;
            m_values.TryGetValue(key, out o);
            return o;
        }

        /**
         * 判断某个字段是否已设置
         * @param key 字段名
         * @return 若字段key已被设置,则返回true,否则返回false
         */
        public bool IsSet(string key)
        {
            object o = null;
            m_values.TryGetValue(key, out o);
            if (null != o)
                return true;
            else
                return false;
        }

        /**
        * @将Dictionary转成xml
        * @return 经转换得到的xml串
        * @throws WxPayException
        **/
        public string ToXml()
        {
            //数据为空时不能转化为xml格式
            if (0 == m_values.Count)
            {
                Log.Error(this.GetType().ToString(), "WxPayData数据为空!");
                throw new WxPayException("WxPayData数据为空!");
            }

            string xml = "<xml>";
            foreach (KeyValuePair<string, object> pair in m_values)
            {
                //字段值不能为null,会影响后续流程
                if (pair.Value == null)
                {
                    Log.Error(this.GetType().ToString(), "WxPayData内部含有值为null的字段!");
                    throw new WxPayException("WxPayData内部含有值为null的字段!");
                }

                if (pair.Value.GetType() == typeof(int))
                {
                    xml += "<" + pair.Key + ">" + pair.Value + "</" + pair.Key + ">";
                }
                else if (pair.Value.GetType() == typeof(string))
                {
                    xml += "<" + pair.Key + ">" + "<![CDATA[" + pair.Value + "]]></" + pair.Key + ">";
                }
                else//除了string和int类型不能含有其他数据类型
                {
                    Log.Error(this.GetType().ToString(), "WxPayData字段数据类型错误!");
                    throw new WxPayException("WxPayData字段数据类型错误!");
                }
            }
            xml += "</xml>";
            return xml;
        }

        /**
        * @将xml转为WxPayData对象并返回对象内部的数据
        * @param string 待转换的xml串
        * @return 经转换得到的Dictionary
        * @throws WxPayException
        */
        public SortedDictionary<string, object> FromXml(string xml)
        {
            if (string.IsNullOrEmpty(xml))
            {
                Log.Error(this.GetType().ToString(), "将空的xml串转换为WxPayData不合法!");
                throw new WxPayException("将空的xml串转换为WxPayData不合法!");
            }


            SafeXmlDocument xmlDoc = new SafeXmlDocument();
            xmlDoc.LoadXml(xml);
            XmlNode xmlNode = xmlDoc.FirstChild;//获取到根节点<xml>
            XmlNodeList nodes = xmlNode.ChildNodes;
            foreach (XmlNode xn in nodes)
            {
                XmlElement xe = (XmlElement)xn;
                m_values[xe.Name] = xe.InnerText;//获取xml的键值对到WxPayData内部的数据中
            }

            try
            {
                //2015-06-29 错误是没有签名
                if(m_values["return_code"] != "SUCCESS")
                {
                    return m_values;
                }
                CheckSign();//验证签名,不通过会抛异常
            }
            catch(WxPayException ex)
            {
                throw new WxPayException(ex.Message);
            }

            return m_values;
        }

        /**
        * @Dictionary格式转化成url参数格式
        * @ return url格式串, 该串不包含sign字段值
        */
        public string ToUrl()
        {
            string buff = "";
            foreach (KeyValuePair<string, object> pair in m_values)
            {
                if (pair.Value == null)
                {
                    Log.Error(this.GetType().ToString(), "WxPayData内部含有值为null的字段!");
                    throw new WxPayException("WxPayData内部含有值为null的字段!");
                }

                if (pair.Key != "sign" && pair.Value.ToString() != "")
                {
                    buff += pair.Key + "=" + pair.Value + "&";
                }
            }
            buff = buff.Trim('&');
            return buff;
        }


        /**
        * @Dictionary格式化成Json
         * @return json串数据
        */
        public string ToJson()
        {
            string jsonStr = JsonMapper.ToJson(m_values);
            return jsonStr;

        }

        /**
        * @values格式化成能在Web页面上显示的结果(因为web页面上不能直接输出xml格式的字符串)
        */
        public string ToPrintStr()
        {
            string str = "";
            foreach (KeyValuePair<string, object> pair in m_values)
            {
                if (pair.Value == null)
                {
                    Log.Error(
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值