首页 > 编程语言 >【Java】SpringBoot集成微信V3支付

【Java】SpringBoot集成微信V3支付

时间:2024-02-02 18:32:08浏览次数:21  
标签:Java SpringBoot 证书 微信 private wxPayConfig import response String

前言

这篇文章主要实现一下通过IJPay来实现微信v3支付案例,本篇文章使用的是JSAPI即小程序支付

准备工作

导入依赖

       <dependency>
            <groupId>com.github.javen205</groupId>
            <artifactId>IJPay-WxPay</artifactId>
            <version>2.9.6</version>
        </dependency>
  • appId 由微信生成的应用ID,全局唯一。
  • mchId 直连商户的商户号,由微信支付生成并下发。
  • apiV3Key v3密钥
  • mchSerialNo 商户证书序列号

还需要三个证书文件 在这里插入图片描述

  1. 通过微信官方指引下载证书并解压缩后得到的文件。apiclient_cert.pem称为商户证书apiclient_key.pem成为商户证书密钥
  2. 通过IJPay的接口获取,称之为微信平台证书。(一般没有这个文件,所以暂时先不管,一会获取的时候会讲这个)

在这里我们给这三个文件起个别名,以便下方代码更容易区分。

  • apiclient_cert.pem:privateCertPath

  • apiclient_key.pem:privateKeyPath

  • wx_platform_cert.pem:platformCertPath

    开始

    1 首先将上面所有的参数配置到application.yml文件中,或者配置到nacos上。 在这里插入图片描述

  • wxJsapiUrl:是获取微信预支付参数的url路径

  • 最下面的三个是文件的绝对路径

2 读取配置文件的配置项,新建WxPayConfig

@Data
@Component
@ConfigurationProperties(prefix = "wx.pay")
public class WxPayConfig {
    /**
     * appId
     */
    private String appId;

    /**
     * 商户号
     */
    private String mchId;

    /**
     * 商户证书序列号
     */
    private String mchSerialNo;

    /**
     * apiv3密钥
     */
    private String apiV3Key;

    /**
     * 支付回调地址
     */
    private String notifyUrl;

    /**
     * 微信支付请求url
     */
    private String wxJsapiUrl;

    /**
     * 私钥路径
     */
    private String privateKeyPath;

    /**
     * 商户证书路径
     */
    private String privateCertPath;

    /**
     * 微信平台证书路径
     */
    private String platformCertPath;
}

3 首先获取到微信平台证书

import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.mjfz.fc.weixin.WxPayConfig;
import com.ijpay.core.IJPayHttpResponse;
import com.ijpay.core.enums.RequestMethodEnum;
import com.ijpay.core.kit.AesUtil;
import com.ijpay.core.kit.PayKit;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.enums.WxDomainEnum;
import com.ijpay.wxpay.enums.v3.OtherApiEnum;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.FileWriter;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;


    @Resource
    private WxPayConfig wxPayConfig;

    public void v3Get() {
        // 获取平台证书列表
        try {
            IJPayHttpResponse response = WxPayApi.v3(
                    RequestMethodEnum.GET,
                    WxDomainEnum.CHINA.toString(),
                    OtherApiEnum.GET_CERTIFICATES.toString(),
                    wxPayConfig.getMchId(),
                    wxPayConfig.getMchSerialNo(),
                    null,
                    wxPayConfig.getPrivateKeyPath(),
                    ""
            );

            String timestamp = response.getHeader("Wechatpay-Timestamp");
            String nonceStr = response.getHeader("Wechatpay-Nonce");
            String serialNumber = response.getHeader("Wechatpay-Serial");
            String signature = response.getHeader("Wechatpay-Signature");

            String body = response.getBody();
            int status = response.getStatus();

            log.info("serialNumber: {}", serialNumber);
            log.info("status: {}", status);
            log.info("body: {}", body);
            if (status == 200) {
                JSONObject jsonObject = JSONUtil.parseObj(body);
                JSONArray dataArray = jsonObject.getJSONArray("data");
                // 默认认为只有一个平台证书
                JSONObject encryptObject = dataArray.getJSONObject(0);
                JSONObject encryptCertificate = encryptObject.getJSONObject("encrypt_certificate");
                String associatedData = encryptCertificate.getStr("associated_data");
                String cipherText = encryptCertificate.getStr("ciphertext");
                String nonce = encryptCertificate.getStr("nonce");
                String serialNo = encryptObject.getStr("serial_no");
                final String platSerialNo = savePlatformCert(associatedData, nonce, cipherText, wxPayConfig.getPlatformCertPath());
                log.info("平台证书序列号: {} serialNo: {}", platSerialNo, serialNo);
            }
            // 根据证书序列号查询对应的证书来验证签名结果
            boolean verifySignature = WxPayKit.verifySignature(response, wxPayConfig.getPlatformCertPath());
            System.out.println("verifySignature:" + verifySignature);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private String savePlatformCert(String associatedData, String nonce, String cipherText, String certPath) {
        try {
            AesUtil aesUtil = new AesUtil(apiV3key.getBytes(StandardCharsets.UTF_8));
            // 平台证书密文解密
            // encrypt_certificate 中的  associated_data nonce  ciphertext
            String publicKey = aesUtil.decryptToString(
                    associatedData.getBytes(StandardCharsets.UTF_8),
                    nonce.getBytes(StandardCharsets.UTF_8),
                    cipherText
            );
            // 保存证书
            FileWriter writer = new FileWriter(certPath);
            writer.write(publicKey);
            writer.close();
            // 获取平台证书序列号
            X509Certificate certificate = PayKit.getCertificate(new ByteArrayInputStream(publicKey.getBytes()));
            return certificate.getSerialNumber().toString(16).toUpperCase();
        } catch (Exception e) {
            e.printStackTrace();
            return e.getMessage();
        }
    }

4 支付和回调

//支付
    @RequestMapping("/jsApiPay")
    @ResponseBody
    public String jsApiPay(String openId) {
        try {
            String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
            UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
                    .setAppid(wxPayConfig.getAppId())
                    .setMchid(wxPayConfig.getMchId())
                    .setDescription("IJPay 让支付触手可及")
                    .setOut_trade_no(PayKit.generateStr())
                    .setTime_expire(timeExpire)
                    .setAttach("微信系开发脚手架 https://gitee.com/javen205/TNWX")
                    .setNotify_url(wxPayConfig.getNotifyUrl())
                    .setAmount(new Amount().setTotal(1))
                    .setPayer(new Payer().setOpenid(openId));

            log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
            IJPayHttpResponse response = WxPayApi.v3(
                    RequestMethodEnum.POST,
                    WxDomainEnum.CHINA.toString(),
                    BasePayApiEnum.JS_API_PAY.toString(),
                    wxPayConfig.getMchId(),
                    wxPayConfig.getMchSerialNo(),
                    null,
                    wxPayConfig.getPrivateKeyPath(),
                    JSONUtil.toJsonStr(unifiedOrderModel)
            );

            log.info("统一下单响应 {}", response);
            // 根据证书序列号查询对应的证书来验证签名结果
            boolean verifySignature = WxPayKit.verifySignature(response, wxPayConfig.getPlatformCertPath());
            log.info("verifySignature: {}", verifySignature);
            if (response.getStatus() == 200 && verifySignature) {
                String body = response.getBody();
                JSONObject jsonObject = JSONUtil.parseObj(body);
                String prepayId = jsonObject.getStr("prepay_id");
                Map<String, String> map = WxPayKit.jsApiCreateSign(wxPayConfig.getAppId(), prepayId, wxPayConfig.getPrivateKeyPath());
                log.info("唤起支付参数:{}", map);
                return JSONUtil.toJsonStr(map);
            }
            return JSONUtil.toJsonStr(response);
        } catch (Exception e) {
            e.printStackTrace();
            return e.getMessage();
        }
    }

    @RequestMapping(value = "/payNotify", method = {org.springframework.web.bind.annotation.RequestMethod.POST, org.springframework.web.bind.annotation.RequestMethod.GET})
    @ResponseBody
    public void payNotify(HttpServletRequest request, HttpServletResponse response) {
        Map<String, String> map = new HashMap<>(12);
        try {
            String timestamp = request.getHeader("Wechatpay-Timestamp");
            String nonce = request.getHeader("Wechatpay-Nonce");
            String serialNo = request.getHeader("Wechatpay-Serial");
            String signature = request.getHeader("Wechatpay-Signature");

            log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
            String result = HttpKit.readData(request);
            log.info("支付通知密文 {}", result);

            // 需要通过证书序列号查找对应的证书,verifyNotify 中有验证证书的序列号
            String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp,
                    wxPayConfig.getApiV3Key(), wxPayConfig.getPlatformCertPath());

            log.info("支付通知明文 {}", plainText);

            if (StrUtil.isNotEmpty(plainText)) {
                response.setStatus(200);
                map.put("code", "SUCCESS");
                map.put("message", "SUCCESS");
            } else {
                response.setStatus(500);
                map.put("code", "ERROR");
                map.put("message", "签名错误");
            }
            response.setHeader("Content-type", ContentType.JSON.toString());
            response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
            response.flushBuffer();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

总结

简单实现微信v3支付,如有问题可以指在评论区,制作不易,点个赞吧!

标签:Java,SpringBoot,证书,微信,private,wxPayConfig,import,response,String
From: https://blog.51cto.com/u_16279665/9560653

相关文章

  • JAVA数组练习代码
    一维数组的有序插入思路代码点击查看代码importjava.util.Scanner;/***@authorLittleBear*@date2024-02-02-16:57*/publicclassseqInsertion{publicstaticvoidmain(String[]args){System.out.println("pleaseinputyournum:");......
  • 微信小程序如何控制元素的显示和隐藏
    Hello大家好我是咕噜铁蛋!在微信小程序开发中,控制元素的显示和隐藏是非常常见的需求。通过控制元素的显示和隐藏,我们可以根据用户的操作或特定的条件来动态地展示或隐藏某些内容,从而提升用户体验。在本文中,我将分享如何在微信小程序中实现元素的显示和隐藏的方法。使用wx:if和hidden......
  • java - 判断时间范围区间
    JSONObjectrespObj=newJSONObject(s);SimpleDateFormatsdf=newSimpleDateFormat("yyyy-MM-dd");StringstartTimeStr="2024-01-01";StringendTimeStr="2024-01-31";DatestartTimDate=sdf.parse(startTimeStr);//strin......
  • Java 中的泛型机制
    泛型JDK5.0之后推出的新特性:泛型泛型这种语法机制,只在程序编译阶段起作用,只给编译器参考的(运行阶段没用)使用泛型的好处:集合中存储的元素统一了从集合中取出来的元素类型是泛型指定的类型,不需要进行大量的“向下转型”泛型的缺点:集合中的元素缺乏多样性importjava.util.Ar......
  • Java 中的HashSet 和 TreeSet
    HashSetHashSet集合:无序不可重复方法HashSet集合的元素实际上是放到HashMap集合的Key中importjava.util.HashSet;importjava.util.Set;/**HashSet集合:无序不可重复**/publicclassHashSetTest{publicstaticvoidmain(String[]args){//演示一......
  • JAVA的一些冷门知识
    1、@Size可以用来校验数组长度。2、构造代码块,在构造函数之前被调用,静态构造代码块,只被调用一次,有点类似C#的静态构造函数。java中的四种代码块_java代码块的分类-CSDN博客3、匿名内部类是Java编程语言中一种特殊的类,它没有显式地定义类名,而是在创建对象时通过传递实现了某个......
  • JAVA二维数组
    二维数组介绍二位数组的每个元素都是一维数组二维数组在内存中的形式动态初始化1动态初始化2动态初始化3(列数不确定)静态初始化二维数组使用细节和注意事项......
  • SpringBoot读取配置文件的几种方式
    示例user:name:zhaotianage:18sex:男@Value注解@Value注解是Spring框架提供的用于注入配置属性值的注解,它可用于类的成员变量、方法参数和构造函数参数上。@Data@ComponentpublicclassMyBean{@Value("${user.name}")privateStringname;@V......
  • Java泛型
    参考:磊叔的技术博客 : 聊一聊-JAVA泛型中的通配符T,E,K,V,?tutorialspoint: Java-Generics Java-泛型泛型的好处在没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参......
  • ssm三大框架和springboot有什么关系?
    SSM框架是指Spring+SpringMVC+MyBatis的组合,它们分别是Java开发中常用的三个框架。而SpringBoot(管家)是基于Spring框架的一种快速开发框架。更具体地说,SSM框架是一种传统的JavaWeb开发框架组合,其中:Spring是一个全功能的企业级Java开发框架,提供了依赖注入......