微信公众号支付开发流程与避坑手册-Java篇
来源:互联网 发布:导航升级软件下载 编辑:程序博客网 时间:2024/06/09 06:43
最近完成了微信公众号内的未支付功能,当然开发的过程中难免遇到各种各样的问题,在这里把我开发的过程分享出来,给大家做个参考。
首先,在准备开发的时候需要进行必要的配置。
1.登录微信商户平台,在产品中心->开发配置中对支付授权目录进行配置
(注意:支付授权目录的配置规则是你使用微信支付控件页面的上一级目录,比如:你在www.xxx.cpm/wx/pay/pay.html中调用微信支付控件,那么你需要配置的目录是www.xxx.cpm/wx/pay/)
2.在账户中心->API安全中配置key值,此参数在生成签名时需要用到。
3.其他参数与配置:appid,开发者密码以及网页授权
在做完这些配置以后,我们就可以进行微信支付的开发了。在这里推荐参考微信平台提供的demo。https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1
按照文档中统一接口的实现流程,我们需要先实现发起微信支付的请求。这里的请求链接是统一下单的链接,POST请求
/** * 发起微信支付请求 * @param requestUrl 请求链接 * @param method 请求方式 * @param param 请求参数 * @param connectTimeOut 连接超时时间 * @param readTimeOut 读取超时时间 * @return 请求结果 */public String requestOnce(final String requestUrl, final String method, String param, boolean useCert, int connectTimeOut, int readTimeOut) { HttpURLConnection conn = null; StringBuilder builder = new StringBuilder(); try { URL url = new URL(requestUrl); conn = (HttpURLConnection) url.openConnection(); conn.setDoOutput(true); conn.setDoInput(true); conn.setUseCaches(false); conn.setRequestMethod(method); conn.setConnectTimeout(connectTimeOut); conn.setReadTimeout(readTimeOut); OutputStream out = conn.getOutputStream(); out.write(param.getBytes()); out.flush(); out.close(); int responseCode = conn.getResponseCode(); if (responseCode == 200) { InputStream inputStream = conn.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "utf-8")); String line = null; while ((line = reader.readLine()) != null) { builder.append(line); } reader.close(); inputStream.close(); } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return builder.toString(); }
在这里我们将微信支付所需要的参数进行填充。
/** * 填充微信支付参数 * @param data 需要填充的Hashmap * @return 参数 */ public Map<String, String> fillRequestData(Map<String, String> data) throws Exception { data.put("appid", config.getAPPID()); data.put("mch_id", config.getMCHID()); data.put("nonce_str", WXPayUtil.generateNonceStr()); data.put("notify_url",config.getNOTIFY_URL()); data.put("spbill_create_ip", config.getSERVER_IP()); data.put("trade_type", config.getJSAPI()); if (WXPayConstants.SignType.MD5.equals(this.signType)) { data.put("sign_type", WXPayConstants.MD5); } else if (WXPayConstants.SignType.HMACSHA256.equals(this.signType)) { data.put("sign_type", WXPayConstants.HMACSHA256); } data.put("sign", WXPayUtil.generateSignature(data, config.getKEY(), this.signType)); return data; }
在请求成功之后,微信服务器会返回xml形式的参数,我们可以将xml转换为Map(此方法在微信demo中有实现)。此外在结果返回成功以后,需要再次生成签名,此签名是由于前端页面调起微信支付控件所用。
/** * 发起微信支付认证请求 * @param requestUrl 请求链接 * @param method 请求方式 * @param param 请求参数 * @param useCert 是否使用证书 * @return 认证结果 */ private Map<String, String> request(final String requestUrl, final String method, String param, boolean useCert) { String strXml = wxPayRequest.request(requestUrl, method, param, useCert, config.getHttpConnectTimeoutMs(), config.getHttpReadTimeoutMs()); Map<String, String> data; try { data = WXPayUtil.xmlToMap(strXml); } catch (Exception e) { data = null; e.printStackTrace(); } return data; }private Map<String, String> returnAuthParam(Map<String, String> temp, String key) { Map<String, String> result = new HashMap<String, String>(); long timestamp = System.currentTimeMillis() / 1000; result.put("appId", temp.get("appid")); result.put("timeStamp", String.valueOf(timestamp)); result.put("nonceStr", temp.get("nonce_str")); result.put("package", "prepay_id=" + temp.get("prepay_id")); result.put("signType", WXPayConstants.MD5); String sign = null; try { sign = WXPayUtil.generateSignature(result, key, WXPayConstants.SignType.MD5); } catch (Exception e) { e.printStackTrace(); } result.put("paySign", sign); result.put("return_code", temp.get("return_code")); result.put("result_code", temp.get("result_code")); return result; }
签名生成方法:
/** * 生成签名 MD5 HMACSHA256 * @param data 参数 * @param key API密钥 * @param signType 签名方式 * @return 签名 */ public static String generateSignature(final Map<String,String> data, String key, WXPayConstants.SignType signType) throws Exception { Set<String> keySet = data.keySet(); String[] keyArray = keySet.toArray(new String[keySet.size()]); Arrays.sort(keyArray); StringBuilder sb = new StringBuilder(); for (String k : keyArray) { if (k.equals(WXPayConstants.FIELD_SIGN)) { continue; } if (data.get(k).trim().length() > 0) { sb.append(k).append("=").append(data.get(k).trim()).append("&"); } } sb.append("key=").append(key); if (WXPayConstants.SignType.MD5 == signType) { return MD5(sb.toString()).toUpperCase(); } throw new Exception(String.format("Invalid sign_type: %s", signType)); }
在做完这些操作以后,就可以在前端实现调起微信支付控件的逻辑了。在微信支付文档中给出了一种写法:
if (typeof WeixinJSBridge == "undefined"){ if( document.addEventListener ){ document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false); }else if (document.attachEvent){ document.attachEvent('WeixinJSBridgeReady', onBridgeReady); document.attachEvent('onWeixinJSBridgeReady', onBridgeReady); } }else{ onBridgeReady(data); }function onBridgeReady(data){ orderPage.orderParam.orderId = data.orderId; WeixinJSBridge.invoke( 'getBrandWCPayRequest', { "appId": data.appId, //公众号名称,由商户传入 "timeStamp": data.timeStamp, //时间戳,自1970年以来的秒数 "nonceStr": data.nonceStr, //随机串 "package": data.package, "signType": data.signType, //微信签名方式: "paySign": data.paySign //微信签名 }, function(res){ alert(res.err_msg + " " + res.err_code + " " + res.err_desc); if(res.err_msg == "get_brand_wcpay_request:ok"){ orderPage.orderParam.status = "WAITSEND"; orderPage.submitOrder(); }else if(res.err_msg == "get_brand_wcpay_request:fail"){ alert("调用微信支付失败"); }else if(res.err_msg == "get_brand_wcpay_request:cancel"){ alert("已取消支付"); } } ); }
在上面所使用到的参数都是服务端微信支付请求成功后获取的参数。完成这些之后,微信支付就可以使用了。
但是我们开发的过程中,如果没有相关的项目经验,难免会遇到很多坑。这里我就列举出我在开发中遇到的问题。
1.参数缺失:缺少openid,在微信公众号内嵌网页中调用微信支付需要添加用户的openid。
2.参数形式错误:在前端调起微信支付控件时,将package参数传错,package参数的正确格式为:“prepay_id=XXXXXXXXX”,而我忘了加入“prepay_id=”,这个错误着实让我找了好久(都是不仔细阅读文档的问题)。
3.签名失败:这个签名失败是在前端提示。造成的原因是我在微信支付请求成功之后,直接使用了服务端申请微信支付请求生成的签名,而正确的应该是在申请支付权限成功之后,需要将前端用到的参数拿过来重新生成一次签名才对。这个错误卡了我大半天才搞定,需要注意。
4.get_wcpay_branch_request:fail,这个错误的排查顺序是:首先查看返回参数中request_code,result_code的返回值,如果都为SUCCESS,那么说明服务端请求微信支付权限已经成功,错误就来自于前端,或者参数错误(参考3)。
微信支付的调试:
由于开发微信支付需要调用第三方接口,所以在调试的时候会遇到诸多不便。这里我们就需要使用一些工具来帮助我们完成。
1.微信web开发者工具,我们可以使用这个工具进行调试,直接输入链接地址,然后在这里可以断点调试也可以查看相关错误信息。
2。打印log。在前端我们可以打印出关键信息进行错误排查。比如说,在调用微信支付控件时,输出err_msg,err_code,err_desc,进行查看。
最后说两句
在做完微信支付以后,其实你会发现并没有太大难度,就是在开发的过程中我们需要善于发现问题。还有就是要仔细阅读文档,我遇到的一些错误有很多都是文档中所提到的,但是没有仔细看所以就花费了很多时间去排查错误,而这些都是可以避免的。
最后一张,微信支付的目录结构(仅供参考):
- 微信公众号支付开发流程与避坑手册-Java篇
- 微信公众号支付开发流程
- 微信支付流程-公众号支付开发-视频教程5
- 微信公众号支付开发 --Java
- java开发微信公众号支付
- java开发微信公众号支付
- 微信公众号支付开发流程总结
- 微信公众号支付开发流程备忘
- 微信公众号支付流程
- 公众号支付开发demo,php开发微信支付接口流程
- Java微信开发之公众号支付接口
- 微信公众号开发---实现微信扫一扫支付 (java)
- java 微信公众号支付接口开发总结
- 微信公众号支付开发全过程 --JAVA
- 微信公众号支付开发全过程 --JAVA
- 【转】微信公众号支付开发全过程 --JAVA
- 微信公众号支付开发-JAVA版DEMO
- 微信支付接入流程——公众号支付
- C++bitset的基本使用方法
- plist 文件详解
- 解密回声消除技术之一(理论篇)
- 天天和不可描述
- (二十三)Animator 实例 —— 开场动画
- 微信公众号支付开发流程与避坑手册-Java篇
- 51nod 1055 最长等差数列
- 嵌入式多路温湿度监控系统(三主控程序之串口采集线程)
- <学习笔记>jar包置放在WEB-INF/lib下和通过build path导入的区别是什么
- Modbus协议学习(三)
- angular如何实现不同模块的变量共享
- 使用IOCP需要注意的一些问题~~(不断补充)
- 操作系统之计算机系统概述(1.1)一张图图解计算机系统基本构成
- 解密回声消除技术之二(应用篇)