• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

小程序微信支付V3版本Java集成

武飞扬头像
不会写代码的周周
帮助2

一、简介

1、关于API v3

相较于之前的微信支付API,主要区别是:

  • 遵循统一的REST的设计风格
  • 使用JSON作为数据交互的格式,不再使用XML
  • 使用基于非对称密钥的SHA256-RSA的数字签名算法,不再使用MD5或HMAC-SHA256
  • 不再要求携带HTTPS客户端证书(仅需携带证书序列号)
  • 使用AES-256-GCM,对回调中的关键信息进行加密保护

2、SDK接入

微信支付API v3官方SDK(目前包含JavaPHPGO三种语言版本)。此外,微信支付也提供API v3的Postman调试工具、微信支付平台证书下载工具,可以通过微信支付的GitHub获取。

3、微信支付商户平台

微信支付商户平台及开发文档中心

1.在【API安全】里需要申请API证书、设置APIv3密钥。
2.在【开发配置】里需要配置合法域名
3.在【AppID账号管理】里需要绑定小程序的AppID

二、代码

1、小程序调起信支付

timeStamp:时间戳(只需要到秒,如果获取的是毫秒级需要除以1000)
nonceStr:随机字符串(不长于32位)
package:订单详情扩展字符串(prepay_id=……)
signType:签名方式(仅支持RSA)
paySign:签名(使用字段appId、timeStamp、nonceStr、package计算得出的签名值)
具体详解请查看微信支付api

wx.requestPayment({
  timeStamp: '',
  nonceStr: '',
  package: '',
  signType: '',
  paySign: '',
  success: (result) => {},
  fail: () => {},
  complete: () => {}
});

2、Java服务端

1.导入maven依赖

<!-- 导入微信支付v3工具包 -->
<dependency>
    <groupId>com.github.wechatpay-apiv3</groupId>
    <artifactId>wechatpay-apache-httpclient</artifactId>
    <version>0.4.7</version>
</dependency>

<!--Hutool工具包-->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.4.4</version>
</dependency>

2.微信支付Utils支付工具类

import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
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.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.util.AesUtil;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.apache.xmlbeans.impl.xb.xsdschema.Public;
import org.springframework.beans.factory.annotation.Autowired;

import javax.servlet.http.PushBuilder;
import java.io.*;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

@Slf4j
public class V3WXPayUtil {


    public static Map<String, Object> createOrder(String buildCode, String openId, String description, Integer total) throws Exception {
//        PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(
//                new ByteArrayInputStream(WXPayConstants.PRIVATE_KEY.getBytes("utf-8")));
        try {
            PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(WXPayConstants.PRIVATE_KEY);

            writeText("C:/log.txt", "获取私钥:=========>"   merchantPrivateKey.toString());
            // 获取证书管理器实例
            CertificatesManager certificatesManager = CertificatesManager.getInstance();
            writeText("C:/log.txt", "获取证书管理器实例:=========>"   certificatesManager.toString()   "\n");
            // 向证书管理器增加需要自动更新平台证书的商户信息
            WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(WXPayConstants.MCH_ID,
                    new PrivateKeySigner(WXPayConstants.MCH_SERIAL_NO, merchantPrivateKey));
            writeText("C:/log.txt", "获取管理器增加需要自动更新平台证书的商户信息前:=========>"   wechatPay2Credentials.toString()   "\n");
            byte[] api_v3KEYBytes = WXPayConstants.API_V3KEY.getBytes(StandardCharsets.UTF_8);
            writeText("C:/log.txt", "获取api_v3KEYBytes:=========>"   api_v3KEYBytes   "\n");
            certificatesManager.putMerchant(WXPayConstants.MCH_ID, wechatPay2Credentials, api_v3KEYBytes);
            writeText("C:/log.txt", "证书管理器实例:=========>"   certificatesManager.toString());
            // 从证书管理器中获取verifier
            Verifier verifier = certificatesManager.getVerifier(WXPayConstants.MCH_ID);
            writeText("C:/log.txt", "从证书管理器中获取verifier:=========>"   verifier);
            CloseableHttpClient httpClient = WechatPayHttpClientBuilder.create()
                    .withMerchant(WXPayConstants.MCH_ID, WXPayConstants.MCH_SERIAL_NO, merchantPrivateKey)
                    .withValidator(new WechatPay2Validator(verifier))
                    .build();
            writeText("C:/log.txt", "从证书管理器中获取verifier后:=========>"   "\n");
            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", WXPayConstants.MCH_ID)
                    .put("appid", WXPayConstants.APP_ID)
                    .put("notify_url", WXPayConstants.NOTIFY_URL)
                    .put("description", description)
                    .put("out_trade_no", buildCode);
            rootNode.putObject("amount")
                    .put("total", total) // 支付总金额,分为单位 需前端传入
                    .put("currency", "CNY");
            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);

            String bodyAsString = EntityUtils.toString(response.getEntity());
            log.info("成功获取微信预支付订单号{}", bodyAsString);
            System.out.println("成功获取微信预支付订单号==========>"   bodyAsString);
            // 时间戳
            String currentTimeMillis = System.currentTimeMillis() / 1000   "";
            // 随机字符串 hutool工具类
            String randomString = RandomUtil.randomString(32);

            StringBuilder builder = new StringBuilder();
            // 应用ID
            builder.append(WXPayConstants.APP_ID).append("\n");
            // 时间戳
            builder.append(currentTimeMillis).append("\n");
            // 随机字符串
            builder.append(randomString).append("\n");

            JsonNode node = objectMapper.readTree(bodyAsString);
            writeText("C:/log.txt", "从获取node后:=========>"   node   "\n");
            // 订单详情扩展字符串
            builder.append("prepay_id=").append(node.get("prepay_id").toString().replace("\"", "")).append("\n");

            String paySign = sign(builder.toString().getBytes());

            Map<String, Object> map = new HashMap();
            map.put("package", "prepay_id="   node.get("prepay_id").toString().replace("\"", ""));
            map.put("prepayId", node.get("prepay_id").toString().replace("\"", ""));
            map.put("nonceStr", randomString);
            map.put("signType", "RSA");
            map.put("paySign", paySign);
            map.put("timeStamp", currentTimeMillis);
            map.put("appId", WXPayConstants.APP_ID);
            return map;
        }catch (Exception e){
            writeText("C:/log.txt", "异常:"   getStackTraceMessage(e));
            throw e;
        }
    }

    public static String getStackTraceMessage(Throwable e) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw, true);
        e.printStackTrace(pw);
        pw.flush();
        sw.flush();
        return sw.toString();
    }

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

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

    /**
     * 获取私钥。
     * @return 私钥对象
     * @throws IOException
     */
    public static PrivateKey getPrivateKey() throws IOException {
        String content = WXPayConstants.PRIVATE_KEY;
        //        String content = new String(Files.readAllBytes(new ClassPathResource(filename).getFile().toPath()), "UTF-8");
        try {
            String privateKey = content.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 boolean signVerify(String serial, String message, String signature) {
        // 从证书管理器中获取verifier
        Verifier verifier = null;
        try {
            PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(WXPayConstants.PRIVATE_KEY);
            // 获取证书管理器实例
            CertificatesManager certificatesManager = CertificatesManager.getInstance();
            // 向证书管理器增加需要自动更新平台证书的商户信息
            certificatesManager.putMerchant(WXPayConstants.MCH_ID, new WechatPay2Credentials(WXPayConstants.MCH_ID,
                    new PrivateKeySigner(WXPayConstants.MCH_SERIAL_NO, merchantPrivateKey)), WXPayConstants.API_V3KEY.getBytes(StandardCharsets.UTF_8));
            // 从证书管理器中获取verifier
            verifier = certificatesManager.getVerifier(WXPayConstants.MCH_ID);
            return verifier.verify(serial, message.getBytes(StandardCharsets.UTF_8), signature);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (GeneralSecurityException e) {
            e.printStackTrace();
        } catch (HttpCodeException e) {
            e.printStackTrace();
        } catch (NotFoundException e) {
            e.printStackTrace();
        }
        return false;
    }

    public static String decryptOrder(String body) {
        AesUtil util = new AesUtil(WXPayConstants.API_V3KEY.getBytes(StandardCharsets.UTF_8));
        ObjectMapper objectMapper = new ObjectMapper();
        JsonNode node = null;
        try {
            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 util.decryptToString(associatedData.getBytes("utf-8"), nonce.getBytes("utf-8"), ciphertext);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        } catch (GeneralSecurityException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void writeText(String fileName, String text) {
        FileOutputStream fop = null;
        File file;
        try {
            file = new File(fileName);
            fop = new FileOutputStream(file,true);
            if (!file.exists()) {
                file.createNewFile();
            }
            fop.write(text.getBytes());
            fop.flush();
            fop.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fop != null) {
                    fop.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

学新通

4.V3支付常量配置类

public class WXPayConstants {

    /*appid*/
    public static final String APP_ID = "";
    /*小程序唯一凭证密钥*/
    public static final String SECRET = "";
    /*商户号*/
    public static final String MCH_ID = "";
    /* 商户证书序列号 */
    public static final String MCH_SERIAL_NO = "";
    /*APIv3密钥*/
    public static final String API_V3KEY = "";
    /*支付成功回调地址*/
    public static final String NOTIFY_URL = "https://*.com/*/wXPay/wxPayNotify";
    /*退款回调接口*/
    public static final String PACKAGE = "Sign=WXPay";
    /*序列号*/
    public static final String PRIVATE_KEY = "-----BEGIN PRIVATE KEY-----\n"  
            "证书内容"  
            "-----END PRIVATE KEY-----\n";
}
学新通

5.异步通知【支付回调】

前端付款成功后微信会主动访问你的服务器WXPayConstants给的NOTIFY_URL回调地址给你一个通知

@ApiOperation(value = "微信回调", notes = "微信回调")
@RequestMapping(value = "/wxPayNotify", method = RequestMethod.POST)
public Map<String, Object> wxPayNotify(HttpServletRequest request) {
//        JSONObject params = (JSONObject) this.requestBody();
//        return busOrderService.productOrder(params);
    System.out.println("Wechatpay-Timestamp:"   request.getHeader("Wechatpay-Timestamp"));
    this.writeText("C:/log.txt", "Wechatpay-Timestamp: {}:=========>"   request.getHeader("Wechatpay-Timestamp")   "\n");

    System.out.println("Wechatpay-Nonce:"   request.getHeader("Wechatpay-Nonce"));
    this.writeText("C:/log.txt", "Wechatpay-Nonce: {}:=========>"   request.getHeader("Wechatpay-Nonce")   "\n");

    System.out.println("Wechatpay-Signature:"   request.getHeader("Wechatpay-Signature"));
    this.writeText("C:/log.txt", "Wechatpay-Signature: {}:=========>"   request.getHeader("Wechatpay-Signature")   "\n");

    System.out.println("Wechatpay-Serial:"   request.getHeader("Wechatpay-Serial"));
    this.writeText("C:/log.txt", "Wechatpay-Serial: {}:=========>"   request.getHeader("Wechatpay-Serial")   "\n");

    Map result = new HashMap();
    result.put("code", "FAIL");
    BufferedReader reader = null;
    try {
        StringBuilder signStr = new StringBuilder();
        // 请求时间戳\n
        signStr.append(request.getHeader("Wechatpay-Timestamp")).append("\n");
        // 请求随机串\n
        signStr.append(request.getHeader("Wechatpay-Nonce")).append("\n");
        // 请求报文主体\n
        reader = request.getReader();
        this.writeText("C:/log.txt", "reader:=========>"   reader.toString()   "\n");
        String str = null;
        StringBuilder builder = new StringBuilder();
        while ((str = reader.readLine()) != null) {
            builder.append(str);
        }
        System.out.println(builder);
        log.info("请求报文主体: {}", builder);
        this.writeText("C:/log.txt", "请求报文主体:=========>"   builder   "\n");
        signStr.append(builder.toString()).append("\n");
        // 1.验签
        if (!V3WXPayUtil.signVerify(request.getHeader("Wechatpay-Serial"), signStr.toString(), request.getHeader("Wechatpay-Signature"))) {
            result.put("message", "sign error");
            return result;
        }
        // 2.解密
        String decryptOrder = V3WXPayUtil.decryptOrder(builder.toString());
        log.info("验签解密: {}", decryptOrder);
        this.writeText("C:/log.txt", "验签解密:=========>"   decryptOrder   "\n");
        System.out.println(decryptOrder);

        String payResult = busOrderService.verifySuccessfulPayment(decryptOrder);
        if (StringUtils.isEmpty(payResult)) {
            result.put("message", "verification error");
            return result;
        } else {
            result.put("code", payResult);
        }
        return result;
    } catch (IOException e) {
        e.printStackTrace();
        result.put("code", "FAIL");
        result.put("message", e.getMessage());
        return result;
    }
}
学新通

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgbjiae
系列文章
更多 icon
同类精品
更多 icon
继续加载