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

这篇具有很好参考价值的文章主要介绍了小程序微信支付V3版本Java集成。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、简介

1、关于API v3

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

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

2、SDK接入

微信支付API v3官方SDK(目前包含Java、PHP、GO三种语言版本)。此外,微信支付也提供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回调地址给你一个通知文章来源地址https://www.toymoban.com/news/detail-511725.html

@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;
    }
}

到了这里,关于小程序微信支付V3版本Java集成的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java实现微信小程序V3支付

    2024年02月12日
    浏览(43)
  • Java实现微信小程序V3支付 (完整demo)

    2024年02月07日
    浏览(26)
  • Node.js关于微信支付V3版相关处理方法

    今天给大家写一个关于Node.js接入微信支付V3接口时一些毕竟复杂的点,主要就是请求签名Authorization、调起支付签名、回调参数解密等。 请求签名Authorization 在微信支付V3接口中,商户需要使用自身的私钥对API URL、消息体等关键数据的组合进行SHA-256 with RSA签名。请求的签名信

    2024年01月16日
    浏览(49)
  • PHP实现对微信支付v3版本回调数据的解密

    PS:本文使用了微信官方给出的demo来实现对回调数据的解密,本文主要对微信官方给出的demo如何使用作出部分个人讲解,以及对解密前后数据的格式进行展示 PHP类:这是微信官方给出的demo

    2024年02月15日
    浏览(31)
  • java微信支付v3系列——5.微信支付成功回调

    java微信支付v3系列——1.微信支付准备工作 java微信支付v3系列——2.微信支付基本配置 java微信支付v3系列——3.订单创建准备操作 java微信支付v3系列——4.创建订单的封装及使用 java微信支付v3系列——5.微信支付成功回调 java微信支付v3系列——6.微信支付查询订单API java微信支

    2024年01月17日
    浏览(31)
  • Java实现微信支付v3的支付回调

    以前都是自己手搓api的, 现在有轮子了, 尝试记录一下如何使用 我的做法是首先打开v3的代码仓库, 直接进去看看他们的文档, 可以看到这么一坨东西 开发前准备 2. 先引入maven 初始化商户配置 先从请求头中获取构建RequestParam需要的参数 初始化解析器 进行验签, 解密并转换成

    2024年02月12日
    浏览(37)
  • SpringBoot整合微信小程序支付V3(支付、退款)

    微信支付开发前,需要先获取商家信息,包括商户号、AppId、证书和密钥。 获取商户号 微信商户平台 申请成为商户 = 提交资料 = 签署协议 = 获取商户号 获取AppID 微信公众平台 注册服务号 = 服务号认证 = 获取APPID = 绑定商户号 申请商户证书 登录商户平台 = 选择 账户中心 = 安

    2024年02月08日
    浏览(27)
  • java微信支付v3系列——6.微信支付查询订单API

    java微信支付v3系列——1.微信支付准备工作 java微信支付v3系列——2.微信支付基本配置 java微信支付v3系列——3.订单创建准备操作 java微信支付v3系列——4.创建订单的封装及使用 java微信支付v3系列——5.微信支付成功回调 java微信支付v3系列——6.微信支付查询订单API java微信支

    2023年04月08日
    浏览(29)
  • SpringBoot 整合微信小程序微信支付V3 jsapi (支付、退款)

    最近的一个微信小程序项目里有用到微信支付,网上找的资料都是特别乱,看起来特别懵,结合了好多文章的内容,终于做了出来,可能我的这个博文看起来也是特别乱,但是是可以直接C走简单改一改就可以用的。(支付成功回调,和退款回调因为昨天刚在阿里申请的域名还

    2024年04月25日
    浏览(40)
  • springboot实现微信小程序V3微信支付功能

    appId:小程序appid appSecret:小程序的secret mchId:商户号 keyPath:商户私钥路径(apiclient_key.pem) certPath:证书路径(apiclient_cert.pem) platFormPath:平台证书(cert.pem) 注 : 需要通过写程序生成平台证书(见v3Get()方法) apiKey3:apiv3密钥 serialnumber:商户证书序列号 notifyUrl:回调地

    2024年02月12日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包