公众号微信支付

Posted 杨帆

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了公众号微信支付相关的知识,希望对你有一定的参考价值。

1、概要

     公众号是以微信用户的一个联系人形式存在的,支付是微信服务号的核心一环。

     本篇主要介绍微信支付这一功能,避免大家再跳微信支付的坑。

1.1 关于Magicodes.WeChat.SDK

      MAGICODES.WECHAT.SDK为心莱团队封装的轻量级微信SDK,现已全部开源,开源库地址为:https://github.com/xin-lai/Magicodes.WeChat.SDK

      更多介绍,请关注后续博客。

2、微信公众号支付

     用户已有商城网址,用户通过微信消息、微信扫描二维码、微信自定义菜单等操作在微信内打开网页时,可以调用微信支付完成下单购买流程。

2.1    支付流程

  2.1.1    微信网页支付流程

      

 

备注:实际流程其实非常简单

  ① 用户支付之前,程序生成订单并调用统一下单API()

  ② 微信系统返回预付单信息

  ③ 根据信息生成JSAPI页面调用的支付参数并签名,jssdk调用

  ④ 用户支付,jsapi向微信系统发送请求

  ⑤ 微信系统返回支付结果给用户,同时异步发送结果给程序后台(程序没有收到通知,可以调用查询接口)

  ⑥ 支付完成,用户界面根据结果做相关页面跳转或提示处理,程序后台根据通知做订单状态变更等逻辑处理。

2.1.2    刷卡支付

    后续更新

2.1.3    扫码支付

     后续更新

2.1.4    app支付

     后续更新 

2.2    注意事项

2.3    开发实践 

   2.3.1    开发配置

   1、设置测试目录

在微信公众平台设置。支付测试状态下,设置测试目录,测试人的微信号添加到白名单,发起支付的页面目录必须与设置的精确匹配。并将支付链接发到对应的公众号会话窗口中才能正常发起支付测试。注意正式目录一定不能与测试目录设置成一样,否则支付会出错。

       

 

友情提示:如果是使用测试目录的地址,一定要记得把个人测试微信号添加到白名单。

   2、设置正式支付目录

根据图中栏目顺序进入修改栏目,勾选JSAPI网页支付开通该权限,并配置好支付授权目录,该目录必须是发起支付的页面的精确目录,子目录下无法正常调用支付。具体界面如图:

      

 

友情提示:注意红色框框里面的说明,一不小心会很容易进坑的。

   2.3.2    开发程序

            直接看代码吧

微信支付业务类

  1 /// <summary>
  2 /// 微信支付接口,官方API:https://mp.weixin.qq.com/paymch/readtemplate?t=mp/business/course2_tmpl&lang=zh_CN&token=25857919#4
  3 
  4     /// </summary>
  5     public class TenPayV3 : PayBase
  6 
  7     {
  8         public UnifiedorderResult Unifiedorder(UnifiedorderRequest model)
  9 
 10         {
 11 
 12             var url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
 13 
 14  
 15 
 16  
 17 
 18             UnifiedorderResult result = null;
 19 
 20  
 21 
 22             model.AppId = WeiChatConfig.AppId;
 23 
 24             model.MchId = PayConfig.MchId;
 25 
 26             if (model.NotifyUrl == null)
 27 
 28                 model.NotifyUrl = PayConfig.Notify;
 29 
 30             Dictionary<string, string> dictionary = PayUtil.GetAuthors<UnifiedorderRequest>(model);
 31 
 32             model.Sign = PayUtil.CreateMd5Sign(dictionary, PayConfig.TenPayKey);//生成Sign
 33 
 34             Dictionary<string, string> dict = PayUtil.GetAuthors<UnifiedorderRequest>(model);
 35 
 36             result = PostXML<UnifiedorderResult>(url, model);
 37 
 38             return result;
 39 
 40         }      
 41 
 42  
 43 
 44         /// <summary>
 45 
 46         /// 订单查询接口
 47 
 48         /// </summary>
 49 
 50         /// <param name="data"></param>
 51 
 52         /// <returns></returns>
 53 
 54         public static string OrderQuery(string data)
 55 
 56         {
 57 
 58             var urlFormat = "https://api.mch.weixin.qq.com/pay/orderquery";
 59 
 60  
 61 
 62             var formDataBytes = data == null ? new byte[0] : Encoding.UTF8.GetBytes(data);
 63 
 64             using (MemoryStream ms = new MemoryStream())
 65 
 66             {
 67 
 68                 ms.Write(formDataBytes, 0, formDataBytes.Length);
 69 
 70                 ms.Seek(0, SeekOrigin.Begin);//设置指针读取位置
 71 
 72                 return RequestUtility.HttpPost(urlFormat, null, ms);
 73 
 74             }
 75 
 76         }
 77 
 78  
 79 
 80         /// <summary>
 81 
 82         /// 关闭订单接口
 83 
 84         /// </summary>
 85 
 86         /// <param name="data">关闭订单需要post的xml数据</param>
 87 
 88         /// <returns></returns>
 89 
 90         public static string CloseOrder(string data)
 91 
 92         {
 93 
 94             var urlFormat = "https://api.mch.weixin.qq.com/pay/closeorder";
 95 
 96  
 97 
 98             var formDataBytes = data == null ? new byte[0] : Encoding.UTF8.GetBytes(data);
 99 
100             using (MemoryStream ms = new MemoryStream())
101 
102             {
103 
104                 ms.Write(formDataBytes, 0, formDataBytes.Length);
105 
106                 ms.Seek(0, SeekOrigin.Begin);//设置指针读取位置
107 
108                 return RequestUtility.HttpPost(urlFormat, null, ms);
109 
110             }
111 
112         }
113 
114  
115 
116       
117 
118  
119 
120         /// <summary>
121 
122         /// 退款查询接口
123 
124         /// </summary>
125 
126         /// <param name="data"></param>
127 
128         /// <returns></returns>
129 
130         public static string RefundQuery(string data)
131 
132         {
133 
134             var urlFormat = "https://api.mch.weixin.qq.com/pay/refundquery";
135 
136  
137 
138             var formDataBytes = data == null ? new byte[0] : Encoding.UTF8.GetBytes(data);
139 
140             using (MemoryStream ms = new MemoryStream())
141 
142             {
143 
144                 ms.Write(formDataBytes, 0, formDataBytes.Length);
145 
146                 ms.Seek(0, SeekOrigin.Begin);//设置指针读取位置
147 
148                 return RequestUtility.HttpPost(urlFormat, null, ms);
149 
150             }
151 
152         }
153 
154  
155 
156         /// <summary>
157 
158         /// 对账单接口
159 
160         /// </summary>
161 
162         /// <param name="data"></param>
163 
164         /// <returns></returns>
165 
166         public static string DownloadBill(string data)
167 
168         {
169 
170             var urlFormat = "https://api.mch.weixin.qq.com/pay/downloadbill";
171 
172  
173 
174             var formDataBytes = data == null ? new byte[0] : Encoding.UTF8.GetBytes(data);
175 
176             using (MemoryStream ms = new MemoryStream())
177 
178             {
179 
180                 ms.Write(formDataBytes, 0, formDataBytes.Length);
181 
182                 ms.Seek(0, SeekOrigin.Begin);//设置指针读取位置
183 
184                 return RequestUtility.HttpPost(urlFormat, null, ms);
185 
186             }
187 
188         }
189 
190  
191 
192         /// <summary>
193 
194         /// 短链接转换接口
195 
196         /// </summary>
197 
198         /// <param name="data"></param>
199 
200         /// <returns></returns>
201 
202         public static string ShortUrl(string data)
203 
204         {
205 
206             var urlFormat = "https://api.mch.weixin.qq.com/tools/shorturl";
207 
208  
209 
210             var formDataBytes = data == null ? new byte[0] : Encoding.UTF8.GetBytes(data);
211 
212             using (MemoryStream ms = new MemoryStream())
213 
214             {
215 
216                 ms.Write(formDataBytes, 0, formDataBytes.Length);
217 
218                 ms.Seek(0, SeekOrigin.Begin);//设置指针读取位置
219 
220                 return RequestUtility.HttpPost(urlFormat, null, ms);
221 
222             }
223 
224         }
225 
226         /// <summary>
227 
228         ///
229 
230         /// </summary>
231 
232         /// <param name="page"></param>
233 
234         /// <returns></returns>
235 
236         public NotifyResult Notify(Stream inputStream)
237 
238         {
239 
240             NotifyResult result = null;
241 
242             string data = PayUtil.PostInput(inputStream);
243 
244             result = XmlHelper.DeserializeObject<NotifyResult>(data);
245 
246             return result;
247 
248         }
249 
250         /// <summary>
251 
252         /// 通知并返回处理XML
253 
254         /// </summary>
255 
256         /// <param name="inputStream">输入流</param>
257 
258         /// <param name="successAction">成功处理逻辑回调函数</param>
259 
260         /// <param name="failAction">失败处理逻辑回调函数</param>
261 
262         /// <param name="successMsg">成功返回消息</param>
263 
264         /// <param name="errorMsg">失败返回消息</param>
265 
266         /// <param name="isSync">是否异步执行相关处理逻辑</param>
267 
268         /// <returns></returns>
269 
270         public string NotifyAndReurnResult(Stream inputStream, Action<NotifyResult> successAction, Action<NotifyResult> failAction, string successMsg = "OK", string errorMsg = "FAIL", bool isSync = true)
271 
272         {
273 
274             var result = Notify(inputStream);
275 
276             var request = new NotifyRequest();
277 
278             request.ReturnCode = "FAIL";
279 
280             if (result.IsSuccess())
281 
282             {
283 
284                 if (isSync)
285 
286                     Task.Run(() => successAction(result));
287 
288                 else
289 
290                     successAction.Invoke(result);
291 
292                 //交易成功
293 
294                 request.ReturnCode = "SUCCESS";
295 
296                 request.ReturnMsg = successMsg;
297 
298                 return XmlHelper.SerializeObject(request);
299 
300             }
301 
302             else
303 
304             {
305 
306                 if (isSync)
307 
308                     Task.Run(() => failAction(result));
309 
310                 else
311 
312                     failAction.Invoke(result);
313 
314                 request.ReturnMsg = errorMsg;
315 
316                 return XmlHelper.SerializeObject(request);
317 
318             }
319 
320         }
321 
322 }
323 
324 }
View Code

把返回参数和请求参数,序列化成对象,方便我们在编写我们本身逻辑的时候调用

  1  [XmlRoot("xml")]
  2     [Serializable()]
  3     public class Result : PayResult
  4     {
  5         /// <summary>
  6         /// 微信分配的公众账号ID
  7         /// </summary>
  8         [XmlElement("appid")]
  9         public string AppId { get; set; }
 10         /// <summary>
 11         /// 微信支付分配的商户号
 12         /// </summary>
 13         [XmlElement("mch_id")]
 14         public string Mch_Id { get; set; }
 15         /// <summary>
 16         /// 微信支付分配的终端设备号
 17         /// </summary>
 18         [XmlElement("device_info")]
 19         public string Device_Info { get; set; }
 20         /// <summary>
 21         /// 随机字符串,不长于32 位
 22         /// </summary>
 23         [XmlElement("nonce_str")]
 24         public string NonceStr { get; set; }
 25         /// <summary>
 26         /// 签名
 27         /// </summary>
 28         [XmlElement("sign")]
 29         public string Sign { get; set; }
 30         /// <summary>
 31         /// SUCCESS/FAIL
 32         /// </summary>
 33         [XmlElement("result_code")]
 34         public string ResultCode { get; set; }
 35         [XmlElement("err_code")]
 36         public string ErrCode { get; set; }
 37         [XmlElement("err_code_des")]
 38         public string ErrCodeDes { get; set; }
 39     }
 40 
 41     [XmlRoot("xml")]
 42     [Serializable()]
 43     public class UnifiedorderResult : Result
 44     {
 45         /// <summary>
 46         /// 交易类型:JSAPI、NATIVE、APP
 47         /// </summary>
 48         [XmlElement("trade_type")]
 49         public string TradeType { get; set; }
 50         /// <summary>
 51         /// 微信生成的预支付ID,用于后续接口调用中使用
 52         /// </summary>
 53         [XmlElement("prepay_id")]
 54         public string PrepayId { get; set; }
 55         /// <summary>
 56         /// trade_type为NATIVE时有返回,此参数可直接生成二维码展示出来进行扫码支付
 57         /// </summary>
 58         [XmlElement("code_url")]
 59         public string CodeUrl { get; set; }
 60     }
 61 
 62 
 63     [XmlRoot("xml")]
 64     [Serializable()]
 65     public class UnifiedorderRequest
 66     {
 67         /// <summary>
 68         /// OpenId
 69         /// </summary>
 70         [XmlElement("openid")]
 71         public string OpenId { get; set; }
 72         /// <summary>
 73         /// 【不用填写】微信开放平台审核通过的应用APPID
 74         /// </summary>
 75         [XmlElement("appid")]
 76         public string AppId { get; set; }
 77         /// <summary>
 78         /// 【不用填写】微信支付分配的商户号
 79         /// </summary>
 80         [XmlElement("mch_id")]
 81         public string MchId { get; set; }
 82 
 83         /// <summary>
 84         /// 终端设备号(门店号或收银设备ID),默认请传"WEB"
 85         /// </summary>
 86         [XmlElement("device_info

以上是关于公众号微信支付的主要内容,如果未能解决你的问题,请参考以下文章

公众号微信支付开发

公众号微信支付

大商创微信公众号微信支付失败报错

微信公众号支付H5调用支付详解

微信支付-公众号支付H5调用支付详解

微信支付-小程序H5 公众号 Payment SDK