从事JAVA开发一年多了,一直都在看博客园,CSDN的博客,从不少前人哪里学习了不少,忽然以为本身也要尽一份力,写点博客本身给本身作作记录,也给要开发微信人提提醒少遇点坑。php
不少人开发微信的时候,老是在抱怨微信的开发文档很坑,里面的参数和使用方式很含糊,其实有时候本身想一想,若是本身去研发API的时候,是否可以作的比微信更好呢?,大师都有一颗虔诚学徒的心,但愿这篇文档能给予从事微信公众号H5支付焦头烂额的朋友,一点帮助。html
先给你们提提从事微信开发,须要作的一些准备条件:一、去微信公共平台和微信商户平台注册一个帐号,这里须要给微信交纳300块RMB成为一个开发者拥有开发资质 ,微信公共平台网址 、微信商户平台网址 二、申请一个服务号详细流程我就不提醒了,服务号是须要企业认证的(若是没有企业资格,又想练练手的朋友能够去申请一个微信公众号测试号,进行一些简单的开发,微信测试号网址) 。三、注意看文档,切记重要的事情说三遍!!!,作操做以前仔细理解文档里面的内容,参数里面的关系,请求的格式之类的,这种错误是最难发现的,微信公共号开发文档网址 ,微信支付文档。前端
进行开发以前,先把思路理清楚了解,了解微信支付的类型,这里类型有不少,咱们须要的是公众号支付,经过官网文档了解,这里须要注册一个微信商户,这个我就不解释了,按照官网的指示撸就好了,这里展现一个官网的业务流程图。ajax
没有理解到的多看看流程图少吃不少亏,也能够带入本身的业务模式,来考虑怎么设计业务。算法
这里咱们了解到,使用微信统一下单支付接口,前提是须要用户的openid,这里又要涉及到微信的受权登陆了,个人另外一篇文章: 网页微信受权登陆文章json
好了咱们这里提到微信支付流程第一步,统一下单支付接口,这里贴上必填的参数:api
字段名 | 变量名 | 示例值 | 类型 | 描述 |
公众帐号ID | appid | wxd678efh567hg6787 | String(32) | 微信支付分配的公众帐号ID(企业号corpid即为此appId) |
商户号 | mch_id | 1230000109 | String(32) | 微信支付分配的商户号 |
随机字符串 | nonce_str | 5K8264ILTKCH16CQ2502SI8ZNMTM67VS | String(32) | 随机字符串,长度要求在32位之内。推荐随机数生成算法 |
签名 | sign | C380BEC2BFD727A4B6845133519F3AD6 | String(32) | 经过签名算法计算得出的签名值,详见签名生成算法 |
商品描述 | body | 腾讯充值中心-QQ会员充值 | String(128) | 商品简单描述,该字段请按照规范传递,具体请见参数规定 |
商户订单号 | out_trade_no | 20150806125346 | String(32) | 商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|* 且在同一个商户号下惟一。详见商户订单号 |
标价金额 | total_fee | 88 | Int | 订单总金额,单位为分,详见支付金额 |
终端IP | spbill_create_ip | 123.12.12.123 | String(16) | APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP |
通知地址 | notify_url | http://www.weixin.qq.com/wxpay/pay.php | String(256) | 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数 |
交易类型 | trade_type | JSAPI | String(16) | JSAPI 公众号支付微信 NATIVE 扫码支付微信开发 APP APP支付app 说明详见参数规定 |
里面要注意:签名算法,不建议本身手写,能够用微信的支付工具类(微信官方工具类,里面有不少的函数,例如生成sign,map组装xml等实用功能,减小开发量),里面有方法能够填入参数就能够生成sign
贴上我本身的代码:
// 统一下单URL String unifiedorder_url = "https://api.mch.weixin.qq.com/pay/unifiedorder"; IWXPayConfig config = new IWXPayConfig(); Map<String, String> data = new HashMap<String, String>(); data.put("appid", config.getAppID());//微信支付分配的公众帐号ID(企业号corpid即为此appId) data.put("mch_id", config.getMchID());//微信支付分配的商户号 String str = WXPayUtil.generateNonceStr(); data.put("nonce_str", str); // 经过微信工具类生成 随机字符串 data.put("body", "超级商品");//商品描述 data.put("out_trade_no", order.getOrderSn()); // 订单惟一编号, 不容许重复 data.put("total_fee", new BigDecimal(100).multiply(order.getAmount()).setScale(0,BigDecimal.ROUND_DOWN).toString()); // 订单金额, 单位分 data.put("spbill_create_ip", GetIp.getTrueIpAddr(request)); // 下单ip data.put("openid", order.getOpenId()); // 微信公众号统一标示openid data.put("notify_url", "URL这里填写,你的回调域名"); // 订单结果通知, 微信主动回调此接口 data.put("trade_type", "JSAPI"); // 固定填写 // 生成带有 sign 的 XML 格式字符串 String xmlparam = WXPayUtil.generateSignedXml(data, config.getKey()); // 发送请求 String resultStr = HttpRequest.sendPost(unifiedorder_url, xmlparam); logger.info("返回消息" + resultStr);
经过post请求微信会回调一个json回调信息,若是里面return_code 回复为SUCCESS并且result_code 也为SUCCESS的话,恭喜你就成功了一半了。这个时候能够放松一下泡杯茶。
下一步,根据官方提示,须要把返回的json回调信息的,参数取出来进行二次封装,组装进map,而后返回前端发起支付请求(这里有一个坑,页面实际的支付路径等于微信商户平台的设置支付目录的下一级,切记!!!微信错误提示不足,致使我这里卡了一下午)
这里贴上个人代码:
// 时间戳 String timeStamp = new Long(WXPayUtil.getCurrentTimestamp()).toString(); // 建立返回值 //组装二次签名 Map<String, String> resultMap = new HashMap<String, String>(); resultMap.put("appId", wxResultMap.get("appid")); resultMap.put("timeStamp", timeStamp); resultMap.put("nonceStr", str); resultMap.put("package", "prepay_id=" + wxResultMap.get("prepay_id")); resultMap.put("signType", "MD5"); // 生成签名 String paySign = WXPayUtil.generateSignature(resultMap, config.getKey()); resultMap.put("paySign", paySign); return MsgJson.getmsg(false, resultMap, "签名生成成功");
注意通常出现错误的地方都是参数错误,若是前端执行报错请检查参数,是否有填写错,大小写是否有问题。
贴上前端代码:
$.ajax({ type:"post", url: "/weixin/pay", dataType:"json", ontentType : "application/x-www-form-urlencoded", data:{orderId:'${orderId}'}, success:function(result) { console.log(result); var data = result; //var data = JSON.parse(result); if(data.status == '200'){ appId = data.data.appId; paySign = data.data.paySign; timeStamp = data.data.timeStamp; nonceStr = data.data.nonceStr; packageStr = data.data.package; signType = data.data.signType; callpay(); }else{ alert("统一下单失败"); } } }); } function onBridgeReady(){ WeixinJSBridge.invoke( 'getBrandWCPayRequest', { "appId":appId, //公众号名称,由商户传入 "timeStamp":timeStamp, //时间戳,自1970年以来的秒数 "nonceStr":nonceStr , //随机串 "package":packageStr, //预支付交易会话标识 "signType":signType, //微信签名方式 "paySign":paySign //微信签名 }, function(res){ if(res.err_msg == "get_brand_wcpay_request:ok" ) { alert('支付成功'); window.location.replace("/eby/index"); }else if(res.err_msg == "get_brand_wcpay_request:cancel"){ alert('支付取消'); }else if(res.err_msg == "get_brand_wcpay_request:fail" ){ alert('支付失败'); } //使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。 } ); } function callpay(){ 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(); }
前端作完,这一步顺利调出来微信支付的弹框
这里就基本大功告成了,还有差一步处理支付的回调,根据微信官方提示前端返回的支付成功是不靠谱的,须要微信异步回调来验证,一共有8次注意通知的延迟重复性,经过生成sign比对成功后才能够确保此次支付成功了,下面贴上个人处理回调代码:
HashMap map = new HashMap(); IWXPayConfig config = new IWXPayConfig(); try { InputStream inStream = request.getInputStream(); ByteArrayOutputStream outSteam = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len = 0; while ((len = inStream.read(buffer)) != -1) { outSteam.write(buffer, 0, len); } outSteam.close(); inStream.close(); String resultStr = new String(outSteam.toByteArray(), "utf-8"); Map<String, String> resultMap = WXPayUtil.xmlToMap(resultStr);//将xml转成排序以后的map logger.info("微信支付回调地址请求参数=requst:{}", resultMap.toString()); String result_code = resultMap.get("result_code");//业务结果 String is_subscribe = resultMap.get("is_subscribe");//是否关注了微信公众号 String out_trade_no = resultMap.get("out_trade_no");//订单号 String transaction_id = resultMap.get("transaction_id");//微信支付订单号 相似于支付宝的交易号 String sign = resultMap.get("sign");//签名 String total_fee = resultMap.get("total_fee");//订单总金额 单位为 分 String openid = resultMap.get("openid");//用户在商户appid下的惟一标识 String time_end = resultMap.get("time_end"); String bank_type = resultMap.get("bank_type"); //签名验证 resultMap.remove("sign"); String signStr = WXPayUtil.generateSignature(resultMap,config.getKey()); logger.warn("验证= signStr:{},sign:{}", signStr, sign); if (!signStr.equals(sign)) { logger.warn("微信支付回调地址请求参数签名验证失败= signStr:{},sign:{}", signStr, sign); map.put("return_code", "FAIL"); map.put("return_msg", "sign不正确"); return WXPayUtil.mapToXml(map); } if (result_code.equals("SUCCESS")) { logger.info("支付成功= 订单号:{},交易号:{}", out_trade_no, transaction_id); BigDecimal bigDecimal_total_fee = new BigDecimal(total_fee); BigDecimal bigDecimal = bigDecimal_total_fee.divide(new BigDecimal(100)); // 支付成功处理业务逻辑 } //通知微信.异步确认成功.必写.否则会一直通知后台.八次以后就认为交易失败了. map.put("return_code","SUCCESS"); map.put("return_msg","OK");
总结的来讲,微信支付坑仍是很多尤为是是地址和参数,多注意一些仍是能够避免的,其余的注意点在我在场景代码里面提出来了,第一次写文章若是有很差的地方,但愿你们指出来,但愿这篇文章能帮到你们。
文章纯手写,转载请带上做者。