• springboot集成微信支付APIv3接口 实现小程序和公众号支付


    前言:

    网上有很多大佬们集成的综合支付包,做相应配置也很方便,我这个是基于微信官方文档流程做的直连模式,实现公众号和小程序支付(需要代码中正确使用appid),不得不吐槽微信的文档还是一如既往的。。。

    微信官方支付文档链接:https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_8_0.shtml,各种证书配置就按照文档上说的来就行了,下边直接上代码。

    1.引入maven依赖

            <dependency>
                <groupId>com.github.wechatpay-apiv3</groupId>
                <artifactId>wechatpay-apache-httpclient</artifactId>
                <version>0.3.0</version>
            </dependency>
            <dependency>
                <groupId>org.apache.httpcomponents</groupId>
                <artifactId>httpclient</artifactId>
                <version>4.5.5</version>
            </dependency>

    2.yaml配置

    wx:
        pay:
            mchId: 16126xxxx #微信支付商户号
            mchSerialNo: 60B04XXXXXXXXX #证书序列号
            v3Secret: 5ovxxxxxxxxxxxxxxxx  #密钥
            p12Path:/usr/local/apiclient_cert.p12 #证书绝对路径地址
            keyPass:16126xxxx  #证书密码 默认微信商户号
            privateKey: | #这个符号一定注意(回车键旁边) 是yaml中保持文本换行读取的写法 防止证书读出来加载失败
                -----BEGIN PRIVATE KEY-----
                xxxx
                -----END PRIVATE KEY----- #私钥
            certificate: |
                -----BEGIN CERTIFICATE-----
                xxxx
                -----END CERTIFICATE----- #平台证书

    特别提示:

      yaml配置中的certificate这个证书,需要自己生成,https://developers.weixin.qq.com/community/pay/doc/000e4a0d5dc1486acc19c6fd15bc00,这个链接中网友果果璐璐和萧的回答给出了答案

    3.读取yaml内容的Properties类

    @Data
    @Component
    @ConfigurationProperties(prefix = "wx.pay")
    public class WeChatPayProperties {
    
        /**
         * 商户id
         */
        private String mchId;
    
        /**
         * 商户证书序列号
         */
        private String mchSerialNo;
    
        /**
         * apiV3密钥
         */
        private String v3Secret;
    
        /**
         * p12证书文件位置
         */
        private String p12Path;
    
        /**
         * 证书密码
         */
        private String keyPass;
    
        /**
         * 商户私钥
         */
        private String privateKey;
    
        /**
         * 微信支付平台证书 jar包生产的
         */
        private String certificate;
    
    }

    4.启动配置类 

    @Configuration
    public class WxPayV3Config {
    
        @Autowired
        WeChatPayProperties weChatPayProperties;
    
        @Bean
        public CloseableHttpClient wxV3Init() {
            PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(weChatPayProperties.getPrivateKey());
            X509Certificate wechatPayCertificate = PemUtil.loadCertificate(
                    new ByteArrayInputStream(weChatPayProperties.getCertificate().getBytes(StandardCharsets.UTF_8)));
    
            ArrayList<X509Certificate> listCertificates = new ArrayList<>();
            listCertificates.add(wechatPayCertificate);
    
            return WechatPayHttpClientBuilder.create()
                    .withMerchant(weChatPayProperties.getMchId(), weChatPayProperties.getMchSerialNo(), merchantPrivateKey)
                    .withWechatPay(listCertificates)
                    .build();
        }
    }

    5.下单api

    @Slf4j
    @Component
    public class WeChatPayApi {
    
        @Autowired
        WeChatPayProperties weChatPayProperties;
        @Autowired
        WeChatAppletProperties weChatAppletProperties;
        @Autowired
        CloseableHttpClient httpClient;
    
        /**
         * 微信小程序下单的请求地址
         */
        private static final String applet_req_url = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";/**
         * 创建微信小程序订单
         *
         * @param orderNo   订单号
         * @param amount    单位 分
         * @param openid    小程序的openid
         * @param goodsName 订单名称
         * @param notify    通知地址
         * @throws IOException
         */
        public String createWxAppletOrder(String orderNo, Integer amount, String openid, String goodsName, String notify) throws IOException {// 请求body参数
            JSONObject paramObject = new JSONObject();
            JSONObject amountObject = new JSONObject();
            amountObject.put("total", amount);
            amountObject.put("currency", "CNY");
            paramObject.put("amount", amountObject);
            paramObject.put("mchid", weChatPayProperties.getMchId());
            paramObject.put("description", goodsName);
            paramObject.put("notify_url", notify);
    
            JSONObject payerObject = new JSONObject();
            payerObject.put("openid", openid);
            paramObject.put("payer", payerObject);
            paramObject.put("out_trade_no", orderNo);
            paramObject.put("appid", "小程序appid");
    
            HttpPost httpPost = new HttpPost(applet_req_url);
            httpPost.setHeader("Accept", "application/json");
            StringEntity entity = new StringEntity(paramObject.toJSONString(), "utf-8");
            entity.setContentType("application/json");
            log.info("[微信下单请求参数列表]=" + paramObject);
            httpPost.setEntity(entity);
    
            //完成签名并执行请求
            CloseableHttpResponse response = httpClient.execute(httpPost);
            Map<String, Object> resMap = new HashMap<>();
            try {
                int statusCode = response.getStatusLine().getStatusCode();
                Assert.isTrue(statusCode == 200, "微信下单请求失败");
                JSONObject jsonObject = JSON.parseObject(EntityUtils.toString(response.getEntity()));
                String prepayId = jsonObject.getString("prepay_id");
                Assert.isTrue(StringUtils.isNotBlank(prepayId), "下单获取参数失败");
    
                String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
                String nonceStr = RandomUtil.randomString(32).toUpperCase();
                String packagep = "prepay_id=" + prepayId;
    
    
                SortedMap<Object, Object> params = new TreeMap<>();
                params.put("appId", weChatAppletProperties.getAppId());
                params.put("timeStamp", timeStamp);
                params.put("nonceStr", nonceStr);
                params.put("package", packagep);
                params.put("signType", "RSA");
    
                resMap.put("appId", "小程序appid");
                resMap.put("timeStamp", timeStamp);
                resMap.put("nonceStr", nonceStr);
                resMap.put("package", packagep);
                resMap.put("signType", "RSA");
                resMap.put("paySign", WechatPayUtils.createSign(params, weChatPayProperties.getP12Path(), weChatPayProperties.getKeyPass()));
                log.info("[微信支付] 支付参数:" + JSON.toJSONString(resMap));
            } catch (Exception e) {
                throw new BusinessException(e.getMessage());
            } finally {
                response.close();
            }
    
            return JSON.toJSONString(resMap);
        }

    6.微信paySign签名生成 WechatPayUtils类,KeyPairFactory类,WechatRSAUtils类

    public class WechatPayUtils {
    
        /**
         * sign签名
         *
         * @param map
         * @return
         */
        public static String createSign(SortedMap<Object, Object> map, String certPath, String keyPass) throws Exception {
            String signatureStr = Stream.of(
                    String.valueOf(map.get("appId"))
                    , String.valueOf(map.get("timeStamp"))
                    , String.valueOf(map.get("nonceStr"))
                    , String.valueOf(map.get("package"))
            ).collect(Collectors.joining("\n", "", "\n"));
            KeyPair keyPair = KeyPairFactory.createPKCS12(certPath, "Tenpay Certificate", keyPass);
            return WechatRSAUtils.payRequestSign(signatureStr, keyPair);  
    }
    public class KeyPairFactory {
    
        private static KeyStore store;
    
        private static final Object lock = new Object();
    
        /**
         * 获取公私钥.
         *
         * @param keyPath  the key path
         * @param keyAlias the key alias
         * @param keyPass  password
         * @return the key pair
         */
        public static KeyPair createPKCS12(String keyPath, String keyAlias, String keyPass) throws Exception {
    //        ClassPathResource resource = new ClassPathResource(keyPath); //喜欢用相对路径的同学使用这一行
            PathResource resource = new PathResource(keyPath);
    
            char[] pem = keyPass.toCharArray();
            synchronized (lock) {
                if (store == null) {
                    synchronized (lock) {
                        store = KeyStore.getInstance("PKCS12");
                        store.load(resource.getInputStream(), pem);
                    }
                }
            }
            X509Certificate certificate = (X509Certificate) store.getCertificate(keyAlias);
            certificate.checkValidity();
            // 证书的序列号 也有用
            String serialNumber = certificate.getSerialNumber().toString(16).toUpperCase();
            // 证书的 公钥
            PublicKey publicKey = certificate.getPublicKey();
            // 证书的私钥
            PrivateKey storeKey = (PrivateKey) store.getKey(keyAlias, pem);
            return new KeyPair(publicKey, storeKey);
        }
    }
    public class WechatRSAUtils {
      /**
         * 生成支付签名
         *
         * @param signStr
         * @param keyPair
         * @return
         **/
        @SneakyThrows
        public static String payRequestSign(String signStr, KeyPair keyPair) {
            Signature sign = Signature.getInstance("SHA256withRSA");
            sign.initSign(keyPair.getPrivate());
            sign.update(signStr.getBytes(StandardCharsets.UTF_8));
            return Base64Utils.encodeToString(sign.sign());
        }     
    }
  • 相关阅读:
    Generator 函数的异步应用
    Generator 函数的语法
    10 个最佳的 Node.js 的 MVC 框架
    Node.js 框架对比之 Express VS Koa
    JavaScript资源分享
    【转载】Web 研发模式演变
    学习资源整理
    超高影响力开源大放送,学习开发必备教科书
    LVS + KEEPALIVED + WINDOWS SERVER 2008 R2 ------高可用负载均衡(转)
    LVS DR模式 RealServer 为 Windows 2008 R2配置
  • 原文地址:https://www.cnblogs.com/runwithraining/p/15819029.html
Copyright © 2020-2023  润新知