微信支付—微信H5支付「微信内部浏览器」

前言

微信支付-微信H5外部浏览器支付
微信支付-微信H5内部浏览器支付本文
微信支付-PC端扫码支付待写

本篇是微信支付系列的第二篇、微信H5内部浏览器支付,关于微信H5外部浏览器唤起微信APP支付,请参考上一篇文章。

开发环境:Java + SpringBoot + Vue +WxJava(开源SDK)

扫盲补充:关于微信内部浏览器支付,支付时会直接调起微信支付,不同于外部浏览器支付,内部浏览器支付首先需要获得当前支付用户对该公众号的唯一标识 openId「是否关注都是唯一的」,拿到 openId 后,结合后端其他参数调用微信预支付接口,获得预支付id,然后交由前端发起微信支付,支付成功后回调后端接口。

如下是正文部分。

1、获取Code

要想获得用户唯一标识 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
    }
},

2、换取openid

拿到 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 了,接下来就是调用支付接口。

3、预支付接口

上边已经提到了,内部浏览器支付是交由前端发起的,但是又依赖于后端的 预支付接口,所以先来看一下后端预支付接口:

/**
 * 生成订单「微信内部浏览器」
 * @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 ,该参数是必须的。

4、前端调用,发起支付

至此,后端基本完成了,我们将参数传递给前端调用,直接模拟返回后的数据:

假设下方是调用接口返回的数据

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("支付成功了");
            }
        }
    );
}

5、效果截图

再来简单总结一下,首先由前端获取 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年专科毕业后,期间一度迷茫,最近我创建了一个公众号用来记录自己的成长。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/414053.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微信支付—微信H5支付「PC端扫码支付」

前言 微信支付-微信H5外部浏览器支付微信支付-微信H5内部浏览器支付微信支付-PC端扫码支付「本文」 本篇是微信支付系列的第三篇,PC端扫码支付。 开发环境:Java SpringBoot Vue WxJava(开源SDK) 流程补充:关于微信PC端扫码支付,…

前后端分离项目,后端是如何处理前端传递的token?

前后端分离项目中,在不使用 SpringSecurity、Shiro 安全框架的情况下,后端是如何处理前段传递的 token 的呢? 简单说一个场景,在一个非常小的项目中,由于业务逻辑比较简单,也没有啥安全要求,所以…

面试必备:多线程学习(一)

这是2020年“水”的第23篇文章 面试中,多线程并发问题基本上是必问的,所以,不背上个线程相关的问题,都不好意思出去面试了。 一提到多线程,相信大部分小伙伴首先想到的一定是 Synchronize、Lock,再就是vola…

MacOS中Nginx的安装「借助Homebrew」

本文Nginx的安装借助于Homebrew; 1、Homebrew2、Nginx安装 1、Homebrew 如果你已经安装过Homebrew了,那么你可以跳过这一步,直接进行Nginx安装步骤; Homebrew是一款MacOS平台下的软件包管理工具,拥有安装、卸载、更新、…

快速下载||AnotherRedisDesktopManagerMedis-Redis可视化工具

尽管是在Gitee上下载这款软件,网速仍然是非常的慢,不知道是不是我的网络问题。 提供一份我的下载链接 MacOS:Another.Redis.Desktop.Manager.1.3.1.dmg Windows:Another.Redis.Desktop.Manager.1.3.1.exe 也许你还想试试Medis Mac…

面试必备:synchronized的底层原理?

最近更新的XX必备系列适合直接背答案,不深究,不喜勿喷。 你能说简单说一下synchronize吗? 可别真简单一句话就说完了呀~ 参考回答: synchronize是java中的关键字,可以用来修饰实例方法、静态方法、还有代码块&#xff…

支付宝手机h5网页支付不再提供「继续浏览器付款」按钮了吗

来自圈友的疑惑,记录一下 之前写过一篇「支付宝手机h5支付的文章」,如果下载运行过Demo的小伙伴肯定发现了一个问题 > 「Demo中有显示继续浏览器付款按钮,但自己实际环境并没有」 难道是操作不对? 其实不然,这是两个…

.NETFramework-Web.Mvc:ViewResult

ylbtech-.NETFramework-Web.Mvc:ViewResult1.程序集 System.Web.Mvc, Version5.2.3.0, Cultureneutral, PublicKeyToken31bf3856ad364e35返回顶部 1、#region 程序集 System.Web.Mvc, Version5.2.3.0, Cultureneutral, PublicKeyToken31bf3856ad364e35 // c:\users\…

ConcurrentHashMap底层原理?

本文为面试必备系列篇,不深入叙述,具体细节可自行查询。 可能会问的问题 1、用过ConcurrentHashMap吗?2、为什么要用ConcurrentHashMap?3、HashMap与HashTable的区别,引出ConcurrentHashMap…4、HashMap在多线程环境下…

支付宝支付-当面付之扫码支付「扫码支付」

前言 支付宝支付—沙箱环境使用支付宝支付-支付宝PC端扫码支付支付宝支付-手机浏览器H5支付支付宝支付-当面付之扫码支付「本文」 当面付包含两种支付方式:商品条形码支付 扫码支付 经过前面两篇PC端扫码支付、手机H5支付,我们可以看到一个共同的特点就…

MybatisCodeHelperNew-2.8.1-191-201插件使用

本文测试环境IDEA_2020.1,文中提供了MacOS用户操作截图 1、文件解压后放置plugs插件目录「Windows」 扫码回复「139」 下载后将文件解压,将压缩包内的 MyBatisCodeHelper-Pro 放入 IDEA 安装目录的 plugins 目录。 2、如果你是MacOS用户「MacOS」 同样找…

Redis简单案例(四) Session的管理

Redis简单案例(四) Session的管理 原文:Redis简单案例(四) Session的管理负载均衡,这应该是一个永恒的话题,也是一个十分重要的话题。毕竟当网站成长到一定程度,访问量自然也是会跟着增长,这个时候, 一般都会对其进行负…

MacOS中Elasticsearch的安装「借助Homebrew」

1、Homebrew 如果你已经安装过Homebrew了,那么你可以跳过这一步,直接进行Elasticsearch安装步骤; Homebrew是一款MacOS平台下的软件包管理工具,拥有安装、卸载、更新、查看、搜索等很多实用的功能,强烈推荐安装。 请…

负载均衡中使用 Redis 实现共享 Session

最近在研究Web架构方面的知识,包括数据库读写分离,Redis缓存和队列,集群,以及负载均衡(LVS),今天就来先学习下我在负载均衡中遇到的问题,那就是session共享的问题。 一、负载均衡 负…

Typora中使用Gitee图床

1、前言 之前好友写了一篇「使用gitee作为图床 ,写markdown自动上传文件」,初衷是由于我一直使用的是Typora来写博客「力推」,但之前的版本都不支持图床功能,现在新版本已经有了图床功能了,赶紧入坑。 本篇环境:MacOS…

【NOIP2017模拟6.25】小W的动漫

题目 小W最近迷上了日本动漫,每天都有无数部动漫的更新等着他去看,所以他必须将所有的动漫排个顺序,当然,虽然有无数部动漫,但除了1号动漫,每部动漫都有且仅有一部动漫是它的前传(父亲&#xff…

用Elasticsearch代替数据库存储日志方式

之前的项目中一直使用的是数据库表记录用户操作日志的,但随着时间的推移,数据库log单表是越来越大「不考虑删除」,再加上近期项目中需要用到Elasticsearch,所以干脆把这些用户日志迁移到ES上来了。 环境:SpringBoot2.2…

[js] 写一个方法实现promise失败后自动重试

[js] 写一个方法实现promise失败后自动重试 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><meta http-equiv&…

如何理解Java中的自动拆箱和自动装箱?

小伟刚毕业时面的第一家公司就被面试官给问住了&#xff0c;记忆尤深啊… 如何理解Java中的自动拆箱和自动装箱&#xff1f; 自动拆箱&#xff1f;自动装箱&#xff1f;什么鬼&#xff0c;听都没听过啊&#xff0c;这…这…知识盲区… 回到家后小伟赶紧查资料&#xff0c;我…

基于Docker的Redis集群简单搭建

环境&#xff1a;Docker ( Redis:5.0.5 * 3 ) 1、拉取镜像 docker pull redis:5.0.52、创建Redis容器 创建三个 redis 容器&#xff1a; redis-node1&#xff1a;6379redis-node2&#xff1a;6380redis-node3&#xff1a;6381 docker create --name redis-node1 -v /data…