Home  >  Article  >  WeChat Applet  >  .NET WeChat public account development (5.0 WeChat payment) detailed example

.NET WeChat public account development (5.0 WeChat payment) detailed example

Y2J
Y2JOriginal
2017-05-03 10:31:303600browse

1. Preface

Before starting to do this function, the first thing we have to do is to think about how to do this WeChat payment, where to start, where to start, the official sdk instructions and so on , is there any official demo, and whether the information the boss gave me is complete, and whether the interfaces to be applied for are available.

After some exploration on my own, I finally finished this under the urging of my boss. WeChat payment is very confusing. I will make some summary here. I hope it will be helpful to you. My ability is limited. If there is anything I said that is not good, I hope you will bear with me.

2. Preparation before development.

1.0 WeChat Payment Official Developer Document

2.0 Official Demo Download We use c#, so we chose the .net version, but this official demo doesn’t run at all

3.0 Official Demo runs Solution

The pitfalls of the official .net version of WeChat Payment 4.0 are not negotiable

Some configurations of the WeChat public platform before the development of 5.0, please be sure to check the configuration carefully.

##3. Encoding

After completing these preparations , we know that there are two types of WeChat payment, 1. The original one, 2. The one directly called by jsapi. The second one is used in my project

After some business logic processing of my own, we came to our On the order details page, you now need to click our payment button to pay. The pay.aspx code on the payment page is as follows,

Front page:

<script type="text/javascript">               //调用微信JS api 支付               function jsApiCall()
               {
                   WeixinJSBridge.invoke(                   &#39;getBrandWCPayRequest&#39;,                   <%=wxJsApiParam%>,//josn串                    function (res)
                    {                      if (res.err_msg == "get_brand_wcpay_request:ok")
                       {                           var OrderId=$("#OrderId").val();                           var orderProductName=$("#orderProductName").val();                           var orderMoneySum=$("#orderMoneySum").val();
                            window.location.href="http://www.baidu.aspx?OrderId="+OrderId+"&orderMoneySum="+orderMoneySum+"&orderProductName="+orderProductName;//支付成功后的跳转页面

                        }else
                        {
                          WeixinJSBridge.call(&#39;closeWindow&#39;);
                        }
                         
                     }
                    );
               }

               function callpay()
               {                   if (typeof WeixinJSBridge == "undefined")
                   {                       if (document.addEventListener)
                       {
                           document.addEventListener(&#39;WeixinJSBridgeReady&#39;, jsApiCall, false);
                       }                       else if (document.attachEvent)
                       {
                           document.attachEvent(&#39;WeixinJSBridgeReady&#39;, jsApiCall);
                           document.attachEvent(&#39;onWeixinJSBridgeReady&#39;, jsApiCall);
                       }
                   }                   else
                   {
                       jsApiCall();
                   }
               }               
           </script>


<body>
    <p>
       
       <br />
       <br />
       <br />
       <input  type="hidden" id="OrderId" name="OrderId" value="<%=OrderId %>"/>
       <input  type="hidden" id="orderMoneySum" name="orderMoneySum" value="<%=orderMoneySum %>"/>
       <input  type="hidden" id="orderProductName" name="orderProductName" value="<%=orderProductName %>"/>
       <span class="fLeft" style="font-size:20px;color:Purple">  您确认付款<label style="font-size:25px;color:Red"><%=Money%></label>元...</span>
      <p><button type="button" class="btn-pay" title="确认支付" onclick="callpay()">立即支付</button></p>
    </p>
</body>
</html>

It should be noted that:

WeChat JS API can only be used in WeChat's built-in browser, and calls from other browsers are invalid. WeChat provides the getBrandWCPayRequest interface for the merchant's front-end web page to call. WeChat will identify the merchant's payment authority before calling it. If the merchant has the authority to initiate payment, the payment process will begin. This section mainly introduces the interface calling rules before payment. Please refer to the payment status message notification mechanism below. The interface needs to be noted: all incoming parameters are string types!

getBrandWCPayRequest parameters are shown in Table 6-5.

public account idtimeStampTimestamp

parameter name

Required

Format

Description

##appId

is

String type

It can be obtained after the merchant successfully registers a public account with payment permission;

is

String type, less than 32 bytes

Generated by the merchant, the number of seconds from 00:00:00 on January 1, 1970 to the present, that is, the current time, And ultimately needs to be converted to string form;

##nonceStr

Random string
is a

string type, less than 32 bytes

A random string generated by the merchant;

package

Order details expansion string
is

String type, less than 4096 bytes

The merchant composes the order information into this string. For the specific composition scheme, please refer to the package grouping help in the interface instructions. ; Spliced ​​in by the merchant in accordance with the specifications;

signType

Signature method
is a string type of

, the parameter value is "SHA1"

Fill in as shown in the document, currently only Support SHA1;

paySign

签名

字符串类型

商户将接口列表中的参数按照指定方式迚行签名,签名方式使用signType中标示的签名方式,具体签名方案参见接口使用说明中签名帮劣;由商户按照规范签名后传入;

表6-5 getBrandWCPayRequest参数

    getBrandWCPayRequest返回值如表6-6所示。


返回值

说明

err_msg

get_brand_wcpay_request:ok  支付成功
get_brand_wcpay_request:cancel 支付过程中用户取消
get_brand_wcpay_request:fail 支付失败

表6-6 getBrandWCPayRequest返回值

JS API的返回结果 get_brand_wcpay_request:ok 仅在用户成功完成支付时返回。由于前端交互复杂,get_brand_wcpay_request:cancel 或者 get_brand_wcpay_request:fail 可以统一处理为用户遇到错误或者 主动放弃,不必细化区分。

 pay.aspx后台页面代码:

     
          wxJsApiParam { ; ; } 
          Money { ;   OrderId { ;   orderMoneySum { ; ; }
          orderProductName { ; ; }
          Page_Load( (!=  JsApiPay( total_fee = Request[
                        orderMoneySum = ParkName = Request[= ParkName+= Request[ (.IsNullOrWhiteSpace(total_fee)||total_fee==  WxPayException( +  + =.Parse((Convert.ToDouble(total_fee)*= (Convert.ToDouble(jsApiPay.total_fee)/=
                         (Common.OpenId ==   WxPayException(=== jsApiPay.GetJsApiParameters(); +  + 

ex.InnerException.Message +  +  + ex.Message +

在这里需要我们注意的是:jsApiPay.openid = common.GetOpenId();

在支付的时候,我们需要首先获取用户的openId,然而获取用户openId的这个过程我们首先要进行Oauth2认证,在官方的demo中提供了JsApiPay.cs这个核心类库,里面已经有这个GetOpenidAndAccessToken()方法 。我这里是拿过来写成了自己的一个公共帮助类。Common.cs

    /// <summary>
    /// 公共帮助类    /// </summary>
    public class Common
    {        private  HttpContext Context { get; set; }        public static string OpenId = "Openid";        public static string access_token = "access_token";        #region 构造函数        /// <summary>
        /// 构造函数        /// </summary>
        /// <param name="Context"></param>
        public Common(HttpContext context)
        {            this.Context = context;
        } 
        #endregion


        #region 通过code换取AccessToken        /// <summary>
        /// 通过code换取AccessToken        /// </summary>
        public  void GetOpenidAndAccessToken()
        {            if (!string.IsNullOrEmpty(Context.Request.QueryString["code"]))
            {                //获取code码,以获取openid和access_token
                string code = Context.Request.QueryString["code"];                GetOpenidAndAccessTokenFromCode(code);
            }            else
            {                //构造网页授权获取code的URL
                string host = Context.Request.Url.Host;
                string path = Context.Request.Path;                string redirect_uri = HttpUtility.UrlEncode("http://" + host + path);


                WxPayData data = new WxPayData();
                data.SetValue("appid", 

WxPayConfig.APPID);
                data.SetValue("redirect_uri", redirect_uri);
                data.SetValue("response_type", "code");
                data.SetValue("scope", "snsapi_base");
                data.SetValue("state", "STATE" + "#wechat_redirect");                string url = "https://open.weixin.qq.com/connect/oauth2/authorize?" + data.ToUrl();                try
                {                    //触发微信返回code码         
                    Context.Response.Redirect(url);//Redirect函数会抛出ThreadAbortException异常,不用处理这个异常                }                catch (System.Threading.ThreadAbortException ex)
                {
                }
            }
        }        
        #endregion

        #region 通过用户授权获取AccessToken和OpenId        /// <summary>
        /// 通过用户授权获取AccessToken和OpenId        /// </summary>
        /// <param name="code"></param>
        public  void GetOpenidAndAccessTokenFromCode(string code)
        {            try
            {                //构造获取openid及access_token的url
                WxPayData data = new WxPayData();
                data.SetValue("appid", 

WxPayConfig.APPID);
                data.SetValue("secret", 

WxPayConfig.APPSECRET);
                data.SetValue("code", 

code);
                data.SetValue("grant_type", "authorization_code");                string url = "https://api.weixin.qq.com/sns/oauth2/access_token?" + data.ToUrl();                //请求url以获取数据
                string result = HttpService.Get(url);                //保存access_token,用于收货地址获取
                JsonData jd = JsonMapper.ToObject(result);
                access_token = (string)jd["access_token"];                //获取用户openid
                OpenId = (string)jd["openid"];
            }            catch (Exception ex)
            {                throw new WxPayException(ex.ToString());
            }
        } 
        #endregion

        #region 获取OpenId        /// <summary>
        /// 获取OpenId        /// </summary>
        /// <param name="postStr"></param>
        /// <returns></returns>
        public string GetOpenId()
        {
                Common common = new Common(Context);
                common.GetOpenidAndAccessToken();                return OpenId;        } 
        #endregion
    }

 public class JsApiPay
    {        /// <summary>
        /// 保存页面对象,因为要在类的方法中使用Page的Request对象        /// </summary>
        private Page page {get;set;}        /// <summary>
        /// openid用于调用统一下单接口        /// </summary>
        public string openid { get; set; }        /// <summary>
        /// access_token用于获取收货地址js函数入口参数        /// </summary>
        public string access_token { get; set; }        /// <summary>
        /// 商品金额,用于统一下单        /// </summary>
        public int total_fee { get; set; }        /// <summary>
        /// 订单Id        /// </summary>
        public string orderid { get; set; }        /// <summary>
        /// 统一下单接口返回结果        /// </summary>
        public WxPayData unifiedOrderResult { get; set; } 

        public JsApiPay(Page page)
        {            this.page = page;
        }        /**
        * 
        * 网页授权获取用户基本信息的全部过程
        * 详情请参看网页授权获取用户基本信息:http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html
        * 第一步:利用url跳转获取code
        * 第二步:利用code去获取openid和access_token
        * 
        */
        public void GetOpenidAndAccessToken()
        {            if (!string.IsNullOrEmpty(page.Request.QueryString["code"]))
            {                //获取code码,以获取openid和access_token
                string code = page.Request.QueryString["code"];                //Log.Debug(this.GetType().ToString(), "Get code : " + code);                GetOpenidAndAccessTokenFromCode(code);
            }            else
            {                //构造网页授权获取code的URL
                string host = page.Request.Url.Host;                //Log.Debug(this.GetType().ToString(), "host" + host);
                string path = page.Request.Path;                string redirect_uri = HttpUtility.UrlEncode("http://" + host + path);
                

                WxPayData data = new WxPayData();
                data.SetValue("appid", 

WxPayConfig.APPID);
                data.SetValue("redirect_uri", redirect_uri);
                data.SetValue("response_type", "code");
                data.SetValue("scope", "snsapi_base");
                data.SetValue("state", "STATE" + "#wechat_redirect");                string url = "https://open.weixin.qq.com/connect/oauth2/authorize?" + data.ToUrl();
                Log.Debug(this.GetType().ToString(), "Will Redirect to URL : 

" + url);                try
                {                    //触发微信返回code码         
                    page.Response.Redirect(url);//Redirect函数会抛出ThreadAbortException异常,不用处理这个异常                }                catch(System.Threading.ThreadAbortException ex)
                {
                }
            }
        }        /**
        * 
        * 通过code换取网页授权access_token和openid的返回数据,正确时返回的JSON数据包如下:
        * {
        *  "access_token":"ACCESS_TOKEN",
        *  "expires_in":7200,
        *  "refresh_token":"REFRESH_TOKEN",
        *  "openid":"OPENID",
        *  "scope":"SCOPE",
        *  "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
        * }
        * 其中access_token可用于获取共享收货地址
        * openid是微信支付jsapi支付接口统一下单时必须的参数
        * 更详细的说明请参考网页授权获取用户基本信息:http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html
        * @失败时抛异常WxPayException        */
        public void GetOpenidAndAccessTokenFromCode(string code)
        {            try
            {                //构造获取openid及access_token的url
                WxPayData data = new WxPayData();
                data.SetValue("appid", 

WxPayConfig.APPID);
                data.SetValue("secret", 

WxPayConfig.APPSECRET);
                data.SetValue("code", 

code);
                data.SetValue("grant_type", "authorization_code");                string url = "https://api.weixin.qq.com/sns/oauth2/access_token?" + data.ToUrl();                //请求url以获取数据
                string result = HttpService.Get(url);                //Log.Debug(this.GetType().ToString(), "GetOpenidAndAccessTokenFromCode response : " + result);                //保存access_token,用于收货地址获取
                JsonData jd = JsonMapper.ToObject(result);
                access_token = (string)jd["access_token"];                //获取用户openid
                openid = (string)jd["openid"];                //Log.Debug(this.GetType().ToString(), "Get openid : " + openid);                //Log.Debug(this.GetType().ToString(), "Get access_token : " + access_token);            }            catch (Exception ex)
            {
                Log.Error(this.GetType().ToString(), ex.ToString());                throw new WxPayException(ex.ToString());
            }
        }        /**
         * 调用统一下单,获得下单结果
         * @return 统一下单结果
         * @失败时抛异常WxPayException         */
        public WxPayData GetUnifiedOrderResult()
        {            //统一下单
            WxPayData data = new WxPayData();
            data.SetValue("body", "test");            //data.SetValue("attach", "test");
            data.SetValue("out_trade_no", 

WxPayApi.GenerateOutTradeNo());
            data.SetValue("total_fee", 

total_fee);
            data.SetValue("time_start", DateTime.Now.ToString("yyyyMMddHHmmss"));
            data.SetValue("time_expire", DateTime.Now.AddMinutes(10).ToString("yyyyMMddHHmmss"));            //data.SetValue("goods_tag", "test");
            data.SetValue("trade_type", "JSAPI");
            data.SetValue("openid", 

openid);
            data.SetValue("orderid", 

orderid);

            WxPayData result = WxPayApi.UnifiedOrder(data);            if (!result.IsSet("appid") || !result.IsSet("prepay_id") || result.GetValue("prepay_id").ToString() == "")
            {
                Log.Error(this.GetType().ToString(), "UnifiedOrder response 

error!");                throw new WxPayException("UnifiedOrder response error!");
            }

            unifiedOrderResult = result;            return result;
        }        public WxPayData GetUnifiedOrderResult(string body)
        {            //统一下单
            WxPayData data = new WxPayData();
            data.SetValue("body", body);            //data.SetValue("attach", "test");
            data.SetValue("out_trade_no", 

WxPayApi.GenerateOutTradeNo());
            data.SetValue("total_fee", 

total_fee);            //data.SetValue("time_start", DateTime.Now.ToString("yyyyMMddHHmmss"));            //data.SetValue("time_expire", DateTime.Now.AddMinutes(10).ToString("yyyyMMddHHmmss"));            //data.SetValue("goods_tag", "test");
            data.SetValue("trade_type", "JSAPI");
            data.SetValue("openid", 

openid);


            WxPayData result = WxPayApi.UnifiedOrder(data);            if (!result.IsSet("appid") || !result.IsSet("prepay_id") || result.GetValue("prepay_id").ToString() == "")
            {
                Log.Error(this.GetType().ToString(), "UnifiedOrder response 

error!");                throw new WxPayException("UnifiedOrder response error!");
            }

            unifiedOrderResult = result;            return result;
        }        /**
        *  
        * 从统一下单成功返回的数据中获取微信浏览器调起jsapi支付所需的参数,
        * 微信浏览器调起JSAPI时的输入参数格式如下:
        * {
        *   "appId" : "wx2421b1c4370ec43b",     //公众号名称,由商户传入     
        *   "timeStamp":" 1395712654",         //时间戳,自1970年以来的秒数     
        *   "nonceStr" : "e61463f8efa94090b1f366cccfbbb444", //随机串     
        *   "package" : "prepay_id=u802345jgfjsdfgsdg888",     
        *   "signType" : "MD5",         //微信签名方式:    
        *   "paySign" : "70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信签名 
        * }
        * @return string 微信浏览器调起JSAPI时的输入参数,json格式可以直接做参数用
        * 更详细的说明请参考网页端调起支付API:http://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7
        * 
        */
        public string GetJsApiParameters()
        {

            WxPayData jsApiParam = new WxPayData();
            jsApiParam.SetValue("appId", unifiedOrderResult.GetValue

("appid"));
            jsApiParam.SetValue("timeStamp", WxPayApi.GenerateTimeStamp());
            jsApiParam.SetValue("nonceStr", WxPayApi.GenerateNonceStr());
            jsApiParam.SetValue("package", "prepay_id=" + unifiedOrderResult.GetValue("prepay_id"));
            jsApiParam.SetValue("signType", "MD5");
            jsApiParam.SetValue("paySign", jsApiParam.MakeSign());            string parameters = jsApiParam.ToJson();            return parameters;
        }        /**
        * 
        * 获取收货地址js函数入口参数,详情请参考收货地址共享接口:http://pay.weixin.qq.com/wiki/doc/api/jsapi.php?

chapter=7_9
        * @return string 共享收货地址js函数需要的参数,json格式可以直接做参数使用        */
        public string GetEditAddressParameters()
        {            string parameter = "";            try
            {                string host = page.Request.Url.Host;                string path = page.Request.Path;                string queryString = page.Request.Url.Query;                //这个地方要注意,参与签名的是网页授权获取用户信息时微信后台回传的完整url
                string url = "http://" + 

host + path + queryString;                //构造需要用SHA1算法加密的数据
                WxPayData signData = new WxPayData();
                signData.SetValue("appid",WxPayConfig.APPID);
                signData.SetValue("url", 

url);
                signData.SetValue("timestamp",WxPayApi.GenerateTimeStamp());
                signData.SetValue("noncestr",WxPayApi.GenerateNonceStr());
                signData.SetValue("accesstoken",access_token);                string param = signData.ToUrl();

                Log.Debug(this.GetType().ToString(), "SHA1 encrypt param : 

" + param);                //SHA1加密
                string addrSign = FormsAuthentication.HashPasswordForStoringInConfigFile(param, "SHA1");
                Log.Debug(this.GetType().ToString(), "SHA1 encrypt result : 

" + addrSign);                //获取收货地址js函数入口参数
                WxPayData afterData = new WxPayData();
                afterData.SetValue("appId",WxPayConfig.APPID);
                afterData.SetValue("scope","jsapi_address");
                afterData.SetValue("signType","sha1");
                afterData.SetValue("addrSign",addrSign);
                afterData.SetValue("timeStamp",signData.GetValue("timestamp"));
                afterData.SetValue("nonceStr",signData.GetValue("noncestr"));                //转为json格式
                parameter = afterData.ToJson();
                Log.Debug(this.GetType().ToString(), "Get EditAddressParam : 

" + parameter);
            }            catch (Exception ex)
            {
                Log.Error(this.GetType().ToString(), ex.ToString());                throw new WxPayException(ex.ToString());
            }            return parameter;
        }
    }

JsApiPay

 微信支付协议接口数据类WxPayData.cs官方都有相应的代码.

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

            XmlDocument xmlDoc = new XmlDocument();
            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(&#39;&&#39;);            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(this.GetType().ToString(), "WxPayData内部含有值

为null的字段!");                    throw new WxPayException("WxPayData内部含有值为null的字段!");
                }

                str += string.Format("{0}={1}<br>", pair.Key, pair.Value.ToString());
            }
            Log.Debug(this.GetType().ToString(), "Print in Web Page : 

" + str);            return str;
        }        /**
        * @生成签名,详见签名生成算法
        * @return 签名, sign字段不参加签名        */
        public string MakeSign()
        {            //转url格式
            string str = ToUrl();            //在string后加入API KEY
            str += "&key=" + WxPayConfig.KEY;            //MD5加密
            var md5 = MD5.Create();            var bs = md5.ComputeHash(Encoding.UTF8.GetBytes(str));            var sb = new StringBuilder();            foreach (byte b in bs)
            {
                sb.Append(b.ToString("x2"));
            }            //所有字符转为大写
            return sb.ToString().ToUpper();
        }        /**
        * 
        * 检测签名是否正确
        * 正确返回true,错误抛异常        */
        public bool CheckSign()
        {            //如果没有设置签名,则跳过检测
            if (!IsSet("sign"))
            {
               Log.Error(this.GetType().ToString(), "WxPayData签名存在但不合法

!");               throw new WxPayException("WxPayData签名存在但不合法!");
            }            //如果设置了签名但是签名为空,则抛异常
            else if(GetValue("sign") == null || GetValue("sign").ToString() == "")
            {
                Log.Error(this.GetType().ToString(), "WxPayData签名存在但不合

法!");                throw new WxPayException("WxPayData签名存在但不合法!");
            }            //获取接收到的签名
            string return_sign = GetValue("sign").ToString();            //在本地计算新的签名
            string cal_sign = MakeSign();            if (cal_sign == return_sign)
            {                return true;
            }

            Log.Error(this.GetType().ToString(), "WxPayData签名验证错误!");            throw new WxPayException("WxPayData签名验证错误!");
        }        /**
        * @获取Dictionary        */
        public SortedDictionary<string, object> GetValues()
        {            return m_values;
        }
    }

WxPayData

 配置文件信息

    /**
    *     配置账号信息    */
    public class WxPayConfig
    {        //=======【基本信息设置】=====================================
        /* 微信公众号信息配置
        * APPID:绑定支付的APPID(必须配置)
        * MCHID:商户号(必须配置)
        * KEY:商户支付密钥,参考开户邮件设置(必须配置)
        * APPSECRET:公众帐号secert(仅JSAPI支付的时候需要配置)        */
        public const string APPID = "wx14e3e56f3";        public const string MCHID = "12352";        public const string KEY = "BB6BE71D7CED49A79409C9";        public const string APPSECRET = "76eb33f66129692da1624f1";        //=======【证书路径设置】===================================== 
        /* 证书路径,注意应该填写绝对路径(仅退款、撤销订单时需要)        */
        public const string SSLCERT_PATH = "cert/apiclient_cert.p12";        public const string SSLCERT_PASSWORD = "123502";        //=======【支付结果通知url】===================================== 
        /* 支付结果通知回调url,用于商户接收支付结果        */
        public const string NOTIFY_URL = "http://www.baidu.com/ResultPay.aspx";        //=======【商户系统后台机器IP】===================================== 
        /* 此参数可手动配置也可在程序中自动获取        */
        public const string IP = "150.24.91.151";        //=======【代理服务器设置】===================================
        /* 默认IP和端口号分别为0.0.0.0和0,此时不开启代理(如有需要才设置)        */
        public const string PROXY_URL = "http://10.152.18.220:8080";        //=======【上报信息配置】===================================
        /* 测速上报等级,0.关闭上报; 1.仅错误时上报; 2.全量上报        */
        public const int REPORT_LEVENL = 1;        //=======【日志级别】===================================
        /* 日志等级,0.不输出日志;1.只输出错误信息; 2.输出错误和正常信息; 3.输出错误信息、正常信息和调试信息        */
        public const int LOG_LEVENL =3;
    }

WxPayConfig

 接着我们在看2行关键的代码:

                            WxPayData unifiedOrderResult == jsApiPay.GetJsApiParameters();

此时如果wxJsApiParam变量能够顺利拿到值,那么我们前台页面的:1572a0d4f3a6a8cd0c7e22edefaeebe0z这里就可以获取到我们要传递的参数,这时候就可以调用微信支付的接口,打开我们的付款页面如图所示:

               //调用微信JS api 支付               function jsApiCall()
               {
                   WeixinJSBridge.invoke(                   &#39;getBrandWCPayRequest&#39;,                   <%=wxJsApiParam%>,//josn串                    function (res)
                    {                      if (res.err_msg == "get_brand_wcpay_request:ok")
                       {                           var OrderId=$("#OrderId").val();                           var orderProductName=$("#orderProductName").val();                           var orderMoneySum=$("#orderMoneySum").val();                             window.location.href="http://www.baodu.com/PaySkip.aspx?OrderId="+OrderId+"&orderMoneySum="+orderMoneySum+"&orderProductName="+orderProductName;

                        }else
                        {
                          WeixinJSBridge.call(&#39;closeWindow&#39;);
                        }
                         
                     }
                    );
               }

(JsApiPay.cs)得到下单结果:

        public WxPayData GetUnifiedOrderResult(string body)
        {            //统一下单
            WxPayData data = new WxPayData();
            data.SetValue("body", 

body);
            data.SetValue("out_trade_no", 

WxPayApi.GenerateOutTradeNo());
            data.SetValue("total_fee", 

total_fee);
            data.SetValue("trade_type", "JSAPI");
            data.SetValue("openid", 

openid);            WxPayData result = WxPayApi.UnifiedOrder(data);            if (!result.IsSet("appid") || !result.IsSet("prepay_id") || result.GetValue("prepay_id").ToString() == "")
            {
                Log.Error(this.GetType().ToString(), "UnifiedOrder response 

error!");                throw new WxPayException("UnifiedOrder response error!");
            }

            unifiedOrderResult = result;            return result;
        }

(WxPayApi.cs)统一下单接口:

        /**
        * 
        * 统一下单
        * @param WxPaydata inputObj 提交给统一下单API的参数
        * @param int timeOut 超时时间
        * @throws WxPayException
        * @return 成功时返回,其他抛异常        */
        public static WxPayData UnifiedOrder(WxPayData inputObj, int timeOut = 

6)
        {            string url = "https://api.mch.weixin.qq.com/pay/unifiedorder";            //检测必填参数
            if (!inputObj.IsSet("out_trade_no"))
            {                throw new WxPayException("缺少统一支付接口必填参数out_trade_no!");
            }            else if (!inputObj.IsSet("body"))
            {                throw new WxPayException("缺少统一支付接口必填参数body!");
            }            else if (!inputObj.IsSet("total_fee"))
            {                throw new WxPayException("缺少统一支付接口必填参数total_fee!");
            }            else if (!inputObj.IsSet("trade_type"))
            {                throw new WxPayException("缺少统一支付接口必填参数trade_type!");
            }            //关联参数
            if (inputObj.GetValue("trade_type").ToString() == "JSAPI" && !inputObj.IsSet("openid"))
            {                throw new WxPayException("统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!");
            }            if (inputObj.GetValue("trade_type").ToString() == "NATIVE" && !inputObj.IsSet("product_id"))
            {                throw new WxPayException("统一支付接口中,缺少必填参数product_id!trade_type为JSAPI时,product_id为必填参数!");
            }            //异步通知url未设置,则使用配置文件中的url
            if (!inputObj.IsSet("notify_url"))
            {
                inputObj.SetValue("notify_url", 

WxPayConfig.NOTIFY_URL);//异步通知url            }

            inputObj.SetValue("appid", WxPayConfig.APPID);//公众账号ID
            inputObj.SetValue("mch_id", WxPayConfig.MCHID);//商户号
            inputObj.SetValue("spbill_create_ip", WxPayConfig.IP);//终端ip              
            inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串            //签名
            inputObj.SetValue("sign", 

inputObj.MakeSign());            string xml = inputObj.ToXml();            var start = DateTime.Now;
            string response = HttpService.Post(xml, url, false, timeOut);
            var end = DateTime.Now;            int timeCost = (int)((end - start).TotalMilliseconds);

            WxPayData result = new WxPayData();
            result.FromXml(response);

            ReportCostTime(url, timeCost, result);//测速上报

            return result;
        }

四.最终开发的效果

五.微信公众号开发系列导航

1.0初始微信公众号

2.0创建自定义菜单

3.0查询自定义菜单

4.0公众号消息处理

5.0微信支付

6.0模板消息

The above is the detailed content of .NET WeChat public account development (5.0 WeChat payment) detailed example. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn