场景:PC端点击下单后,生成二维码返回给前端,用户扫码支付

直接上代码
主要依赖

<!-- 微信开发工具  -->
		<dependency>
			<groupId>com.github.liyiorg</groupId>
			<artifactId>weixin-popular</artifactId>
			<version>2.8.16</version>
		</dependency>
		<dependency>
			<groupId>com.yungouos.pay</groupId>
			<artifactId>yungouos-pay-sdk</artifactId>
			<version>1.1.19</version>
		</dependency>
		<dependency>
			<groupId>com.github.wechatpay-apiv3</groupId>
			<artifactId>wechatpay-apache-httpclient</artifactId>
			<version>0.2.1</version>
		</dependency>

MyConfig类是微信的一些配置类,大家可以用自己的写法

/**
*获取二维码
*/
public PreOrderResult getQRCode(Product product) throws Exception {
        ObjectMapper objectMapper = new ObjectMapper();
        //微信以分为单位,所以要转换一下
        Double i = Double.parseDouble(product.getTotalFee()) * 100;
        int totaltoint = new Double(i).intValue();
        ObjectNode rootNode = objectMapper.createObjectNode();
        rootNode.put("appid", MyConfig.getAppID())
                .put("mchid", MyConfig.getMchID())
                .put("description", product.getSubject())
                .put("out_trade_no", product.getOutTradeNo());
        rootNode.putObject("amount")
                .put("total", totaltoint)
                .put("currency", "CNY");

        // 异步通知地址
        rootNode.put("notify_url", MyConfig.getNotifyUrlV3());
        CloseableHttpResponse response = null;
        try {
        //生成封装请求头
            String Post = myConfigV3.getToken(myConfigV3.getPOST(), HttpUrl.parse(myConfigV3.getPcPAYurl()),
                    MyConfig.getMchID(), MyConfig.getSerialno(), MyConfig.getPrivateKey(), rootNode.toString());
            //请求微信端服务
            response = WeChatV3Util.WeChatPostNative(myConfigV3.getPcPAYurl(), rootNode, Post);
            if (response == null) {
                log.info("请求失败!");
            }
            int statusCode = response.getStatusLine().getStatusCode();
            HttpEntity entity1 = response.getEntity();
            if (statusCode == 200) {
                HttpEntity entity = response.getEntity();
                String boye = EntityUtils.toString(entity);
         
                JSONObject jsonObject = JSONObject.fromObject(boye);
                String code_url = (String) jsonObject.get("code_url");
                return new PreOrderResult(true, "成功获取二维码", code_url, product.getOutTradeNo());
//               return new AsyncResult<String>(boye);
            } else if (statusCode == 204) {
                log.info("支付请求发送成功:  返回代码:+statusCode+ 返回的信息:" + EntityUtils.toString(response.getEntity()));
                return null;
            } else {
                log.info("支付请求发送失败:  错误代码:" + statusCode + " 返回的信息:  " + EntityUtils.toString(response.getEntity()));
                return new PreOrderResult(false, "获取二维码失败", null, product.getOutTradeNo());
            }
        } catch (Exception e) {
            e.printStackTrace();
            return new PreOrderResult(false, "接口异常失败", null, product.getOutTradeNo());
        } finally {
            if (response != null) {
                response.close();
            }
        }

    }

总体的逻辑代码如上,接下来看看关键的几个方法

package com.pay.modules.wxpay.util;

import com.pay.common.util.CommonConstant;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;
import okhttp3.HttpUrl;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.*;
import java.security.cert.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.text.ParseException;
import java.util.Base64;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
@Data
@Component
public class MyConfigV3 {

    static final int TAG_LENGTH_BIT = 128;
    /**微信Native支付请求*/
    private  String PcPAYurl = "https://api.mch.weixin.qq.com/v3/pay/transactions/native";
    private  String query ="https://api.mch.weixin.qq.com/v3/pay/transactions/id/out_trade_no?mchid=Mchid";
    /**请求类型*/
    private  String POST="POST";
    /**
     * 生成组装请求头
     *
     * @param method             请求方式
     * @param url                请求地址
     * @param mercId             商户ID
     * @param serial_no          证书序列号
     * @param privateKeyFilePath 私钥路径
     * @param body               请求体
     * @return 组装请求的数据
     * @throws Exception
     */
    public  String getToken(String method, HttpUrl url, String mercId, String serial_no, String privateKeyFilePath, String body) throws Exception {
        String nonceStr = UUID.randomUUID().toString().replace("-", "");
        long timestamp = System.currentTimeMillis() / 1000;
        String message = buildMessage(method, url, timestamp, nonceStr, body);
        System.out.println("明文: "+message);
        String signature = sign(message.getBytes("UTF-8"), privateKeyFilePath);
        System.out.println("生成的签名:"+signature);
        return "mchid=\"" + mercId + "\","
                + "nonce_str=\"" + nonceStr + "\","
                + "timestamp=\"" + timestamp + "\","
                + "serial_no=\"" + serial_no + "\","
                + "signature=\"" + signature + "\"";
    }

    public  String sign(byte[] message ,String privateKeyFilePath) throws Exception {
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(getPrivateKey(privateKeyFilePath));
        sign.update(message);

        return Base64.getEncoder().encodeToString(sign.sign());
    }

    public  String buildMessage(String method, HttpUrl url, long timestamp, String nonceStr, String body) {
        String canonicalUrl = url.encodedPath();
        if (url.encodedQuery() != null) {
            canonicalUrl += "?" + url.encodedQuery();
        }
        return method + "\n"
                + canonicalUrl + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                + body + "\n";
    }

    /**
     * 获取私钥。
     *
     * @param filename 私钥文件路径  (required)
     * @return 私钥对象
     */
    public PrivateKey getPrivateKey(String filename) throws IOException {
        String s="";
        String configContentStr = "";
        try {
            InputStream is = this.getClass().getResourceAsStream(filename);
            BufferedReader br = new BufferedReader(new InputStreamReader(is));

            while((s=br.readLine())!=null) {
                configContentStr = configContentStr+s;
            }
            String privateKey = configContentStr.replace("-----BEGIN PRIVATE KEY-----", "")
                    .replace("-----END PRIVATE KEY-----", "")
                    .replaceAll("\\s+", "");
            KeyFactory kf = KeyFactory.getInstance("RSA");
            return kf.generatePrivate(
                    new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持RSA", e);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException("无效的密钥格式");
        }
     }
    public static   X509Certificate getCertificate(InputStream fis) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(fis);
        try {
            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);
        } finally {
            bis.close();
        }
    }
}
//                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                

回调方法,如果用户支付成功后,微信服务端会以起请求到我们的后台(前面填的异步通知地址),具体方法如下,这里会有一个验签操作,校验是不是微信端发来的,防止恶意请求。

 /**
     * Native支付 微信支付成功回调 ojj
     * 通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m
     * @param response
     * @param request
     * @throws Exception
     */
    @ApiOperation(value = "微信支付成功回调")
    @PostMapping("/notifyurl")
    public void notifyurl(HttpServletResponse response, HttpServletRequest request) throws Exception {
        //微信返回的证书序列号
        String serialNo = request.getHeader("Wechatpay-Serial");

        //微信返回的随机字符串
        String nonceStr = request.getHeader("Wechatpay-Nonce");

        //微信返回的时间戳
        String timestamp = request.getHeader("Wechatpay-Timestamp");

        //微信返回的签名
        String wechatSign = request.getHeader("Wechatpay-Signature");

        Map<String, String> map = new HashMap<>(12);
        try{
            String result = WeChatV3Util.readData(request);
            log.info("回调的信息" +result);
            if (WeChatV3Util.verifiedSign(request,result)){
                //验证通过,做你的业务
        
            }else {
            
                response.setStatus(500);
                map.put("code", "ERROR");
                map.put("message", "签名错误");
            }
        }catch (Exception e){
            e.printStackTrace();
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
        response.setHeader("Content-type", ContentType.JSON.toString());
        response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
        response.flushBuffer();
    }

版权声明:本文为weixin_46164384原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_46164384/article/details/121378370