• SpringBoot对接微信小程序支付功能开发(一,下单功能)


    1,接入前准备:

    1. 接入模式选择直连模式;
    2. 申请小程序,得到APPID,并开通微信支付;
    3. 申请微信商户号,得到mchid,并绑定APPID;
    4. 配置商户API key,下载并配置商户证书,根据微信官方文档操作:https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_8_1.shtml
    5. 上面都配置完之后会得到:小程序APPID、商户号mchid、商户证书序列号、API_V3密钥、商户私钥。

    2,代码开发

    引入微信支付API v3依赖

            
            <dependency>
                <groupId>com.github.wechatpay-apiv3groupId>
                <artifactId>wechatpay-apache-httpclientartifactId>
                <version>0.2.2version>
            dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    微信参数常量类:WechatPayConstant 参数自行更换

    package com.office.miniapp.constants;
    
    /**
     * @InterfaceName: WechatPayConstant
     * @Description: 微信支付常量类
     * @Authror: XQD
     * @Date: 2022/1/6 15:02
     */
    public interface WechatPayConstant {
    
        /**
         * 支付结果回调地址
         */
        String NOTIFY_URL = "线上域名地址/miniapp/wxPay/callback";
        
        /**
         * 退款结果回调地址
         */
        String REFUNDS_URL = "线上域名地址/miniapp/refunds/callback";
    
        /**
         * 商户号
         */
        String MCH_ID = "***";
    
        /**
         * 商户证书序列号
         */
        String MCH_SERIAL_NO = "***";
    
        /**
         * API_V3密钥
         */
        String API_V3KEY = "***";
    
        /**
         * 小程序appId
         */
        String MP_APP_ID = "***";
    
        /**
         * 商户私钥
         */
        String PRIVATE_KEY = "-----BEGIN PRIVATE KEY-----\n" +
                "省略..." +
                "-----END PRIVATE KEY-----\n";
    
        /**
         * jsapi下单url
         */
        String V3_JSAPI_URL = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
    
        /**
         * 商户订单号查询url
         */
        String V3_OUT_TRADE_NO = "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{out_trade_no}";
    
        /**
         * 关闭订单url
         */
        String V3_CLOSE = "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{out_trade_no}/close";
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    微信支付工具类:WxPayUtil (自己封装的)

    package com.office.miniapp.utils;
    
    import com.fasterxml.jackson.databind.JsonNode;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.office.miniapp.constants.WechatPayConstant;
    import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
    import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
    import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
    import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
    import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
    import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
    import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.springframework.stereotype.Component;
    
    import java.io.ByteArrayInputStream;
    import java.io.IOException;
    import java.io.UnsupportedEncodingException;
    import java.security.GeneralSecurityException;
    import java.security.PrivateKey;
    import java.security.Signature;
    import java.util.Base64;
    
    /**
     * @ClassName: WxPayUtil
     * @Description: 微信支付工具类
     * @Authror: XQD
     * @Date: 2022/8/13 23:04
     */
    @Component
    public class WxPayUtil {
    
        /**
         * @Description: 获取httpClient
         * @Param: []
         * @return: org.apache.http.impl.client.CloseableHttpClient
         * @Author: XQD
         * @Date:2022/8/13 23:16
         */
        public static CloseableHttpClient getHttpClient() {
            CloseableHttpClient httpClient = null;
            // 加载商户私钥(privateKey:私钥字符串)
            try {
                PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(
                        new ByteArrayInputStream(WechatPayConstant.PRIVATE_KEY.getBytes("utf-8")));
            //使用自动更新的签名验证器,不需要传入证书
            AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
                    new WechatPay2Credentials(WechatPayConstant.MCH_ID, new PrivateKeySigner(WechatPayConstant.MCH_SERIAL_NO, merchantPrivateKey)),
                    WechatPayConstant.API_V3KEY.getBytes("utf-8"));
            // 初始化httpClient
            httpClient = WechatPayHttpClientBuilder.create()
                    .withMerchant(WechatPayConstant.MCH_ID, WechatPayConstant.MCH_SERIAL_NO, merchantPrivateKey)
                    .withValidator(new WechatPay2Validator(verifier))
                    .build();
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            return httpClient;
        }
    
    
        /**
         * @Description: 生成签名
         * @Param: [message]
         * @return: java.lang.String
         * @Author: XQD
         * @Date:2022/6/7 16:00
         */
        public static String sign(byte[] message) throws Exception {
            Signature sign = Signature.getInstance("SHA256withRSA");
            PrivateKey privateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(WechatPayConstant.PRIVATE_KEY.getBytes("utf-8")));
            sign.initSign(privateKey);
            sign.update(message);
            return Base64.getEncoder().encodeToString(sign.sign());
        }
    
    
        /**
         * @Description: 验证签名
         * @Param: [serial, message, signature]请求头中带的序列号, 验签名串, 请求头中的应答签名
         * @return: boolean
         * @Author: XQD
         * @Date:2021/9/14 10:36
         */
        public static boolean signVerify(String serial, String message, String signature) {
            AutoUpdateCertificatesVerifier verifier = null;
            try {
                // 加载商户私钥(privateKey:私钥字符串)
                PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(
                        new ByteArrayInputStream(WechatPayConstant.PRIVATE_KEY.getBytes("utf-8")));
    
                //使用自动更新的签名验证器,不需要传入证书
                verifier = new AutoUpdateCertificatesVerifier(
                        new WechatPay2Credentials(WechatPayConstant.MCH_ID, new PrivateKeySigner(WechatPayConstant.MCH_SERIAL_NO, merchantPrivateKey)),
                        WechatPayConstant.API_V3KEY.getBytes("utf-8"));
                return verifier.verify(serial, message.getBytes("utf-8"), signature);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            return false;
        }
    
        /**
         * @Description: 解密订单信息
         * @Param: [body] 应答报文主体
         * @return: java.lang.String
         * @Author: XQD
         * @Date:2021/9/14 11:48
         */
        public static String decryptOrder(String body) {
            try {
                AesUtil aesUtil = new AesUtil(WechatPayConstant.API_V3KEY.getBytes("utf-8"));
                ObjectMapper objectMapper = new ObjectMapper();
                JsonNode node = objectMapper.readTree(body);
                JsonNode resource = node.get("resource");
                String ciphertext = resource.get("ciphertext").textValue();
                String associatedData = resource.get("associated_data").textValue();
                String nonce = resource.get("nonce").textValue();
                return aesUtil.decryptToString(associatedData.getBytes("utf-8"), nonce.getBytes("utf-8"), ciphertext);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (GeneralSecurityException e) {
                e.printStackTrace();
            }
            return "";
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    下单接口调用

    下单功能实现,返回预支付交易会话标识prepay_id

    	/**
         * @Description: 小程序下单
         * @Param: [userId, orders]
         * @return: java.lang.Object
         * @Author: XQD
         * @Date:2022/8/13 18:48
         */
        @Override
        public CommonResult submitOrder() throws Exception {
            // 小程序下单
            CloseableHttpClient httpClient = WxPayUtil.getHttpClient();
            HttpPost httpPost = new HttpPost(WechatPayConstant.V3_JSAPI_URL);
            httpPost.addHeader("Accept", "application/json");
            httpPost.addHeader("Content-type", "application/json; charset=utf-8");
    
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectMapper objectMapper = new ObjectMapper();
    
            ObjectNode rootNode = objectMapper.createObjectNode();
            rootNode.put("mchid", WechatPayConstant.MCH_ID)
                    .put("appid", WechatPayConstant.MP_APP_ID)
                    .put("notify_url", WechatPayConstant.NOTIFY_URL)
                    .put("description", "商品描述信息")
                    .put("out_trade_no", System.currentTimeMillis() + "");
            rootNode.putObject("amount")
                    .put("total", 100);// 订单支付金额
            rootNode.putObject("payer")
                    .put("openid", "小程序用户openid");
            objectMapper.writeValue(bos, rootNode);
    
            httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
            //完成签名并执行请求
            CloseableHttpResponse response = httpClient.execute(httpPost);
            try {
                int statusCode = response.getStatusLine().getStatusCode();
                if (statusCode == 200) {
                    //处理成功返回的数据
                    String body= EntityUtils.toString(response.getEntity());
                    //JSONObject jsonObject = JSON.parseObject(body);
                    //获取预支付交易会话标识  prepay_id
                    //String prepayId= jsonObject.getString("prepay_id");
                    //把上面的订单信息存入订单数据库
                    return body;
                } else if (statusCode == 204) {
                    // 处理成功,无返回Body
                    return "success";
                } else {
                    System.out.println("failed,resp code = " + statusCode + ",return body = " + EntityUtils.toString(response.getEntity()));
                    throw new IOException("request failed");
                }
            } finally {
                response.close();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    到这里代表下单操作完成了。把预支付交易id(prepay_id)返回前端,进行拉起支付操作

    查询订单接口调用
        /**
         * @Description: 查询订单
         * @Param: [outTradeNo] 商户订单号
         * @return: java.lang.Object
         * @Author: XQD
         * @Date:2022/8/14 21:43
         */
        @Override
        public Object queryOrder(String outTradeNo) throws Exception {
            CloseableHttpClient httpClient = WxPayUtil.getHttpClient();
            String v3_out_trade_no = WechatPayConstant.V3_OUT_TRADE_NO.replaceFirst("\\{out_trade_no}", outTradeNo);
            HttpGet httpGet = new HttpGet(v3_out_trade_no + "?mchid=" + WechatPayConstant.MCH_ID);
            httpGet.addHeader("Accept", "application/json");
            CloseableHttpResponse response = httpClient.execute(httpGet);
            String bodyAsString = EntityUtils.toString(response.getEntity());
            System.out.println(bodyAsString);
            return bodyAsString;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    关闭订单接口调用
     /**
         * @Description: 关闭订单
         * @Param: [outTradeNo] 商户订单号
         * @return: java.lang.Object
         * @Author: XQD
         * @Date:2022/8/14 21:43
         */
        @Override
        public Object closeOrder(String outTradeNo) throws Exception {
            CloseableHttpClient httpClient = WxPayUtil.getHttpClient();
            String v3_close = WechatPayConstant.V3_CLOSE.replaceFirst("\\{out_trade_no}", outTradeNo);
            HttpPost httpPost = new HttpPost(v3_close);
            httpPost.addHeader("Accept", "application/json");
            httpPost.addHeader("Content-type", "application/json; charset=utf-8");
    
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectMapper objectMapper = new ObjectMapper();
    
            ObjectNode rootNode = objectMapper.createObjectNode();
            rootNode.put("mchid", WechatPayConstant.MCH_ID);
    
            objectMapper.writeValue(bos, rootNode);
    
            httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
            CloseableHttpResponse response = httpClient.execute(httpPost);
            // 状态码 204表示关单成功
            int statusCode = response.getStatusLine().getStatusCode();
            log.info("关闭订单返回码:{}", statusCode);
            if (statusCode == 204) {
                return "关单成功";
            }
            return "关单失败";
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    下一篇: SpringBoot对接微信小程序支付功能开发(二,支付回调功能)

  • 相关阅读:
    LQ0139 油漆面积【枚举】
    群狼调研(长沙产品概念测试)|如何做新品上市满意度调研
    事件抽取(and 检测)经典论文
    如何像我这样创建一个酷炫且能赚钱的网站(使用宝塔安装WordPress搭建子比主题)
    Gradle 构建环境变量配置
    如何使用Pyarmor保护你的Python脚本
    Vue中实现在线画流程图实现
    为什么mysql索引用B+树而不用Hash表
    处理死锁策略2
    【旅游网】前后端分离——用户管理
  • 原文地址:https://blog.csdn.net/weixin_44467567/article/details/126557248