公众号微信支付
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 }
把返回参数和请求参数,序列化成对象,方便我们在编写我们本身逻辑的时候调用
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以上是关于公众号微信支付的主要内容,如果未能解决你的问题,请参考以下文章