1.申请商户获取参数或文件

微信小程序appid:xxxx
商户API证书私钥文件:xxxx(微信支付)
商户号:xxxx (微信支付)
商户API证书序列号:xxxx (微信支付)
商户API v2密钥:xxxx(自定义)
商户API v3密钥:xxxx(自定义)
微信平台证书文件:xxxx (自己生成或自动生成)
微信小程序开发密钥:xxxx(小程序)

平台证书不是API证书:需5年更换一次
下载jar包https://github.com/wechatpay-apiv3/CertificateDownloader
https://github.com/wechatpay-apiv3/CertificateDownloader/releases 2.application.properties配置

wechat.pay.app_id= xxxx # 微信小程序appid
wechat.pay.private_key_path=xxxx.pem #商户API证书私钥文件路径
wechat.pay.merchant_id= xxxx #商户号
wechat.pay.merchant_serial_number= xxxx #商户API证书序列号
wechat.pay.api_v2_key= xxxx #商户API v2密钥
wechat.pay.api_v3_key= xxxx #商户API v3密钥
wechat.pay.wechat_certificate_path= xxxx.pem #微信平台证书文件路径
wechat.pay.app_select=xxxx #微信小程序开发密钥
wechat.pay.notify_url=https://xxxxx.com:port/succeed.atcion#支付成功回调 https开头

3.maven pom

<dependency>
    <groupId>com.github.wechatpay-apiv3</groupId>
     <artifactId>wechatpay-apache-httpclient</artifactId>
     <version>0.4.8</version>
 </dependency>
 <dependency>
     <groupId>commons-httpclient</groupId>
     <artifactId>commons-httpclient</artifactId>
     <version>3.1</version>
 </dependency>

4.微信支付属性配置

package com.mt.applets.config;

import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

import java.io.*;
import java.security.PrivateKey;
import java.security.cert.*;

@Data
@Component
@PropertySource("classpath:application.properties")
public class WxPayProperties {
    /**
     *小程序appId
     */
    @Value("${wechat.pay.app_id}")
    private String appId;
    /**
     * 私钥
     */
    @Value("${wechat.pay.private_key_path}")
    private String privateKeyPath;
    /**
     * 商户号
      */
    @Value("${wechat.pay.merchant_id}")
    private String merchantId;
    /**
     * 商户证书序列号
     * */
    @Value("${wechat.pay.merchant_serial_number}")
    private String merchantSerialNumber;
    /**
     * apiV3密钥
     */
    @Value("${wechat.pay.api_v3_key}")
    private String apiV3Key;
    /**
     * 平台证书路径
     */
    @Value("${wechat.pay.wechat_certificate_path}")
    private String wechatCertificatePath;
    /**
     * 回调路径
     */
    @Value("${wechat.pay.notify_url}")
    private String notifyUrl;

    /**
     * App密钥
     */
    @Value("${wechat.pay.app_select}")
    private String appSelect;

    /**
     * @Decription 获取商户的私钥
     * @Return  java.security.PrivateKey
     */
    public PrivateKey getPrivateKey() {
        try {
            return PemUtil.loadPrivateKey(
                    new FileInputStream(this.privateKeyPath));
        } catch (FileNotFoundException e) {
            //抛出异常,并把错误文件继续向上抛出
            throw new RuntimeException("私钥文件不存在", e);
        }
    }
    public X509Certificate getCertificate() throws IOException {
        InputStream fis = new FileInputStream(this.wechatCertificatePath);
        try (BufferedInputStream bis = new BufferedInputStream(fis)) {
            CertificateFactory cf = CertificateFactory.getInstance("X509");
            X509Certificate cert = (X509Certificate) cf.generateCertificate(bis);
            cert.checkValidity();
            return cert;
        } catch (CertificateExpiredException e) {
            throw new RuntimeException("证书已过期", e);
        } catch (CertificateNotYetValidException e) {
            throw new RuntimeException("证书尚未生效", e);
        } catch (CertificateException e) {
            throw new RuntimeException("无效的证书文件", e);
        }
    }

}

5.支付相关业务,非自身业务

package com.mt.applets.service.impl;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.mt.api.dto.OrderPayInfo;
import com.mt.api.util.StringUtils;
import com.mt.applets.config.WxPayProperties;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.*;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.exception.HttpCodeException;
import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException;
import com.wechat.pay.contrib.apache.httpclient.notification.Notification;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationHandler;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationRequest;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;

import javax.annotation.PostConstruct;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.cert.X509Certificate;
import java.util.Base64;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
public class PayServiceImpl {

    @Autowired
    private WxPayProperties wxPayProperties;

    private Verifier verifier;

    private CloseableHttpClient httpClient;

    private final Logger logger= LoggerFactory.getLogger(PayServiceImpl.class);

    @PostConstruct
    private void init() throws IOException, HttpCodeException, GeneralSecurityException, NotFoundException {
        String merchantId= wxPayProperties.getMerchantId();
        String merchantSerialNumber= wxPayProperties.getMerchantSerialNumber();
        PrivateKey merchantPrivateKey =wxPayProperties.getPrivateKey();
        String apiV3Key =wxPayProperties.getApiV3Key();
        // 平台证书管理器
        CertificatesManager certificatesManager = CertificatesManager.getInstance();
        // 向证书管理器增加需要自动更新平台证书的商户信息
        certificatesManager.putMerchant(merchantId, new WechatPay2Credentials(merchantId,
                        new PrivateKeySigner(merchantSerialNumber, merchantPrivateKey)),
                apiV3Key.getBytes(StandardCharsets.UTF_8));
        // 从证书管理器中获取verifier
        verifier = certificatesManager.getVerifier(merchantId);
        // 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(merchantId, merchantSerialNumber, merchantPrivateKey)
                .withValidator(new WechatPay2Validator(verifier));
        httpClient = builder.build();
    }

    /**
     *  签名
     */
    public String sign(byte[] message) throws SignatureException, NoSuchAlgorithmException, InvalidKeyException {
        Signature sign = Signature.getInstance("SHA256withRSA");
        PrivateKey merchantPrivateKey = wxPayProperties.getPrivateKey();
        sign.initSign(merchantPrivateKey);
        sign.update(message);
        return Base64.getEncoder().encodeToString(sign.sign());
    }

    //创建订单,返回预处理订单
    public CloseableHttpResponse createPayOrder(OrderPayInfo orderPayInfo) throws IOException {
        // 请求body参数
        String billNo= orderPayInfo.getBillNo();
        Integer price= orderPayInfo.getPrice();
        String description= orderPayInfo.getDesc();
        String openId= orderPayInfo.getOpenId();
        String merchantId = wxPayProperties.getMerchantId();
        String appId = wxPayProperties.getAppId();
        String notifyUrl = wxPayProperties.getNotifyUrl();
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi");
        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",merchantId)
                .put("appid", appId)
                .put("description", description)
                .put("notify_url", notifyUrl)
                .put("out_trade_no", billNo);
        rootNode.putObject("amount")
                .put("total", price);
        rootNode.putObject("payer")
                .put("openid", openId);

        objectMapper.writeValue(bos, rootNode);

        httpPost.setEntity(new StringEntity(bos.toString(StandardCharsets.UTF_8), "UTF-8"));
        CloseableHttpResponse response = httpClient.execute(httpPost);
        String bodyAsString = EntityUtils.toString(response.getEntity());
        logger.info(bodyAsString);
        response.close();
        return response;
    }

    //回调解析,获取支付回调数据
    public String notificationHandler(String nonce,String timestamp,String signature,String body) throws Exception {
        X509Certificate certificate = wxPayProperties.getCertificate();
        String serialNo = certificate.getSerialNumber().toString(16).toUpperCase();
        String apiV3Key = wxPayProperties.getApiV3Key();
        // 构建request,传入必要参数
        NotificationRequest request = new NotificationRequest.Builder().withSerialNumber(serialNo)
                .withNonce(nonce)
                .withTimestamp(timestamp)
                .withSignature(signature)
                .withBody(body)
                .build();
        NotificationHandler handler = new NotificationHandler(verifier, apiV3Key.getBytes(StandardCharsets.UTF_8));
        // 验签和解析请求体
        Notification notification = handler.parse(request);
        // 从notification中获取解密报文
        return notification.getDecryptData();
    }

    //获取小程序登录者的appID
    public String getOpenId(String code) {
        String secret=wxPayProperties.getAppSelect();
        String appId=wxPayProperties.getAppId();
        //appId和secret是开发者分别是小程序ID和小程序密钥,开发者通过微信公众平台-》设置-》开发设置就可以直接获取,
        String url="https://api.weixin.qq.com/sns/jscode2session?appid="
                +appId+"&secret="+secret+"&js_code="+code+"&grant_type=authorization_code";
        try{
            String res = null;
            CloseableHttpClient httpClient = HttpClientBuilder.create().build();
            HttpGet httpget = new HttpGet(url);
            CloseableHttpResponse response;
            // 配置信息
            RequestConfig requestConfig = RequestConfig.custom()
                    // 设置连接超时时间(单位毫秒)
                    .setConnectTimeout(5000)
                    // 设置请求超时时间(单位毫秒)
                    .setConnectionRequestTimeout(5000)
                    // socket读写超时时间(单位毫秒)
                    .setSocketTimeout(5000)
                    // 设置是否允许重定向(默认为true)
                    .setRedirectsEnabled(false).build();
                    // 将上面的配置信息 运用到这个Get请求里
            httpget.setConfig(requestConfig);
            // 由客户端执行(发送)Get请求
            response = httpClient.execute(httpget);
            // 从响应模型中获取响应实体
            HttpEntity responseEntity = response.getEntity();
            if (responseEntity != null) {
                res = EntityUtils.toString(responseEntity);
                logger.info(res);
            }
            // 释放资源
            httpClient.close();
            response.close();
            JSONObject jo = (JSONObject) JSONUtil.parse(res);
            if(jo.get("openid")==null||StringUtils.isEmpty((String) jo.get("openid"))){
                throw new RuntimeException("获取openId失败");
            }
            return (String) jo.get("openid");
        }catch (Exception e) {
            throw new RuntimeException("获取openId失败", e);
        }
    }
}

6.调用试例(非完整)

//调用支付试例,返回给前端调起支付
public WeChatPayResult generateOrderToPay(String desc,Integer price,String billNo,String code) throws IOException {
        OrderPayInfo orderPayInfo = new OrderPayInfo();
        orderPayInfo.setDesc(desc);
        orderPayInfo.setPrice(price);
        orderPayInfo.setBillNo(billNo);
        String openId=payService.getOpenId(code);
        orderPayInfo.setOpenId(openId);
        try (CloseableHttpResponse response = payService.createPayOrder(orderPayInfo)) {
            String appId = wxPayProperties.getAppId();
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == 200 || statusCode == 204) {
                String entity = EntityUtils.toString(response.getEntity());
                JSONObject parse = JSONUtil.parseObj(entity);
                Object prepayIdObj = parse.get("prepay_id");
                String prepayId = String.valueOf(prepayIdObj);
                WeChatPayResult weChatPayResult = new WeChatPayResult();
                weChatPayResult.setPrepayId(prepayId);
                String nonceStr = StringUtils.getUUID(20);
                weChatPayResult.setNonceStr(nonceStr);
                long timestamp = System.currentTimeMillis() / 1000;
                weChatPayResult.setTimeStamp(timestamp);
                weChatPayResult.setSignType("RSA");
                String str = appId + "\n" +
                        timestamp + "\n" +
                        nonceStr + "\n" +
                        "prepay_id=" + prepayId +
                        "\n";
                String sign = payService.sign(str.getBytes(StandardCharsets.UTF_8));
                weChatPayResult.setPaySign(sign);
                weChatPayResult.setAppId(appId);
                return weChatPayResult;
            } else {
                logger.error(EntityUtils.toString(response.getEntity()));
            }
        } catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException e) {
            e.printStackTrace();
        }
        return null;
    }
    //回调试例
    public boolean confirmOrder(HttpServletRequest httpServletRequest) {
        String serial = httpServletRequest.getHeader("Wechatpay-Serial");
        String nonceStr = httpServletRequest.getHeader("Wechatpay-Nonce");
        String timestamp = httpServletRequest.getHeader("Wechatpay-Timestamp");
        String signature = httpServletRequest.getHeader("Wechatpay-Signature");
        if(StringUtils.isAnyEmpty(signature,nonceStr,timestamp,serial)){
            logger.error("微信回调请求头参数缺失");
            return false;
        }
        try {
            String bodyStr = this.getBodyStr(httpServletRequest);
            String result = payService.notificationHandler(nonceStr, timestamp, signature, bodyStr);
            JSONObject jsonObject = JSONUtil.parseObj(result);
            if("SUCCESS".equals(jsonObject.get("trade_state"))){
                Object successTime = jsonObject.get("success_time");
                JSONObject payer = (JSONObject)jsonObject.get("payer");
                Object openid = payer.get("openid");
                Object outTradeNo = jsonObject.get("out_trade_no");
                //自身业务
            }else{
                logger.error("支付失败");
            }
        }catch (Exception e){
            logger.error(e.getMessage());
            return false;
        }
        return true;
    }
    //获取请求体串
    public String getBodyStr(HttpServletRequest request) throws IOException {
        BufferedReader br = request.getReader();
        String str;
        StringBuilder wholeStr = new StringBuilder();
        while((str = br.readLine()) != null){
            wholeStr.append(str);
        }
        return wholeStr.toString();
    }