用户选择商品发起购买请求,后端接收到请求后,先生成本地订单,以得到一个本地订单的商户订单号。同时从表中读取该用户的openID。 我是用一个对象pay来存统一下单需要传的参数,pay的字段如下(部分参数并非必填参数,可传可不传): 注:notify_url是必填参数,是微信支付成功后需要调用的回调方法,系统支付成功需要做的业务处理都是在这方法里面执行的。 进行统一下单之前,我们需要根据请求参数进行第一次签名,签名算法可以参考微信支付开发文档,这里提几点注意事项: 1、订单金额需要转换成以分为单位; 2、参数区分大小写,且值为空的参数不参与签名 3、参数需按ASCII码从小到大排序 签名方法举例: /** * 生成sign用于请求微信 * @param key 秘钥 * @param parameters 参数map * @return */ publicstatic StringcreateSign(Stringkey,Map<Object, Object>parameters) { StringBuffersb =new StringBuffer(); if (!(parametersinstanceof SortedMap<?,?>)) { parameters = new TreeMap<Object,Object>(parameters); } Set<?>es =parameters.entrySet();//所有参与传参的参数按照accsii排序(升序),sign参数不参与签名 Iterator<?>it =es.iterator(); while (it.hasNext()) { Map.Entryentry= (Map.Entry)it.next(); Stringk = (String)entry.getKey(); Objectv =entry.getValue(); if (null !=v && !"".equals(v) && !"sign".equals(k) &&!"key".equals(k)) { sb.append(k +"=" +v +"&"); } } sb.append("key=" +key); Stringsign=md5Util.getMD5ofStr(sb.toString()).toUpperCase(); returnsign; } 签名成功后,得到sign并赋值pay.setSign(sign) ;接下来就是将pay对象转换成xml,调用统一下单接口进行统一支付,并将统一支付返回的xml转换成bean,这里使用的是WechatPayResult对象,WechatPayResult的字段如下图; 统一下单成功会返回微信预支付订单号prepay_id,我们需要根据这个prepay_id进行二次签名,二次签名所用参数如下(不包括paySign): 其中,预支付订单号prepay_id存储在package字段中。 二次签名成功后,即可将参与签名参数及签名paySign一并传到小程序端,小程序端在接受到这些参数后,即可调用方法调起支付,调起代码如下: wx.requestPayment({ 'timeStamp': pay.timeStamp, 'nonceStr': pay.nonceStr, 'package': pay.package, 'signType': pay.signType, 'paySign': pay.paySign, 'success':function (res) { console.log("支付成功") callback(); }, 'fail':function (res) { } }); 支付成功后,微信服务器将会调用我们在调用统一下单时传过去的回调方法路径notify_url,在这个方法里是本地系统在支付成功后需要做的一些业务操作。 需要注意的是,商户系统对于支付结果通知的内容一定要做签名验证,并校验返回的订单金额是否与商户侧的订单金额一致,防止数据泄漏导致出现“假通知”,造成资金损失。其次就是在业务处理完成后,需要向微信服务器返回一个成功的信息,否则微信将按一定的策略定期重新发起通知,这样会频繁请求本地服务器的,所以本地系统必须能够正确处理重复的通知,并在处理完成后一定要向微信端发送成功的消息。 推荐的做法是,当收到通知进行处理时,首先检查对应业务数据的状态,判断该通知是否已经处理过,如果没有处理过再进行处理,如果处理过直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。 附: 对象转换成xml的方法如下: publicstaticStringmyBeanToXML(Objectmodel,StringrootName){ StringBuildersb =new StringBuilder(); sb.append("<" +rootName +">"); if (model !=null) { Field[]field=model.getClass().getDeclaredFields();//获取实体类的所有属性,返回Field数组 for (intj = 0;j <field.length;j++) {// 遍历所有属性 field[j].setAccessible(true); Stringname=field[j].getName();//获取属性的名字 Objectvalue=null; try { value = field[j].get(model); }catch(IllegalArgumentExceptione) { e.printStackTrace(); }catch(IllegalAccessExceptione) { e.printStackTrace(); } if (value !=null) { sb.append("<" +name +">"); if(!"total_fee".equals(name)){ sb.append("<![CDATA["+value.toString()+"]]>"); }else{ sb.append(value.toString()); } sb.append("</"+name+">"); } } } sb.append("</"+rootName+">"); returnsb.toString(); } |
请发表评论