微信支付APP支付.net
之前公司说要为APP开发一个微信支付,由于没写过支付类的服务端支持,在网上找过不少代码,也走了不少弯路,网上关于在.net微信支付这一块大多比较坑,故整理出来给大家分享!废话不多说,上代码!

看到支付流程,前端是APP的话我们后端只需要处理 生成商户订单、调用微信统一下单API处理后返回参数给前端APP、最后处理支付结果。
发送请求的类如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Data; 4 using System.IO; 5 using System.Linq; 6 using System.Net; 7 using System.Net.Security; 8 using System.Reflection; 9 using System.Security.Cryptography.X509Certificates; 10 using System.Text; 11 using System.Web; 12 using System.Xml.Serialization; 13 14 namespace Controllers 15 { 16 public class WeiXin_PayMent 17 { 18 public static PayType _payConfig = GetPay();//初始化支付参数 19 20 /// <summary> 21 /// 获取支付参数 22 /// </summary> 23 /// <returns></returns> 24 private static PayType GetPay() 25 { 26 PayType data = new PayType(); 27 try 28 { 29 var result = LJL_Cloud.BLL.BLLPay.GetInstance().getPay(2);//访问数据库获取支付参数配置 你们写自己获取参数的方法 30 if (result != null && result.Rows.Count > 0) 31 { 32 var row = result.Rows[0]; 33 data.PayAccount = row["PayAccount"].ToString(); 34 data.CooperationID = row["CooperationID"].ToString(); 35 data.PayUrl = row["PayUrl"].ToString(); 36 data.NotifyUrl = row["NotifyUrl"].ToString(); 37 } 38 } 39 catch (Exception) 40 { 41 data.PayAccount = "";//公众帐号ID 42 data.CooperationID = "";//商户号 43 data.PayUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";//支付请求地址 44 data.NotifyUrl = "";//请求回调地址 45 } 46 return data; 47 } 48 49 private WeiXin_Config _config; 50 public WeiXin_PayMent(WeiXin_Config config) 51 { 52 this._config = config; 53 54 // 55 // TODO: 在此处添加构造函数逻辑 56 // 57 } 58 59 /// <summary> 60 /// 生成随机字符串 61 /// </summary> 62 /// <returns></returns> 63 public static string GetNonce() 64 { 65 string str = "1234567890qwertyuiopasdfghjklzxcvbnm"; 66 Random r = new Random(); 67 StringBuilder result = new StringBuilder(); 68 for (int i = 0; i < 32; i++) 69 { 70 int m = r.Next(0, str.Length); 71 string s = str.Substring(m, 1); 72 result.Append(s); 73 } 74 return result.ToString(); 75 } 76 77 /// <summary> 78 /// 签名算法 79 /// sign不参与签名 80 /// </summary> 81 /// <returns></returns> 82 public static string Sign(object pars) 83 { 84 /*获取所有字段并添加到字典中并排序*/ 85 var dic = pars.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance) 86 .Where(x => x.GetValue(pars) != null && x.Name != "sign")//参数值为空或者为sign不参与签名 87 .ToDictionary(k => k.Name, v => v.GetValue(pars).ToString())//转换为dictionary 88 .OrderBy(x => x.Key);//ASII排序 89 StringBuilder sb = new StringBuilder(); 90 foreach (var item in dic) 91 sb.AppendFormat("{0}={1}&", item.Key, item.Value); 92 /*拼接API密匙*/ 93 string strSignTemp = sb.ToString() + "key=" + _payConfig.VerifyCode; 94 string sign = GetMd5Str32(strSignTemp);//MD5加密 95 return sign; 96 } 97 98 99 /// <summary> 100 /// 32位MD5加密 101 /// 加密后为大写 102 /// </summary> 103 /// <param name="ConvertString"></param> 104 /// <returns></returns> 105 public static string GetMd5Str32(string str) 106 { 107 return System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(str, "MD5").ToUpper(); 108 } 109 110 /// <summary> 111 /// 处理参数 112 /// 返回微信接口需要的XML文档 113 /// </summary> 114 public string HandleParams() 115 { 116 this._config.nonce_str = GetNonce();//生成随机数 117 this._config.sign = Sign(this._config);//生成签名 118 119 StringBuilder xml = new StringBuilder(); 120 xml.Append("<xml>"); 121 var list = this._config.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance) 122 .Where(x => x.GetValue(this._config) != null); 123 foreach (var item in list) 124 { 125 xml.AppendFormat("<{0}><![CDATA[{1}]]></{0}>", item.Name, item.GetValue(this._config).ToString()); 126 } 127 xml.Append("</xml>"); 128 return xml.ToString(); 129 } 130 131 public WeiXin_Result PostOrder() 132 { 133 var xmlPars = HandleParams();//经过处理的请求参数 XML 134 /*发送请求*/ 135 HttpWebResponse res = CreatePostHttpResponse(_payConfig.PayUrl, xmlPars, null, null, Encoding.UTF8, null); 136 string result = string.Empty; 137 using (StreamReader sr = new StreamReader(res.GetResponseStream())) 138 { 139 result = sr.ReadToEnd(); 140 } 141 /*处理返回结果*/ 142 WeiXin_Result entityResult = null; 143 try 144 { 145 using (StringReader sr = new StringReader("<?xml version=\"1.0\"?>" + result))//必须加上XML头文件 不加会报错 146 { 147 XmlSerializer xmldes = new XmlSerializer(typeof(WeiXin_Result)); 148 entityResult = xmldes.Deserialize(sr) as WeiXin_Result; 149 } 150 } 151 catch (InvalidOperationException) 152 { 153 154 } 155 return entityResult; 156 } 157 158 /// <summary> 159 /// 创建POST方式的HTTP请求 160 /// </summary> 161 /// <param name="url">请求的URL</param> 162 /// <param name="parameters">随同请求POST的参数名称及参数值字典</param> 163 /// <param name="timeout">请求的超时时间</param> 164 /// <param name="userAgent">请求的客户端浏览器信息,可以为空</param> 165 /// <param name="requestEncoding">发送HTTP请求时所用的编码</param> 166 /// <param name="cookies">随同HTTP请求发送的Cookie信息,如果不需要身份验证可以为空</param> 167 /// <returns></returns> 168 public static HttpWebResponse CreatePostHttpResponse(string url, string parameters, int? timeout, string userAgent, Encoding requestEncoding, CookieCollection cookies) 169 { 170 if (string.IsNullOrEmpty(url)) 171 { 172 throw new ArgumentNullException("url"); 173 } 174 if (requestEncoding == null) 175 { 176 throw new ArgumentNullException("requestEncoding"); 177 } 178 HttpWebRequest request = null; 179 //如果是发送HTTPS请求 180 if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase)) 181 { 182 ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult); 183 request = WebRequest.Create(url) as HttpWebRequest; 184 request.ProtocolVersion = HttpVersion.Version10; 185 } 186 else 187 { 188 request = WebRequest.Create(url) as HttpWebRequest; 189 } 190 request.Method = "POST"; 191 request.ContentType = "application/x-www-form-urlencoded"; 192 193 if (!string.IsNullOrEmpty(userAgent)) 194 { 195 request.UserAgent = userAgent; 196 } 197 else 198 { 199 request.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)"; 200 } 201 202 if (timeout.HasValue) 203 { 204 request.Timeout = timeout.Value; 205 } 206 if (cookies != null) 207 { 208 request.CookieContainer = new CookieContainer(); 209 request.CookieContainer.Add(cookies); 210 } 211 //如果需要POST数据 212 if (!string.IsNullOrEmpty(parameters)) 213 { 214 byte[] data = requestEncoding.GetBytes(parameters); 215 using (Stream stream = request.GetRequestStream()) 216 { 217 stream.Write(data, 0, data.Length); 218 } 219 } 220 return request.GetResponse() as HttpWebResponse; 221 } 222 223 private static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) 224 { 225 return true; //总是接受 226 } 227 } 228 /// <summary> 229 /// 请求下单接口参数实体 230 /// </summary> 231 public class WeiXin_Config 232 { 233 private LJL_Cloud.Model.PayType _payConfig = WeiXin_PayMent._payConfig; 234 public WeiXin_Config() 235 { 236 this.appid = _payConfig.PayAccount;//获取公众帐号ID 237 this.mch_id = _payConfig.CooperationID;//获取商户号 238 this.fee_type = "CNY";//货币类型 239 this.spbill_create_ip = "";//本机IP地址 240 this.notify_url = _payConfig.NotifyUrl;//支付成功后消息通知地址 241 this.trade_type = "APP";//交易类型 242 } 243 /// <summary> 244 /// 微信分配的公众账号ID(企业号corpid即为此appId)not null 245 /// </summary> 246 public string appid; 247 /// <summary> 248 /// 微信支付分配的商户号 not null 249 /// </summary> 250 public string mch_id; 251 /// <summary> 252 /// 终端设备号(门店号或收银设备ID),注意:PC网页或公众号内支付请传"WEB" null 253 /// </summary> 254 public string device_info; 255 /// <summary> 256 /// 随机字符串,不长于32位。推荐随机数生成算法 not null 257 /// </summary> 258 public string nonce_str; 259 /// <summary> 260 /// 签名,详见签名生成算法 not null 261 /// </summary> 262 public string sign; 263 /// <summary> 264 /// 商品或支付单简要描述 not null 265 /// </summary> 266 public string body; 267 /// <summary> 268 /// 商品名称明细列表 null 269 /// </summary> 270 public string detail; 271 /// <summary> 272 /// 附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据 null 273 /// </summary> 274 public string attach; 275 /// <summary> 276 /// 商户系统内部的订单号,32个字符内、可包含字母, 其他说明见商户订单号 not null 277 /// </summary> 278 public string out_trade_no; 279 /// <summary> 280 /// 符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型 null 281 /// </summary> 282 public string fee_type; 283 /// <summary> 284 /// 订单总金额,单位为分,详见支付金额 not null 285 /// </summary> 286 public int total_fee; 287 /// <summary> 288 /// APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。 not null 289 /// </summary> 290 public string spbill_create_ip; 291 /// <summary> 292 /// 订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010 null 293 /// </summary> 294 public string time_start; 295 /// <summary> 296 /// 订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010 null 297 /// </summary> 298 public string time_expire; 299 /// <summary> 300 /// 商品标记 null 301 /// </summary> 302 public string goods_tag; 303 /// <summary> 304 /// 通知地址 not null 305 /// </summary> 306 public string notify_url; 307 /// <summary> 308 /// 取值如下:JSAPI,NATIVE,APP,详细说明见参数规定 not null 309 /// </summary> 310 public string trade_type; 311 /// <summary> 312 /// trade_type=NATIVE,此参数必传 null 313 /// </summary> 314 public string product_id; 315 /// <summary> 316 /// no_credit--指定不能使用信用卡支付 null 317 /// </summary> 318 public string limit_pay; 319 /// <summary> 320 /// trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识。openid如何获取,可参考【获取openid】。企业号请使用【企业号OAuth2.0接口】获取企业号内成员userid,再调用【企业号userid转openid接口】进行转换 null 321 /// </summary> 322 public string openid; 323 } 324 325 /// <summary> 326 /// 微信统一下单接口返回参数实体 327 /// </summary> 328 [XmlRoot("xml")] 329 public class WeiXin_Result 330 { 331 /// <summary> 332 /// 通信标识 SUCCESS 333 /// </summary> 334 [XmlElement("return_code")] 335 public string return_code { get; set; } 336 337 /// <summary> 338 /// 返回信息,如非空,为错误原因 339 /// </summary> 340 [XmlElement("return_msg")] 341 public string return_msg { get; set; } 342 343 /// <summary> 344 /// 调用接口提交的公众账号ID 345 /// </summary> 346 [XmlElement("appid")] 347 public string appid { get; set; } 348 349 /// <summary> 350 /// 调用接口提交的商户号 351 /// </summary> 352 [XmlElement("mch_id")] 353 public string mch_id { get; set; } 354 355 /// <summary> 356 /// 调用接口提交的终端设备号, 357 /// </summary> 358 [XmlElement("device_info")] 359 public string device_info { get; set; } 360 361 /// <summary> 362 /// 微信返回的随机字符串 363 /// </summary> 364 [XmlElement("nonce_str")] 365 public string nonce_str { get; set; } 366 367 368 /// <summary> 369 /// 微信返回的签名,详见签名算法 370 /// </summary> 371 [XmlElement("sign")] 372 public string sign { get; set; } 373 374 /// <summary> 375 /// SUCCESS/FAIL 376 /// </summary> 377 [XmlElement("result_code")] 378 public string result_code { get; set; } 379 380 /// <summary> 381 /// 错误代码 382 /// </summary> 383 [XmlElement("err_code")] 384 public string err_code { get; set; } 385 386 /// <summary> 387 /// 错误代码描述 388 /// </summary> 389 [XmlElement("err_code_des")] 390 public string err_code_des { get; set; } 391 392 393 /// <summary> 394 /// 交易类型 395 /// </summary> 396 [XmlElement("trade_type")] 397 public string trade_type { get; set; } 398 399 /// <summary> 400 /// 预支付交易会话标识 401 /// </summary> 402 [XmlElement("prepay_id")] 403 public string prepay_id { get; set; } 404 405 /// <summary> 406 /// 二维码链接 407 /// </summary> 408 [XmlElement("code_url")] 409 public string code_url { get; set; } 410 } 411 412 /// <summary> 413 /// 返回手机所需要的参数 414 /// </summary> 415 public class WeiXin_Result_APP 416 { 417 public string appid;//公众账号ID 418 public string partnerid;//商户号 419 public string prepayid;//预支付交易会话ID 420 public string package;//扩展字段 421 public string noncestr;//随机字符串 422 public string timestamp;//时间戳 423 public string sign;//签名 424 } 425 }
调用方式(调用微信预付单接口):
1
//初始化参数
WeiXin_Config config = new WeiXin_Config 2 { 3 body = body,//支付的请求主体 4 out_trade_no = orderID,//商户唯一订单号 5 total_fee = priceInt,//支付价格 单位分 6 }; 7 WeiXin_PayMent payMent = new WeiXin_PayMent(config); 8 var result = payMent.PostOrder();//调用微信统一下单接口 9 /*处理返回结果*/ 10 if (result.return_code == "SUCCESS")//通讯是否成功 11 { 12 /*通讯成功*/ 13 if (result.result_code == "SUCCESS")//交易是否成功 14 { 15 resultMsg.Result = OperateResult.Success; 16 resultMsg.ResultMsg = "下单成功"; 17 TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0); 18 var timeStamp = Convert.ToInt64(ts.TotalSeconds).ToString(); 19 var data = new WeiXin_Result_APP 20 { 21 appid = result.appid, 22 partnerid = result.mch_id, 23 prepayid = result.prepay_id, 24 package = "Sign=WXPay", 25 noncestr = WeiXin_PayMent.GetNonce(), 26 timestamp = timeStamp, 27 }; 28 data.sign = WeiXin_PayMent.Sign(data); 29 resultMsg.PageData = data; 30 } 31 else 32 { 33 throw new OperateException(OperateResult.Fail, result.err_code_des/*错误描述*/); 34 } 35 } 36 else 37 { 38 /*通讯失败*/ 39 throw new OperateException(OperateResult.Fail, result.return_msg/*错误描述*/); 40 }
回调页面:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using System.Web.UI; 6 using System.Web.UI.WebControls; 8 using System.IO; 9 using System.Xml.Serialization; 10 11 namespace Controllers.Pay 12 { 13 public partial class WeiXin_Notify_URL : System.Web.UI.Page 14 { 15 protected void Page_Load(object sender, EventArgs e) 16 { 17 #region 获取参数 18 System.IO.StreamReader reader = new System.IO.StreamReader(Request.InputStream); 19 String xmlData = reader.ReadToEnd(); 20 Parmas entityResult; 21 using (StringReader sr = new StringReader("<?xml version=\"1.0\"?>" + xmlData))//必须加上XML头文件 不加会报错 22 { 23 XmlSerializer xmldes = new XmlSerializer(typeof(Parmas)); 24 entityResult = xmldes.Deserialize(sr) as Parmas;//解析回调参数 25 } 26 #endregion 27 28 #region 业务处理 29 /*签名验证 防止数据泄漏导致出现“假通知”,造成资金损失*/ 30 var sign = WeiXin_PayMent.Sign(entityResult); 31 if (entityResult.sign != sign) 32 { 33 Response.End(); 34 } 35 string result = "FAIL"; 36 if (entityResult.return_code == "SUCCESS") 37 { 38 /*通知可能会多次发送给商户系统,必须处理重复的通知*/ 39 //订单号P开头为商品 S开头为服务费 40 //更改商品订单状态 41 //支付成功 记录日志 42 } 43 else 44 { 45 //支付失败 记录日志 46 } 47 #endregion 48 49 #region 处理业务后返回同步返回结果给微信 注意格式为XML 50 // return_code SUCCESS/FAIL SUCCESS表示商户接收通知成功并校验成功 必填参数 51 // return_msg 返回信息,如非空,为错误原因:签名失败、参数格式校验错误 52 string strResult = string.Format("<xml><return_code>{0}</return_code></xml>", result); 53 Response.Write(strResult); 54 Response.End(); 55 #endregion 56 57 } 58 } 59 /// <summary> 60 /// 微信回调地址返回参数 61 /// </summary> 62 [XmlRoot("xml")] 63 public class Parmas 64 { 65 [XmlElement("return_code")] 66 public string return_code ;//返回状态码 67 [XmlElement("return_msg")] 68 public string return_msg ;//返回信息 69 [XmlElement("appid")] 70 public string appid ;//公众账号ID 71 [XmlElement("mch_id")] 72 public string mch_id ;//商户号 73 [XmlElement("device_info")] 74 public string device_info ;//设备号 75 [XmlElement("nonce_str")] 76 public string nonce_str ; //随机字符串 77 [XmlElement("sign")] 78 public string sign ;//签名 79 [XmlElement("result_code")] 80 public string result_code ; //业务结果 81 [XmlElement("err_code")] 82 public string err_code ;//错误代码 83 [XmlElement("err_code_des")] 84 public string err_code_des ;//错误代码描述 85 [XmlElement("openid")] 86 public string openid ;//用户标识 87 [XmlElement("is_subscribe")] 88 public string is_subscribe ;//是否关注公众账号 89 [XmlElement("trade_type")] 90 public string trade_type ;//交易类型 91 [XmlElement("bank_type")] 92 public string bank_type ;//付款银行 93 [XmlElement("total_fee")] 94 public string total_fee ;//总金额 95 [XmlElement("fee_type")] 96 public string fee_type ;//货币种类 97 [XmlElement("cash_fee")] 98 public string cash_fee ;//现金支付金额 99 [XmlElement("cash_fee_type")] 100 public string cash_fee_type ;//现金支付货币类型 101 [XmlElement("coupon_fee")] 102 public string coupon_fee ;//代金券或立减优惠金额 103 [XmlElement("coupon_count")] 104 public string coupon_count ;//代金券或立减优惠使用数量 105 [XmlElement("coupon_id_$n")] 106 public string coupon_id_n ;//代金券或立减优惠ID 标记 107 [XmlElement("coupon_fee_$n")] 108 public string coupon_fee_n ;//单个代金券或立减优惠支付金额 109 [XmlElement("transaction_id")] 110 public string transaction_id ;//微信支付订单号 111 [XmlElement("out_trade_no")] 112 public string out_trade_no ;//商户订单号 113 [XmlElement("attach")] 114 public string attach ;//商家数据包 115 [XmlElement("time_end")] 116 public string time_end ;//支付完成时间 117 } 118 }
就到这里了,有什么不明白或指教请在下面留言或参考开发者文档 https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_1 !谢谢!
浙公网安备 33010602011771号