微信支付接入,就差这一步
首先测试号是无法接入微信支付功能的。必需有公众号并开通支付功能,不会的可以让公司的商务帮你开通。
1. 公众号后台 找到“接口权限”选项,然后找到“网页授权获取用户基本信息”,点“修改”
2.打开界面点“设置”
3.添加网页授权域名,注意去掉“http://”
将下载下来的MP_verify_qHZ4JkZ2soVXMEYX.txt文件放到Tomcat的root下。放好后先试试能不能访问,启动tomcat,用chenyuan.tunnel.2bdata.com/MP_verify_qHZ4JkZ2soVXMEYX.txt看能不能正常显示,最好点击确认会提示到成功的。
4.添加授权目录
按图找到授权目录添加地址。添加授权必需精确到支付页面的父目录,而且如果父目录有子目录,子目录下也是不可以发起支付的,切记,切记。
5.看支付流程图:
太多,太晕,简而言之,除被扫支付场景以外,商户系统先调用该接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易回话标识后再按扫码、JSAPI、APP等不同场景生成交易串调起支付。
前台先通知后台向微信服务器发起“生成预支付交易单”请求,后台正确获取数据再组织报文将数据返回给前端,前台再组织数据调用JSAPI接口把预支付交易单上的数据携带给微信服务器,后面就不用我们管了,微信服务器验证正确后会自动通知微信弹出输入密码框,微信服务器输入成功后会给前端返回成功还是失败值,同时把交易数据以post方式传给商户后台系统,链接是发预支付交易单请求时传的notifyUrl值,微信服务器不能保证向前端和后端返回的数据有先后顺序,一般情况下不用管。
先上代码,java后台收到前端要支付的请求,先发起预支付交易单请求:
@ApiOperation(value = "预付请求", notes = "预付请求") @ApiImplicitParams({ @ApiImplicitParam(name = "ordCd", value = "订单号", required = true, dataType = "String") }) @RequestMapping(value = "ordPrePay", method = RequestMethod.GET) @ResponseBody public Object ordPrePay(@RequestParam(value = "ordCd", required = true) String ordCd, HttpServletRequest request) { try { UserInfo userInfo = (UserInfo) request.getSession().getAttribute("userInfo"); if (null == userInfo) { ExpUtil.handle("未网页授权获得用户基本信息,请从微信首页进入!!", logger); } OrdDtox ordDtox = ordMgrx.getOrdByOrdCd(ordCd); if (null == ordDtox) { ExpUtil.handle("订单号【" + ordCd + "】查询失败,请确认!", logger); } //weixin-java-mp.jar提供的API Map<String, String> payInfo = this.wxMpPayService.getPayInfo(WxPayUnifiedOrderRequest.builder() .body(ordDtox.getOrdVo().getWacNm() + "-" + ordDtox.getOrdVo().getNm()) .totalFee(ordDtox.getOrdVo().getOrdAmt().multiply(new BigDecimal(100)).intValue()) .spbillCreateIp(request.getRemoteAddr()).outTradeNo(ordDtox.getOrdVo().getCd()) .openid((String) request.getSession().getAttribute("openId")).build()); System.out.println(payInfo.toString()); return payInfo; } catch (Throwable t) { return buildFailure(ExpUtil.capture(BaseSvcMsgCode.insertFailure, "下单失败", t, logger)); } }
其中有些参数你以为没传吗,其实我在外部已经配了。
将配置的参数存放到WxMpConfigStorage里,再将WxMpConfigStorage注入到WxMpService里,那就不会每次都添加appid等参数了。
其中:notifyURL的作用是:当前端调用WeixinJSBridge.getBrandWCPayRequest方法发请支付请求并支付成功后,微信服务器将参数通知的该地址,数据以POST携带。我们在些链接上做支付成功的后台逻辑操作,比如记录消费记录,修改订单详情等。
在预支付交易单里appid,macId,key不用设置,后台会WxMpConfigStorage里得到配置参数从配置文件里补充,nonceStr,sign可由java自动生成,如下
看看unifiedOrder的方法。
当关键参数没有时,会从config里取,注意对着api看哪些参数可以不用传,当然也可参照我上面写的例子。
如果返回:
<xml><return_code><![CDATA[FAIL]]></return_code> <return_msg><![CDATA[appid and openid not match]]></return_msg> </xml>
则说明,传的openId不是指定公众号下的用户,出现这种情况有可能开发时测试公众号与开发公众号混合开发了。
正确返回数据如下:
prePay返回给前端数据:
{appId=wxf81943674XXX, timeStamp=1494902640, signType=MD5, package=prepay_id=wx20170516104359f71cd93cc00819XXXX, nonceStr=1494902640216, paySign=0DFFC6C2A9319E718B5XXXXX}
成功返回给前端后,前端使用JSAPI里的WeixinJSBridge.getBrandWCPayRequest方法发请支付请求。
注意:WeixinJSBridge内置对象在其他浏览器中无效
前端支付请求代码如下:
//用ajax向后台发请预计交易支付请求,当后台成功获取数据返回给前端时,前端组织数据发请真正的扣款支付请求。 function payforNow() { var json = {}; json["ordCd"] = vue.cd; jQuery.ajax({ type: "GET", url: "../bss/pay/ordPrePay", xhrFields: { withCredentials: true }, data: json, contentType: "application/x-www-form-urlencoded", dataType: "json", async: true, success: function (data) { prePayData = data; //成功得到预支付交易数据 doBridgeReady(data); console.log("appId:" + data.appId); }, error: function (res) { console.log("ajax请求失败,res:" + res.toString()); } }); } //判断浏览器是否支持JSAPI,只用微信内置浏览器可用。 function doBridgeReady(data){ 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){ WeixinJSBridge.invoke( "getBrandWCPayRequest", { "appId" : data.appId, /* 微信支付,坑一 冒号是中文字符 */ "appId": data.appId, //公众号名称,由商户传入 "timeStamp": data.timeStamp, //时间戳,自1970年以来的秒数 "nonceStr": data.nonceStr, //随机串 "package": data.package, "signType": data.signType, //微信签名方式: "paySign": data.paySign //微信签名 }, function(res){ if(res.err_msg == "get_brand_wcpay_request:ok" ) { window.location.href="../page/orderWaitTakeCloth.jsp"; }else{ alert("支付失败,提示【" + res.err_msg + "】"); } } ); }
微信服务器验证参数是否正确,如里正确则让微信客户端弹出输入密码框
之后微信服务器验证成功则支付成功,并通知前端。
返回get_brand_wcpay_request:cancel,get_brand_wcpay_request:fail请检查一下授权目录是否包含当前调用h5支付的目录。注意是精确,即使是子目录也没权限,必需授权。
notifyURL指定的接收微信服务器返回支付成功的数据:
@ApiOperation(value = "支付成功后台通知", notes = "支付成功后台通知") @RequestMapping(value = "paySuccessCallBack", method = RequestMethod.POST) @ControllerTraced(name = "支付成功后台通知") public Object paySuccessCallBack(HttpServletRequest request, HttpServletResponse response) throws IOException { try { String xmlData = textRequestStream(request); //将post里的数据读取出来映射成class对象。后台的操作就是自己服务器的逻辑了。 WxPayOrderNotifyResult wxPayOrderNotifyResult = this.wxMpPayService.getOrderNotifyResult(xmlData); //微信订单号,先判断该笔订单是否处理过,如果处理过,则直接返回 PayLogDtox payLogDtox = payLogMgrx.getPayLogDtox(wxPayOrderNotifyResult.getTransactionId()); if(null == payLogDtox){ payMgrx.wxMpPayForSuccessCBack(wxPayOrderNotifyResult); }else{ return WxPayOrderNotifyResponse.fail("收到重复消息"); } } catch (WxErrorException e) { e.printStackTrace(); return WxPayOrderNotifyResponse.fail(e.getMessage()); }catch (Throwable t) { // ExpUtil.capture(BaseSvcMsgCode.selectFailure,"保存失败", t, logger); logger.error("保存失败"); } return WxPayOrderNotifyResponse.success("成功"); }
结,第一,添加授权目录,而且要精确到支付页面的父目录
第二,自己后台服务器向微信服务器发请预支付交易请求,并将得到的数据传给前端
第三,前端使用WeixinJSBridge.getBrandWCPayRequest方法组织报文数据向微信服务器发起真正的支付请求,验证成功并扣款成功后,微信服务器会将数据通知到预支付交易里填写的notifyURL上。如果交易失败,请查看是否授权目录填写正确,大部分情况是这里出了问题。
第四,nofityURL接收支付数据并插入到数据库。
以上帮助大家了解过程,以及解决些bug,我还是要亲自去微信支付接入文档仔细阅读。
微信接入官方文档
- 上一篇: 如何在SecureCRT中给linux上传和下载文件
- 下一篇:没有了