标签:nic 货币 毕业 tor nice border lower gettime amp
微信支付-微信H5外部浏览器支付
微信支付-微信H5内部浏览器支付「本文」
微信支付-PC端扫码支付「待写」
本篇是微信支付系列的第二篇、微信H5内部浏览器支付,关于微信H5外部浏览器唤起微信APP支付,请参考上一篇文章。
开发环境:Java + SpringBoot + Vue +WxJava(开源SDK)
扫盲补充:关于微信内部浏览器支付,支付时会直接调起微信支付,不同于外部浏览器支付,内部浏览器支付首先需要获得当前支付用户对该公众号的唯一标识 openId「是否关注都是唯一的」,拿到 openId 后,结合后端其他参数调用微信预支付接口,获得预支付id,然后交由前端发起微信支付,支付成功后回调后端接口。
如下是正文部分。
要想获得用户唯一标识 openid,首先需要办的事就是获得 code。
code 部分在本文中交由前端去获取「调用微信authorize授权方法」,拿到 code 后传递给后端换取 openid「用户唯一标识」;通常这个操作都是在用户登录时去实现的,登录成功后同时拿到 openid,而且还可以存(更新)到该用户的数据库方便后面使用。
前端获取code,具体如下:
let ua = navigator.userAgent.toLowerCase()
if (ua.match(/MicroMessenger/i) == ‘micromessenger‘) {
if (!this.GetQueryString(‘code‘)) {
alert("跳转");
// this.$vux.toast.text(‘微信授权中……‘, ‘default‘)
let currentUrl = encodeURIComponent(window.location.href)
window.location.href = ‘https://open.weixin.qq.com/connect/oauth2/authorize?appid=我是appid&redirect_uri=‘+currentUrl+‘&response_type=code&scope=snsapi_base&state=STATE&connect_redirect=1#wechat_redirect‘
} else {
let code = this.GetQueryString(‘code‘)
// 此处调用后端方法,通过 code 换取 openid
}
}
补充:授权链接中的 scope 参数分为 snsapi_base、snsapi_userinfo,snsapi_base 可以获得用户的唯一标识 openid,snsapi_userinfo 则在此基础上获得用户资料「昵称、头像等」
上述方法中 ua.match(/MicroMessenger/i) 是用来判断是否是微信环境的, GetQueryString 方法用来获取微信中的 code,如果当前浏览器 url 并没有附带 code 参数,那么就会调用微信的 authorize 方法进行授权,授权后获得 code,该方法具体如下:
GetQueryString (name) {
let url = new RegExp(‘(^|&)‘ + name + ‘=([^&]*)(&|$)‘)
let newUrl = window.location.search.substr(1).match(url)
if (newUrl != null) {
return unescape(newUrl[2])
} else {
return false
}
},
拿到 code 后,下一步就是调用后端接口换取 openid 了, 简单看一下换取 openid 的后端方法:
try {
String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=我是appid&secret=我是secret&grant_type=authorization_code"+
"&code=" + loginRequest.getCode();
String body = RestTemplateUtils.get(url,new JSONObject());
JSONObject jsonObject = JSONObject.parseObject(body);
Integer errcode = jsonObject.getInteger("errcode");
if (errcode == null || errcode == 0) {
String openId = jsonObject.getString("openid");
//将此次登录的openId,暂且放入user的域里面,支付的时候会用到
System.out.println("openId:"+openId);
loginRequest.setOpenId(openId);
return ResultUtil.success(userService.login(loginRequest));
}else{
logger.error("[微信第三方登录] 异常”);
抛出自定义异常
throw new CommonException("微信第三方登录异常","");
}
} catch (Exception e) {
logger.error("[微信第三方登录] 异常", e);
抛出自定义异常
throw new CommonException("微信第三方登录异常","");
}
简单说一下该方法,前端传递 code 致后端方法,后端拿到 code 后,调用 access_token 接口获取 openid,同时完成登录操作。
至此,已经成功登录并拿到用户 openid 了,接下来就是调用支付接口。
上边已经提到了,内部浏览器支付是交由前端发起的,但是又依赖于后端的 预支付接口,所以先来看一下后端预支付接口:
/**
* 生成订单「微信内部浏览器」
* @return
*/
@Transactional
public Object wxPrepay(Orders orders,String openId) {
Object result = null;
try {
WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
orderRequest.setOutTradeNo(orders.getOrderId());
orderRequest.setOpenid(openId);
orderRequest.setBody(“我是商品描述信息");
orderRequest.setTotalFee(orders.getAmount().multiply(new BigDecimal("100")).intValue());
orderRequest.setSpbillCreateIp(DispatchParams.getInstance().getWechatSpbillCreateIp());
orderRequest.setTradeType(WxPayConstants.TradeType.JSAPI);
result = wxPayService.createOrder(orderRequest);
if (result instanceof WxPayMpOrderResult) {
String prepayId = ((WxPayMpOrderResult)result).getPackageValue();
String paySign = ((WxPayMpOrderResult) result).getPaySign();
prepayId = prepayId.replace("prepay_id=", "");
orders.setPrepayId(prepayId);
orders.setSign(paySign);
ordersDao.updateOrders(orders);
}
} catch (WxPayException e) {
logger.error("[微信支付] 异常", e);
抛出自定义全局异常
throw new CommonException(WechatStatusEn.WECHAT_CREATE_CODE_ERROR.getErrorMsg()+"‘:微信支付异常", WechatStatusEn.WECHAT_CREATE_CODE_ERROR.getErrorCode());
} catch (Exception e) {
logger.error("[预付款异常]", e);
抛出自定义全局异常
throw new CommonException(WechatStatusEn.WECHAT_CREATE_CODE_ERROR.getErrorMsg()+"‘:预付款异常", WechatStatusEn.WECHAT_CREATE_CODE_ERROR.getErrorCode());
}
return result;
}
简单说一下预支付方法,首先是根据自己情况创建订单记录,然后就是通过 openid 调用 wxPayService.createOrder 方法「WxJava」获取预支付id,该方法返回的实体为 WxPayMpOrderResult,实体参数为前端调起微信支付的必要参数,具体如下:
private String appId;
private String timeStamp;
private String nonceStr;
@XStreamAlias("package")
private String packageValue;
private String signType;
private String paySign;
为啥获得的预支付id没有用到呀?上方返回的参数并没有看到呀!
其实不然,属性 packageValue 的值为 prepay_id=预支付id ,该参数是必须的。
至此,后端基本完成了,我们将参数传递给前端调用,直接模拟返回后的数据:
假设下方是调用接口返回的数据
console.log(“我是后端返回的数据 - res:"+JSON.stringify(res))
const payParam = {
appId: res.appId,
nonceStr: res.nonceStr,
package: res.packageValue,
timeStamp: res.timeStamp,
signType: res.signType,
paySign: res.paySign,
}
if (typeof WeixinJSBridge === ‘undefined‘) {
if (document.addEventListener) {
document.addEventListener(‘WeixinJSBridgeReady‘, this.onBridgeReady(payParam), false)
} else if (document.attachEvent) {
document.attachEvent(‘WeixinJSBridgeReady‘, this.onBridgeReady(payParam))
document.attachEvent(‘onWeixinJSBridgeReady‘, this.onBridgeReady(payParam))
}
} else {
this.onBridgeReady(payParam)
}
发起支付的 onBridgeReady 方法:
onBridgeReady(res){
alert("发起请求:"+JSON.stringify(res));
WeixinJSBridge.invoke(
‘getBrandWCPayRequest‘, {
"appId":res.appId, //公众号名称,由商户传入
"timeStamp":res.timeStamp, //时间戳,自1970年以来的秒数
"nonceStr":res.nonceStr, //随机串
"package":res.package, // prepay_id=xxx
"signType":res.signType, //微信签名方式:
"paySign":res.paySign //微信签名
},
function(res){
alert(JSON.stringify("我是支付返回的信息:\n"+res));
alert("我是支付返回的信息:\n"+res.err_msg);
if(res.err_msg == "get_brand_wcpay_request:ok" ){
// 使用以上方式判断前端返回,微信团队郑重提示:
//res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
alert("支付成功了");
}
}
);
}
再来简单总结一下,首先由前端获取 code,获取 code 后传递给后端换取 openid,openid 是预支付必须的参数,前端发起支付时,需要6个参数,此时调用后端预支付接口获取「wxPayService.createOrder」,前端支付成功后同样微信会自动回调后端 notify 接口,具体如下「代码仅供参考」:
@RequestMapping(value = "/notify")
@ResponseBody
public String notify(@RequestBody String body) throws Exception {
WxPayOrderNotifyResult result = null;
try {
result = wxPayService.parseOrderNotifyResult(body);
} catch (WxPayException e) {
logger.error("[微信解析回调请求] 异常", e);
return WxPayNotifyResponse.fail(e.getMessage());
}
logger.info("处理微信支付平台的订单支付");
logger.info(JSONObject.toJSONString(result));
String appid = result.getAppid();//应用ID
String attach = result.getAttach();//商家数据包
String bank_type =result.getBankType();//付款银行
Integer cash_fee = result.getCashFee();//现金支付金额
String fee_type = result.getFeeType();//货币种类
String is_subscribe = result.getIsSubscribe();//是否关注公众账号
String mch_id = result.getMchId();//商户号
String nonce_str = result.getNonceStr();//随机字符串
String openid = result.getOpenid();//用户标识
String out_trade_no = result.getOutTradeNo();// 获取商户订单号
String result_code = result.getResultCode();// 业务结果
String return_code = result.getReturnCode();// SUCCESS/FAIL
String sign = result.getSign();// 获取签名
String time_end = result.getTimeEnd();//支付完成时间
Integer total_fee = result.getTotalFee();// 获取订单金额
String trade_type = result.getTradeType();//交易类型
String transaction_id = result.getTransactionId();//微信支付订单号
//如果成功写入数据库
if("SUCCESS".equals(return_code)) {// 如果微信返回的结果是success,则修改订单状态
Orders orders = ordersDao.selectByOrderId(out_trade_no);
// 验证签名
if(orders != null){
if(!"1".equals(orders.getOrderStatus())){//判断是否订单已经完成了
// 判断金额是否跟数据库订单金额一致,放置人为修改
if(orders.getAmount().multiply(new BigDecimal("100")).compareTo(new BigDecimal(total_fee)) == 0){
//更新订单状态
业务逻辑处理部分...
return WxPayNotifyResponse.success("订单已经处理成功!");
}else{
logger.error("微信:金额不一致!");
return WxPayNotifyResponse.fail("订单金额不一致");
}
}else {
return WxPayNotifyResponse.success("订单已经处理成功!");
}
}else{
return WxPayNotifyResponse.fail("商户订单号不匹配");
}
}
System.out.println("回调成功");
System.out.println("----返回给微信的xml:" + result);
return WxPayNotifyResponse.success("支付成功!");
}
博客地址:https://www.cgblog.com/niceyoo
如果觉得这篇文章有丶东西,不放关注一下我,关注是对我最大的鼓励~
18年专科毕业后,期间一度迷茫,最近我创建了一个公众号用来记录自己的成长。
标签:nic 货币 毕业 tor nice border lower gettime amp
原文地址:https://www.cnblogs.com/niceyoo/p/12235649.html