Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 32792|回复: 0

C#版微信公众号支付

[复制链接]
  • TA的每日心情
    奋斗
    2024-4-6 11:05
  • 签到天数: 748 天

    [LV.9]以坛为家II

    2034

    主题

    2092

    帖子

    70万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    705612
    发表于 2021-4-9 17:27:49 | 显示全部楼层 |阅读模式

    最近负责的一些项目开发,都用到了微信支付(微信公众号支付、微信H5支付、微信扫码支付)。在开发的过程中,在调试支付的过程中,或多或少都遇到了一些问题,今天总结下,分享,留存。代码在文章结尾处,有需要的同学可以下载看下。

    先说注意的第一点,所有支付的第一步都是请求统一下单,统一下单,统一下单,请求URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder。

    再说一个微信官方提供的一个很重要的工具,微信支付接口签名校验工具(网址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=20_1),此工具旨在帮助开发者检测调用【微信支付接口API】时发送的请求参数中生成的签名是否正确,提交相关信息后可获得签名校验结果。特别实用!特别实用!特别实用!签名只要正确了,一切就OK了!

     

    第一部分 微信公众号支付

     

    微信公众号支付需要配置的参数有:APPID(微信公众号开发者ID)、APPSECRET(微信公众号开发者密码)、MCHID(商户ID)、KEY(商户密钥)。

     

    微信公众号支付应用的场景是在微信内部的H5环境中使用的支付方式。因为要通过网页授权获取用户的OpenId,所以必须要配置网页授权域名。同时要配置JS接口安全域名。

    JsApiConfig.cs

     1 using System.Web;
     2 using System.Text;
     3 using System.IO;
     4 using System.Net;
     5 using System;
     6 using System.Xml;
     7 using System.Collections.Generic;
     8 using Gwbnsh.Common;
     9 
    10 namespace Gwbnsh.API.Payment.wxpay
    11 {
    12     public class JsApiConfig
    13     {
    14         #region 字段
    15         private string partner = string.Empty;
    16         private string key = string.Empty;
    17         private string appid = string.Empty;
    18         private string appsecret = string.Empty;
    19         private string redirect_url = string.Empty;
    20         private string notify_url = string.Empty;
    21         #endregion
    22 
    23         public JsApiConfig(int site_payment_id)
    24         {
    25             Model.site_payment model = new BLL.site_payment().GetModel(site_payment_id); //站点支付方式
    26             if (model != null)
    27             {
    28                 Model.payment payModel = new BLL.payment().GetModel(model.payment_id); //支付平台
    29                 Model.sites siteModel = new BLL.sites().GetModel(model.site_id); //站点配置
    30                 Model.sysconfig sysConfig = new BLL.sysconfig().loadConfig(); //系统配置
    31 
    32                 partner = model.key1; //商户号(必须配置)
    33                 key = model.key2; //商户支付密钥,参考开户邮件设置(必须配置)
    34                 appid = model.key3; //绑定支付的APPID(必须配置)
    35                 appsecret = model.key4; //公众帐号secert(仅JSAPI支付的时候需要配置)
    36 
    37                 //获取用户的OPENID回调地址及登录后的回调地址
    38                 redirect_url = "http://m.gwbnsh.net.cn/hd/SellPhone" + payModel.return_url;
    39                 notify_url = "http://m.gwbnsh.net.cn/hd/SellPhone" + payModel.notify_url;
    40                 }
    41             }
    42         }
    43 
    44         #region 属性
    45         /// <summary>
    46         /// 商户号(必须配置)
    47         /// </summary>
    48         public string Partner
    49         {
    50             get { return partner; }
    51             set { partner = value; }
    52         }
    53 
    54         /// <summary>
    55         /// 获取或设交易安全校验码
    56         /// </summary>
    57         public string Key
    58         {
    59             get { return key; }
    60             set { key = value; }
    61         }
    62 
    63         /// <summary>
    64         /// 绑定支付的APPID(必须配置)
    65         /// </summary>
    66         public string AppId
    67         {
    68             get { return appid; }
    69             set { appid = value; }
    70         }
    71 
    72         /// <summary>
    73         /// 公众帐号secert(仅JSAPI支付的时候需要配置)
    74         /// </summary>
    75         public string AppSecret
    76         {
    77             get { return appsecret; }
    78             set { appsecret = value; }
    79         }
    80 
    81         /// <summary>
    82         /// 获取用户的OPENID回调地址
    83         /// </summary>
    84         public string Redirect_url
    85         {
    86             get { return redirect_url; }
    87         }
    88 
    89         /// <summary>
    90         /// 获取服务器异步通知页面路径
    91         /// </summary>
    92         public string Notify_url
    93         {
    94             get { return notify_url; }
    95         }
    96 
    97         #endregion
    98     }
    99 }
    JsApiConfig.cs

    JsApiPay.cs

      1 using System;
      2 using System.Collections.Generic;
      3 using System.Web;
      4 using System.Net;
      5 using System.IO;
      6 using System.Text;
      7 using Gwbnsh.Common;
      8 
      9 namespace Gwbnsh.API.Payment.wxpay
     10 {
     11     public class JsApiPay
     12     {
     13         /**
     14         * 
     15         * 测速上报
     16         * @param string interface_url 接口URL
     17         * @param int timeCost 接口耗时
     18         * @param WxPayData inputObj参数数组
     19         */
     20         public static void ReportCostTime(int paymentId, string interface_url, int timeCost, WxPayData inputObj)
     21         {
     22             //如果仅失败上报
     23             if (inputObj.IsSet("return_code") && inputObj.GetValue("return_code").ToString() == "SUCCESS" &&
     24              inputObj.IsSet("result_code") && inputObj.GetValue("result_code").ToString() == "SUCCESS")
     25             {
     26                 return;
     27             }
     28 
     29             //上报逻辑
     30             WxPayData data = new WxPayData();
     31             data.SetValue("interface_url", interface_url);
     32             data.SetValue("execute_time_", timeCost);
     33             //返回状态码
     34             if (inputObj.IsSet("return_code"))
     35             {
     36                 data.SetValue("return_code", inputObj.GetValue("return_code"));
     37             }
     38             //返回信息
     39             if (inputObj.IsSet("return_msg"))
     40             {
     41                 data.SetValue("return_msg", inputObj.GetValue("return_msg"));
     42             }
     43             //业务结果
     44             if (inputObj.IsSet("result_code"))
     45             {
     46                 data.SetValue("result_code", inputObj.GetValue("result_code"));
     47             }
     48             //错误代码
     49             if (inputObj.IsSet("err_code"))
     50             {
     51                 data.SetValue("err_code", inputObj.GetValue("err_code"));
     52             }
     53             //错误代码描述
     54             if (inputObj.IsSet("err_code_des"))
     55             {
     56                 data.SetValue("err_code_des", inputObj.GetValue("err_code_des"));
     57             }
     58             //商户订单号
     59             if (inputObj.IsSet("out_trade_no"))
     60             {
     61                 data.SetValue("out_trade_no", inputObj.GetValue("out_trade_no"));
     62             }
     63             //设备号
     64             if (inputObj.IsSet("device_info"))
     65             {
     66                 data.SetValue("device_info", inputObj.GetValue("device_info"));
     67             }
     68 
     69             try
     70             {
     71                 Report(paymentId, data);
     72             }
     73             catch (WxPayException ex)
     74             {
     75                 //不做任何处理
     76             }
     77         }
     78 
     79         /**
     80         * 
     81         * 测速上报接口实现
     82         * @param WxPayData inputObj 提交给测速上报接口的参数
     83         * @param int timeOut 测速上报接口超时时间
     84         * @throws WxPayException
     85         * @return 成功时返回测速上报接口返回的结果,其他抛异常
     86         */
     87         public static WxPayData Report(int paymentId, WxPayData inputObj, int timeOut = 1)
     88         {
     89             JsApiConfig jsApiConfig = new JsApiConfig(paymentId);
     90             string url = "https://api.mch.weixin.qq.com/payitil/report";
     91             //检测必填参数
     92             if (!inputObj.IsSet("interface_url"))
     93             {
     94                 throw new WxPayException("接口URL,缺少必填参数interface_url!");
     95             }
     96             if (!inputObj.IsSet("return_code"))
     97             {
     98                 throw new WxPayException("返回状态码,缺少必填参数return_code!");
     99             }
    100             if (!inputObj.IsSet("result_code"))
    101             {
    102                 throw new WxPayException("业务结果,缺少必填参数result_code!");
    103             }
    104             if (!inputObj.IsSet("user_ip"))
    105             {
    106                 throw new WxPayException("访问接口IP,缺少必填参数user_ip!");
    107             }
    108             if (!inputObj.IsSet("execute_time_"))
    109             {
    110                 throw new WxPayException("接口耗时,缺少必填参数execute_time_!");
    111             }
    112 
    113             inputObj.SetValue("appid", jsApiConfig.AppId);//公众账号ID
    114             inputObj.SetValue("mch_id", jsApiConfig.Partner);//商户号
    115             inputObj.SetValue("user_ip", DTRequest.GetIP());//终端ip
    116             inputObj.SetValue("time", DateTime.Now.ToString("yyyyMMddHHmmss"));//商户上报时间     
    117             inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
    118             inputObj.SetValue("sign", inputObj.MakeSign(jsApiConfig.Key));//签名
    119             string xml = inputObj.ToXml();
    120 
    121             string response = HttpService.Post(xml, url, false, timeOut);
    122 
    123             WxPayData result = new WxPayData();
    124             result.FromXml(response, jsApiConfig.Key);
    125             return result;
    126         }
    127 
    128         /**
    129         * 生成时间戳,标准北京时间,时区为东八区,自1970年1月1日 0点0分0秒以来的秒数
    130          * @return 时间戳
    131         */
    132         public static string GenerateTimeStamp()
    133         {
    134             TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
    135             return Convert.ToInt64(ts.TotalSeconds).ToString();
    136         }
    137 
    138         /**
    139         * 生成随机串,随机串包含字母或数字
    140         * @return 随机串
    141         */
    142         public static string GenerateNonceStr()
    143         {
    144             return Guid.NewGuid().ToString().Replace("-", "");
    145         }
    146 
    147         /// <summary>
    148         /// 接收从微信支付后台发送过来的数据暂不验证签名
    149         /// </summary>
    150         /// <returns>微信支付后台返回的数据</returns>
    151         public static WxPayData GetNotifyData()
    152         {
    153             //接收从微信后台POST过来的数据
    154             System.IO.Stream s = HttpContext.Current.Request.InputStream;
    155             int count = 0;
    156             byte[] buffer = new byte[1024];
    157             StringBuilder builder = new StringBuilder();
    158             while ((count = s.Read(buffer, 0, 1024)) > 0)
    159             {
    160                 builder.Append(Encoding.UTF8.GetString(buffer, 0, count));
    161             }
    162             s.Flush();
    163             s.Close();
    164             s.Dispose();
    165 
    166             //转换数据格式并验证签名
    167             WxPayData data = new WxPayData();
    168             try
    169             {
    170                 data.FromXml(builder.ToString());
    171             }
    172             catch (WxPayException ex)
    173             {
    174                 //若有错误,则立即返回结果给微信支付后台
    175                 WxPayData res = new WxPayData();
    176                 res.SetValue("return_code", "FAIL");
    177                 res.SetValue("return_msg", ex.Message);
    178                 HttpContext.Current.Response.Write(res.ToXml());
    179                 HttpContext.Current.Response.End();
    180             }
    181 
    182             return data;
    183         }
    184 
    185         /**
    186         *    
    187         * 查询订单
    188         * @param WxPayData inputObj 提交给查询订单API的参数
    189         * @param int timeOut 超时时间
    190         * @throws WxPayException
    191         * @return 成功时返回订单查询结果,其他抛异常
    192         */
    193         public static WxPayData OrderQuery(int paymentId, WxPayData inputObj, int timeOut = 6)
    194         {
    195             string sendUrl = "https://api.mch.weixin.qq.com/pay/orderquery";
    196             //检测必填参数
    197             if (!inputObj.IsSet("out_trade_no") && !inputObj.IsSet("transaction_id"))
    198             {
    199                 throw new WxPayException("订单查询接口中,out_trade_no、transaction_id至少填一个!");
    200             }
    201             JsApiConfig jsApiConfig = new JsApiConfig(paymentId);
    202             inputObj.SetValue("appid", jsApiConfig.AppId);//公众账号ID
    203             inputObj.SetValue("mch_id", jsApiConfig.Partner);//商户号
    204             inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
    205             inputObj.SetValue("sign", inputObj.MakeSign(jsApiConfig.Key));//签名
    206             string xml = inputObj.ToXml();
    207             var startTime = DateTime.Now; //开始时间
    208             string response = HttpService.Post(xml, sendUrl, false, timeOut);//调用HTTP通信接口提交数据
    209             var endTime = DateTime.Now; //结束时间
    210             int timeCost = (int)((endTime - startTime).TotalMilliseconds); //计算所用时间
    211             //将xml格式的数据转化为对象以返回
    212             WxPayData result = new WxPayData();
    213             result.FromXml(response, jsApiConfig.Key);
    214             ReportCostTime(paymentId, sendUrl, timeCost, result);//测速上报
    215             return result;
    216         }
    217 
    218     }
    219 }
    JsApiPay.cs

    第二部分 微信H5支付

    微信H5支付是微信官方2017年上半年刚刚对外开放的支付模式,它主要应用于在手机网站在移动浏览器(非微信环境)调用微信支付的场景。

    注意:微信H5支付需要在微信支付商户平台单独申请开通,否则无法使用。

    微信H5支付的流程比较简单,就是拼接请求的xml数据,进行统一下单,获取到支付的mweb_url,然后请求这个url网址就行。

    H5Config.cs

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 
     6 namespace Gwbnsh.API.Payment.wxpay
     7 {
     8     /// <summary>
     9     /// 移动端非微信浏览器支付
    10     /// </summary>
    11     public class H5Config
    12     {
    13         #region 字段
    14         private string partner = string.Empty;
    15         private string key = string.Empty;
    16         private string appid = string.Empty;
    17         private string notify_url = string.Empty;
    18         #endregion
    19 
    20         public H5Config(int site_payment_id)
    21         {
    22             Model.site_payment model = new BLL.site_payment().GetModel(site_payment_id); //站点支付方式
    23             if (model != null)
    24             {
    25                 Model.payment payModel = new BLL.payment().GetModel(model.payment_id); //支付平台
    26                 Model.sites siteModel = new BLL.sites().GetModel(model.site_id); //站点配置
    27                 Model.sysconfig sysConfig = new BLL.sysconfig().loadConfig(); //系统配置
    28 
    29                 partner = model.key1; //商户号(必须配置)
    30                 key = model.key2; //商户支付密钥,参考开户邮件设置(必须配置)
    31                 appid = model.key3; //绑定支付的APPID(必须配置)
    32                 notify_url = "";
    33             }
    34         }
    35 
    36         #region 属性
    37         /// <summary>
    38         /// 商户号(必须配置)
    39         /// </summary>
    40         public string Partner
    41         {
    42             get { return partner; }
    43             set { partner = value; }
    44         }
    45 
    46         /// <summary>
    47         /// 获取或设交易安全校验码
    48         /// </summary>
    49         public string Key
    50         {
    51             get { return key; }
    52             set { key = value; }
    53         }
    54 
    55         /// <summary>
    56         /// 绑定支付的APPID(必须配置)
    57         /// </summary>
    58         public string AppId
    59         {
    60             get { return appid; }
    61             set { appid = value; }
    62         }
    63 
    64         /// <summary>
    65         /// 获取服务器异步通知页面路径
    66         /// </summary>
    67         public string Notify_url
    68         {
    69             get { return notify_url; }
    70         }
    71 
    72         #endregion
    73     }
    74 }
    H5Config.cs

    H5Pay.cs

      1 using Gwbnsh.Common;
      2 using System;
      3 using System.Collections.Generic;
      4 using System.Linq;
      5 using System.Text;
      6 using System.Web;
      7 
      8 namespace Gwbnsh.API.Payment.wxpay
      9 {
     10     public class H5Pay
     11     {
     12         /**
     13         * 
     14         * 测速上报
     15         * @param string interface_url 接口URL
     16         * @param int timeCost 接口耗时
     17         * @param WxPayData inputObj参数数组
     18         */
     19         public static void ReportCostTime(int paymentId, string interface_url, int timeCost, WxPayData inputObj)
     20         {
     21             //如果仅失败上报
     22             if (inputObj.IsSet("return_code") && inputObj.GetValue("return_code").ToString() == "SUCCESS" &&
     23              inputObj.IsSet("result_code") && inputObj.GetValue("result_code").ToString() == "SUCCESS")
     24             {
     25                 return;
     26             }
     27 
     28             //上报逻辑
     29             WxPayData data = new WxPayData();
     30             data.SetValue("interface_url", interface_url);
     31             data.SetValue("execute_time_", timeCost);
     32             //返回状态码
     33             if (inputObj.IsSet("return_code"))
     34             {
     35                 data.SetValue("return_code", inputObj.GetValue("return_code"));
     36             }
     37             //返回信息
     38             if (inputObj.IsSet("return_msg"))
     39             {
     40                 data.SetValue("return_msg", inputObj.GetValue("return_msg"));
     41             }
     42             //业务结果
     43             if (inputObj.IsSet("result_code"))
     44             {
     45                 data.SetValue("result_code", inputObj.GetValue("result_code"));
     46             }
     47             //错误代码
     48             if (inputObj.IsSet("err_code"))
     49             {
     50                 data.SetValue("err_code", inputObj.GetValue("err_code"));
     51             }
     52             //错误代码描述
     53             if (inputObj.IsSet("err_code_des"))
     54             {
     55                 data.SetValue("err_code_des", inputObj.GetValue("err_code_des"));
     56             }
     57             //商户订单号
     58             if (inputObj.IsSet("out_trade_no"))
     59             {
     60                 data.SetValue("out_trade_no", inputObj.GetValue("out_trade_no"));
     61             }
     62             //设备号
     63             if (inputObj.IsSet("device_info"))
     64             {
     65                 data.SetValue("device_info", inputObj.GetValue("device_info"));
     66             }
     67 
     68             try
     69             {
     70                 Report(paymentId, data);
     71             }
     72             catch (WxPayException ex)
     73             {
     74                 //不做任何处理
     75             }
     76         }
     77 
     78         /**
     79         * 
     80         * 测速上报接口实现
     81         * @param WxPayData inputObj 提交给测速上报接口的参数
     82         * @param int timeOut 测速上报接口超时时间
     83         * @throws WxPayException
     84         * @return 成功时返回测速上报接口返回的结果,其他抛异常
     85         */
     86         public static WxPayData Report(int paymentId, WxPayData inputObj, int timeOut = 1)
     87         {
     88             H5Config h5Config = new H5Config(paymentId);
     89             string url = "https://api.mch.weixin.qq.com/payitil/report";
     90             //检测必填参数
     91             if (!inputObj.IsSet("interface_url"))
     92             {
     93                 throw new WxPayException("接口URL,缺少必填参数interface_url!");
     94             }
     95             if (!inputObj.IsSet("return_code"))
     96             {
     97                 throw new WxPayException("返回状态码,缺少必填参数return_code!");
     98             }
     99             if (!inputObj.IsSet("result_code"))
    100             {
    101                 throw new WxPayException("业务结果,缺少必填参数result_code!");
    102             }
    103             if (!inputObj.IsSet("user_ip"))
    104             {
    105                 throw new WxPayException("访问接口IP,缺少必填参数user_ip!");
    106             }
    107             if (!inputObj.IsSet("execute_time_"))
    108             {
    109                 throw new WxPayException("接口耗时,缺少必填参数execute_time_!");
    110             }
    111 
    112             inputObj.SetValue("appid", h5Config.AppId);//公众账号ID
    113             inputObj.SetValue("mch_id", h5Config.Partner);//商户号
    114             inputObj.SetValue("user_ip", DTRequest.GetIP());//终端ip
    115             inputObj.SetValue("time", DateTime.Now.ToString("yyyyMMddHHmmss"));//商户上报时间     
    116             inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
    117             inputObj.SetValue("sign", inputObj.MakeSign(h5Config.Key));//签名
    118             string xml = inputObj.ToXml();
    119 
    120             string response = HttpService.Post(xml, url, false, timeOut);
    121 
    122             WxPayData result = new WxPayData();
    123             result.FromXml(response, h5Config.Key);
    124             return result;
    125         }
    126 
    127         /**
    128         * 生成时间戳,标准北京时间,时区为东八区,自1970年1月1日 0点0分0秒以来的秒数
    129          * @return 时间戳
    130         */
    131         public static string GenerateTimeStamp()
    132         {
    133             TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
    134             return Convert.ToInt64(ts.TotalSeconds).ToString();
    135         }
    136 
    137         /**
    138         * 生成随机串,随机串包含字母或数字
    139         * @return 随机串
    140         */
    141         public static string GenerateNonceStr()
    142         {
    143             return Guid.NewGuid().ToString().Replace("-", "");
    144         }
    145         /// <summary>
    146         /// 接收从微信支付后台发送过来的数据未验证签名
    147         /// </summary>
    148         /// <returns>微信支付后台返回的数据</returns>
    149         public static WxPayData GetNotifyData()
    150         {
    151             //接收从微信后台POST过来的数据
    152             System.IO.Stream s = HttpContext.Current.Request.InputStream;
    153             int count = 0;
    154             byte[] buffer = new byte[1024];
    155             StringBuilder builder = new StringBuilder();
    156             while ((count = s.Read(buffer, 0, 1024)) > 0)
    157             {
    158                 builder.Append(Encoding.UTF8.GetString(buffer, 0, count));
    159             }
    160             s.Flush();
    161             s.Close();
    162             s.Dispose();
    163 
    164             //转换数据格式暂不验证签名
    165             WxPayData data = new WxPayData();
    166             try
    167             {
    168                 data.FromXml(builder.ToString());
    169             }
    170             catch (WxPayException ex)
    171             {
    172                 //若签名错误,则立即返回结果给微信支付后台
    173                 WxPayData res = new WxPayData();
    174                 res.SetValue("return_code", "FAIL");
    175                 res.SetValue("return_msg", ex.Message);
    176                 HttpContext.Current.Response.Write(res.ToXml());
    177                 HttpContext.Current.Response.End();
    178             }
    179 
    180             return data;
    181         }
    182 
    183         /**
    184         *    
    185         * 查询订单
    186         * @param WxPayData inputObj 提交给查询订单API的参数
    187         * @param int timeOut 超时时间
    188         * @throws WxPayException
    189         * @return 成功时返回订单查询结果,其他抛异常
    190         */
    191         public static WxPayData OrderQuery(int paymentId, WxPayData inputObj, int timeOut = 6)
    192         {
    193             string sendUrl = "https://api.mch.weixin.qq.com/pay/orderquery";
    194             //检测必填参数
    195             if (!inputObj.IsSet("out_trade_no") && !inputObj.IsSet("transaction_id"))
    196             {
    197                 throw new WxPayException("订单查询接口中,out_trade_no、transaction_id至少填一个!");
    198             }
    199             H5Config h5Config = new H5Config(paymentId);
    200             inputObj.SetValue("appid", h5Config.AppId);//公众账号ID
    201             inputObj.SetValue("mch_id", h5Config.Partner);//商户号
    202             inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
    203             inputObj.SetValue("sign", inputObj.MakeSign(h5Config.Key));//签名
    204             string xml = inputObj.ToXml();
    205             var startTime = DateTime.Now; //开始时间
    206             string response = HttpService.Post(xml, sendUrl, false, timeOut);//调用HTTP通信接口提交数据
    207             var endTime = DateTime.Now; //结束时间
    208             int timeCost = (int)((endTime - startTime).TotalMilliseconds); //计算所用时间
    209             //将xml格式的数据转化为对象以返回
    210             WxPayData result = new WxPayData();
    211             result.FromXml(response, h5Config.Key);
    212             ReportCostTime(paymentId, sendUrl, timeCost, result);//测速上报
    213             return result;
    214         }
    215 
    216     }
    217 }
    H5Pay.cs

    第三部分 微信扫码支付

    微信扫码支付一般应用的场景是PC端电脑支付。微信扫码支付可分为两种模式,根据支付场景选择相应模式。一般情况下的PC端扫码支付选择的是模式二,需要注意的是模式二无回调函数。

    【模式一】商户后台系统根据微信支付规则链接生成二维码,链接中带固定参数productid(可定义为产品标识或订单号)。用户扫码后,微信支付系统将productid和用户唯一标识(openid)回调商户后台系统(需要设置支付回调URL),商户后台系统根据productid生成支付交易,最后微信支付系统发起用户支付流程。

    【模式二】商户后台系统调用微信支付【统一下单API】生成预付交易,将接口返回的链接生成二维码,用户扫码后输入密码完成支付交易。注意:该模式的预付单有效期为2小时,过期后无法支付。

    微信扫码支付最友好的解决方案就是支付完成之后通过JS设置监听函数,通过该函数完成跳转。可参考的代码如下:

    NativeConfig.cs

     1 using System.Web;
     2 using System.Text;
     3 using System.IO;
     4 using System.Net;
     5 using System;
     6 using System.Xml;
     7 using System.Collections.Generic;
     8 using Gwbnsh.Common;
     9 
    10 namespace Gwbnsh.API.Payment.wxpay
    11 {
    12     public class NativeConfig
    13     {
    14         #region 字段
    15         private string partner = string.Empty;
    16         private string key = string.Empty;
    17         private string appid = string.Empty;
    18         private string notify_url = string.Empty;
    19         #endregion
    20 
    21         public NativeConfig(int site_payment_id)
    22         {
    23             Model.site_payment model = new BLL.site_payment().GetModel(site_payment_id); //站点支付方式
    24             if (model != null)
    25             {
    26                 Model.payment payModel = new BLL.payment().GetModel(model.payment_id); //支付平台
    27                 Model.sites siteModel = new BLL.sites().GetModel(model.site_id); //站点配置
    28                 Model.sysconfig sysConfig = new BLL.sysconfig().loadConfig(); //系统配置
    29 
    30                 partner = model.key1; //商户号(必须配置)
    31                 key = model.key2; //商户支付密钥,参考开户邮件设置(必须配置)
    32                 appid = model.key3; //绑定支付的APPID(必须配置)
    33                 notify_url = "";
    34             }
    35         }
    36 
    37         #region 属性
    38         /// <summary>
    39         /// 商户号(必须配置)
    40         /// </summary>
    41         public string Partner
    42         {
    43             get { return partner; }
    44             set { partner = value; }
    45         }
    46 
    47         /// <summary>
    48         /// 获取或设交易安全校验码
    49         /// </summary>
    50         public string Key
    51         {
    52             get { return key; }
    53             set { key = value; }
    54         }
    55 
    56         /// <summary>
    57         /// 绑定支付的APPID(必须配置)
    58         /// </summary>
    59         public string AppId
    60         {
    61             get { return appid; }
    62             set { appid = value; }
    63         }
    64 
    65         /// <summary>
    66         /// 获取服务器异步通知页面路径
    67         /// </summary>
    68         public string Notify_url
    69         {
    70             get { return notify_url; }
    71         }
    72 
    73         #endregion
    74     }
    75 }
    NativeConfig.cs

    NativePay.cs

      1 using System;
      2 using System.Collections.Generic;
      3 using System.Web;
      4 using System.Net;
      5 using System.IO;
      6 using System.Text;
      7 using Gwbnsh.Common;
      8 
      9 namespace Gwbnsh.API.Payment.wxpay
     10 {
     11     public class NativePay
     12     {
     13         /**
     14         * 
     15         * 测速上报
     16         * @param string interface_url 接口URL
     17         * @param int timeCost 接口耗时
     18         * @param WxPayData inputObj参数数组
     19         */
     20         public static void ReportCostTime(int paymentId, string interface_url, int timeCost, WxPayData inputObj)
     21         {
     22             //如果仅失败上报
     23             if (inputObj.IsSet("return_code") && inputObj.GetValue("return_code").ToString() == "SUCCESS" &&
     24              inputObj.IsSet("result_code") && inputObj.GetValue("result_code").ToString() == "SUCCESS")
     25             {
     26                 return;
     27             }
     28 
     29             //上报逻辑
     30             WxPayData data = new WxPayData();
     31             data.SetValue("interface_url", interface_url);
     32             data.SetValue("execute_time_", timeCost);
     33             //返回状态码
     34             if (inputObj.IsSet("return_code"))
     35             {
     36                 data.SetValue("return_code", inputObj.GetValue("return_code"));
     37             }
     38             //返回信息
     39             if (inputObj.IsSet("return_msg"))
     40             {
     41                 data.SetValue("return_msg", inputObj.GetValue("return_msg"));
     42             }
     43             //业务结果
     44             if (inputObj.IsSet("result_code"))
     45             {
     46                 data.SetValue("result_code", inputObj.GetValue("result_code"));
     47             }
     48             //错误代码
     49             if (inputObj.IsSet("err_code"))
     50             {
     51                 data.SetValue("err_code", inputObj.GetValue("err_code"));
     52             }
     53             //错误代码描述
     54             if (inputObj.IsSet("err_code_des"))
     55             {
     56                 data.SetValue("err_code_des", inputObj.GetValue("err_code_des"));
     57             }
     58             //商户订单号
     59             if (inputObj.IsSet("out_trade_no"))
     60             {
     61                 data.SetValue("out_trade_no", inputObj.GetValue("out_trade_no"));
     62             }
     63             //设备号
     64             if (inputObj.IsSet("device_info"))
     65             {
     66                 data.SetValue("device_info", inputObj.GetValue("device_info"));
     67             }
     68 
     69             try
     70             {
     71                 Report(paymentId, data);
     72             }
     73             catch (WxPayException ex)
     74             {
     75                 //不做任何处理
     76             }
     77         }
     78 
     79         /**
     80         * 
     81         * 测速上报接口实现
     82         * @param WxPayData inputObj 提交给测速上报接口的参数
     83         * @param int timeOut 测速上报接口超时时间
     84         * @throws WxPayException
     85         * @return 成功时返回测速上报接口返回的结果,其他抛异常
     86         */
     87         public static WxPayData Report(int paymentId, WxPayData inputObj, int timeOut = 1)
     88         {
     89             NativeConfig nativeConfig = new NativeConfig(paymentId);
     90             string url = "https://api.mch.weixin.qq.com/payitil/report";
     91             //检测必填参数
     92             if (!inputObj.IsSet("interface_url"))
     93             {
     94                 throw new WxPayException("接口URL,缺少必填参数interface_url!");
     95             }
     96             if (!inputObj.IsSet("return_code"))
     97             {
     98                 throw new WxPayException("返回状态码,缺少必填参数return_code!");
     99             }
    100             if (!inputObj.IsSet("result_code"))
    101             {
    102                 throw new WxPayException("业务结果,缺少必填参数result_code!");
    103             }
    104             if (!inputObj.IsSet("user_ip"))
    105             {
    106                 throw new WxPayException("访问接口IP,缺少必填参数user_ip!");
    107             }
    108             if (!inputObj.IsSet("execute_time_"))
    109             {
    110                 throw new WxPayException("接口耗时,缺少必填参数execute_time_!");
    111             }
    112 
    113             inputObj.SetValue("appid", nativeConfig.AppId);//公众账号ID
    114             inputObj.SetValue("mch_id", nativeConfig.Partner);//商户号
    115             inputObj.SetValue("user_ip", DTRequest.GetIP());//终端ip
    116             inputObj.SetValue("time", DateTime.Now.ToString("yyyyMMddHHmmss"));//商户上报时间     
    117             inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
    118             inputObj.SetValue("sign", inputObj.MakeSign(nativeConfig.Key));//签名
    119             string xml = inputObj.ToXml();
    120 
    121             string response = HttpService.Post(xml, url, false, timeOut);
    122 
    123             WxPayData result = new WxPayData();
    124             result.FromXml(response, nativeConfig.Key);
    125             return result;
    126         }
    127 
    128         /**
    129         * 生成时间戳,标准北京时间,时区为东八区,自1970年1月1日 0点0分0秒以来的秒数
    130          * @return 时间戳
    131         */
    132         public static string GenerateTimeStamp()
    133         {
    134             TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
    135             return Convert.ToInt64(ts.TotalSeconds).ToString();
    136         }
    137 
    138         /**
    139         * 生成随机串,随机串包含字母或数字
    140         * @return 随机串
    141         */
    142         public static string GenerateNonceStr()
    143         {
    144             return Guid.NewGuid().ToString().Replace("-", "");
    145         }
    146         /// <summary>
    147         /// 接收从微信支付后台发送过来的数据未验证签名
    148         /// </summary>
    149         /// <returns>微信支付后台返回的数据</returns>
    150         public static WxPayData GetNotifyData()
    151         {
    152             //接收从微信后台POST过来的数据
    153             System.IO.Stream s = HttpContext.Current.Request.InputStream;
    154             int count = 0;
    155             byte[] buffer = new byte[1024];
    156             StringBuilder builder = new StringBuilder();
    157             while ((count = s.Read(buffer, 0, 1024)) > 0)
    158             {
    159                 builder.Append(Encoding.UTF8.GetString(buffer, 0, count));
    160             }
    161             s.Flush();
    162             s.Close();
    163             s.Dispose();
    164 
    165             //转换数据格式暂不验证签名
    166             WxPayData data = new WxPayData();
    167             try
    168             {
    169                 data.FromXml(builder.ToString());
    170             }
    171             catch (WxPayException ex)
    172             {
    173                 //若签名错误,则立即返回结果给微信支付后台
    174                 WxPayData res = new WxPayData();
    175                 res.SetValue("return_code", "FAIL");
    176                 res.SetValue("return_msg", ex.Message);
    177                 HttpContext.Current.Response.Write(res.ToXml());
    178                 HttpContext.Current.Response.End();
    179             }
    180 
    181             return data;
    182         }
    183 
    184         /**
    185         *    
    186         * 查询订单
    187         * @param WxPayData inputObj 提交给查询订单API的参数
    188         * @param int timeOut 超时时间
    189         * @throws WxPayException
    190         * @return 成功时返回订单查询结果,其他抛异常
    191         */
    192         public static WxPayData OrderQuery(int paymentId, WxPayData inputObj, int timeOut = 6)
    193         {
    194             string sendUrl = "https://api.mch.weixin.qq.com/pay/orderquery";
    195             //检测必填参数
    196             if (!inputObj.IsSet("out_trade_no") && !inputObj.IsSet("transaction_id"))
    197             {
    198                 throw new WxPayException("订单查询接口中,out_trade_no、transaction_id至少填一个!");
    199             }
    200             NativeConfig nativeConfig = new NativeConfig(paymentId);
    201             inputObj.SetValue("appid", nativeConfig.AppId);//公众账号ID
    202             inputObj.SetValue("mch_id", nativeConfig.Partner);//商户号
    203             inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
    204             inputObj.SetValue("sign", inputObj.MakeSign(nativeConfig.Key));//签名
    205             string xml = inputObj.ToXml();
    206             var startTime = DateTime.Now; //开始时间
    207             string response = HttpService.Post(xml, sendUrl, false, timeOut);//调用HTTP通信接口提交数据
    208             var endTime = DateTime.Now; //结束时间
    209             int timeCost = (int)((endTime - startTime).TotalMilliseconds); //计算所用时间
    210             //将xml格式的数据转化为对象以返回
    211             WxPayData result = new WxPayData();
    212             result.FromXml(response, nativeConfig.Key);
    213             ReportCostTime(paymentId, sendUrl, timeCost, result);//测速上报
    214             return result;
    215         }
    216 
    217     }
    218 }
    NativePay.cs

    以下为扫码支付、H5支付以及公众号支付需要用到的共同类:

    HttpService.cs

      1 using System;
      2 using System.Collections.Generic;
      3 using System.Web;
      4 using System.Net;
      5 using System.IO;
      6 using System.Text;
      7 using System.Net.Security;
      8 using System.Security.Authentication;
      9 using System.Security.Cryptography.X509Certificates;
     10 
     11 namespace Gwbnsh.API.Payment.wxpay
     12 {
     13     /// <summary>
     14     /// http连接基础类,负责底层的http通信
     15     /// </summary>
     16     public class HttpService
     17     {
     18         public static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
     19         {
     20             //直接确认,否则打不开    
     21             return true;
     22         }
     23 
     24         public static string Post(string xml, string url, bool isUseCert, int timeout)
     25         {
     26             System.GC.Collect();//垃圾回收,回收没有正常关闭的http连接
     27 
     28             string result = "";//返回结果
     29 
     30             HttpWebRequest request = null;
     31             HttpWebResponse response = null;
     32             Stream reqStream = null;
     33 
     34             try
     35             {
     36                 //设置最大连接数
     37                 ServicePointManager.DefaultConnectionLimit = 200;
     38                 //设置https验证方式
     39                 if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase))
     40                 {
     41                     ServicePointManager.ServerCertificateValidationCallback =
     42                             new RemoteCertificateValidationCallback(CheckValidationResult);
     43                 }
     44 
     45                 /***************************************************************
     46                 * 下面设置HttpWebRequest的相关属性
     47                 * ************************************************************/
     48                 request = (HttpWebRequest)WebRequest.Create(url);
     49 
     50                 request.Method = "POST";
     51                 request.Timeout = timeout * 1000;
     52 
     53                 //设置代理服务器
     54                 /*WebProxy proxy = new WebProxy();                          //定义一个网关对象
     55                 proxy.Address = new Uri(WxPayConfig.PROXY_URL);              //网关服务器端口:端口
     56                 request.Proxy = proxy;*/
     57 
     58                 //设置POST的数据类型和长度
     59                 request.ContentType = "text/xml";
     60                 byte[] data = System.Text.Encoding.UTF8.GetBytes(xml);
     61                 request.ContentLength = data.Length;
     62 
     63                 //是否使用证书
     64                 /*if (isUseCert)
     65                 {
     66                     string path = HttpContext.Current.Request.PhysicalApplicationPath;
     67                     X509Certificate2 cert = new X509Certificate2(path + WxPayConfig.SSLCERT_PATH, WxPayConfig.SSLCERT_PASSWORD);
     68                     request.ClientCertificates.Add(cert);
     69                 }*/
     70 
     71                 //往服务器写入数据
     72                 reqStream = request.GetRequestStream();
     73                 reqStream.Write(data, 0, data.Length);
     74                 reqStream.Close();
     75 
     76                 //获取服务端返回
     77                 response = (HttpWebResponse)request.GetResponse();
     78 
     79                 //获取服务端返回数据
     80                 StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
     81                 result = sr.ReadToEnd().Trim();
     82                 sr.Close();
     83             }
     84             catch (System.Threading.ThreadAbortException e)
     85             {
     86                 System.Threading.Thread.ResetAbort();
     87             }
     88             catch (WebException e)
     89             {
     90                 throw new WxPayException(e.ToString());
     91             }
     92             catch (Exception e)
     93             {
     94                 throw new WxPayException(e.ToString());
     95             }
     96             finally
     97             {
     98                 //关闭连接和流
     99                 if (response != null)
    100                 {
    101                     response.Close();
    102                 }
    103                 if (request != null)
    104                 {
    105                     request.Abort();
    106                 }
    107             }
    108             return result;
    109         }
    110 
    111         /// <summary>
    112         /// 处理http GET请求,返回数据
    113         /// </summary>
    114         /// <param name="url">请求的url地址</param>
    115         /// <returns>http GET成功后返回的数据,失败抛WebException异常</returns>
    116         public static string Get(string url)
    117         {
    118             System.GC.Collect();
    119             string result = "";
    120 
    121             HttpWebRequest request = null;
    122             HttpWebResponse response = null;
    123 
    124             //请求url以获取数据
    125             try
    126             {
    127                 //设置最大连接数
    128                 ServicePointManager.DefaultConnectionLimit = 200;
    129                 //设置https验证方式
    130                 if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase))
    131                 {
    132                     ServicePointManager.ServerCertificateValidationCallback =
    133                             new RemoteCertificateValidationCallback(CheckValidationResult);
    134                 }
    135 
    136                 /***************************************************************
    137                 * 下面设置HttpWebRequest的相关属性
    138                 * ************************************************************/
    139                 request = (HttpWebRequest)WebRequest.Create(url);
    140 
    141                 request.Method = "GET";
    142 
    143                 //设置代理
    144                 /*WebProxy proxy = new WebProxy();
    145                 proxy.Address = new Uri(WxPayConfig.PROXY_URL);
    146                 request.Proxy = proxy;*/
    147 
    148                 //获取服务器返回
    149                 response = (HttpWebResponse)request.GetResponse();
    150 
    151                 //获取HTTP返回数据
    152                 StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
    153                 result = sr.ReadToEnd().Trim();
    154                 sr.Close();
    155             }
    156             catch (System.Threading.ThreadAbortException e)
    157             {
    158                 System.Threading.Thread.ResetAbort();
    159             }
    160             catch (WebException e)
    161             {
    162                 throw new WxPayException(e.ToString());
    163             }
    164             catch (Exception e)
    165             {
    166                 throw new WxPayException(e.ToString());
    167             }
    168             finally
    169             {
    170                 //关闭连接和流
    171                 if (response != null)
    172                 {
    173                     response.Close();
    174                 }
    175                 if (request != null)
    176                 {
    177                     request.Abort();
    178                 }
    179             }
    180             return result;
    181         }
    182     }
    183 }
    HttpService.cs

    WxPayData.cs

      1 using System;
      2 using System.Collections.Generic;
      3 using System.Web;
      4 using System.Xml;
      5 using System.Security.Cryptography;
      6 using System.Text;
      7 using Gwbnsh.Common;
      8 
      9 namespace Gwbnsh.API.Payment.wxpay
     10 {
     11     /// <summary>
     12     /// 微信支付协议接口数据类,所有的API接口通信都依赖这个数据结构,
     13     /// 在调用接口之前先填充各个字段的值,然后进行接口通信,
     14     /// 这样设计的好处是可扩展性强,用户可随意对协议进行更改而不用重新设计数据结构,
     15     /// 还可以随意组合出不同的协议数据包,不用为每个协议设计一个数据包结构
     16     /// </summary>
     17     public class WxPayData
     18     {
     19         public WxPayData()
     20         {
     21 
     22         }
     23 
     24         //采用排序的Dictionary的好处是方便对数据包进行签名,不用再签名之前再做一次排序
     25         private SortedDictionary<string, object> m_values = new SortedDictionary<string, object>();
     26 
     27         /**
     28         * 设置某个字段的值
     29         * @param key 字段名
     30          * @param value 字段值
     31         */
     32         public void SetValue(string key, object value)
     33         {
     34             m_values[key] = value;
     35         }
     36 
     37         /**
     38         * 根据字段名获取某个字段的值
     39         * @param key 字段名
     40          * @return key对应的字段值
     41         */
     42         public object GetValue(string key)
     43         {
     44             object o = null;
     45             m_values.TryGetValue(key, out o);
     46             return o;
     47         }
     48 
     49         /**
     50          * 判断某个字段是否已设置
     51          * @param key 字段名
     52          * @return 若字段key已被设置,则返回true,否则返回false
     53          */
     54         public bool IsSet(string key)
     55         {
     56             object o = null;
     57             m_values.TryGetValue(key, out o);
     58             if (null != o)
     59                 return true;
     60             else
     61                 return false;
     62         }
     63 
     64         /**
     65         * @将Dictionary转成xml
     66         * @return 经转换得到的xml串
     67         * @throws WxPayException
     68         **/
     69         public string ToXml()
     70         {
     71             //数据为空时不能转化为xml格式
     72             if (0 == m_values.Count)
     73             {
     74                 throw new WxPayException("WxPayData数据为空!");
     75             }
     76 
     77             string xml = "<xml>";
     78             foreach (KeyValuePair<string, object> pair in m_values)
     79             {
     80                 //字段值不能为null,会影响后续流程
     81                 if (pair.Value == null)
     82                 {
     83                     throw new WxPayException("WxPayData内部含有值为null的字段!");
     84                 }
     85 
     86                 if (pair.Value.GetType() == typeof(int))
     87                 {
     88                     xml += "<" + pair.Key + ">" + pair.Value + "</" + pair.Key + ">";
     89                 }
     90                 else if (pair.Value.GetType() == typeof(string))
     91                 {
     92                     xml += "<" + pair.Key + ">" + "<![CDATA[" + pair.Value + "]]></" + pair.Key + ">";
     93                 }
     94                 else//除了string和int类型不能含有其他数据类型
     95                 {
     96                     throw new WxPayException("WxPayData字段数据类型错误!");
     97                 }
     98             }
     99             xml += "</xml>";
    100             return xml;
    101         }
    102 
    103         /**
    104         * @接收从微信后台POST过来的数据(未验证签名)
    105         * @return 经转换得到的Dictionary
    106         * @throws WxPayException
    107         */
    108         public SortedDictionary<string, object> GetRequest()
    109         {
    110             //接收从微信后台POST过来的数据
    111             System.IO.Stream s = HttpContext.Current.Request.InputStream;
    112             int count = 0;
    113             byte[] buffer = new byte[1024];
    114             StringBuilder builder = new StringBuilder();
    115             while ((count = s.Read(buffer, 0, 1024)) > 0)
    116             {
    117                 builder.Append(Encoding.UTF8.GetString(buffer, 0, count));
    118             }
    119             s.Flush();
    120             s.Close();
    121             s.Dispose();
    122 
    123             if (string.IsNullOrEmpty(builder.ToString()))
    124             {
    125                 throw new WxPayException("将空的xml串转换为WxPayData不合法!");
    126             }
    127 
    128             XmlDocument xmlDoc = new XmlDocument();
    129             xmlDoc.LoadXml(builder.ToString());
    130             XmlNode xmlNode = xmlDoc.FirstChild;//获取到根节点<xml>
    131             XmlNodeList nodes = xmlNode.ChildNodes;
    132             foreach (XmlNode xn in nodes)
    133             {
    134                 XmlElement xe = (XmlElement)xn;
    135                 m_values[xe.Name] = xe.InnerText;//获取xml的键值对到WxPayData内部的数据中
    136             }
    137 
    138             return m_values;
    139         }
    140 
    141         /**
    142         * @将xml转为WxPayData对象并返回对象内部的数据
    143         * @param string 待转换的xml串
    144         * @return 经转换得到的Dictionary
    145         * @throws WxPayException
    146         */
    147         public SortedDictionary<string, object> FromXml(string xml, string key)
    148         {
    149             if (string.IsNullOrEmpty(xml))
    150             {
    151                 throw new WxPayException("将空的xml串转换为WxPayData不合法!");
    152             }
    153 
    154             XmlDocument xmlDoc = new XmlDocument();
    155             xmlDoc.LoadXml(xml);
    156             XmlNode xmlNode = xmlDoc.FirstChild;//获取到根节点<xml>
    157             XmlNodeList nodes = xmlNode.ChildNodes;
    158             foreach (XmlNode xn in nodes)
    159             {
    160                 XmlElement xe = (XmlElement)xn;
    161                 m_values[xe.Name] = xe.InnerText;//获取xml的键值对到WxPayData内部的数据中
    162             }
    163 
    164             try
    165             {
    166                 //2015-06-29 错误是没有签名
    167                 if (m_values["return_code"] != "SUCCESS")
    168                 {
    169                     return m_values;
    170                 }
    171                 CheckSign(key);//验证签名,不通过会抛异常
    172             }
    173             catch (Exception ex)
    174             {
    175                 throw new WxPayException(ex.Message);
    176             }
    177 
    178             return m_values;
    179         }
    180         /**
    181        * @将xml转为WxPayData对象并返回对象内部的数据(未验证签名)
    182        * @param string 待转换的xml串
    183        * @return 经转换得到的Dictionary
    184        * @throws WxPayException
    185        */
    186         public SortedDictionary<string, object> FromXml(string xml)
    187         {
    188             if (string.IsNullOrEmpty(xml))
    189             {
    190                 throw new WxPayException("将空的xml串转换为WxPayData不合法!");
    191             }
    192 
    193             XmlDocument xmlDoc = new XmlDocument();
    194             xmlDoc.LoadXml(xml);
    195             XmlNode xmlNode = xmlDoc.FirstChild;//获取到根节点<xml>
    196             XmlNodeList nodes = xmlNode.ChildNodes;
    197             foreach (XmlNode xn in nodes)
    198             {
    199                 XmlElement xe = (XmlElement)xn;
    200                 m_values[xe.Name] = xe.InnerText;//获取xml的键值对到WxPayData内部的数据中
    201             }
    202 
    203             try
    204             {
    205                 //2015-06-29 错误是没有签名
    206                 if (m_values["return_code"] != "SUCCESS")
    207                 {
    208                     return m_values;
    209                 }
    210             }
    211             catch (Exception ex)
    212             {
    213                 throw new WxPayException(ex.Message);
    214             }
    215 
    216             return m_values;
    217         }
    218         /**
    219         * @Dictionary格式转化成url参数格式
    220         * @ return url格式串, 该串不包含sign字段值
    221         */
    222         public string ToUrl()
    223         {
    224             string buff = "";
    225             foreach (KeyValuePair<string, object> pair in m_values)
    226             {
    227                 if (pair.Value == null)
    228                 {
    229                     throw new WxPayException("WxPayData内部含有值为null的字段!");
    230                 }
    231 
    232                 if (pair.Key != "sign" && pair.Value.ToString() != "")
    233                 {
    234                     buff += pair.Key + "=" + pair.Value + "&";
    235                 }
    236             }
    237             buff = buff.Trim('&');
    238             return buff;
    239         }
    240 
    241 
    242         /**
    243         * @Dictionary格式化成Json
    244          * @return json串数据
    245         */
    246         public string ToJson()
    247         {
    248             string jsonStr = JsonHelper.ObjectToJSON(m_values);
    249             return jsonStr;
    250         }
    251 
    252         /**
    253         * @values格式化成能在Web页面上显示的结果(因为web页面上不能直接输出xml格式的字符串)
    254         */
    255         public string ToPrintStr()
    256         {
    257             string str = "";
    258             foreach (KeyValuePair<string, object> pair in m_values)
    259             {
    260                 if (pair.Value == null)
    261                 {
    262                     throw new WxPayException("WxPayData内部含有值为null的字段!");
    263                 }
    264 
    265                 str += string.Format("{0}={1}<br>", pair.Key, pair.Value.ToString());
    266             }
    267             return str;
    268         }
    269 
    270         /**
    271         * @生成签名,详见签名生成算法
    272         * @return 签名, sign字段不参加签名
    273         */
    274         public string MakeSign(string key)
    275         {
    276             //转url格式
    277             string str = ToUrl();
    278             //在string后加入API KEY
    279             str += "&key=" + key;
    280             //MD5加密
    281             var md5 = MD5.Create();
    282             var bs = md5.ComputeHash(Encoding.UTF8.GetBytes(str));
    283             var sb = new StringBuilder();
    284             foreach (byte b in bs)
    285             {
    286                 sb.Append(b.ToString("x2"));
    287             }
    288             //所有字符转为大写
    289             return sb.ToString().ToUpper();
    290         }
    291 
    292         /**
    293         * 
    294         * 检测签名是否正确
    295         * 正确返回true,错误抛异常
    296         */
    297         public bool CheckSign(string key)
    298         {
    299             //如果没有设置签名,则跳过检测
    300             if (!IsSet("sign"))
    301             {
    302                 throw new WxPayException("WxPayData签名存在但不合法!");
    303             }
    304             //如果设置了签名但是签名为空,则抛异常
    305             else if (GetValue("sign") == null || GetValue("sign").ToString() == "")
    306             {
    307                 throw new WxPayException("WxPayData签名存在但不合法!");
    308             }
    309 
    310             //获取接收到的签名
    311             string return_sign = GetValue("sign").ToString();
    312 
    313             //在本地计算新的签名
    314             string cal_sign = MakeSign(key);
    315 
    316             if (cal_sign == return_sign)
    317             {
    318                 return true;
    319             }
    320 
    321             throw new WxPayException("WxPayData签名验证错误!");
    322         }
    323 
    324         /**
    325         * @获取Dictionary
    326         */
    327         public SortedDictionary<string, object> GetValues()
    328         {
    329             return m_values;
    330         }
    331     }
    332 }
    WxPayData.cs

    WxPayException.cs

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Web;
     4 
     5 namespace Gwbnsh.API.Payment.wxpay
     6 {
     7     public class WxPayException : Exception 
     8     {
     9         public WxPayException(string msg) : base(msg)
    10         {
    11 
    12         }
    13     }
    14 }
    WxPayException.cs

    最后,总结一下上述几种支付方式需要注意的点。

    1. 所有的支付参数都需要到微信支付商户平台(pay.weixin.qq.com)配置参数。

    2. 微信公众号支付、微信扫码支付需要在微信公众号里面申请开通;H5支付需要在微信商户平台申请开通。

    3. 仅有公众号支付和扫码支付一模式需配置支付域名,H5无需配置域名,但是使用的网站域名和申请时填写的要一致。

    4. 所有使用JS API方式发起支付请求的链接地址,都必须在当前页面所配置的支付授权目录之下。

    5. 当公众平台接到扫码支付请求时,会回调当前页面所配置的支付回调链接传递订单信息。

    以下为源码,包含aspx页面文件和详细使用说明:下载

     

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-5-3 19:42 , Processed in 0.096570 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表