开发环境

  • java1.8
  • maven 3.3.9
  • springboot 2.1.3.RELEASE

第一步:开通JSAPI支付

1、登陆已认证企业服务号,开通微信支付

在这里插入图片描述
在这里插入图片描述

注册微信商户号请参考官方文档,参考地址如下:
https://pay.weixin.qq.com/index.php/apply/applyment_home/guide_normal#none

2、开通JSAPI

开通微信支付后即可在微信商户平台(pay.weixin.qq.com)开通JSAPI支付。登录商品平台:
在这里插入图片描述
进入产品中心,开通JSAPI支付:
在这里插入图片描述
设置JSAPI支付目录,注意:支付授权目录为公网域名且备案通过。
以下图片来源于网络:
在这里插入图片描述

3、JSAPI调用接口文档

获取openId地址(由于是网页调用,所以采用网页授权的方式获取):https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html

获取接口所需参数地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1

JSAPI接口地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6
在这里插入图片描述
统一下单接口定义:
红色:程序设置
蓝色:微信sdk(开发工具包)自动配置
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

第二步:获取openid

1、设置js接口安全域名

在这里插入图片描述
在这里插入图片描述

2、添加白名单(调用获取access_token接口时,需要设置访问来源IP为白名单)

在这里插入图片描述

3、获取openid流程图(图片来源于网络)

在这里插入图片描述

4、获取openid

获取openid分为两步走,第一步先获取code,第二步获取openId

第一步:获取code

访问URL:https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_re

参数说明:
在这里插入图片描述
关于网页授权的两种scope的区别说明

1、以snsapi_base为scope发起的网页授权,是用来获取进入页面的用户的openid的,并且是静默授权并自动跳转到回调页的。用户感知的就是直接进入了回调页(往往是业务页面)

2、以snsapi_userinfo为scope发起的网页授权,是用来获取用户的基本信息的。但这种授权需要用户手动同意,并且由于用户同意过,所以无须关注,就可在授权后获取该用户的基本信息。

3、用户管理类接口中的“获取用户基本信息接口”,是在用户和公众号产生消息交互或关注后事件推送后,才能根据用户OpenID来获取用户基本信息。这个接口,包括其他微信接口,都是需要该用户(即openid)关注了公众号后,才能调用成功的。

第二步:获取openid

获取code后,请求以下链接获取access_token,在响应数据中包含openid: https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

参数说明:
在这里插入图片描述
在这里插入图片描述

5、代码编写

1、新建一个maven项目,引入依赖

 <!--微信支付依赖-->
 <dependency>
     <groupId>com.github.tedzhdz</groupId>
     <artifactId>wxpay-sdk</artifactId>
     <version>3.0.10</version>
 </dependency>
 <dependency>
     <groupId>com.github.binarywang</groupId>
     <artifactId>weixin-java-pay</artifactId>
     <version>3.4.0</version>
 </dependency>

2、编写代码

@Controller
public class WxPayController {

    String appID = "wxd2bf2dba2e86a8c7";
    String mchID = "1502570431";
    String appSecret = "cec1a9185ad435abe1bced4b93f7ef2e";
    String key = "95fe355daca50f1ae82f0865c2ce87c8";
    //申请授权码地址
    String wxOAuth2RequestUrl = "https://open.weixin.qq.com/connect/oauth2/authorize";
    //授权回调地址
    String wxOAuth2CodeReturnUrl = "http://ces123/transaction/wx-oauth-code-return";
    String state="";

    //获取授权码
    @GetMapping("/getWXOAuth2Code")
    public String getWXOAuth2Code(HttpServletRequest request, HttpServletResponse response){

        //https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
        String url = String.format("https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect",
                appID, wxOAuth2CodeReturnUrl
        );

        return "redirect:"+url;

    }

    /**
     * //授权码回调,传入授权码和state,/wx-oauth-code-return?code=授权码&state=
     * @param code 授权码
     * @param state 申请授权码传入微信的值,被原样返回
     * @return
     */
    @GetMapping("/wx-oauth-code-return")
    public String wxOAuth2CodeReturn(@RequestParam String code,@RequestParam String state){

        //https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
        String url = String.format("https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code",
                appID, appSecret, code
        );

        //申请openid,请求url
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<String> exchange = restTemplate.exchange(url, HttpMethod.GET, null, String.class);
        //申请openid接口响应的内容,其中包括了openid
        String body = exchange.getBody();
        log.info("申请openid响应的内容:{}",body);
        //获取openid
        String openid = JSON.parseObject(body).getString("openid");
        return openid;
    }
}

第三步:统一下单

第二步走完后,下单所需的所有参数都获取到了,下面开始支付:

后端代码,将所有参数返回到前端

@Controller
public class WxPayController {

    String appID = "wxd2bf2dba2e86a8c7";
    String mchID = "1502570431";
    String appSecret = "cec1a9185ad435abe1bced4b93f7ef2e";
    String key = "95fe355daca50f1ae82f0865c2ce87c8";
    //申请授权码地址
    String wxOAuth2RequestUrl = "https://open.weixin.qq.com/connect/oauth2/authorize";
    //授权回调地址
    String wxOAuth2CodeReturnUrl = "http://ces123/transaction/wx-oauth-code-return";
    String state="";

    //获取授权码
    @GetMapping("/getWXOAuth2Code")
    public String getWXOAuth2Code(HttpServletRequest request, HttpServletResponse response){

        //https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
        String url = String.format("https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect",
                appID, wxOAuth2CodeReturnUrl
        );

        return "redirect:"+url;

    }

    /**
     * //授权码回调,传入授权码和state,/wx-oauth-code-return?code=授权码&state=
     * @param code 授权码
     * @param state 申请授权码传入微信的值,被原样返回
     * @return
     */
    @GetMapping("/wx-oauth-code-return")
    public String wxOAuth2CodeReturn(@RequestParam String code,@RequestParam String state){

        //https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
        String url = String.format("https://api.weixin.qq.com/sns/oauth2/access_token?appid=%s&secret=%s&code=%s&grant_type=authorization_code",
                appID, appSecret, code
        );

        //申请openid,请求url
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<String> exchange = restTemplate.exchange(url, HttpMethod.GET, null, String.class);
        //申请openid接口响应的内容,其中包括了openid
        String body = exchange.getBody();
        log.info("申请openid响应的内容:{}",body);
        //获取openid
        String openid = JSON.parseObject(body).getString("openid");
        //重定向到统一下单接口
        return "redirect:http://ces123/transaction/wxjspay?openid=" + openid;
    }


    //统一下单,接收openid
    @GetMapping("/wxjspay")
    public ModelAndView wxjspay(HttpServletRequest request,HttpServletResponse response) throws Exception {
        //创建sdk客户端
        WXPay wxPay = new WXPay(new WXPayConfigCustom());
        //构造请求的参数
        Map<String,String> requestParam = new HashMap<>();
        requestParam.put("out_trade_no","10029293889");//订单号
        requestParam.put("body", "iphone8");//订单描述
        requestParam.put("fee_type", "CNY");//人民币
        requestParam.put("total_fee", String.valueOf(1)); //金额
        requestParam.put("spbill_create_ip", "127.0.0.1");//客户端ip
        requestParam.put("notify_url", "none");//微信异步通知支付结果接口,暂时不用
        requestParam.put("trade_type", "JSAPI");
        //从请求中获取openid
        String openid = request.getParameter("openid");
        requestParam.put("openid",openid);
        //调用统一下单接口
        Map<String, String> resp = wxPay.unifiedOrder(requestParam);

        //准备h5网页需要的数据
        Map<String,String> jsapiPayParam = new HashMap<>();
        jsapiPayParam.put("appId",appID);
        jsapiPayParam.put("timeStamp",System.currentTimeMillis()/1000+"");
        jsapiPayParam.put("nonceStr", UUID.randomUUID().toString());//随机字符串
        jsapiPayParam.put("package","prepay_id="+resp.get("prepay_id"));
        jsapiPayParam.put("signType","HMAC-SHA256");
        jsapiPayParam.put("paySign", WXPayUtil.generateSignature(jsapiPayParam,key,WXPayConstants.SignType.HMACSHA256));
        //将h5网页响应给前端
        return new ModelAndView("wxpay",jsapiPayParam);
    }

    class  WXPayConfigCustom extends WXPayConfig{

        @Override
        protected String getAppID() {
            return appID;
        }

        @Override
        protected String getMchID() {
            return mchID;
        }

        @Override
        protected String getKey() {
            return key;
        }

        @Override
        protected InputStream getCertStream() {
            return null;
        }

        @Override
        protected IWXPayDomain getWXPayDomain() {
            return new IWXPayDomain() {
                @Override
                public void report(String s, long l, Exception e) {

                }

                @Override
                public DomainInfo getDomain(WXPayConfig wxPayConfig) {
                    return new DomainInfo(WXPayConstants.DOMAIN_API,true);
                }
            };
        }
    }

}

前端代码:
在这里插入图片描述

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="Pragma" content="no-cache">
    <meta http-equiv="Cache-Control" content="no-cache">
    <meta name="renderer" content="webkit">
    <meta http-equiv="Expires" content="0">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>同步通知</title>
    <script>

        function onBridgeReady(){
            WeixinJSBridge.invoke(
                'getBrandWCPayRequest', {
                    "appId" : "${appId}",     //公众号名称,由商户传入
                    "timeStamp" : "${timeStamp}",         //时间戳,自1970年以来的秒数
                    "nonceStr" : "${nonceStr}", //随机串
                    "package" : "${package}",
                    "signType" : "${signType}",         //微信签名方式:
                    "paySign" : "${paySign}" //微信签名,paySign 采用统一的微信支付 Sign 签名生成方法,注意这里 appId 也要参与签名,appId 与 config 中传入的 appId 一致,即最后参与签名的参数有appId, timeStamp, nonceStr, package, signType。
                },
                function(res) {
                    if(res.err_msg == "get_brand_wcpay_request:ok" ) {     // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回    ok,但并不保证它绝对可靠。
                        alert('支付成功!');
                    } else {
                        alert('支付失败:' + res.err_msg);
                    }
                    WeixinJSBridge.call('closeWindow');
                }
            );
        }
        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();
        }
    </script>

</head>
<body>
<div id="app">

</div>
</body>
</html>

至此,完成!!!

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐