html+java做一个完整的JSAPIv3微信支付功能

使用场景

在微信公众号里面做一个网页链接,带有微信支付的功能。

这里用的是html+springboot

接入前准备

准备好一个公众号,我用的公众号是公司申请的。有商户号的。

然后看官方文档,准备好所需步骤。

准备好所需接入前准备-JSAPI支付 | 微信支付服务商平台文档中心

做了一个简单的前端页面,附源码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>test</title>
    <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=0">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <link rel="icon" type="image/png" href="img/logo.ico" sizes="16x16">



</head>
<body style="max-width: 400px;min-height:300px;margin: 0 auto;border: 1px solid wheat;">
<button id="payBt">支付5元</button>
<input id="code" value="">

<script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
<script src="js/jquery-3.4.1.min.js"></script>
<script>

    $(document).ready(function () {

        // sessionStorage.setItem("openid","oJq4-5vrCjeeMz36PQkCK57Js0bo");
        if (sessionStorage.getItem("openid") && sessionStorage.getItem("openid") !== "undefined") {
            return false;
        }
        alert("----没有openid----");
        var code = getUrlParam('code'); // 截取路径中的code,如果没有就去微信授权,如果已经获取到了就直接传code给后台获取openId
        var local = window.location.href;
        alert(local)
        var AppId = 'AppId';
        alert(code);
        if (code == null || code === '') {
            alert("-----开始获取code----");
            $.ajax({
                type: "POST",
                url: "http://192.168.2.55/gbmsDev/api/getCodeUrl",
                data: {reUrl: local.split("?")[0]},
                dataType: "string",
                jsonp: 'callback',
                success(data) {
                    // alert(data);
                    window.location.href = data;
                }, error() {
                    alert("error");
                }
                , complete() {
                    // alert("complete");
                }
            });
        } else {
            alert("-----已存在code----");
            $("#code").val(code);
            getOpenId(code);
        }
    });
    // 去后台获取opendi
    function getOpenId(code){
        alert("-----开始获取openid----");
        alert("/api/getAccessToken");
        alert(code);
        $.ajax({
            type: "POST",
            url: "http://192.168.2.55/gbmsDev/api/getAccessToken",
            data: {code:code},
            dataType: "json",
            jsonp: 'callback',
            success(data) {
                alert(data);
                alert(data.openid);
                if (data.openid!==undefined &&data.openid!=='' ){
                    sessionStorage.setItem("openid",data.openid);
                    alert("设置openid")
                }
            }, error() {
                alert("error");
            }
            , complete() {
                alert("complete");
            }
        });

    }

    $("#payBt").on("click", function () {

        if (typeof WeixinJSBridge == "undefined"){
            if( document.addEventListener ){
                alert("addEventListener");
                document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
            }else if (document.attachEvent){
                alert("attachEvent");
                document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
                document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
            }
        }else{
            onBridgeReady();
        }
    });

    // 微信支付
    function onBridgeReady(){

        var openId = sessionStorage.getItem("openid");
        $("#code").val(openId);
        if(openId === undefined || openId ===''){
            return false;
        }
        $.ajax({
            type: "POST",
            url: "http://192.168.2.55/gbmsDev/api/getPayParam",
            data: {openId:openId},
            dataType: "json",
            jsonp: 'callback',
            success(data) {
                WeixinJSBridge.invoke(
                    'getBrandWCPayRequest', data,
                    function(res){
                        alert(JSON.stringify(res));
                        if(res.err_msg == "get_brand_wcpay_request:ok" ){
                               alter("支付成功")
                            // 使用以上方式判断前端返回,微信团队郑重提示:
                            //res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
                        }
                    });

            }, error() {
                alert("error");
            }
            , complete() {
                // alert("complete");
            }
        });

    }


    // 获取地址栏参数
    function getUrlParam(name) {
        var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
        var r = window.location.search.substr(1).match(reg);
        if (r != null) return unescape(r[2]);
        return null;
    }




</script>
</body>
</html>

 html需要引入:

<script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>

下面的是java的代码:

Controller层:
package com.game.spring.controller;


import com.game.spring.utils.WxPayUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping("/gbmsDev/api")
public class WxController {

    @RequestMapping(value = "/test")
    public Object banUser(@RequestParam(value = "state",required = false,defaultValue = "999")Integer state) {
        System.out.println(state);
        return "suc";
    }

    @RequestMapping(value = "/getCodeUrl")
    public Object getCodeUrl(@RequestParam(value = "reUrl",required = false,defaultValue = "") String reUrl) {
        return WxPayUtils.getCodeUrl(reUrl);
    }
//    获取getAccessToken 里面包含openid
    @RequestMapping(value = "/getAccessToken")
    public Object getAccessToken(@RequestParam(value = "code",required = false,defaultValue = "") String code) {
        return WxPayUtils.getAccessToken(code);
    }


    @RequestMapping(value = "/getPayParam")
    public Object getPayParam(@RequestParam(value = "openId",required = false,defaultValue = "") String openId) {
        return WxPayUtils.getPayParam(openId);
    }





}

下面是WxPayUtils的代码:

package com.game.spring.utils;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.*;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.*;

public class WxPayUtils {

    private static Logger logger = LoggerFactory.getLogger(WxPayUtils.class);

    //appId 微信公众号里创建的AppId,这里的公众号需要关联到微信支付上,
    // 微信公众平台-基本配置-开发者ID(AppID),这个绑定到微信支付-产品中心-AppID账号管理-关联AppID.这里关联公众号的AppId
    public static String appId = "wx3crrrrd4ca";

    //微信支付商户号 微信支付-账户中心-商户信息-微信支付商户号.查看
    public static String mchId = "1611111996";

    //    微信下单回调地址,支付成功后会回调这个地址。
    //对应文档 https://pay.weixin.qq.com/wiki/doc/apiv3_partner/apis/chapter4_1_5.shtml
    public static String callbackUri = "http://www.baidu.com";

    //APIv3密钥  在微信支付商户平台-账户中心-API安全里设置,如果设置了不知道,就只有修改
    public static String apiV3Key = "2q5w8e3a6s9d1eeeeee8p5M6N9a09";

    //  第三方用户唯一凭证密钥,即appsecret  微信公众平台-基本配置-开发者密码(AppSecret) 公众号基本配置里查询
    public static String appSecret = "39fb76597aeeeeeeed532471";

    //    JSApi下单url地址
    public static String JSApiPayUrl = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";

    // 商户私钥   获取对应的签名需要 在微信支付商户平台-账户中心-API安全里设置 养宠物那边复制的
//    对应文档 https://pay.weixin.qq.com/wiki/doc/apiv3_partner/open/pay/chapter2_1.shtml
    private static String privateKey = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC3qbz0hDWoilpKR6vYnyi4q+CCjmQUjZVIO9R9QE3OfU4qVIaUYavTL/QReO9QfMZcU3LN8JrfKBRedleBfObXXVTP9e1FZh078LEYOiI9Kdzipf3NvSzjaJqkweMe7gOaGWj60zUBQyIVN9+kQVIcqA/2oNgq3Pz88fRFaOgV91okPTj4yfE5mVZujsuHN1ov1Bbky+cbd77SIA5Vl0dXDVUB+A7ZSpc3GXeXPxbEUvHeeeeeeebOpZzcVYsnR82BfTyKmfKIhvHhQjJpHVwmjw/USxxyblLh7Nc1td7pjoommr9yvBn/knq52mPolurmUTHloOCUyxLFDfUeeeepF9iogaSswxGw6B2qK+Jd1ZOoUHnHS2XrazMwKnG1RuvZ9S6On2/lJBXxjWOcvAeKm0c/Nza3lGxcNFwD6CjiXwo+2cq82L9Y3aBiSJRQKb1";

    //商户证书序列号 
    //    对应文档 https://pay.weixin.qq.com/wiki/doc/apiv3_partner/open/pay/chapter2_1.shtml
    private static String mchSerialNo = "23BC1D2F580D5eeeeeeF63D95837CC5";

    private static CloseableHttpClient httpClient;

    /**
     * 第一步:获取code
     * 获取用户openID第一步,获取下一个接口的code参数
     * 访问这个url后,页面将跳转至 redirect_uri/?code=CODE&state=STATE。
     * 跳转的url携带code参数
     * 官方文档路径
     * https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html
     * @return url
     */
    public static String getCodeUrl(String reUrl) {
        String url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect";
        url = url.replace("APPID", appId).replace("REDIRECT_URI",URLEncoder.encode(reUrl));
        return url;
    }


    /**
     *第二部:获取AccessToken,里面包含openId
     * 根据微信公众号的信息获取微信的accessToken
     * {"access_token":"61_SiCTd--EFgkIpWZ4MrciAKJN0N_Dv-qdZLnJyh-","expires_in":7870,"refresh_token":"-kpaMn5czZelR87thefNLEC2WK_-","openid":"5vrCjeeMz36PQkCK57bo","scope":"snsapi_base"}
     * 官方文档路径
     * https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html
     */
    public static String getAccessToken(String code) {
        String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
        url = url.replace("APPID", appId).replace("SECRET", appSecret).replace("CODE",code);
        System.out.println(url);
        String res = HttpRequestsUtils.sendGet(url,"");
        System.out.println(res);
        return res;
    }

    /**
     *第三步:JSAPI下单
     * 使用官方开发库请求下单
     * @param openId openId
     * @return  {"prepay_id": "wx2611215250487459928b000"}
     * 官方文档路径
     * https://pay.weixin.qq.com/wiki/doc/apiv3_partner/open/pay/chapter2_1.shtml
     */
    public static String createOrderJSApiV3(String openId) {
        payLoading();
        //请求URL
        HttpPost httpPost = new HttpPost(JSApiPayUrl);
        // 请求body参数
        StringEntity entity = new StringEntity(JSONObject.toJSONString(buildWxJsApiV3PayJson(openId,0)), "utf-8");
        entity.setContentType("application/json");
        httpPost.setEntity(entity);
        httpPost.setHeader("Accept", "application/json");

        //完成签名并执行请求
        CloseableHttpResponse response = null;
        try {
            response = httpClient.execute(httpPost);
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == 200) {
                return EntityUtils.toString(response.getEntity());
            } else if (statusCode == 204) {
                return "";
            } else {
                System.out.println("failed,resp code = " + statusCode + ",return body = " + EntityUtils.toString(response.getEntity()));
                throw new IOException("request failed");
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                assert response != null;
                response.close();
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return "";
    }


    /**
     *第四步:生成支付参数
     * 生成支付所需的参数
     * @param prepay_id 下单接口返回的参数 预支付交易会话标识
     * @return JSONObject
     * @throws Exception e
     */
    public static JSONObject getTokenWeiXin(String prepay_id){
        // 获取随机字符串
        String nonceStr = getNonceStr();
        // 获取微信小程序支付package
        String packagestr = "prepay_id=" + prepay_id;
        long timestamp = System.currentTimeMillis() / 1000;
        //签名,使用字段appId、timeStamp、nonceStr、package计算得出的签名值
        String message = buildMessageTwo(appId, timestamp, nonceStr, packagestr);
        //获取对应的签名
        String signature = sign(message.getBytes(StandardCharsets.UTF_8));
        // 组装返回
        JSONObject json = new JSONObject(new JSONObject());
        json.put("appId", appId);
        json.put("timeStamp", String.valueOf(timestamp));
        json.put("nonceStr", nonceStr);
        json.put("package", packagestr);
        json.put("signType", "RSA");
        json.put("paySign", signature);
        return json;
    }


    /**
     * 先调用下单,然后调用生成支付参数
     * @param openId openId
     * @return  支付参数
     */
    public static Object  getPayParam(String openId){
        JSONObject jsonObject = JSON.parseObject(createOrderJSApiV3(openId));
        return getTokenWeiXin(jsonObject.getString("prepay_id"));

    }


    /**
     * 构造下单的json(第三步需要)
     * @param description 商品描述
     * @param amount      订单金额
     * @param openId      用户ID       onAky51Fojn3NoLrnKwcY
     * @return JSONObject
     */
    public static JSONObject buildWxJsApiV3PayJson(String description, String amount, String openId) {

        //订单金额json
        JSONObject amountJson = new JSONObject();
        amountJson.put("total", Integer.valueOf(amount));
        amountJson.put("currency", "CNY");

        //支付者json
        JSONObject payerJson = new JSONObject();
        payerJson.put("openid", openId);

        //基础信息json
        JSONObject json = new JSONObject();
//        微信公众号里创建的APPID
        json.put("appid", appId);
//        直连商户号 微信支付-账户中心-商户信息查看
        json.put("mchid", mchId);
        json.put("description", description);
//        商户订单号 自己生成
        json.put("out_trade_no", generateNonceStr());
        json.put("notify_url", callbackUri);
        json.put("amount", amountJson);
        json.put("payer", payerJson);
        return json;
    }

    /**
     * 根据商品goodsId构造下单的json (第三步需要)
     * @return JSONObject
     */
    public static JSONObject buildWxJsApiV3PayJson(String openId,Integer goodsId) {
        return buildWxJsApiV3PayJson("测试商品", "1", openId);
    }
    /**
     * 构造下单的json 无参数
     * @return JSONObject
     */
    public static JSONObject buildWxJsApiV3PayJson() {
        return buildWxJsApiV3PayJson("测试商品", "1", "123123123124");
    }

    /**
     * 初始化httpClient的通用方法(第三步:JSAPI下单)
     * 微信支付官方开发库
     * 创建加载商户私钥、加载平台证书
     */
    public static void payLoading() {
        logger.info("----------初始化httpClient-----------");
        // 加载商户私钥(privateKey:私钥字符串)
        PrivateKey merchantPrivateKey = PemUtil
                .loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes(StandardCharsets.UTF_8)));

        // 加载平台证书(mchId:商户号,mchSerialNo:商户证书序列号,apiV3Key:V3密钥)
        AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
                new WechatPay2Credentials(mchId, new PrivateKeySigner(mchSerialNo, merchantPrivateKey)), apiV3Key.getBytes(StandardCharsets.UTF_8));

        // 初始化httpClient
        httpClient = WechatPayHttpClientBuilder.create()
                .withMerchant(mchId, mchSerialNo, merchantPrivateKey)
                .withValidator(new WechatPay2Validator(verifier)).build();
    }

    /**
     *看情况调用 ,关闭httpClient客户端
     * @throws IOException e
     */
    public void after() throws IOException {
        httpClient.close();
        logger.info("----------关闭httpClient-----------");
    }

    /**
     * 根据微信公众号的信息获取微信的Token(没有使用)
     * 官方文档:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html
     */
    public static String getToken() {
        String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
        url = url.replace("APPID", appId).replace("APPSECRET", appSecret);

        String result = HttpRequestsUtils.sendGet(url,"");
        System.out.println(result);
        JSONObject jsonObject = JSON.parseObject(result);
        return jsonObject.getString("access_token");
    }


    public static void main(String[] args) {
        System.out.println(WxPayUtils.getCodeUrl(""));
//        String str = WxPayUtils.getAccessToken();
//        System.out.println(str);
    }




    //    获取随机字符串
    public static String getNonceStr() {
        return UUID.randomUUID().toString()
                .replaceAll("-", "")
                .substring(0, 32);
    }

    //    签名连接成一串
    private static String buildMessageTwo(String appId, long timestamp, String nonceStr, String packag) {
        return appId + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                + packag + "\n";
    }


    /**
     * 生成签名
     * 官方文档:https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay4_0.shtml
     * @param message message
     * @return String
     */
    private static String sign(byte[] message) {
        Signature sign = null;
        try {
            sign = Signature.getInstance("SHA256withRSA");
            sign.initSign(PemUtil.loadPrivateKey(privateKey));
            sign.update(message);
            return Base64.getEncoder().encodeToString(sign.sign());
        } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
            e.printStackTrace();
        }
        return null;

    }

    /**
     * 随机字符串 订单号
     *
     * @return
     */
    public static String generateNonceStr() {
        return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
    }


}

pom.xml

         <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--        json-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.2</version>
        </dependency>

<!--        HttpUtils-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.22</version>
        </dependency>

<!--        微信SDK-->
        <dependency>
            <groupId>com.github.wechatpay-apiv3</groupId>
            <artifactId>wechatpay-apache-httpclient</artifactId>
            <version>0.4.8</version>
        </dependency>
<!--        HttpUrl要用-->
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>4.9.3</version>
        </dependency>

        <!--        java基础类-->
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>

到这里就差不多了,每个步骤都有具体官方文档,这个必须得看,因为有些要在微信公众号里设置,有些要在微信支付里设置,弄的地方还是挺多的。

代码写的丑,讲究看吧,又不是不能用。。。

如果有什么问题,欢迎大佬指正,最后如果对你有一点点帮助,麻烦支持一下。


全国寄快递4元起,电影票8.8折。更多优惠关注公众号:【折价寄件】

感谢观看!!!!

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 10
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值