首页 > 代码库 > C#微信开发之旅(九):JSAPI支付(V3)(相关代码待补全)

C#微信开发之旅(九):JSAPI支付(V3)(相关代码待补全)

微信开发遇到最复杂的就是支付了,无论V2还是V3。这篇文章将给出全套的V3版本JSAPI支付代码,包括预支付->支付->订单查询->通知->退款,其中前三步已经上线应用,退款只是简单测试了一下,大家要用的话需要谨慎。。。

一、预支付&支付

实际就是讲订单信息交给微信端,返回给我们一个预支付id(与V2app支付相似),支付时将预支付id交给微信处理。注意:预支付id 需存储,每个out_trade_no(我们自己的订单号)只能对应一个预支付id。代码奉上:(mvc demo 最后会一并发出)

 1         public ActionResult Pay() 2         { 3             string code = "";//网页授权获得的code 4             string orderNo = ""; //文档中的out_trade_no 5             string description = ""; //商品描述 6             string totalFee = "1";//订单金额(单位:分) 7             string createIp = "127.0.0.1"; 8             string notifyUrl = ""; //通知url 9             string openId = WeiXinHelper.GetUserOpenId(code);//通过网页授权code获取用户openid(或者之前有存储用户的openid 也可以直接拿来用)10 11             //prepayid 只有第一次支付时生成,如果需要再次支付,必须拿之前生成的prepayid。12             //也就是说一个orderNo 只能对应一个prepayid13             string prepayid = string.Empty;14 15             #region 之前生成过 prepayid,此处可略过16 17             //创建Model18             UnifiedWxPayModel model = UnifiedWxPayModel.CreateUnifiedModel(WeiXinConst.AppId, WeiXinConst.PartnerId, WeiXinConst.PartnerKey);19 20             //预支付21             UnifiedPrePayMessage result = WeiXinHelper.UnifiedPrePay(model.CreatePrePayPackage(description, orderNo, totalFee, createIp, notifyUrl, openId));22 23             if (result == null24                     || !result.ReturnSuccess25                     || !result.ResultSuccess26                     || string.IsNullOrEmpty(result.Prepay_Id))27             {28                 throw new Exception("获取PrepayId 失败");29             }30 31             //预支付订单32             prepayid = result.Prepay_Id;33 34             #endregion35             36             //JSAPI 支付参数的Model37             PayModel payModel = new PayModel()38             {39                 AppId = model.AppId,40                 Package = string.Format("prepay_id={0}", prepayid),41                 Timestamp = ((DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000000).ToString(),42                 Noncestr = CommonUtil.CreateNoncestr(),43             };44 45             Dictionary<string, string> nativeObj = new Dictionary<string, string>();46             nativeObj.Add("appId", payModel.AppId);47             nativeObj.Add("package", payModel.Package);48             nativeObj.Add("timeStamp", payModel.Timestamp);49             nativeObj.Add("nonceStr", payModel.Noncestr);50             nativeObj.Add("signType", payModel.SignType);51             payModel.PaySign = model.GetCftPackage(nativeObj); //生成JSAPI 支付签名52 53 54             return View(payModel);55         }
View Code

UnifiedWxPayModel 为V3统一支付帮助类,包括V3相关接口参数生成及签名的实现:

这里用到的 生成预支付请求参数Xml:

 1         #region 生成 预支付 请求参数(XML) 2         /// <summary> 3         /// 生成 预支付 请求参数(XML) 4         /// </summary> 5         /// <param name="description"></param> 6         /// <param name="tradeNo"></param> 7         /// <param name="totalFee"></param> 8         /// <param name="createIp"></param> 9         /// <param name="notifyUrl"></param>10         /// <param name="openid"></param>11         /// <returns></returns>12         public string CreatePrePayPackage(string description, string tradeNo, string totalFee, string createIp, string notifyUrl, string openid)13         {14             Dictionary<string, string> nativeObj = new Dictionary<string, string>();15 16             nativeObj.Add("appid", AppId);17             nativeObj.Add("mch_id", PartnerId);18             nativeObj.Add("nonce_str", CommonUtil.CreateNoncestr());19             nativeObj.Add("body", description);20             nativeObj.Add("out_trade_no", tradeNo);21             nativeObj.Add("total_fee", totalFee); //todo:写死为122             nativeObj.Add("spbill_create_ip", createIp);23             nativeObj.Add("notify_url", notifyUrl);24             nativeObj.Add("trade_type", "JSAPI");25             nativeObj.Add("openid", openid);26             nativeObj.Add("sign", GetCftPackage(nativeObj));27 28             return DictionaryToXmlString(nativeObj);29         }30 31         #endregion
View Code

预支付请求在WeiXinHelper中,实现方式与前几篇中相似,这里就不上代码了。

二、订单查询

JSAPI返回支付成功,我们需要到后台查询下订单状态以确定支付是否成功,如果后台未接到通知,则要到微信服务器查询订单状态;最后才能展示给用户支付的结果:

 1         /// <summary> 2         /// 到微信服务器查询 订单支付的结果 (jsapi支付返回ok,我们要判断下服务器支付状态,如果没有支付成功,到微信服务器查询) 3         /// </summary> 4         /// <param name="orderNo"></param> 5         public bool QueryOrder(string orderNo) 6         { 7             //这里应先判断服务器 订单支付状态,如果接到通知,并已经支付成功,就不用 执行下面的查询了 8             UnifiedWxPayModel model = UnifiedWxPayModel.CreateUnifiedModel(WeiXinConst.AppId, WeiXinConst.PartnerId, WeiXinConst.PartnerKey); 9             UnifiedOrderQueryMessage message = WeiXinHelper.UnifiedOrderQuery(model.CreateOrderQueryXml(orderNo));10             //此处主动查询的结果,只做查询用(不能作为支付成功的依据)11             return message.Success;12         }
View Code

三、通知

微信支付通知以Post Xml方式:

 1         /// <summary> 2         /// 微信支付通知(貌似比较臃肿,待优化) 3         /// </summary> 4         /// <returns></returns> 5         public void Notify() 6         { 7             ReturnMessage returnMsg = new ReturnMessage() { Return_Code = "SUCCESS", Return_Msg = "" }; 8             string xmlString = GetXmlString(Request); 9             NotifyMessage message = null;10             try11             {12                 //此处应记录日志13                 message = HttpClientHelper.XmlDeserialize<NotifyMessage>(xmlString);14 15                 #region 验证签名并处理通知16                 XmlDocument doc = new XmlDocument();17                 doc.LoadXml(xmlString);18 19                 Dictionary<string, string> dic = new Dictionary<string, string>();20                 string sign = string.Empty;21                 foreach (XmlNode node in doc.FirstChild.ChildNodes)22                 {23                     if (node.Name.ToLower() != "sign")24                         dic.Add(node.Name, node.InnerText);25                     else26                         sign = node.InnerText;27                 }28 29                 UnifiedWxPayModel model = UnifiedWxPayModel.CreateUnifiedModel(WeiXinConst.AppId, WeiXinConst.PartnerId, WeiXinConst.PartnerKey);30                 if (model.ValidateMD5Signature(dic, sign))31                 {32                     //处理通知33                 }34                 else35                 {36                     throw new Exception("签名未通过!");37                 }38 39                 #endregion40 41             }42             catch (Exception ex)43             {44                 //此处记录异常日志45                 returnMsg.Return_Code = "FAIL";46                 returnMsg.Return_Msg = ex.Message;47             }48             Response.Write(returnMsg.ToXmlString());49             Response.End();50         }51 52         /// <summary>53         /// 获取Post Xml数据54         /// </summary>55         /// <param name="request"></param>56         /// <returns></returns>57         private string GetXmlString(HttpRequestBase request)58         {59             using (System.IO.Stream stream = request.InputStream)60             {61                 Byte[] postBytes = new Byte[stream.Length];62                 stream.Read(postBytes, 0, (Int32)stream.Length);63                 return System.Text.Encoding.UTF8.GetString(postBytes);64             }65         }
View Code

四、退款

退款需要用到证书,配置WeiXinConst内证书相关常量再使用:

 1         /// <summary> 2         /// 订单退款 3         /// </summary> 4         /// <param name="transaction_Id">微信交易单号</param> 5         /// <param name="orderNo">我们自己的单号</param> 6         /// <param name="totalFee">订单金额(分)</param> 7         /// <param name="refundNo">退款单号(我们自己定义)</param> 8         /// <param name="refundFee">退款金额(分)</param> 9         /// <returns></returns>10         public bool UnifiedOrderRefund(string transaction_Id,string orderNo,string totalFee, string refundNo,string refundFee)11         {12             UnifiedWxPayModel model = UnifiedWxPayModel.CreateUnifiedModel(WeiXinConst.AppId, WeiXinConst.PartnerId, WeiXinConst.PartnerKey);13             string postData =http://www.mamicode.com/ model.CreateOrderRefundXml(orderNo, transaction_Id, totalFee, refundNo, refundFee);14             //退款需要用到证书, 要配置WeiXineConst CertPath 和 CertPwd15             return WeiXinHelper.Refund(postData, WeiXinConst.CertPath, WeiXinConst.CertPwd);16         }
View Code

私有的方法:

  1. Dictionary<string,string>转为XmlDocument 
     1         /// <summary> 2         /// dictionary转为xml 字符串 3         /// </summary> 4         /// <param name="dic"></param> 5         /// <returns></returns> 6         private static string DictionaryToXmlString(Dictionary<string, string> dic) 7         { 8             StringBuilder xmlString = new StringBuilder(); 9             xmlString.Append("<xml>");10             foreach (string key in dic.Keys)11             {12                 xmlString.Append(string.Format("<{0}><![CDATA[{1}]]></{0}>", key, dic[key]));13             }14             xmlString.Append("</xml>");15             return xmlString.ToString();16         }
    View Code
  2. XmlDocument转为Dictionary<string,string> 
     1         /// <summary> 2         /// xml字符串 转换为  dictionary 3         /// </summary> 4         /// <param name="document"></param> 5         /// <returns></returns> 6         public static Dictionary<string, string> XmlToDictionary(string xmlString) 7         { 8             System.Xml.XmlDocument document = new System.Xml.XmlDocument(); 9             document.LoadXml(xmlString);10 11             Dictionary<string, string> dic = new Dictionary<string, string>();12 13             var nodes = document.FirstChild.ChildNodes;14 15             foreach (System.Xml.XmlNode item in nodes)16             {17                 dic.Add(item.Name, item.InnerText);18             }19             return dic;20         }
    View Code

C#微信开发之旅(九):JSAPI支付(V3)(相关代码待补全)