zoukankan      html  css  js  c++  java
  • .Net后台实现微信APP支付

    上一节分享了微信小程序支付的后台,这一节来分享一下微信APP支付的后台。微信APP支付和微信小程序差别不大,微信APP支付后台不需要微信登录凭证、后台下单时交易类型(trade_type)不再是"JSAPI",而是“APP”、商户后台传递给支付端的下单参数也有所不同。由于微信小程序支付和APP支付使用的APPID不同,索性直接写了两套支付,不再在代码里区分究竟该使用小程序支付的配置参数还是APP支付的参数。

    官方是这样介绍的


    具体实现:

    在WePay文件夹下新建AppPay文件夹(微信支付的公共类在上一节),用于存放微信APP支付用到的类,新建AppPayConfig类

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 using System.Web.Configuration;
     7 
     8 namespace App.Pay.WePay.XcxPay
     9 {
    10     public class XcxPayConfig : WePayConfig
    11     {
    12         //=======【基本信息设置】=====================================
    13         /* 微信公众号信息配置
    14         * APPID:绑定支付的APPID(必须配置)
    15         * MCHID:商户号(必须配置)
    16         * KEY:商户支付密钥,参考开户邮件设置(必须配置)
    17         * APPSECRET:公众帐号secert(仅JSAPI支付的时候需要配置)
    18         */
    19         /// 小程序支付
    20         public static string APPID = WebConfigurationManager.AppSettings["XcxAppID"].ToString();
    21         public static string MCHID = WebConfigurationManager.AppSettings["XcxMchID"].ToString();
    22         public static string KEY = WebConfigurationManager.AppSettings["XcxKey"].ToString();
    23         public static string APPSECRET = WebConfigurationManager.AppSettings["XcxAppSecret"].ToString();
    24 
    25         //=======【证书路径设置】===================================== 
    26         /* 证书路径,注意应该填写绝对路径(仅退款、撤销订单时需要)
    27         */
    28         public const string SSLCERT_PATH = "cert/apiclient_cert.p12";
    29         public const string SSLCERT_PASSWORD = "1233410002";
    30 
    31         //=======【支付结果通知url】===================================== 
    32         /* 支付结果通知回调url,用于商户接收支付结果
    33         */
    34         public static string NOTIFY_URL = WebConfigurationManager.AppSettings["XcxNotifyUrl"].ToString();
    35 
    36         // log记录
    37         public static string LogPath = WebConfigurationManager.AppSettings["XcxLog"].ToString();
    38     }
    39 }
    View Code

    新建AppPayHttpService类

      1 using System;
      2 using System.Collections.Generic;
      3 using System.IO;
      4 using System.Linq;
      5 using System.Net;
      6 using System.Net.Security;
      7 using System.Security.Cryptography.X509Certificates;
      8 using System.Text;
      9 using System.Threading.Tasks;
     10 using System.Web;
     11 
     12 namespace App.Pay.WePay.AppPay
     13 {
     14     public class AppPayHttpService
     15     {
     16         private static Log Log = new Log(AppPayConfig.LogPath);
     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 + AppPayConfig.SSLCERT_PATH, AppPayConfig.SSLCERT_PASSWORD);
     68                     request.ClientCertificates.Add(cert);
     69                     //Log.Debug("WxPayApi", "PostXml used cert");
     70                 }
     71 
     72                 //往服务器写入数据
     73                 reqStream = request.GetRequestStream();
     74                 reqStream.Write(data, 0, data.Length);
     75                 reqStream.Close();
     76 
     77                 //获取服务端返回
     78                 response = (HttpWebResponse)request.GetResponse();
     79 
     80                 //获取服务端返回数据
     81                 StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
     82                 result = sr.ReadToEnd().Trim();
     83                 sr.Close();
     84             }
     85             catch (System.Threading.ThreadAbortException e)
     86             {
     87                 Log.Error("HttpService", "Thread - caught ThreadAbortException - resetting.");
     88                 Log.Error("Exception message: {0}", e.Message);
     89                 System.Threading.Thread.ResetAbort();
     90             }
     91             catch (WebException e)
     92             {
     93                 Log.Error("HttpService", e.ToString());
     94                 if (e.Status == WebExceptionStatus.ProtocolError)
     95                 {
     96                     Log.Error("HttpService", "StatusCode : " + ((HttpWebResponse)e.Response).StatusCode);
     97                     Log.Error("HttpService", "StatusDescription : " + ((HttpWebResponse)e.Response).StatusDescription);
     98                 }
     99                 throw new WePayException(e.ToString());
    100             }
    101             catch (Exception e)
    102             {
    103                 Log.Error("HttpService", e.ToString());
    104                 throw new WePayException(e.ToString());
    105             }
    106             finally
    107             {
    108                 //关闭连接和流
    109                 if (response != null)
    110                 {
    111                     response.Close();
    112                 }
    113                 if (request != null)
    114                 {
    115                     request.Abort();
    116                 }
    117             }
    118             return result;
    119         }
    120 
    121         /// <summary>
    122         /// 处理http GET请求,返回数据
    123         /// </summary>
    124         /// <param name="url">请求的url地址</param>
    125         /// <returns>http GET成功后返回的数据,失败抛WebException异常</returns>
    126         public static string Get(string url)
    127         {
    128             System.GC.Collect();
    129             string result = "";
    130 
    131             HttpWebRequest request = null;
    132             HttpWebResponse response = null;
    133 
    134             //请求url以获取数据
    135             try
    136             {
    137                 //设置最大连接数
    138                 ServicePointManager.DefaultConnectionLimit = 200;
    139                 //设置https验证方式
    140                 if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase))
    141                 {
    142                     ServicePointManager.ServerCertificateValidationCallback =
    143                             new RemoteCertificateValidationCallback(CheckValidationResult);
    144                 }
    145 
    146                 /***************************************************************
    147                 * 下面设置HttpWebRequest的相关属性
    148                 * ************************************************************/
    149                 request = (HttpWebRequest)WebRequest.Create(url);
    150 
    151                 request.Method = "GET";
    152 
    153                 //设置代理
    154                 //WebProxy proxy = new WebProxy();
    155                 //proxy.Address = new Uri(WxPayConfig.PROXY_URL);
    156                 //request.Proxy = proxy;
    157 
    158                 //获取服务器返回
    159                 response = (HttpWebResponse)request.GetResponse();
    160 
    161                 //获取HTTP返回数据
    162                 StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
    163                 result = sr.ReadToEnd().Trim();
    164                 sr.Close();
    165             }
    166             catch (System.Threading.ThreadAbortException e)
    167             {
    168                 Log.Error("HttpService", "Thread - caught ThreadAbortException - resetting.");
    169                 Log.Error("Exception message: {0}", e.Message);
    170                 System.Threading.Thread.ResetAbort();
    171             }
    172             catch (WebException e)
    173             {
    174                 Log.Error("HttpService", e.ToString());
    175                 if (e.Status == WebExceptionStatus.ProtocolError)
    176                 {
    177                     Log.Error("HttpService", "StatusCode : " + ((HttpWebResponse)e.Response).StatusCode);
    178                     Log.Error("HttpService", "StatusDescription : " + ((HttpWebResponse)e.Response).StatusDescription);
    179                 }
    180                 throw new WePayException(e.ToString());
    181             }
    182             catch (Exception e)
    183             {
    184                 Log.Error("HttpService", e.ToString());
    185                 throw new WePayException(e.ToString());
    186             }
    187             finally
    188             {
    189                 //关闭连接和流
    190                 if (response != null)
    191                 {
    192                     response.Close();
    193                 }
    194                 if (request != null)
    195                 {
    196                     request.Abort();
    197                 }
    198             }
    199             return result;
    200         }
    201     }
    202 }
    View Code

    新建AppPayData类

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

    新建AppPayNotify类

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 using System.Web;
     7 
     8 namespace App.Pay.WePay.AppPay
     9 {
    10     /// <summary>
    11     /// 回调处理基类
    12     /// 主要负责接收微信支付后台发送过来的数据,对数据进行签名验证
    13     /// 子类在此类基础上进行派生并重写自己的回调处理过程
    14     /// </summary>
    15     public class AppPayNotify
    16     {
    17         public HttpContext context { get; set; }
    18         public Log Log = new Log(AppPayConfig.LogPath);
    19 
    20         public AppPayNotify(HttpContext context)
    21         {
    22             this.context = context;
    23         }
    24 
    25         /// <summary>
    26         /// 接收从微信支付后台发送过来的数据并验证签名
    27         /// </summary>
    28         /// <returns>微信支付后台返回的数据</returns>
    29         public AppPayData GetNotifyData()
    30         {
    31             //接收从微信后台POST过来的数据
    32             System.IO.Stream s = context.Request.InputStream;
    33             int count = 0;
    34             byte[] buffer = new byte[1024];
    35             StringBuilder builder = new StringBuilder();
    36             while ((count = s.Read(buffer, 0, 1024)) > 0)
    37             {
    38                 builder.Append(Encoding.UTF8.GetString(buffer, 0, count));
    39             }
    40             s.Flush();
    41             s.Close();
    42             s.Dispose();
    43 
    44             //转换数据格式并验证签名
    45             AppPayData data = new AppPayData();
    46             try
    47             {
    48                 data.FromXml(builder.ToString());
    49             }
    50             catch (WePayException ex)
    51             {
    52                 //若签名错误,则立即返回结果给微信支付后台
    53                 AppPayData res = new AppPayData();
    54                 res.SetValue("return_code", "FAIL");
    55                 res.SetValue("return_msg", ex.Message);
    56                 Log.Error(this.GetType().ToString(), "Sign check error : " + res.ToXml());
    57                 context.Response.Write(res.ToXml());
    58                 context.Response.End();
    59             }
    60 
    61             Log.Info(this.GetType().ToString(), "Check sign success");
    62             return data;
    63         }
    64 
    65         //派生类需要重写这个方法,进行不同的回调处理
    66         public virtual void ProcessNotify()
    67         {
    68 
    69         }
    70     }
    71 }
    View Code

    新建AppPayApi类

      1 using System;
      2 using System.Collections.Generic;
      3 using System.Linq;
      4 using System.Text;
      5 using System.Threading.Tasks;
      6 
      7 namespace App.Pay.WePay.AppPay
      8 {
      9     public class AppPayApi
     10     {
     11         public static Log Log = new Log(AppPayConfig.LogPath);
     12 
     13         /**
     14         * 提交被扫支付API
     15         * 收银员使用扫码设备读取微信用户刷卡授权码以后,二维码或条码信息传送至商户收银台,
     16         * 由商户收银台或者商户后台调用该接口发起支付。
     17         * @param WxPayData inputObj 提交给被扫支付API的参数
     18         * @param int timeOut 超时时间
     19         * @throws WePayException
     20         * @return 成功时返回调用结果,其他抛异常
     21         */
     22         public static AppPayData Micropay(AppPayData inputObj, int timeOut = 10)
     23         {
     24             string url = "https://api.mch.weixin.qq.com/pay/micropay";
     25             //检测必填参数
     26             if (!inputObj.IsSet("body"))
     27             {
     28                 throw new WePayException("提交被扫支付API接口中,缺少必填参数body!");
     29             }
     30             else if (!inputObj.IsSet("out_trade_no"))
     31             {
     32                 throw new WePayException("提交被扫支付API接口中,缺少必填参数out_trade_no!");
     33             }
     34             else if (!inputObj.IsSet("total_fee"))
     35             {
     36                 throw new WePayException("提交被扫支付API接口中,缺少必填参数total_fee!");
     37             }
     38             else if (!inputObj.IsSet("auth_code"))
     39             {
     40                 throw new WePayException("提交被扫支付API接口中,缺少必填参数auth_code!");
     41             }
     42 
     43             inputObj.SetValue("spbill_create_ip", WePayConfig.IP);//终端ip
     44             inputObj.SetValue("appid", AppPayConfig.APPID);//公众账号ID
     45             inputObj.SetValue("mch_id", AppPayConfig.MCHID);//商户号
     46             inputObj.SetValue("nonce_str", Guid.NewGuid().ToString().Replace("-", ""));//随机字符串
     47             inputObj.SetValue("sign", inputObj.MakeSign());//签名
     48             string xml = inputObj.ToXml();
     49 
     50             var start = DateTime.Now;//请求开始时间
     51 
     52             Log.Info("XcxPayApi", "MicroPay request : " + xml);
     53             string response = AppPayHttpService.Post(xml, url, false, timeOut);//调用HTTP通信接口以提交数据到API
     54             Log.Info("XcxPayApi", "MicroPay response : " + response);
     55 
     56             var end = DateTime.Now;
     57             int timeCost = (int)((end - start).TotalMilliseconds);//获得接口耗时
     58 
     59             //将xml格式的结果转换为对象以返回
     60             AppPayData result = new AppPayData();
     61             result.FromXml(response);
     62 
     63             ReportCostTime(url, timeCost, result);//测速上报
     64 
     65             return result;
     66         }
     67 
     68 
     69         /**
     70         *    
     71         * 查询订单
     72         * @param WxPayData inputObj 提交给查询订单API的参数
     73         * @param int timeOut 超时时间
     74         * @throws WePayException
     75         * @return 成功时返回订单查询结果,其他抛异常
     76         */
     77         public static AppPayData OrderQuery(AppPayData inputObj, int timeOut = 6)
     78         {
     79             string url = "https://api.mch.weixin.qq.com/pay/orderquery";
     80             //检测必填参数
     81             if (!inputObj.IsSet("out_trade_no") && !inputObj.IsSet("transaction_id"))
     82             {
     83                 throw new WePayException("订单查询接口中,out_trade_no、transaction_id至少填一个!");
     84             }
     85 
     86             inputObj.SetValue("appid", AppPayConfig.APPID);//公众账号ID
     87             inputObj.SetValue("mch_id", AppPayConfig.MCHID);//商户号
     88             inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
     89             inputObj.SetValue("sign", inputObj.MakeSign());//签名
     90 
     91             string xml = inputObj.ToXml();
     92 
     93             var start = DateTime.Now;
     94 
     95             Log.Info("XcxPayApi", "OrderQuery request : " + xml);
     96             string response = AppPayHttpService.Post(xml, url, false, timeOut);//调用HTTP通信接口提交数据
     97             Log.Info("XcxPayApi", "OrderQuery response : " + response);
     98 
     99             var end = DateTime.Now;
    100             int timeCost = (int)((end - start).TotalMilliseconds);//获得接口耗时
    101 
    102             //将xml格式的数据转化为对象以返回
    103             AppPayData result = new AppPayData();
    104             result.FromXml(response);
    105 
    106             ReportCostTime(url, timeCost, result);//测速上报
    107 
    108             return result;
    109         }
    110 
    111 
    112         /**
    113         * 
    114         * 撤销订单API接口
    115         * @param WxPayData inputObj 提交给撤销订单API接口的参数,out_trade_no和transaction_id必填一个
    116         * @param int timeOut 接口超时时间
    117         * @throws WePayException
    118         * @return 成功时返回API调用结果,其他抛异常
    119         */
    120         public static AppPayData Reverse(AppPayData inputObj, int timeOut = 6)
    121         {
    122             string url = "https://api.mch.weixin.qq.com/secapi/pay/reverse";
    123             //检测必填参数
    124             if (!inputObj.IsSet("out_trade_no") && !inputObj.IsSet("transaction_id"))
    125             {
    126                 throw new WePayException("撤销订单API接口中,参数out_trade_no和transaction_id必须填写一个!");
    127             }
    128 
    129             inputObj.SetValue("appid", AppPayConfig.APPID);//公众账号ID
    130             inputObj.SetValue("mch_id", AppPayConfig.MCHID);//商户号
    131             inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
    132             inputObj.SetValue("sign", inputObj.MakeSign());//签名
    133             string xml = inputObj.ToXml();
    134 
    135             var start = DateTime.Now;//请求开始时间
    136 
    137             Log.Info("XcxPayApi", "Reverse request : " + xml);
    138 
    139             string response = AppPayHttpService.Post(xml, url, true, timeOut);
    140 
    141             Log.Info("XcxPayApi", "Reverse response : " + response);
    142 
    143             var end = DateTime.Now;
    144             int timeCost = (int)((end - start).TotalMilliseconds);
    145 
    146             AppPayData result = new AppPayData();
    147             result.FromXml(response);
    148 
    149             ReportCostTime(url, timeCost, result);//测速上报
    150 
    151             return result;
    152         }
    153 
    154 
    155         /**
    156         * 
    157         * 申请退款
    158         * @param WxPayData inputObj 提交给申请退款API的参数
    159         * @param int timeOut 超时时间
    160         * @throws WePayException
    161         * @return 成功时返回接口调用结果,其他抛异常
    162         */
    163         public static AppPayData Refund(AppPayData inputObj, int timeOut = 6)
    164         {
    165             string url = "https://api.mch.weixin.qq.com/secapi/pay/refund";
    166             //检测必填参数
    167             if (!inputObj.IsSet("out_trade_no") && !inputObj.IsSet("transaction_id"))
    168             {
    169                 throw new WePayException("退款申请接口中,out_trade_no、transaction_id至少填一个!");
    170             }
    171             else if (!inputObj.IsSet("out_refund_no"))
    172             {
    173                 throw new WePayException("退款申请接口中,缺少必填参数out_refund_no!");
    174             }
    175             else if (!inputObj.IsSet("total_fee"))
    176             {
    177                 throw new WePayException("退款申请接口中,缺少必填参数total_fee!");
    178             }
    179             else if (!inputObj.IsSet("refund_fee"))
    180             {
    181                 throw new WePayException("退款申请接口中,缺少必填参数refund_fee!");
    182             }
    183             else if (!inputObj.IsSet("op_user_id"))
    184             {
    185                 throw new WePayException("退款申请接口中,缺少必填参数op_user_id!");
    186             }
    187 
    188             inputObj.SetValue("appid", AppPayConfig.APPID);//公众账号ID
    189             inputObj.SetValue("mch_id", AppPayConfig.MCHID);//商户号
    190             inputObj.SetValue("nonce_str", Guid.NewGuid().ToString().Replace("-", ""));//随机字符串
    191             inputObj.SetValue("sign", inputObj.MakeSign());//签名
    192 
    193             string xml = inputObj.ToXml();
    194             var start = DateTime.Now;
    195 
    196             Log.Info("XcxPayApi", "Refund request : " + xml);
    197             string response = AppPayHttpService.Post(xml, url, true, timeOut);//调用HTTP通信接口提交数据到API
    198             Log.Info("XcxPayApi", "Refund response : " + response);
    199 
    200             var end = DateTime.Now;
    201             int timeCost = (int)((end - start).TotalMilliseconds);//获得接口耗时
    202 
    203             //将xml格式的结果转换为对象以返回
    204             AppPayData result = new AppPayData();
    205             result.FromXml(response);
    206 
    207             ReportCostTime(url, timeCost, result);//测速上报
    208 
    209             return result;
    210         }
    211 
    212 
    213         /**
    214         * 
    215         * 查询退款
    216         * 提交退款申请后,通过该接口查询退款状态。退款有一定延时,
    217         * 用零钱支付的退款20分钟内到账,银行卡支付的退款3个工作日后重新查询退款状态。
    218         * out_refund_no、out_trade_no、transaction_id、refund_id四个参数必填一个
    219         * @param WxPayData inputObj 提交给查询退款API的参数
    220         * @param int timeOut 接口超时时间
    221         * @throws WePayException
    222         * @return 成功时返回,其他抛异常
    223         */
    224         public static AppPayData RefundQuery(AppPayData inputObj, int timeOut = 6)
    225         {
    226             string url = "https://api.mch.weixin.qq.com/pay/refundquery";
    227             //检测必填参数
    228             if (!inputObj.IsSet("out_refund_no") && !inputObj.IsSet("out_trade_no") &&
    229                 !inputObj.IsSet("transaction_id") && !inputObj.IsSet("refund_id"))
    230             {
    231                 throw new WePayException("退款查询接口中,out_refund_no、out_trade_no、transaction_id、refund_id四个参数必填一个!");
    232             }
    233 
    234             inputObj.SetValue("appid", AppPayConfig.APPID);//公众账号ID
    235             inputObj.SetValue("mch_id", AppPayConfig.MCHID);//商户号
    236             inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
    237             inputObj.SetValue("sign", inputObj.MakeSign());//签名
    238 
    239             string xml = inputObj.ToXml();
    240 
    241             var start = DateTime.Now;//请求开始时间
    242 
    243             Log.Info("XcxPayApi", "RefundQuery request : " + xml);
    244             string response = AppPayHttpService.Post(xml, url, false, timeOut);//调用HTTP通信接口以提交数据到API
    245             Log.Info("XcxPayApi", "RefundQuery response : " + response);
    246 
    247             var end = DateTime.Now;
    248             int timeCost = (int)((end - start).TotalMilliseconds);//获得接口耗时
    249 
    250             //将xml格式的结果转换为对象以返回
    251             AppPayData result = new AppPayData();
    252             result.FromXml(response);
    253 
    254             ReportCostTime(url, timeCost, result);//测速上报
    255 
    256             return result;
    257         }
    258 
    259 
    260         /**
    261         * 下载对账单
    262         * @param WxPayData inputObj 提交给下载对账单API的参数
    263         * @param int timeOut 接口超时时间
    264         * @throws WePayException
    265         * @return 成功时返回,其他抛异常
    266         */
    267         public static AppPayData DownloadBill(AppPayData inputObj, int timeOut = 6)
    268         {
    269             string url = "https://api.mch.weixin.qq.com/pay/downloadbill";
    270             //检测必填参数
    271             if (!inputObj.IsSet("bill_date"))
    272             {
    273                 throw new WePayException("对账单接口中,缺少必填参数bill_date!");
    274             }
    275 
    276             inputObj.SetValue("appid", AppPayConfig.APPID);//公众账号ID
    277             inputObj.SetValue("mch_id", AppPayConfig.MCHID);//商户号
    278             inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
    279             inputObj.SetValue("sign", inputObj.MakeSign());//签名
    280 
    281             string xml = inputObj.ToXml();
    282 
    283             Log.Info("XcxPayApi", "DownloadBill request : " + xml);
    284             string response = AppPayHttpService.Post(xml, url, false, timeOut);//调用HTTP通信接口以提交数据到API
    285             Log.Info("XcxPayApi", "DownloadBill result : " + response);
    286 
    287             AppPayData result = new AppPayData();
    288             //若接口调用失败会返回xml格式的结果
    289             if (response.Substring(0, 5) == "<xml>")
    290             {
    291                 result.FromXml(response);
    292             }
    293             //接口调用成功则返回非xml格式的数据
    294             else
    295                 result.SetValue("result", response);
    296 
    297             return result;
    298         }
    299 
    300 
    301         /**
    302         * 
    303         * 转换短链接
    304         * 该接口主要用于扫码原生支付模式一中的二维码链接转成短链接(weixin://wxpay/s/XXXXXX),
    305         * 减小二维码数据量,提升扫描速度和精确度。
    306         * @param WxPayData inputObj 提交给转换短连接API的参数
    307         * @param int timeOut 接口超时时间
    308         * @throws WePayException
    309         * @return 成功时返回,其他抛异常
    310         */
    311         public static AppPayData ShortUrl(AppPayData inputObj, int timeOut = 6)
    312         {
    313             string url = "https://api.mch.weixin.qq.com/tools/shorturl";
    314             //检测必填参数
    315             if (!inputObj.IsSet("long_url"))
    316             {
    317                 throw new WePayException("需要转换的URL,签名用原串,传输需URL encode!");
    318             }
    319 
    320             inputObj.SetValue("appid", AppPayConfig.APPID);//公众账号ID
    321             inputObj.SetValue("mch_id", AppPayConfig.MCHID);//商户号
    322             inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串    
    323             inputObj.SetValue("sign", inputObj.MakeSign());//签名
    324             inputObj.SetValue("device_info", "wxAPP");//设备名称
    325             string xml = inputObj.ToXml();
    326 
    327             var start = DateTime.Now;//请求开始时间
    328 
    329             Log.Info("XcxPayApi", "ShortUrl request : " + xml);
    330             string response = AppPayHttpService.Post(xml, url, false, timeOut);
    331             Log.Info("XcxPayApi", "ShortUrl response : " + response);
    332 
    333             var end = DateTime.Now;
    334             int timeCost = (int)((end - start).TotalMilliseconds);
    335 
    336             AppPayData result = new AppPayData();
    337             result.FromXml(response);
    338             ReportCostTime(url, timeCost, result);//测速上报
    339 
    340             return result;
    341         }
    342 
    343 
    344         /**
    345         * 
    346         * 统一下单
    347         * @param WxPaydata inputObj 提交给统一下单API的参数
    348         * @param int timeOut 超时时间
    349         * @throws WePayException
    350         * @return 成功时返回,其他抛异常
    351         */
    352         public static AppPayData UnifiedOrder(AppPayData inputObj, int timeOut = 6)
    353         {
    354             string url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    355             //检测必填参数
    356             if (!inputObj.IsSet("out_trade_no"))
    357             {
    358                 throw new WePayException("缺少统一支付接口必填参数out_trade_no!");
    359             }
    360             else if (!inputObj.IsSet("body"))
    361             {
    362                 throw new WePayException("缺少统一支付接口必填参数body!");
    363             }
    364             else if (!inputObj.IsSet("total_fee"))
    365             {
    366                 throw new WePayException("缺少统一支付接口必填参数total_fee!");
    367             }
    368             else if (!inputObj.IsSet("trade_type"))
    369             {
    370                 throw new WePayException("缺少统一支付接口必填参数trade_type!");
    371             }
    372 
    373             //关联参数
    374             if (inputObj.GetValue("trade_type").ToString() == "JSAPI" && !inputObj.IsSet("openid"))
    375             {
    376                 throw new WePayException("统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!");
    377             }
    378             if (inputObj.GetValue("trade_type").ToString() == "NATIVE" && !inputObj.IsSet("product_id"))
    379             {
    380                 throw new WePayException("统一支付接口中,缺少必填参数product_id!trade_type为JSAPI时,product_id为必填参数!");
    381             }
    382 
    383             //异步通知url未设置,则使用配置文件中的url
    384             if (!inputObj.IsSet("notify_url"))
    385             {
    386                 inputObj.SetValue("notify_url", AppPayConfig.NOTIFY_URL);//异步通知url
    387             }
    388 
    389             inputObj.SetValue("appid", AppPayConfig.APPID);//公众账号ID
    390             inputObj.SetValue("mch_id", AppPayConfig.MCHID);//商户号
    391             inputObj.SetValue("spbill_create_ip", WePayConfig.IP);//终端ip              
    392             inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
    393 
    394             //签名
    395             inputObj.SetValue("sign", inputObj.MakeSign());
    396             string xml = inputObj.ToXml();
    397 
    398             var start = DateTime.Now;
    399 
    400             Log.Info("XcxPayApi", "UnfiedOrder request : " + xml);
    401             string response = AppPayHttpService.Post(xml, url, false, timeOut);
    402             Log.Info("XcxPayApi", "UnfiedOrder response : " + response);
    403 
    404             var end = DateTime.Now;
    405             int timeCost = (int)((end - start).TotalMilliseconds);
    406 
    407             AppPayData result = new AppPayData();
    408             result.FromXml(response);
    409 
    410             ReportCostTime(url, timeCost, result);//测速上报
    411 
    412             return result;
    413         }
    414 
    415         /**
    416         * 
    417         * 统一下单
    418         * @param WxPaydata inputObj 提交给统一下单API的参数
    419         * @param int timeOut 超时时间
    420         * @throws WePayException
    421         * @return 成功时返回,其他抛异常
    422         */
    423         public static AppPayData UnifiedOrderApp(AppPayData inputObj, int timeOut = 6)
    424         {
    425             string url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    426             //检测必填参数
    427             if (!inputObj.IsSet("out_trade_no"))
    428             {
    429                 throw new WePayException("缺少统一支付接口必填参数out_trade_no!");
    430             }
    431             else if (!inputObj.IsSet("body"))
    432             {
    433                 throw new WePayException("缺少统一支付接口必填参数body!");
    434             }
    435             else if (!inputObj.IsSet("total_fee"))
    436             {
    437                 throw new WePayException("缺少统一支付接口必填参数total_fee!");
    438             }
    439             else if (!inputObj.IsSet("trade_type"))
    440             {
    441                 throw new WePayException("缺少统一支付接口必填参数trade_type!");
    442             }
    443 
    444             //关联参数
    445             if (inputObj.GetValue("trade_type").ToString() == "JSAPI" && !inputObj.IsSet("openid"))
    446             {
    447                 throw new WePayException("统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!");
    448             }
    449             if (inputObj.GetValue("trade_type").ToString() == "NATIVE" && !inputObj.IsSet("product_id"))
    450             {
    451                 throw new WePayException("统一支付接口中,缺少必填参数product_id!trade_type为JSAPI时,product_id为必填参数!");
    452             }
    453 
    454             //异步通知url未设置,则使用配置文件中的url
    455             if (!inputObj.IsSet("notify_url"))
    456             {
    457                 inputObj.SetValue("notify_url", AppPayConfig.NOTIFY_URL);//异步通知url
    458             }
    459 
    460             inputObj.SetValue("appid", AppPayConfig.APPID);//公众账号ID
    461             inputObj.SetValue("mch_id", AppPayConfig.MCHID);//商户号
    462             inputObj.SetValue("spbill_create_ip", WePayConfig.IP);//终端ip              
    463             inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
    464 
    465             //签名
    466             inputObj.SetValue("sign", inputObj.MakeSign());
    467             string xml = inputObj.ToXml();
    468 
    469             var start = DateTime.Now;
    470 
    471             Log.Info("XcxPayApi", "UnfiedOrder request : " + xml);
    472             string response = AppPayHttpService.Post(xml, url, false, timeOut);
    473             Log.Info("XcxPayApi", "UnfiedOrder response : " + response);
    474 
    475             var end = DateTime.Now;
    476             int timeCost = (int)((end - start).TotalMilliseconds);
    477 
    478             AppPayData result = new AppPayData();
    479             result.FromXml(response);
    480 
    481             ReportCostTime(url, timeCost, result);//测速上报
    482 
    483             return result;
    484         }
    485 
    486 
    487         /**
    488         * 
    489         * 关闭订单
    490         * @param WxPayData inputObj 提交给关闭订单API的参数
    491         * @param int timeOut 接口超时时间
    492         * @throws WePayException
    493         * @return 成功时返回,其他抛异常
    494         */
    495         public static AppPayData CloseOrder(AppPayData inputObj, int timeOut = 6)
    496         {
    497             string url = "https://api.mch.weixin.qq.com/pay/closeorder";
    498             //检测必填参数
    499             if (!inputObj.IsSet("out_trade_no"))
    500             {
    501                 throw new WePayException("关闭订单接口中,out_trade_no必填!");
    502             }
    503 
    504             inputObj.SetValue("appid", AppPayConfig.APPID);//公众账号ID
    505             inputObj.SetValue("mch_id", AppPayConfig.MCHID);//商户号
    506             inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串        
    507             inputObj.SetValue("sign", inputObj.MakeSign());//签名
    508             string xml = inputObj.ToXml();
    509 
    510             var start = DateTime.Now;//请求开始时间
    511 
    512             string response = AppPayHttpService.Post(xml, url, false, timeOut);
    513 
    514             var end = DateTime.Now;
    515             int timeCost = (int)((end - start).TotalMilliseconds);
    516 
    517             AppPayData result = new AppPayData();
    518             result.FromXml(response);
    519 
    520             ReportCostTime(url, timeCost, result);//测速上报
    521 
    522             return result;
    523         }
    524 
    525 
    526         /**
    527         * 
    528         * 测速上报
    529         * @param string interface_url 接口URL
    530         * @param int timeCost 接口耗时
    531         * @param WxPayData inputObj参数数组
    532         */
    533         private static void ReportCostTime(string interface_url, int timeCost, AppPayData inputObj)
    534         {
    535             //如果不需要进行上报
    536             if (WePayConfig.REPORT_LEVENL == 0)
    537             {
    538                 return;
    539             }
    540 
    541             //如果仅失败上报
    542             if (WePayConfig.REPORT_LEVENL == 1 && inputObj.IsSet("return_code") && inputObj.GetValue("return_code").ToString() == "SUCCESS" &&
    543              inputObj.IsSet("result_code") && inputObj.GetValue("result_code").ToString() == "SUCCESS")
    544             {
    545                 return;
    546             }
    547 
    548             //上报逻辑
    549             AppPayData data = new AppPayData();
    550             data.SetValue("interface_url", interface_url);
    551             data.SetValue("execute_time_", timeCost);
    552             //返回状态码
    553             if (inputObj.IsSet("return_code"))
    554             {
    555                 data.SetValue("return_code", inputObj.GetValue("return_code"));
    556             }
    557             //返回信息
    558             if (inputObj.IsSet("return_msg"))
    559             {
    560                 data.SetValue("return_msg", inputObj.GetValue("return_msg"));
    561             }
    562             //业务结果
    563             if (inputObj.IsSet("result_code"))
    564             {
    565                 data.SetValue("result_code", inputObj.GetValue("result_code"));
    566             }
    567             //错误代码
    568             if (inputObj.IsSet("err_code"))
    569             {
    570                 data.SetValue("err_code", inputObj.GetValue("err_code"));
    571             }
    572             //错误代码描述
    573             if (inputObj.IsSet("err_code_des"))
    574             {
    575                 data.SetValue("err_code_des", inputObj.GetValue("err_code_des"));
    576             }
    577             //商户订单号
    578             if (inputObj.IsSet("out_trade_no"))
    579             {
    580                 data.SetValue("out_trade_no", inputObj.GetValue("out_trade_no"));
    581             }
    582             //设备号
    583             if (inputObj.IsSet("device_info"))
    584             {
    585                 data.SetValue("device_info", inputObj.GetValue("device_info"));
    586             }
    587 
    588             try
    589             {
    590                 Report(data);
    591             }
    592             catch (WePayException ex)
    593             {
    594                 //不做任何处理
    595             }
    596         }
    597 
    598 
    599         /**
    600         * 
    601         * 测速上报接口实现
    602         * @param WxPayData inputObj 提交给测速上报接口的参数
    603         * @param int timeOut 测速上报接口超时时间
    604         * @throws WePayException
    605         * @return 成功时返回测速上报接口返回的结果,其他抛异常
    606         */
    607         public static AppPayData Report(AppPayData inputObj, int timeOut = 1)
    608         {
    609             string url = "https://api.mch.weixin.qq.com/payitil/report";
    610             //检测必填参数
    611             if (!inputObj.IsSet("interface_url"))
    612             {
    613                 throw new WePayException("接口URL,缺少必填参数interface_url!");
    614             }
    615             if (!inputObj.IsSet("return_code"))
    616             {
    617                 throw new WePayException("返回状态码,缺少必填参数return_code!");
    618             }
    619             if (!inputObj.IsSet("result_code"))
    620             {
    621                 throw new WePayException("业务结果,缺少必填参数result_code!");
    622             }
    623             if (!inputObj.IsSet("user_ip"))
    624             {
    625                 throw new WePayException("访问接口IP,缺少必填参数user_ip!");
    626             }
    627             if (!inputObj.IsSet("execute_time_"))
    628             {
    629                 throw new WePayException("接口耗时,缺少必填参数execute_time_!");
    630             }
    631 
    632             inputObj.SetValue("appid", AppPayConfig.APPID);//公众账号ID
    633             inputObj.SetValue("mch_id", AppPayConfig.MCHID);//商户号
    634             inputObj.SetValue("user_ip", WePayConfig.IP);//终端ip
    635             inputObj.SetValue("time", DateTime.Now.ToString("yyyyMMddHHmmss"));//商户上报时间     
    636             inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
    637             inputObj.SetValue("sign", inputObj.MakeSign());//签名
    638             string xml = inputObj.ToXml();
    639 
    640             Log.Info("XcxPayApi", "Report request : " + xml);
    641 
    642             string response = AppPayHttpService.Post(xml, url, false, timeOut);
    643 
    644             Log.Info("XcxPayApi", "Report response : " + response);
    645 
    646             AppPayData result = new AppPayData();
    647             result.FromXml(response);
    648             return result;
    649         }
    650 
    651         /**
    652         * 根据当前系统时间加随机序列来生成订单号
    653          * @return 订单号
    654         */
    655         public static string GenerateOutTradeNo()
    656         {
    657             var ran = new Random();
    658             return string.Format("{0}{1}{2}", AppPayConfig.MCHID, DateTime.Now.ToString("yyyyMMddHHmmss"), ran.Next(999));
    659         }
    660 
    661         /**
    662         * 生成时间戳,标准北京时间,时区为东八区,自1970年1月1日 0点0分0秒以来的秒数
    663          * @return 时间戳
    664         */
    665         public static string GenerateTimeStamp()
    666         {
    667             TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
    668             return Convert.ToInt64(ts.TotalSeconds).ToString();
    669         }
    670 
    671         /**
    672         * 生成随机串,随机串包含字母或数字
    673         * @return 随机串
    674         */
    675         public static string GenerateNonceStr()
    676         {
    677             return Guid.NewGuid().ToString().Replace("-", "");
    678         }
    679     }
    680 }
    View Code

    接下来是业务类,注意业务类中不必再传微信登录凭证。

      1 using App.Common.Extension;
      2 using App.Pay.WePay.AppPay;
      3 using System;
      4 using System.Collections.Generic;
      5 using System.Linq;
      6 using System.Web;
      7 using System.Web.Configuration;
      8 using System.Web.Mvc;
      9 
     10 namespace App.WebTest.Controllers
     11 {
     12     public class WeAppPayController : BaseController
     13     {
     14         /// <summary>
     15         /// 微信app支付
     16         /// </summary>
     17         /// <param name="oidStr">订单编号</param>
     18         /// <returns></returns>
     19         public ActionResult WxPayApp(string oidStr)
     20         {
     21             // App调用只能传参
     22             int[] oIds = Serialize.JsonTo<List<int>>(oidStr).ToArray();
     23 
     24             #region 验证订单是否有效
     25 
     26             decimal payPrice = 0;
     27             string detail = "";
     28 
     29             /// 验证订单是否有效,并统计订单总金额
     30             /// ...
     31 
     32             #endregion
     33 
     34             #region 统一下单
     35             try
     36             {
     37                 //string userId = LoginUser.Id.ToString();
     38                 var address = WebConfigurationManager.AppSettings["WxAppNotifyUrl"].ToString();
     39                 AppPayData data = new AppPayData();
     40                 data.SetValue("body", "民政社工培训-课程购买");
     41                 //data.SetValue("attach", userId + "|" + String.Join(",", oIds).ToString());
     42                 data.SetValue("attach", String.Join(",", oIds).ToString());
     43                 Random rd = new Random();
     44                 var payNum = DateTime.Now.ToString("yyyyMMddHHmmss") + rd.Next(0, 1000).ToString().PadLeft(3, '0');
     45                 data.SetValue("out_trade_no", payNum);
     46                 data.SetValue("detail", detail.Substring(0, detail.Length - 1));
     47                 data.SetValue("total_fee", Convert.ToInt32(payPrice * 100));
     48                 data.SetValue("time_start", DateTime.Now.ToString("yyyyMMddHHmmss"));
     49                 data.SetValue("time_expire", DateTime.Now.AddMinutes(10).ToString("yyyyMMddHHmmss"));
     50                 data.SetValue("notify_url", address);
     51 
     52                 //注意,这里交易方式是APP
     53                 data.SetValue("trade_type", "APP");
     54 
     55                 AppPayData result = AppPayApi.UnifiedOrder(data);
     56                 var appid = "";
     57                 var partnerid = "";
     58                 var prepayid = "";
     59                 var package = "";
     60                 var nonceStr = "";
     61                 var timestamp = "";
     62                 var sign = "";
     63                 if (!result.IsSet("appid") || !result.IsSet("prepay_id") || result.GetValue("prepay_id").ToString() == "")
     64                 {
     65                     return Json(false, "下单失败!");
     66                 }
     67                 else
     68                 {
     69                     //统一下单
     70                     /// 修改订单状态
     71                     //OrderBll.Value.UpdateOrderApp(oIds, payNum);
     72 
     73                     appid = result.GetValue("appid").ToString();
     74                     nonceStr = result.GetValue("nonce_str").ToString();
     75                     partnerid = result.GetValue("mch_id").ToString();
     76                     prepayid = result.GetValue("prepay_id").ToString();
     77                     package = "Sign=WXPay";// "prepay_id=" + result.GetValue("prepay_id").ToString();
     78                 }
     79                 var signType = "MD5";
     80                 timestamp = ((DateTime.Now.Ticks - TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1)).Ticks) / 10000).ToString();
     81                 AppPayData applet = new AppPayData();
     82                 applet.SetValue("appid", appid);
     83                 applet.SetValue("noncestr", nonceStr);
     84                 applet.SetValue("package", package);
     85                 applet.SetValue("partnerid", partnerid);
     86                 applet.SetValue("prepayid", prepayid);
     87                 //applet.SetValue("signType", signType);
     88                 applet.SetValue("timestamp", timestamp);
     89                 sign = applet.MakeSign();
     90                 return Json(new { appid, partnerid, prepayid, package, nonceStr, timestamp, sign });
     91             }
     92             catch (Exception ex)
     93             {
     94                 return Json(false, "缺少参数");
     95             }
     96 
     97             #endregion
     98 
     99         }
    100 
    101         /// <summary>
    102         /// 微信支付回调
    103         /// </summary>
    104         /// <returns></returns>
    105         public string WxAppNotifyUrl()
    106         {
    107             Pay.Log Log = new Pay.Log(AppPayConfig.LogPath);
    108             Log.Info("WxAppNotifyUrl", "支付回调");
    109             AppPayNotify notify = new AppPayNotify(System.Web.HttpContext.Current);
    110             AppPayData notifyData = notify.GetNotifyData();
    111 
    112             //检查支付结果中transaction_id是否存在
    113             if (!notifyData.IsSet("transaction_id"))
    114             {
    115                 //若transaction_id不存在,则立即返回结果给微信支付后台
    116                 AppPayData res = new AppPayData();
    117                 res.SetValue("return_code", "FAIL");
    118                 res.SetValue("return_msg", "支付结果中微信订单号不存在");
    119                 Log.Error(this.GetType().ToString(), "The Pay result is error : " + res.ToXml());
    120                 Response.Write(res.ToXml());
    121                 Response.End();
    122             }
    123 
    124             string transaction_id = notifyData.GetValue("transaction_id").ToString();
    125 
    126             //查询订单,判断订单真实性
    127             if (!AppQueryOrder(transaction_id))
    128             {
    129                 //若订单查询失败,则立即返回结果给微信支付后台
    130                 AppPayData res = new AppPayData();
    131                 res.SetValue("return_code", "FAIL");
    132                 res.SetValue("return_msg", "订单查询失败");
    133                 Log.Error(this.GetType().ToString(), "Order query failure : " + res.ToXml());
    134 
    135                 Response.Write(res.ToXml());
    136                 Response.End();
    137             }
    138             //查询订单成功
    139             else
    140             {
    141                 AppPayData res = new AppPayData();
    142                 res.SetValue("return_code", "SUCCESS");
    143                 res.SetValue("return_msg", "OK");
    144                 Log.Info(this.GetType().ToString(), "Order query success : " + res.ToXml());
    145                 Log.Info(this.GetType().ToString(), "Order query success,notifyData : " + notifyData.ToXml());
    146                 var returnCode = notifyData.GetValue("return_code").ToString();
    147                 var transactionNo = transaction_id;//微信订单号
    148                 var outTradeNo = notifyData.GetValue("out_trade_no").ToString();//自定义订单号
    149                 var attach = notifyData.GetValue("attach").ToString();//身份证
    150                 var endTime = notifyData.GetValue("time_end").ToString();//交易结束时间
    151                 //var body = notifyData.GetValue("body").ToString();//projectIdlist
    152                 var totalFee = notifyData.GetValue("total_fee").ToString(); ;//支付金额
    153 
    154                 int userId = Convert.ToInt32(attach.Split('|')[0]);
    155                 string msg;
    156                 try
    157                 {
    158                     ///修改数据库订单状态
    159                     //var result = OrderBll.Value.CompleteWePay(userId, totalFee, transactionNo, returnCode, outTradeNo, attach, endTime, out msg);
    160                     var result = true;
    161 
    162                     Log.Info(this.GetType().ToString(), "CompleteWePay:" + result);
    163                 }
    164                 catch (Exception e)
    165                 {
    166                     Log.Error(this.GetType().ToString(), "CompleteWePay:" + e.ToString());
    167                 }
    168 
    169                 Response.Write(res.ToXml());
    170                 Response.End();
    171             }
    172 
    173             return "";
    174         }
    175 
    176         /// <summary>
    177         /// 查询订单
    178         /// </summary>
    179         /// <param name="transaction_id"></param>
    180         /// <returns></returns>
    181         private bool AppQueryOrder(string transaction_id)
    182         {
    183             AppPayData req = new AppPayData();
    184             req.SetValue("transaction_id", transaction_id);
    185             AppPayData res = AppPayApi.OrderQuery(req);
    186             if (res.GetValue("return_code").ToString() == "SUCCESS" && res.GetValue("result_code").ToString() == "SUCCESS")
    187             {
    188                 return true;
    189             }
    190             else
    191             {
    192                 return false;
    193             }
    194         }
    195     }
    196 }
    View Code

     源码https://github.com/wenha/Utility.git

  • 相关阅读:
    只要实现了IListSource, IEnumerable, or IDataSource的对象都可以作为DataSource进行Binding
    我的博客开张了!
    EnableViewState对Table, TableRow, TableCell不起作用?
    Bubble Sort (c#)
    trigger
    查询数据库中触发器的两种方法
    招聘要求
    调用搜索引擎代码
    JavaScript,只有你想不到
    [RIA]HTML5怎么就成了RIA“杀手”?
  • 原文地址:https://www.cnblogs.com/wenha/p/10262334.html
Copyright © 2011-2022 走看看