使用微信支付v3实现jsapi和扫码支付

本文详细介绍了微信支付V3接口的更新内容,包括数据交互格式改用JSON,接口请求头新增Authorization,接口签名及Token计算,支付回调的AES-256-GCM加密,扫码支付流程简化等。并提供了Java代码示例,涵盖了Token生成、HTTPS请求、回调解密、签名计算、公钥获取和解密响应体等关键步骤。同时展示了JSAPI和扫码支付的实现代码,以及回调逻辑处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一 和旧版的差异

1 数据交互格式由原先的xml改为json

2 接口请求方式由原来的无需额外请求头变为需要Authorization请求头

3 几乎所有接口都需要使用证书计算签名或者Token

4 支付回调使用AES-256-GCM进行加密,安全性提高

5 扫码支付无需再设置扫码回调url

6 无需像之前一样调用长链接转短链接接口提高扫码支付识别成功率

总体来说变化还是挺大的,开发时只需要正确计算token和签名就没什么问题了

开发前必读:https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay-1.shtml

二 分步拆解技术难点

1 token的计算,这一步算是最简单的只需按文档写工具代码即可

public class WxPayTokenUtilV3 {
    /**
     * 生成Token.
     *
     * @param mchId     商户号
     * @param nonceStr  随机字符串
     * @param timestamp 时间戳
     * @param serialNo  证书序列号
     * @param signature 签名
     * @return the string
     */
    public static String token(String mchId, String nonceStr, long timestamp, String serialNo, String signature) {
        String TOKEN_PATTERN = "mchid=\"%s\",nonce_str=\"%s\",timestamp=\"%d\",serial_no=\"%s\",signature=\"%s\"";
        // 生成token
        return String.format(TOKEN_PATTERN, mchId, nonceStr, timestamp, serialNo, signature);
    }

}

2 封装支付接口请求方法

 /**
     * 微信支付发送Https请求通用方法
     *
     * @param requestUrl
     * @param requestMethod
     * @param outputStr
     * @param token
     * @return
     */
    public static JSONObject httpsRequestForWxPay(String requestUrl, String requestMethod, String outputStr, String token) {

        JSONObject jsonObject = null;
        StringBuffer buffer = new StringBuffer();
        try {
            // 创建SSLContext对象,并使用我们指定的信任管理器初始化
            TrustManager[] tm = {new MyX509TrustManager()};
            SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
            sslContext.init(null, tm, new java.security.SecureRandom());
            // 从上述SSLContext对象中得到SSLSocketFactory对象
            SSLSocketFactory ssf = sslContext.getSocketFactory();

            URL url = new URL(requestUrl);
            HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection();
            httpUrlConn.setSSLSocketFactory(ssf);

            httpUrlConn.setDoOutput(true);
            httpUrlConn.setDoInput(true);
            httpUrlConn.setUseCaches(false);
            // 设置请求方式(GET/POST)
            httpUrlConn.setRequestMethod(requestMethod);
            httpUrlConn.setRequestProperty("Content-type", "application/json");
            httpUrlConn.setRequestProperty("Authorization", " WECHATPAY2-SHA256-RSA2048 " + token);

            if ("GET".equalsIgnoreCase(requestMethod)) {
                httpUrlConn.connect();
            }

            // 当有数据需要提交时
            if (null != outputStr) {
                OutputStream outputStream = httpUrlConn.getOutputStream();
                // 注意编码格式,防止中文乱码
                outputStream.write(outputStr.getBytes("UTF-8"));
                outputStream.close();
            }

            // 将返回的输入流转换成字符串
            InputStream inputStream = httpUrlConn.getInputStream();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

            String str = null;
            while ((str = bufferedReader.readLine()) != null) {
                buffer.append(str);
            }
            bufferedReader.close();
            inputStreamReader.close();
            // 释放资源
            inputStream.close();
            inputStream = null;
            httpUrlConn.disconnect();

            jsonObject = JSONObject.parseObject(buffer.toString());
        } catch (ConnectException ce) {
            ce.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return jsonObject;
    }

本段代码可以根据自己的需要改为使用httpclient或者其他形式,本人习惯使用java自带的这套

import javax.net.ssl.X509TrustManager;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

public class MyX509TrustManager implements X509TrustManager {

    public void checkClientTrusted(X509Certificate[] chain, String authType)
            throws CertificateException {
    }

    public void checkServerTrusted(X509Certificate[] chain, String authType)
            throws CertificateException {
    }

    public X509Certificate[] getAcceptedIssuers() {
        return null;
    }
}

3 封装回调请求数据解密代码

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

public class WxPayAesUtilV3 {

    static final int KEY_LENGTH_BYTE = 32;
    static final int TAG_LENGTH_BIT = 128;
    private final byte[] aesKey;

    public WxPayAesUtilV3(byte[] key) {
        if (key.length != KEY_LENGTH_BYTE) {
            throw new IllegalArgumentException("无效的ApiV3Key,长度必须为32个字节");
        }
        this.aesKey = key;
    }

    public String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext)
            throws GeneralSecurityException, IOException {
        try {
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

            SecretKeySpec key = new SecretKeySpec(aesKey, "AES");
            GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce);

            cipher.init(Cipher.DECRYPT_MODE, key, spec);
            cipher.updateAAD(associatedData);

            return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), "utf-8");
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new IllegalStateException(e);
        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
            throw new IllegalArgumentException(e);
        }
    }
}

4 主要工具类的封装,此工具类主要实现签名计算、公钥获取、解密响应体等

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.lg.wx.consts.ConstantsWx;
import com.lg.wx.pojo.SignVerify;

import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Base64;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 微信V3支付签名工具类
 */
public class WxPaySignUtilV3 extends NoncestrUtil {

    /**
     * V3  SHA256withRSA 签名.
     *
     * @param method       请求方法  GET  POST PUT DELETE 等
     * @param canonicalUrl 例如  https://api.mch.weixin.qq.com/v3/pay/transactions/app?version=1 ——> /v3/pay/transactions/app?version=1
     * @param timestamp    当前时间戳   因为要配置到TOKEN 中所以 签名中的要跟TOKEN 保持一致
     * @param nonceStr     随机字符串  要和TOKEN中的保持一致
     * @param body         请求体 GET 为 "" POST 为JSON
     * @param keyPair      商户API 证书解析的密钥对  实际使用的是其中的私钥
     * @return the string
     */

    public static String sign(String method, String canonicalUrl, long timestamp, String nonceStr, String body, KeyPair keyPair) throws Exception {
        String signatureStr = Stream.of(method, canonicalUrl, String.valueOf(timestamp), nonceStr, body)
                .collect(Collectors.joining("\n", "", "\n"));

        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(keyPair.getPrivate());
        sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
        return new String(Base64.getEncoder().encode(sign.sign()));
    }

    /**
     * jsapi计算签名
     *
     * @param appId
     * @param timestamp
     * @param nonceStr
     * @param prepayId
     * @param keyPair
     * @return
     * @throws Exception
     */
    public static String signForJSApi(String appId, long timestamp, String nonceStr, String prepayId, KeyPair keyPair) throws Exception {
        String signatureStr = Stream.of(appId, String.valueOf(timestamp), nonceStr, prepayId)
                .collect(Collectors.joining("\n", "", "\n"));

        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(keyPair.getPrivate());
        sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
        return new String(Base64.getEncoder().encode(sign.sign()));
    }

    /**
     * 回调验签
     *
     * @param signVerify
     * @return
     * @throws Exception
     * @para
     */
    public static boolean responseSignVerify(SignVerify signVerify) throws Exception {

        //获取最新的微信支付平台公钥
        X509Certificate certificate = refreshCertificate(signVerify);

        String signatureStr = responseSign(signVerify);
        Signature signer = Signature.getInstance("SHA256withRSA");
        signer.initVerify(certificate);
        signer.update(signatureStr.getBytes(StandardCharsets.UTF_8));

        boolean flag = signer.verify(Base64.getDecoder().decode(signVerify.getWechatpaySignature()));

        return flag;
    }

    /**
     * 构造验签名串.
     *
     * @param signVerify
     * @return the string
     */
    public static String responseSign(SignVerify signVerify) {
        return Stream.of(signVerify.getWechatpayTimestamp(), signVerify.getWechatpayNonce(), signVerify.getBody())
                .collect(Collectors.joining("\n", "", "\n"));
    }

    /**
     * 解密响应体-取得公钥
     *
     * @param apiV3Key       API V3 KEY  API v3密钥 商户平台设置的32位字符串
     * @param associatedData response.body.data[i].encrypt_certificate.associated_data
     * @param nonce          response.body.data[i].encrypt_certificate.nonce
     * @param ciphertext     response.body.data[i].encrypt_certificate.ciphertext
     * @return the string
     * @throws GeneralSecurityException the general security exception
     */
    public static String decryptResponseBody(String apiV3Key, String associatedData, String nonce, String ciphertext) {
        try {
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

            SecretKeySpec key = new SecretKeySpec(apiV3Key.getBytes(StandardCharsets.UTF_8), "AES");
            GCMParameterSpec spec = new GCMParameterSpec(128, nonce.getBytes(StandardCharsets.UTF_8));

            cipher.init(Cipher.DECRYPT_MODE, key, spec);
            cipher.updateAAD(associatedData.getBytes(StandardCharsets.UTF_8));

            byte[] bytes;
            try {

                bytes = cipher.doFinal(Base64.getDecoder().decode(ciphertext));
            } catch (GeneralSecurityException e) {
                throw new IllegalArgumentException(e);
            }
            return new String(bytes, StandardCharsets.UTF_8);
        } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw new IllegalStateException(e);
        } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
            throw new IllegalArgumentException(e);
        }
    }

    /**
     * 网络请求获取微信支付平台公钥
     *
     * @param pair
     * @return
     */
    public static JSONObject getPlatformPubKey(KeyPair pair, String serialNo, String mchId) {

        String nonceStr = createNoncestr();
        long timeStamp = System.currentTimeMillis() / 1000;

        String signStr = "";
        try {
            signStr = sign("GET", "/v3/certificates", timeStamp, nonceStr, "", pair);
        } catch (NumberFormatException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }

        WxPayTokenUtilV3 tk = new WxPayTokenUtilV3();
        String resToken = tk.token(mchId, nonceStr, timeStamp, serialNo, signStr);

        HttpArgs args = HttpArgs.builder(ConstantsWx.WX_CERTIFICATES_URL).method("GET").outputStr(null).build();
        JSONObject result = AdvancedUtil.httpsGetBeanForPay(args, JSONObject.class, resToken);


        return result;
    }

    /**
     * 获取最新的微信支付平台公钥并封装成证书
     *
     * @param signVerify
     * @return
     * @throws Exception
     */
    public static X509Certificate refreshCertificate(SignVerify signVerify) throws Exception {


        KeyPairFactory kp = new KeyPairFactory();
        KeyPair keyPair = kp.createPKCS12(signVerify.getCertUrl(), "Tenpay Certificate", signVerify.getMchId());

        //获取公钥信息
        JSONObject json = getPlatformPubKey(keyPair, signVerify.getSerialNo(), signVerify.getMchId());

        JSONArray arr = json.getJSONArray("data");
        JSONObject child = arr.getJSONObject(0).getJSONObject("encrypt_certificate");
        //associated_data
        String associatedData = child.getString("associated_data");
        //nonce
        String nonce = child.getString("nonce");
        //ciphertext
        String ciphertext = child.getString("ciphertext");

        String publicKey = decryptResponseBody(signVerify.getApiV3Key(), associatedData, nonce, ciphertext);

        CertificateFactory cf = null;
        try {
            cf = CertificateFactory.getInstance("X509");
        } catch (CertificateException e1) {
            e1.printStackTrace();
        }

        ByteArrayInputStream inputStream = new ByteArrayInputStream(publicKey.getBytes(StandardCharsets.UTF_8));
        X509Certificate certificate = null;
        try {
            certificate = (X509Certificate) cf.generateCertificate(inputStream);

        } catch (CertificateException e) {
            e.printStackTrace();
        }

        return certificate;

    }


}

5 JSAPI和扫码支付具体实现代码封装

import com.alibaba.fastjson.JSONObject;
import com.lg.wx.consts.ConstantsWx;
import com.lg.wx.pojo.JsApiOrder;
import com.lg.wx.pojo.RefundsInfo;

import java.security.KeyPair;
import java.util.Date;

/**
 * 微信支付v3版本工具类
 *
 * @author chenruiyin
 * @version 1.0
 */
public class WxPayUtilV3 extends NoncestrUtil {
    /**
     * jsapi下单并取得支付json串
     *
     * @param order
     * @return
     * @throws Exception
     */
    public static JSONObject jsApiCreateOrder(JsApiOrder order) throws Exception {
        JSONObject json = new JSONObject();
        json.put("appid", order.getAppId());
        json.put("mchid", order.getMchId());
        json.put("description", order.getDescription());
        json.put("out_trade_no", order.getOutTradeNo());
        json.put("notify_url", order.getNotifyUrl());

        JSONObject childs = new JSONObject();
        childs.put("total", order.getTotalPrice());
        json.put("amount", childs);
        JSONObject childs1 = new JSONObject();
        childs1.put("openid", order.getOpenId());
        json.put("payer", childs1);
        json.put("attach", order.getAttach());


        KeyPairFactory kp = new KeyPairFactory();
        KeyPair res = kp.createPKCS12(order.getCertUrl(), ConstantsWx.KEY_ALIAS, order.getMchId());

        String nonceStr = WxPaySignUtilV3.createNoncestr();
        long timeStamp = System.currentTimeMillis() / 1000;

        //WxPaySignUtilV3 ts=new WxPaySignUtilV3();
        String signStr = "";
        try {
            signStr = WxPaySignUtilV3.sign("POST", "/v3/pay/transactions/jsapi", timeStamp, nonceStr, json.toString(), res);
        } catch (Exception e) {
            e.printStackTrace();
        }

        WxPayTokenUtilV3 tk = new WxPayTokenUtilV3();
        //计算下单token
        String resToken = tk.token(order.getMchId(), nonceStr, timeStamp, order.getSerialNo(), signStr);
        //调用微信下单接口
        HttpArgs args = HttpArgs.builder(ConstantsWx.TRANSACTIONS_JSAPI_URL).method("POST").outputStr(json.toString()).build();
        JSONObject result = AdvancedUtil.httpsGetBeanForPay(args, JSONObject.class, resToken);

        //计算签名
        long timeStamp1 = new Date().getTime();
        String nonceStr1 = WxPaySignUtilV3.createNoncestr();
        String paySign = "";
        try {
            paySign = WxPaySignUtilV3.signForJSApi(order.getAppId(), timeStamp1, nonceStr1, "prepay_id=" + result.getString("prepay_id"), res);
        } catch (Exception e) {
            e.printStackTrace();
        }

        JSONObject payJson = new JSONObject();
        payJson.put("appId", order.getAppId());
        payJson.put("timeStamp", String.valueOf(timeStamp1));
        payJson.put("nonceStr", nonceStr1);
        payJson.put("package", "prepay_id=" + result.getString("prepay_id"));
        payJson.put("signType", "RSA");
        payJson.put("paySign", paySign);

        return payJson;
    }


    /**
     * 微信支付申请退款
     *
     * @param refund
     * @return
     * @throws Exception
     */
    public static boolean refundsMoney(RefundsInfo refund) throws Exception {
        JSONObject json = new JSONObject();
        json.put("out_trade_no", refund.getOutTradeNo());
        json.put("out_refund_no", String.valueOf(new Date().getTime()));

        JSONObject child = new JSONObject();
        child.put("refund", refund.getRefund());
        child.put("total", refund.getTotal());
        child.put("currency", refund.getCurrency());

        json.put("amount", child);

        KeyPairFactory kp = new KeyPairFactory();
        KeyPair res = kp.createPKCS12(refund.getCertUrl(), ConstantsWx.KEY_ALIAS, refund.getMchId());

        long timeStamp = System.currentTimeMillis() / 1000;

        String signStr = "";
        String nonceStr = createNoncestr();
        try {
            signStr = WxPaySignUtilV3.sign("POST", "/v3/refund/domestic/refunds", timeStamp, nonceStr, json.toString(), res);
        } catch (Exception e) {
            e.printStackTrace();
        }


        WxPayTokenUtilV3 tk = new WxPayTokenUtilV3();
        //计算下单token
        String resToken = tk.token(refund.getMchId(), nonceStr, timeStamp, refund.getSerialNo(), signStr);

        JSONObject result = CommonUtil.httpsRequestForWxPay(ConstantsWx.REFUNDS_API_URL, "POST", json.toJSONString(), resToken);

        if (null != result) {
            return true;
        }

        return false;
    }

    /**
     * native扫码支付
     * @param order
     * @return
     * @throws Exception
     */
    public static String nativeCreateOrder(JsApiOrder order) throws Exception {
        JSONObject json = new JSONObject();
        json.put("appid", order.getAppId());
        json.put("mchid", order.getMchId());
        json.put("description", order.getDescription());
        json.put("out_trade_no", order.getOutTradeNo());
        json.put("notify_url", order.getNotifyUrl());

        JSONObject childs = new JSONObject();
        childs.put("total", order.getTotalPrice());
        json.put("amount", childs);


        KeyPairFactory kp = new KeyPairFactory();
        KeyPair res = kp.createPKCS12(order.getCertUrl(), ConstantsWx.KEY_ALIAS, order.getMchId());

        String nonceStr = WxPaySignUtilV3.createNoncestr();
        long timeStamp = System.currentTimeMillis() / 1000;

        //WxPaySignUtilV3 ts=new WxPaySignUtilV3();
        String signStr = "";
        try {
            signStr = WxPaySignUtilV3.sign("POST", "/v3/pay/transactions/native", timeStamp, nonceStr, json.toString(), res);
        } catch (Exception e) {
            e.printStackTrace();
        }

        WxPayTokenUtilV3 tk = new WxPayTokenUtilV3();
        //计算下单token
        String resToken = tk.token(order.getMchId(), nonceStr, timeStamp, order.getSerialNo(), signStr);
        //调用微信下单接口
        HttpArgs args = HttpArgs.builder(ConstantsWx.TRANSACTIONS_NATIVEAPI_URL).method("POST").outputStr(json.toString()).build();
        JSONObject result = AdvancedUtil.httpsGetBeanForPay(args, JSONObject.class, resToken);
        //System.out.println(result);
        String codeUrl = result.getString("code_url");
        return codeUrl;
    }

JsApiOrder是支付重要的实体类,里面主要有调用支付接口需要的各种字段,封装起来目的是方便使用

import lombok.Data;

/**
 * TODO
 *
 * @author Administrator
 * @version 1.0
 * @date 2021/3/30 15:02
 */
@Data
public class JsApiOrder {
    /**
     * appId
     */
    private String appId;
    /**
     * 商户号
     */
    private String mchId;
    /**
     * 订单描述
     */
    private String description;
    /**
     * 订单号
     */
    private String outTradeNo;
    /**
     * 支付成功回调
     */
    private String notifyUrl;
    /**
     * 订单金额(单位为分)
     */
    private Integer totalPrice;
    /**
     * 支付用户openId
     */
    private String openId;
    /**
     * 诊所ID
     */
    private Long clinicId;
    /**
     * 支付p12证书url
     */
    private String certUrl;
    /**
     * 支付证书序列号
     */
    private String serialNo;

    /**
     * 额外参数
     */
    private String attach;

}

AdvancedUtil这个工具类则是把调用支付接口返回的数据二次封装,这一步可有可无,代码大致如下

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.Builder;

@Builder
public class AdvancedUtil {

    private String method = "get";
    private String url;

    /**
     * 通过https请求获取返回json的数据,并把数据格式化为相应的bean
     * @param httpArgs 请求参数列表使用方式  HttpArgs.builder().url("xxxurl").method("post").build()
     * @param clazz    需要构建的数据对象
     */
    public static <T> T httpsGetBean(HttpArgs httpArgs, Class<T> clazz) {
        JSONObject jsonObject = CommonUtil.httpsRequest(httpArgs.getUrl(), httpArgs.getMethod(), httpArgs.getOutputStr());
        //System.out.println("请求链接:"+httpArgs.getUrl());
        //System.out.println("返回结果:"+jsonObject.toString());
        return JSON.toJavaObject(jsonObject, clazz);
    }

    /**
     * 通过https请求获取返回json的数据,并把数据格式化为相应的bean
     *
     * @param httpArgs 请求参数列表使用方式  HttpArgs.builder().url("xxxurl").method("post").build()
     * @param clazz    需要构建的数据对象
     * @return
     * @Date 2021/03/13 15:32
     */
    public static <T> T httpsGetBeanForPay(HttpArgs httpArgs, Class<T> clazz, String token) {
        JSONObject jsonObject = CommonUtil.httpsRequestForWxPay(httpArgs.getUrl(), httpArgs.getMethod(), httpArgs.getOutputStr(), token);
        return JSON.toJavaObject(jsonObject, clazz);
    }


}
HttpArgs这个把代码调用改为连缀的形式,也可根据喜好选择用或者不用
import lombok.Builder;
import lombok.Data;

import java.util.Map;

/**
 * @description: http接口请求参数, 构建请求参数
 **/
@Data
public class HttpArgs {

    public final static String GET = "GET";

    public final static String POST = "POST";

    private final static String UTF8 = "UTF-8";

    private final static String GBK = "GBK";

    private final static String DEFAULT_MEDIA_TYPE = "application/json";


    private String url;

    @Builder.Default
    private String method = GET;

    private String data;

    private Map<String, Object> queryMap;

    private String outputStr;

    HttpArgs(final String url, final String method, final String data, final Map<String, Object> queryMap, final String outputStr) {
        this.url = url;
        this.method = method;
        this.data = data;
        this.queryMap = queryMap;
        this.outputStr = outputStr;
    }

    public static HttpArgsBuilder builder(String url) {
        return new HttpArgsBuilder(url);
    }

    public static class HttpArgsBuilder {
        private String url;
        private String method = GET;
        private String data;
        private Map<String, Object> queryMap;
        private String outputStr;

        HttpArgsBuilder(String url) {
            this.url = url;
        }

        public HttpArgsBuilder url(final String url) {
            this.url = url;
            return this;
        }

        public HttpArgsBuilder method(final String method) {
            this.method = method;
            return this;
        }


        public HttpArgsBuilder queryMap(final Map<String, Object> queryMap) {
            this.queryMap = queryMap;
            return this;
        }

        public HttpArgsBuilder outputStr(final String outputStr) {
            this.outputStr = outputStr;
            return this;
        }

        public HttpArgs build() {
            String method = this.method;

            return new HttpArgs(this.url, method, this.data, this.queryMap, this.outputStr);
        }
    }


}

6 回调逻辑代码

 @RequestMapping("/notify")
    public void notify(HttpServletRequest request, HttpServletResponse response) throws Exception {
        InputStream inStream = request.getInputStream();
        ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len = 0;
        while ((len = inStream.read(buffer)) != -1) {
            outSteam.write(buffer, 0, len);
        }

        outSteam.close();
        inStream.close();
        String result = new String(outSteam.toByteArray(), "utf-8");//获取微信调用我们notify_url的返回信息

        //System.out.println("result="+result);

        JSONObject resJson = JSONObject.parseObject(result);

        String wechatpayTimestamp = request.getHeader("Wechatpay-Timestamp");
        String wechatpayNonce = request.getHeader("Wechatpay-Nonce");
        String wechatpaySignature = request.getHeader("Wechatpay-Signature");

        JSONObject resources = resJson.getJSONObject("resource");
        //associated_data
        String associated_data = resources.getString("associated_data");
        //nonce
        String nonce = resources.getString("nonce");
        //ciphertext
        String ciphertext = resources.getString("ciphertext");

        JSONObject returnRes = new JSONObject();


        WxPayAesUtilV3 aesUtil = new WxPayAesUtilV3(WxPayConfig.APIV3KEY.getBytes());
        String decryptResources = aesUtil.decryptToString(associated_data.getBytes(), nonce.getBytes(), ciphertext);
        //System.out.println("decryptResources=" + decryptResources);

        //获取订单号
        JSONObject decryptResourcesJson = JSONObject.parseObject(decryptResources);
        String out_trade_no = decryptResourcesJson.getString("out_trade_no");
        String trade_state = decryptResourcesJson.getString("trade_state");
        //获取订单金额信息
        JSONObject ammountJson = decryptResourcesJson.getJSONObject("amount");
        //总金额
        Integer total = ammountJson.getInteger("total");
        //用户支付金额
        Integer payerTotal = ammountJson.getInteger("payer_total ");

        SignVerify signVerify = new SignVerify();
        signVerify.setSerialNo(WxPayConfig.SERIALNO);
        signVerify.setApiV3Key(WxPayConfig.APIV3KEY);
        signVerify.setMchId(WxPayConfig.MCHID);
        signVerify.setCertUrl(WxPayConfig.CERTURL);
        signVerify.setWechatpaySignature(wechatpaySignature);
        signVerify.setWechatpayTimestamp(wechatpayTimestamp);
        signVerify.setWechatpayNonce(wechatpayNonce);
        signVerify.setBody(result);

        try {
            if (WxPaySignUtilV3.responseSignVerify(signVerify)) {

                if ("SUCCESS".equalsIgnoreCase(trade_state)) {
                    //此处可根据实际情况编写处理逻辑
                    //例如修改订单状态,积分操作,发消息等等
                    returnRes.put("code", "SUCCESS");
                    returnRes.put("message", "成功");
                } else {
                    returnRes.put("code", "500");
                    returnRes.put("message", "交易未完成");

                }

            } else {
                returnRes.put("code", "500");
                returnRes.put("message", "签名校验失败");
            }
        } catch (Exception e) {

            e.printStackTrace();
        }

        response.getWriter().write(returnRes.toString());
    }

7  公钥私钥获取

import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.X509Certificate;

/**
 * KeyPairFactory
 *
 * @author dax
 * @since 13:41
 **/
public class KeyPairFactory {

    private final Object lock = new Object();
    private KeyStore store;

    /**
     * 获取公私钥.
     *
     * @param keyPath  the key path
     * @param keyAlias the key alias
     * @param keyPass  password
     * @return the key pair
     */
    public KeyPair createPKCS12(String keyPath, String keyAlias, String keyPass) throws Exception {
        //ClassPathResource resource = new ClassPathResource(keyPath);
        URL url = new URL(keyPath);
        HttpURLConnection httpUrlConn = (HttpURLConnection) url.openConnection();
        httpUrlConn.setDoOutput(true);
        httpUrlConn.setDoInput(true);
        httpUrlConn.setUseCaches(false);
        // 设置请求方式(GET/POST)
        httpUrlConn.setRequestMethod("GET");
        httpUrlConn.connect();

        InputStream in = httpUrlConn.getInputStream();
        //in.close();
        //httpUrlConn.disconnect();
        char[] pem = keyPass.toCharArray();
        try {
            synchronized (lock) {
                if (store == null) {
                    synchronized (lock) {
                        store = KeyStore.getInstance("PKCS12");
                        store.load(in, 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);

        } catch (Exception e) {
            throw new IllegalStateException("Cannot load keys from store: " + keyPath, e);
        }
    }

}

此处秘钥获取需要使用商户平台的证书,这里把p12证书放到的oss上然后通过get请求获取

备注:微信支付的证书别名为Tenpay Certificate写错代码将会报错

    /**
     * 支付p12证书别名
     */
    String KEY_ALIAS = "Tenpay Certificate";

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值