微信支付接入,就差这一步
首先测试号是无法接入微信支付功能的。必需有公众号并开通支付功能,不会的可以让公司的商务帮你开通。
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自动生成,如下
当关键参数没有时,会从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,我还是要亲自去微信支付接入文档仔细阅读。
上一篇: Hive(on spark)优化
下一篇: 甲骨文展示Java SE 8蓝图