嗨,欢迎好学的小伙伴们,今天我们来一起攻克微信支付所以遇到的各种坑,欢迎大家共同留言讨论,本节将的是微信的支付功能-----JAVA版
首先,我们先要去微信官方申请【公众号】支付接口开通:https://mp.weixin.qq.com,注意,订阅号是没有开通支付接口的能力,所以注意下,一定是公众号才能行的哦,然后去讲商户号和公众号绑定:https://pay.weixin.qq.com/index.php/core/home/login?return_url=%2F,提供一系列的证书,材料之类的,提交后,需要微信官方进行审核,慢的话,大概就7个工作日,快也就半天都可能的,通过后,即可开通公众号支付接口,还有就是需要一个域名,而且这个域名是在备案过的哈,一定要是备案过的,不然不能使用哦。
其次,我们要准备下我们开发中需要的参数(这些参数在申请通过后,都能查看):appid 应用id 支付接口开通后可查看、
openid 后期可以用微信每次的code获取、
mch_id 商户id也叫商户号 支付接口开通后可查看 、
key key为商户平台设置的密钥key 32位数 由操作密码和自定义密码32生成、
secret 第三方用户唯一凭证密钥
然后,去看下微信支付的时序图,当然微信的官方文档写的很详细,不过有几个细节没有说到位,这里我们来看下我自己整理的下的流程图吧,感觉简单好懂些
时序思维图
说明:首先是访问微信授权连接进行授权以及获取到openid还有code,然后就是统一下单,统一下单分为两次,第一次是签名获取pepay_id,也就是预支付id,通过发送xml报文给微信连接,获取这个预支付id,然后第二次签名就是,拿到预支付id在加上第二次的报文,发送给微信,生成预支付订单,于支付订单生成好了后,返回前台,利用微信自带的接口,然后将后台返回来的签名和一系列参数去请求这个接口,将会钓起微信支付的预支付界面,然后这个接口会返回参数res.err_msg,从这个里面我们可以判断出用户是付款了还是取消了,返回get_brand_wcpay_request:ok,就是成功,不过这个可能会有延迟,所以只是用来提示用户付款成功,最后的逻辑判断我们还是得到回调界面里面写,保险点,因为有时候有网络延迟,然后基本上流程就是这样了,其他都是就是后续事情
然后,我们来上最重要的东西,也是最关心的东西代码和jar包,以及带着jar和代码来走一遍流程
第一:首先,我们到商品详情界面点击下单按钮,来到一个微信接口jsp,进行授权下图,
序号1,是商品详情界面穿进来的参数,金额,订单id,以及订单描述,
序号2,是上面说的开通微信支付后,会收到的几个参数,这里我们先给其封装到session里面,方便后续拿值,
序号3,则是微信授权的链接:https://open.weixin.qq.com/connect/oauth2/authorize?appid=<%=session.getAttribute("appid")%>&redirect_uri=http://new.nbtongtai.com/weixin/wxPay/orderPayDeail.jsp&response_type=code&scope=snsapi_userinfo&state='+order_money+'#wechat_redirect,访问该链接即可授权,如授权通过,则会跳往支付详细界面,进行进一步确认 orderPayDeail.jsp 就是授权后跳往的界面
第二:来到授权后的界面orderPayDeail.jsp,然后就是获取授权后,微信返给我们的参数,
通过这个上面JS拿到微信返回的参数列表,然后则可以拿到返回的code
获取到code后,我们就可以更加code去拿到openid
前台访问:
后台根据code拿到openid,然后将openid和code存session里面,最后在返回前台,注意这里的appid还有secret都是授权那里存session里面的,所以这里可以直接拿出来用
前台将返回来的openid,存界面上,然后就可以进行统一下单了,如下图:再次去后台,将前面准备的参数,传后台,请求微信接口,生成预支付订单,第一个参数就是订单id,第二个就是金额也就是之前授权前商品界面返回来的金额,第三个就是code,每次访问coe都不一样,所以这里要从请求里面获取。
序号1则是在上一步的js方法返回过来的请求,并且获取的参数信息
序号2则是获取到金额,还有订单金额,已经code后,我们就可以生成预支付订单了,也就是说统一下单流程,通过post方法将金额,openid,订单id去请求后台,进行预支付统一下单
来到后台统一下单后台,先获取金额,openid,订单id,然后进行两次签名,第一次通过封装restata集合,然后请求微信预支付连接,得到预支付id,第二次在将获取的预支付id封装,再次请求微信生成微信预支付订单
//得到前台传过来的金额,消费者id
String order_money=JspUtilCode.unescape(JspUtil.changeNull(request.getParameter("order_money"))); //应收金额
String openId=JspUtilCode.unescape(JspUtil.changeNull(request.getParameter("openId"))); //openId //openId="owFJm07ds2P0MAKoezy3qF9EfN2k";
String gm_id=JspUtilCode.unescape(JspUtil.changeNull(request.getParameter("gm_id"))); //商品id
//得到商户的支付金额,单位为分,这里换算为元
Double _money=Double.parseDouble(order_money)*100;
int money=_money.intValue();
Random rand = new Random(); //生成随机数,因为订单号不能太短
int r1=rand.nextInt(10000);
/****************************第一次签名,获取预支付id*******************************/
Map<String, String> restData=new HashMap<String, String>();
restData.put("appid",session.getAttribute("appid").toString()); //应用ID 第三方用户唯一凭证
restData.put("openid",openId); //消费者id
restData.put("body",session.getAttribute("body").toString()); //商品描述 寄存续费充值
restData.put("mch_id",session.getAttribute("mch_id").toString());//商户号
restData.put("notify_url","http://new.nbtongtai.com/weixin/wxPay/BackMessage.jsp"); //通知地址 不管支付成功还是失败都会返回到这个界面
restData.put("nonce_str",WXPayUtil.generateNonceStr()); //随机字符串
restData.put("out_trade_no",(r1+""+r1)+"O"+gm_id); //订单号,不能太短O 后面才是我们想要的订单id
restData.put("sign_type","MD5"); //签名类型
restData.put("total_fee",money+""); //交易金额
restData.put("trade_type","JSAPI"); //交易类型 NATIVE二维码
restData.put("spbill_create_ip","47.114.88.38"); //浏览器ip地址,可以不写
String oneSign = WXPayUtil.generateSignature(restData,session.getAttribute("cusid").toString()); //第二个参数是商户id里面的那个key 32位数
restData.put("sign",oneSign); //拼接签名
//将map集合转成xml报文,方便请求微信接口获取 预支付定单id
String xml = WXPayUtil.mapToXml(restData);
String unifiedorder_url = "https://api.mch.weixin.qq.com/pay/unifiedorder"; //预支付请求连接
String response2 = HttpUtil.doPost(unifiedorder_url, xml); //通过带有签名的xml报文,和预支付连接获取到响应结果
String prepay_id = "";// 预支付id
if(response2.indexOf("SUCCESS") != -1) { //若响应结果成功,则取出预支付id
Map<String, String> map = WXPayUtil.xmlToMap(response2);
prepay_id = (String) map.get("prepay_id");
}
/****************************第一次签名,获取预支付id*******************************/
/****************************第二次签名,生成预支付订单*******************************/
SortedMap<String, String> finalpackage = new TreeMap<String, String>();
String packages="prepay_id="+prepay_id; //预支付id
String timeStamp=""+System.currentTimeMillis(); //预支付订单时间戳
finalpackage.put("appId",session.getAttribute("appid").toString()); //应用ID 第三方用户唯一凭证
finalpackage.put("timeStamp",timeStamp);
finalpackage.put("nonceStr", WXPayUtil.generateNonceStr()); //随机字符串
finalpackage.put("package", packages);
finalpackage.put("signType", "MD5");
//调用签名接口,进行支付
String twoSign = WXPayUtil.generateSignature(finalpackage,session.getAttribute("cusid").toString());
/****************************第二次签名,生成预支付订单*******************************/
//生成预支付订单
json=new JsonUtil();
json.addJsons();
json.addJsonData("gm_id",gm_id);
json.addJsonData("appid",session.getAttribute("appid").toString());
json.addJsonData("packages",packages);
json.addJsonData("timeStamp",timeStamp);
json.addJsonData("nonceStr",finalpackage.get("nonceStr"));
json.addJsonData("finalsign",twoSign);//根据第二次签名,返回前台,然后钓起支付面板
json.addJsonData("status","ok");
message=json.getJson();
前台收到你返回的状态,进行判断,订单预支付生成成功后,则跳往微信的钓起支付面板界面
来到支付面板界面,要做到事情就通过签名,openid还有等参数,去请求微信,调用微信的微信支付界面即可支付,参数就是上面返回的,将其传给下图即可,然后就可以钓起来微信预支付界面,若用户支付成功,则跳往自己写的成功界面,失败也是一样,写个失败的界面,然后就是这里注意下,下面的这个js界面钓起的微信支付,成功有延迟,所以最好不在这里做最终的支付成功判断
(function ($, doc, $$) {
$.init();
mui.plusReady(function(){
// 在这里调用plus api
//获取数据
},false);
wx.config({
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: $$("#appid").val(), // 必填,公众号的唯一标识
timestamp: $$("#timestampJsp").val() , // 必填,生成签名的时间戳
nonceStr: $$("#nonceStrJsp").val(), // 必填,生成签名的随机串
signature: $$("#signatureJsp").val(), // 必填,签名,见附录1
jsApiList: ['getBrandWCPayRequest'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
});
wx.ready(function(){
function onBridgeReady(){
WeixinJSBridge.invoke('getBrandWCPayRequest',{
"appId":$$("#appid").val(), //公众号名称,由商户传入
"timeStamp":$$("#timeStamp").val(), //时间戳,自1970年以来的秒数
"nonceStr":$$("#nonceStr").val(), //随机串
"package":$$("#packages").val(),
"signType":"MD5", //微信签名方式:
"paySign":$$("#finalsign").val() //微信签名
},function(res){
if(res.err_msg == "get_brand_wcpay_request:ok" ) { // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。
//下单完成后,跳往逻辑的界面,向但与同步通知
mui.openWindow({
url:"/weixin/wxPay/successOrder.jsp?gm_id="+$$("#gm_id").val(),
id:"xufeidengPaydeail"
})
}else{
//取消了订单
mui.openWindow({
url:"/weixin/wxPay/faildOrder.jsp?gm_id="+$$("#gm_id").val(),
id:"xufeidengPaydeail"
})
}
});
}
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();
}
});
wx.error(function (res) {
alert("支付状态:"+res.errMsg);
});
})(mui, document, jQuery);
然后我们将最后逻辑写在回调界面里面,也就是第一次签名那个回调界面
然后得到微信支付成功的数据对象,根据这个对象,然后判断到底支付成功还是失败
如支付成功,则可以将自己的业务逻辑写到这里面即可
最后,为大家提供参考,我将demo已经打包,欢迎随便下载
com,为后台,
jar需要的jar包,
wxPay前端
注意:1,微信支付,同一个订单号,不能频繁支付而且还不能太长,为了解决这个问题,所以们在第一次签名生成预支付订单的时候,可以将商品id,使用随机数,然后中间拼一个字母,后面再根据这个字母去获取我们需要订单id
2,旧版本的支付类是一个接口,新版本是一个抽象类,所以这里如果自己去下载的话,需要重新写一下那个类,不过我这里是写好了,下载下来只需要改里面几个参数,即可
3,微信支付需要httpclient4.5,httpcore4.3及以上版本,不然会报错哦
欢迎大家有问题,咨询,1693940631,有问题的话,也欢迎及时沟通哦,