微信公众号-JSAPI支付(保姆级教程)

微信公众号-JSAPI支付

1、微信公众号配置

https://mp.weixin.qq.com/cgi-bin/home
① 开通网页授权域名(前置条件:需开通网页授权接口权限)
在这里插入图片描述
②暴露网络授权域名
在这里插入图片描述
③获取基本配置中的AppID、AppSecret
在这里插入图片描述
④微信支付关联商户号
在这里插入图片描述

2、微信支付配置

https://pay.weixin.qq.com/
① 配置API证书 获取证书序列号
在这里插入图片描述
②获取正式序列号
在这里插入图片描述
在这里插入图片描述
③设置APIV3秘钥
在这里插入图片描述
④ 产品中心—开通JSAPI支付

在这里插入图片描述
⑤添加支付域名配置
在这里插入图片描述
⑥暴露域名
在这里插入图片描述
⑦AppId账号设置-商户关联公众号
在这里插入图片描述

程序配置

必要配置信息:
公众号appId
公众号appSecret
商户id
证书序列号
商户API秘钥
①配置yml文件
在这里插入图片描述
② 微信支付平台下载的秘钥证书放入项目resources目录下
在这里插入图片描述

主要流程及代码实现

①前端通过url获取code
(url参数配置)
Appid:公众号的appid
Redirect_uri:重定向至微信支付调用接口
Scope:snsapi_base(授权方式 无需弹窗静默授权)
https://open.weixin.qq.com/connect/oauth2/authorize?appid=xx&redirect_uri=xxx&response_type=code&scope=snsapi_base&state=STATE&connect_redirect=1#wechat_redirect

②通过前端获取的code,传入指定支付接口(Redirect_uri),获取openId
1)通过appID、appSecret、code构建请求URL,解析返回结果获取openId

 public static String getOpenId(String code) throws Exception {
        // 构建请求URL
        String requestUrl = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + APP_ID
                + "&secret=" + APP_SECRET
                + "&code=" + code
                + "&grant_type=authorization_code";

        // 发送HTTP GET请求
        URL url = new URL(requestUrl);
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.setRequestMethod("GET");
        int responseCode = connection.getResponseCode();

        if (responseCode == HttpURLConnection.HTTP_OK) { // 请求成功
            BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            StringBuilder response = new StringBuilder();
            String line;
            while ((line = in.readLine()) != null) {
                response.append(line);
            }
            in.close();

            // 解析JSON响应,获取openid
            String jsonResponse = response.toString();
            JsonObject jsonObject = new Gson().fromJson(jsonResponse, JsonObject.class);
            String openid = jsonObject.get("openid").getAsString();
            return openid;
        } else {
            throw new Exception("HTTP GET请求失败:" + responseCode);
        }

②获取到openId后,构建请求参数

 log.info("code:{}", code);
        String openId = WXGetOpenIdUtil.getOpenId(code);
        //请求URL
        HttpPost httpPost = new HttpPost(WxPayJsApiConstant.JS_API_URL);
        // 请求body参数
        WxPayJsApiAmountBO amount = new WxPayJsApiAmountBO();
        amount.setTotal(1);//总金额
        amount.setCurrency("CNY");//货币类型

        WxPayPayerBO payer = new WxPayPayerBO();
        payer.setOpenid(openId);//openId

        WxPayJsApiOrderBO request = new WxPayJsApiOrderBO();
        request.setMchid(mchId);//商户号
        String out_trade_no = DateUtil.format(new Date(), DatePattern.PURE_DATETIME_MS_PATTERN);
        request.setOut_trade_no(out_trade_no);//生成的订单号
        request.setAttach("--非必填数据--");//附加信息
        request.setAppid(appId);//appId
        request.setDescription("停车费用");//描述
        request.setNotify_url(notifyUrl);//异步接收微信支付结果通知的回调地址
        request.setAmount(amount);//订单金额信息:总金额 货币类型
        request.setPayer(payer);//openId如何获取

        StringEntity entity = new StringEntity(JSONUtil.toJsonStr(request), "utf-8");
        entity.setContentType("application/json");
        httpPost.setEntity(entity);
        httpPost.setHeader(WxPayJsApiHeaderConstant.HTTP_HEADER_ACCEPT, WxPayJsApiHeaderConstant.HTTP_HEADER_ACCEPT_VAL);

其中initCloseableHttpClientV3方法为:读取resources下的apiclient_key.pem密钥文件 获取秘钥 构建请求信息

    /**
     * 构造 - 通用CloseableHttpClient(定时更新平台证书功能)
     **/
    protected CloseableHttpClient initCloseableHttpClientV3() throws IOException, HttpCodeException, GeneralSecurityException, NotFoundException {
        // 获取 - 秘钥证书信息
        PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new FileInputStream(WxPayJsApiHttpUtil.getResourcesPath() + "apiclient_key.pem"));
        // 获取 - 证书管理器实例
        CertificatesManager certificatesManager = CertificatesManager.getInstance();

        // 操作 - 向证书管理器增加需要自动更新平台证书的商户信息
        certificatesManager.putMerchant(mchId, new WechatPay2Credentials(mchId,
                new PrivateKeySigner(mchSerialNo, merchantPrivateKey)), apiV3Key.getBytes(StandardCharsets.UTF_8));
        // ... 若有多个商户号,可继续调用putMerchant添加商户信息
        // 操作 - 从证书管理器中获取verifier
        Verifier verifier = certificatesManager.getVerifier(mchId);
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(mchId, mchSerialNo, merchantPrivateKey)
                .withValidator(new WechatPay2Validator(verifier));

        // 构造通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
        return builder.build();
    }

构建完成后,调用微信JSAPI下单地址httpPost: https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi

 // 构建 - 请求信息
        CloseableHttpClient closeableHttpClient = initCloseableHttpClientV3();
        // 操作 - 完成请求
        CloseableHttpResponse response = closeableHttpClient.execute(httpPost);
        String bodyAsString = EntityUtils.toString(response.getEntity());
        System.out.println(bodyAsString);
        try {
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == 200) {
                JsonObject jsonObject = new Gson().fromJson(bodyAsString, JsonObject.class);
                //获取微信支付中的预支付id
                String prepayId = jsonObject.get("prepay_id").getAsString();
                System.out.println("success,return body = " + EntityUtils.toString(response.getEntity())
                        + "prepay_id = " + prepayId);
            } else if (statusCode == 204) {
                System.out.println("success");
            } else {
                System.out.println("failed,resp code = " + statusCode + ",return body = " + EntityUtils.toString(response.getEntity()));
                throw new IOException("request failed");
            }
        } finally {
            response.close();
            closeableHttpClient.close();
        }
        // 构造 - 响应信息 prepay_id
        WxPayJsApiPreVO result = convertWxPayJsApiPreInfo((String) JSONUtil.parseObj(bodyAsString).get(WxPayJsApiOtherConstant.HTTP_OTHER_PREPAY_ID));
        System.out.println("result -> " + JSONUtil.toJsonStr(result));
        return result;

其中convertWxPayJsApiPreInfo方法获取JSAPI加密串,构造响应信息result

    protected WxPayJsApiPreVO convertWxPayJsApiPreInfo(String prepayId) throws Exception {
        logger.info("[开始]预支付信息-prepayId : {}", prepayId);
        // 构建 - 相关参数
        String time = String.valueOf(System.currentTimeMillis() / 1000);
        String nonceStr = UUID.randomUUID().toString().replace("-", "");
        String packageStr = StrUtil.format(WxPayJsApiConstant.SIGN_PACKAGE_STR, prepayId);
        // 构建 - 签名信息
        ArrayList<String> list = new ArrayList<>();
        list.add(appId);
        list.add(time);
        list.add(nonceStr);
        list.add(packageStr);

        // 操作 - 签名
        String packageSign = wxPayJsApiSignUtil.signV3(WxPayJsApiSignUtil.buildSignMessage(list).getBytes());

        // 构建 - 响应信息
        WxPayJsApiPreVO result = new WxPayJsApiPreVO();
        result.setWxAppId(appId);
        result.setWxTimeStamp(time);
        result.setWxNonceStr(nonceStr);
        result.setWxPackage(packageStr);
        result.setWxSignType(WxPayJsApiConstant.SIGN_TYPE);
        result.setWxPaySign(packageSign);
        logger.info("[结束]预支付信息-WxPayJsApiPreVO : {}", JSONUtil.toJsonStr(result));
        return result;
    }

返回预支付相关信息 Result示例参数:
{“status”:200,“msg”:“OK”,“data”:{“wxAppId”:“xxx”,“wxTimeStamp”:“xxx”,“wxNonceStr”:“xxxx”,“wxPackage”:"prepay_id=“xxxx”,“wxSignType”:“RSA”,“wxPaySign”:“xxxx”}}
③Result返回给前端,前端调用WeixinJSBridge方法,完成微信支付

1.	function onBridgeReady() {  
2.	    WeixinJSBridge.invoke('getBrandWCPayRequest', {  
3.	        "appId": "xxx",     //公众号ID,由商户传入       
4.	        "timeStamp": "xxx",     //时间戳,自1970年以来的秒数       
5.	        "nonceStr": "xxx",      //随机串       
6.	        "package": "prepay_id=xxx",  
7.	        "signType": "RSA",     //微信签名方式:       
8.	        "paySign": "xxx" //微信签名   
9.	    },  
10.	    function(res) {  
11.	        if (res.err_msg == "get_brand_wcpay_request:ok") {  
12.	            // 使用以上方式判断前端返回,微信团队郑重提示:  
13.	            //res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。  
14.	        }  
15.	    });  
16.	} 

④微信回调 支付后微信向改地址发送支付结果通知

1.	//读取微信回调的内容  
2.	InputStream inStream = request.getInputStream();  
3.	ByteArrayOutputStream outSteam = new ByteArrayOutputStream();  
4.	byte[] buffer = new byte[1024];  
5.	int len;  
6.	while ((len = inStream.read(buffer)) != -1) {  
7.	    outSteam.write(buffer, 0, len);  
8.	}  
9.	String resultXml = new String(outSteam.toByteArray(), "utf-8");  
10.	outSteam.close();  
11.	inStream.close();  
12.	//xml转成map  
13.	Map<String, String> params = WXPayUtil.xmlToMap(resultXml);  
14.	Map<String, String> return_data = new HashMap<>(); //返回给微信信息  
15.	if ("SUCCESS".equals(params.get("return_code"))) { //返回成功 付款成功  
16.	    String outTradeNo = params.get("out_trade_no"); //返回的商户订单号  
17.	    Integer totalFee = Integer.parseInt(params.get("total_fee")); //总金额  
18.	    ParkingBasicInfo parkingBasicInfo = parkingBasicInfoService.lambdaQuery().eq(ParkingBasicInfo::getOutTradeNo, outTradeNo).one();  
19.	    if (ObjectUtil.isNotEmpty(parkingBasicInfo)) {  
20.	        if (totalFee.equals(parkingBasicInfo.getPayMoney())) {  
21.	            String paymentTime = params.get("time_end"); //支付完成时间  
22.	            //发票表添加支付时间  
23.	            String transactionId = params.get("transaction_id"); //微信支付订单号  
24.	            //支付成功后修改订单信息 paymentTime transactionId微信支付订单号  
25.	        }  
26.	    }  
27.	    //正确返回给微信  
28.	    return_data.put("return_code", "SUCCESS");  
29.	    return_data.put("return_msg", "OK");  
30.	    return WXPayUtil.mapToXml(return_data);  
31.	}  
32.	// 支付失败  
33.	return_data.put("return_code", "FAIL");  
34.	return_data.put("return_msg", "return_code不正确");  
35.	return WXPayUtil.mapToXml(return_data);

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值