微信支付问题

如今,愈来愈多公司,选择借微信的势来发展本身的平台,进入工做没多久,我也被告知了要对接微信支付的需求。本来觉得这样的对接,跟着文档走,应该没多大的难度的,但是后来,我才发现,原来我太天真了。在此,留下印记,说说我在微信支付上面遇到的那些问题。 一、关于微信支付 首先说下微信支付。随着微信的红火,微信支付在第三方支付也占了一大块地盘,愈来愈多的公司在本身的APP或者网站上集成了微信支付。从微信支付的官网https://pay.weixin.qq.com/index.php/home/login?return_url=/ 能够看出,微信支付主要分为四大块,公众号支付、APP支付、扫码支付(网站)、刷卡支付。工做上,我接触到了前三种,遇到了各类各样的问题。 二、关于官方文档 对于开发者来讲,对接这种第三方支付,看其官方文档尤为重要。开发者能够经过官网,查到对应不一样支付模块的官方文档,可是,请你们注意,该文档有待完善,彻底照着文档作,可能实现不了你的功能 三、微信支付流程  php




    微信支付的流程,在微信支付官网上也有所显示,这里更加泛化的说一下,其实微信支付须要的是集成了微信SDK的客户端,客户先经过客户端浏览完成订单,而后后台首先在业务系统生成了订单,订单生成后,业务系统请求微信服务器,进行统一下单。统一下单完成后,微信返回相关信息,后台就能够造成相应的支付二维码或者是封装出能够调起微信支付须要的信息。接下来,用户只要经过扫一扫或者点击确认支付,即可以调出微信支付。支付成功后,微信会给用户发送信息,同时也会对业务系统指定的地址发送对应的回调信息,将支付结果告知微信。同时,微信支付信息也能够经过后台直接请求微信支付来进行确认。 四、微信支付相关 首先,微信支付有一个最重要的过程,就是统一下单,简单的说,开发者须要将业务系统中的订单信息发送给微信,让微信后台造成在微信那边的一个支付订单。在向微信请求的时候,传送的数据为xml格式,微信要求xml传送的数据须要进行一次加密,而后将加密的字符串附加在xml中一块儿传输到服务器端,服务器端验证经过以后才能进行下订单操做。具体的算法说明地址https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3 如下,是基于java实现的加密算法:前端

view sourceprint?
01.1 /**
02.2 * 微信支付加密工具,须要加入key
03.3 */
04.4 public static String signature(Map<String, String> map, String key) {
05.5 Set<String> keySet = map.keySet();
06.6 String[] str = new String[map.size()];
07.7 StringBuilder tmp = new StringBuilder();
08.8 // 进行字典排序
09.9 str = keySet.toArray(str);
10.10 Arrays.sort(str);
11.11 for (int i = 0; i < str.length; i++) {
12.12 String t = str[i] + '=' + map.get(str[i]) + '&';
13.13 tmp.append(t);
14.14 }
15.15 if (StringUtils.isNotBlank(key)) {
16.16 tmp.append('key=' + key);
17.17 }
18.18 String tosend = tmp.toString();
19.19 MessageDigest md = null;
20.20 byte[] bytes = null;
21.21 try {
22.22
23.23 md = MessageDigest.getInstance('MD5');
24.24 bytes = md.digest(tosend.getBytes('utf-8'));
25.25 } catch (Exception e) {
26.26 e.printStackTrace();
27.27 }
28.28
29.29 String singe = byteToStr(bytes);
30.30 return singe.toUpperCase();
31.31
32.32 }

     微信支付第一个问题,数据加密的key。这个坑在于不细心,微信支付有不少key,包括咱们微信绑定时候本身输入的key,还有微信给的随机字符key,而这里,在用于加密的key,并非咱们微信公众号中的 AppSecret,而是在微信支付商户后台设置的key,设置的位置为: key设置路径:微信商户平台(pay.weixin.qq.com)-->帐户设置-->API安全-->密钥设置 若是不是使用该Key,哪怕是你的算法写对了,数据传到用户那里,依然返回的是签名错误。 PS:微信官方给出了一个验证签名准确性的工具,该工具地址为https://pay.weixin.qq.com/wiki/tools/signverify/,AnyWay,正如刚才说了,若是key设置的不正确,好比说使用了AppSecret ,那么,你会发现,该工具的出的加密字符串和本身得出的如出一辙,而后当你发送给微信服务端的时候,永远返回的是错误。   微信支付第二个问题,post编码问题。当组装好数据后,须要经过POST的形式向微信服务器发送数据。但是,问题来了,微信的数据封装的彻底正确,key也设置正确了,在官方的验证工具上验证出来也是正确的,但是,微信老是提示签名错误。这个问题出如今post请求的编码问题上,遇到这个问题的状况是,在封装数据的时候,xml里面加入了中文,而后每次请求就会报错,但是若是中文去掉,下单成功。最后才发现,原来POST的时候,没有设置编码,设置成为UTF8以后就没事了。但是,返回的签名错误,也真心难排查啊   微信支付第三个问题,js-sdk调起支付控件。这一步时讲在微信里面H5调起支付控件的。须要注意的是要在H5上面调出支付控件,第一件事须要在微信公众要后台添加指定域名容许该域名调起控件,不然,是不能调起的。设置的教程在这:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_3 。 设置完成以后,接下来是经过js调起,在此吐槽一下,我第一次作的时候,是直接copy的官方的js下来改的,但是。。。。。。。官方的JS上面全角半角的字符混合,致使的别说是他的JS了,就是我本身写的JS最后都没调出来。。。。。而后,关于提示,,苹果版还好,安卓版的微信,若是调不出控件,它一点反应都不会有的。。相对而言,苹果版会有一个弹框提示,因此后期,只要出现问题,都先用苹果测测看看出了什么错。   微信支付第四个问题,app端数据封装。可以统一下单了,这样一来就是对数据封装返回给前端了,这一部,仍是须要进行签名,按理来讲,前面和前面采用的是一样的方法,应该问题不大才对。确实,在web端和扫码支付都没多大问题,但是,app端问题来了。我在公司刚开始和安卓的同事调这个的时候,原本觉得一个下午能搞定了。但是,却不如咱们所想。咱们所有采用的是官方给的要求进行封装的数据,我后台统一下单完成以后,给到安卓,结果安卓死活调不出支付控件,并且一直都返回-1的结果,该结果,能够说一点用处都没有。安卓端的同时调了很久,一直没有找到解决方案,值得一说的是,它官方给的Demo是能够调出结果界面,但是也是调不出支付控件的。并且,他的java文件,utf-8和GBK两种编码混在一块儿的。最后说一下,为啥app调不出支付控件。java


 图1 git

view sourceprint? 01.1 //网页调起的时候 02.2 String time = Long.toString(System.currentTimeMillis()); 03.3 back.put('appId', mchappid); 04.4 back.put('timeStamp', time); 05.5 back.put('nonceStr', '5K8264ILTKCH16CQ2502SI8ZNMTM67VS'); 06.6 back.put('package', 'prepay_id=' + order.getPrepay_id()); 07.7 back.put('signType', 'MD5'); 08.8 String sign2 = SignatureUtils.signature(back, wx_key); 09.9 10.10 JSONObject jsonObject = new JSONObject(); 11.11 jsonObject.put('appId', mchappid); 12.12 jsonObject.put('timeStamp', time); 13.13 jsonObject.put('nonceStr', '5K8264ILTKCH16CQ2502SI8ZNMTM67VS'); 14.14 jsonObject.put('package', 'prepay_id=' + order.getPrepay_id()); 15.15 jsonObject.put('signType', 'MD5'); 16.16 jsonObject.put('paySign', sign2); 17.17 18.18 result.put('status', 'success'); 19.19 result.put('msg', '下单成功'); 20.20 result.put('obj', jsonObject); 21.21 return result; view sourceprint? 01.1 //APP调起的时候,请注意,安卓端不能用驼峰法,全部的key必须使用小写 02.2 String time = Long.toString(System.currentTimeMillis()); 03.3 back.put('appid', app_mchappid); 04.4 back.put('timestamp', time); 05.5 back.put('partnerid', app_mchid); 06.6 back.put('noncestr', '5K8264ILTKCH16CQ2502SI8ZNMTM67VS'); 07.7 back.put('prepayid', order.getPrepay_id()); 08.8 back.put('package', 'Sign=WXPay'); 09.9 String sign2 = SignatureUtils.signature(back, wx_key); 10.10 11.11 JSONObject jsonObject = new JSONObject(); 12.12 jsonObject.put('appid', app_mchappid); 13.13 jsonObject.put('timestamp', time); 14.14 jsonObject.put('partnerid', app_mchid); 15.15 jsonObject.put('noncestr', '5K8264ILTKCH16CQ2502SI8ZNMTM67VS'); 16.16 jsonObject.put('prepayid', order.getPrepay_id()); 17.17 //jsonObject.put('package', 'Sign=WXPay'); 18.18 jsonObject.put('sign', sign2); 19.19 result.put('status', 'success'); 20.20 result.put('msg', '下单成功'); 21.21 result.put('obj', jsonObject); 22.22 return result; 如图,图1为微信官方文档中安卓调起支付控件的示例代码,接下来为web端调起支付控件时候进行加密的算法,最后为解决问题后返回给APP数据时候数据封装的代码。问题所在就是在于,它数据的封装不像官网所说的使用驼峰法,app的时候,须要把全部的字符小写,,,小写,,,,,,,。还有,官方说的packageValue是错的,要用package,就是由于这些错,加密出来的数据是错的,因此app端才调不出支付控件。在此,为我那个调到奔溃的同事默哀。   至今为止遇到的问题大体如上,作下笔记,同时但愿对同为开发的朋友有用   最后,附上本人在github上的一个基于java的微信支付后台Demo https://github.com/Seanid/wechatPay