微信小程序支付Jsapi下单Java版保姆级教程

 更新时间:2025年07月01日 10:20:52   作者:小江-  
微信支付作为国内领先的第三方支付平台,为商户提供了多样化的支付解决方案,包括JSAPI支付、APP支付、H5支付、Native支付以及小程序支付,这篇文章主要介绍了微信小程序支付Jsapi下单Java版的相关资料,需要的朋友可以参考下

前置环境

  • jdk:21
  • maven:3.9.9

需要自己去注册申请微信小程序和微信支付平台商户号绑定

注意上述两张图片是微信公众平台和微信支付平台,两个地方的appid和商户号必须得对的上

支付产品许可申请

需要把这个申请上,这个就 不做赘述了,这个跟着申请内容填资料就行了。

支付参数申请

这里点击进去后申请一个证书

然后进入这个API安全,这里比较关键,注意操作步骤。

这里的密钥记清楚,最好新建一个文件做一个备注。

点击这个下载,按照这个下载证书文档,操作,这里注意的是这个outputFilePath以及apiclinet_key.pem地址

apiclinet_key.pem地址

这个地址需要在解密回调里面去申请,同样里面的密钥也需要记住。

然后按照这个工具里面的操作进行来回复制就会得到一个文件,这个文件里面的apiclinet_key.pem存放的地址。

这样操作下来我们就得到一个

java -jar CertificateDownloader.jar -k CZBK5123643FFDuv3 -m 1622408443 -f E:\code\AI\乞讨\1622408443_20250305_cert\apiclient_key.pem -s 495F550990E8FF3FC72C7 -o E:\www\code

运行后

得到密钥文件。至此我们所需要的文件已全部收集齐全。

代码:

application.yaml

cant:
  wechat:
    # 微信公众平台里面的小程序appid
    appid: wx8b8656de482b
    # 商户号
    mchid: 1622408443
    # 商户API证书里面的序列号
    mchSerialNo: 405172E0770000864CE6A4136411
    # 解密回调的密钥
    apiV3Key: Cwxpay435434323FFDuv3
    # 这个是解密回调也就是生成的(微信支付商户平台证书工具)
    privateKeyFilePath: E:\code\AI\乞讨\1622408443_20250307_cert\apiclient_cert.pem
    # 这个是通cmd生成的
    weChatPayCertFilePath: E:\www\code\wechatpay_54D388B975FD231C6CA45BE67F78D0E4181AC0C2.pem
    # 支付成功回调地址
    notifyUrl: https://taluop.top/cant/notify

导入依赖

<dependency>
            <groupId>com.github.wechatpay-apiv3</groupId>
            <artifactId>wechatpay-java</artifactId>
            <version>0.2.10</version>
        </dependency>

配置类

@Component
@ConfigurationProperties(prefix = "cant.wechat")
@Data
@EnableConfigurationProperties
public class WeChatProperties {
    private String appid; //小程序的appid
    private String secret; //小程序的秘钥
    private String mchid; //商户号
    private String mchSerialNo; //商户API证书的证书序列号
    private String privateKeyFilePath; //商户私钥文件
    private String apiV3Key; //证书解密的密钥
    private String weChatPayCertFilePath; //平台证书
    private String notifyUrl; //支付成功的回调地址
    private String refundNotifyUrl; //退款成功的回调地址

}

工具类

/**
 * 微信支付工具类
 */
@Component
public class WeChatPayUtil {

    //微信支付下单接口地址
    public static final String JSAPI = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";

    //申请退款接口地址
    public static final String REFUNDS = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";

    @Autowired
    private WeChatProperties weChatProperties;

    /**
     * 获取调用微信接口的客户端工具对象
     *
     * @return
     */
    private CloseableHttpClient getClient() {
        PrivateKey merchantPrivateKey = null;
        try {
            // merchantPrivateKey商户API私钥,如何加载商户API私钥请看常见问题
            merchantPrivateKey = PemUtil.loadPrivateKey(new FileInputStream(new File(weChatProperties.getPrivateKeyFilePath())));
            // 加载平台证书文件
            X509Certificate x509Certificate = PemUtil.loadCertificate(new FileInputStream(new File(weChatProperties.getWeChatPayCertFilePath())));
            // wechatPayCertificates微信支付平台证书列表。你也可以使用“定时更新平台证书功能”
            List<X509Certificate> wechatPayCertificates = Arrays.asList(x509Certificate);

            WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                    .withMerchant(weChatProperties.getMchid(), weChatProperties.getMchSerialNo(), merchantPrivateKey)
                    .withWechatPay(wechatPayCertificates);

            // 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签
            CloseableHttpClient httpClient = builder.build();
            return httpClient;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 发送post方式请求
     *
     * @param url
     * @param body
     * @return
     */
    private String post(String url, String body) throws Exception {
        CloseableHttpClient httpClient = getClient();

        HttpPost httpPost = new HttpPost(url);
        httpPost.addHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString());
        httpPost.addHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString());
        httpPost.addHeader("Wechatpay-Serial", weChatProperties.getMchSerialNo());
        httpPost.setEntity(new StringEntity(body, "UTF-8"));

        CloseableHttpResponse response = httpClient.execute(httpPost);
        try {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            return bodyAsString;
        } finally {
            httpClient.close();
            response.close();
        }
    }

    /**
     * jsapi下单
     *
     * @param orderNum    商户订单号
     * @param total       总金额
     * @param description 商品描述
     * @param openid      微信用户的openid
     * @return
     */
    private String jsapi(String orderNum, BigDecimal total, String description, String openid) throws Exception {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("appid", weChatProperties.getAppid());
        jsonObject.put("mchid", weChatProperties.getMchid());
        jsonObject.put("description", description);
        jsonObject.put("out_trade_no", orderNum);
        jsonObject.put("notify_url", weChatProperties.getNotifyUrl());

        JSONObject amount = new JSONObject();
        amount.put("total", total.multiply(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).intValue());
        amount.put("currency", "CNY");

        jsonObject.put("amount", amount);

        JSONObject payer = new JSONObject();
        payer.put("openid", openid);

        jsonObject.put("payer", payer);

        String body = jsonObject.toJSONString();
        return post(JSAPI, body);
    }

    /**
     * 小程序支付
     *
     * @param orderNum    商户订单号
     * @param total       金额,单位 元
     * @param description 商品描述
     * @param openid      微信用户的openid
     * @return
     */
    public JSONObject pay(String orderNum, BigDecimal total, String description, String openid) throws Exception {
        //统一下单,生成预支付交易单
        String bodyAsString = jsapi(orderNum, total, description, openid);
        //解析返回结果
        JSONObject jsonObject = JSON.parseObject(bodyAsString);

        System.out.println(jsonObject);

        String prepayId = jsonObject.getString("prepay_id");
        if (prepayId != null) {
            String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
            String nonceStr = RandomStringUtils.randomNumeric(32);
            ArrayList<Object> list = new ArrayList<>();
            list.add(weChatProperties.getAppid());
            list.add(timeStamp);
            list.add(nonceStr);
            list.add("prepay_id=" + prepayId);
            //二次签名,调起支付需要重新签名
            StringBuilder stringBuilder = new StringBuilder();
            for (Object o : list) {
                stringBuilder.append(o).append("\n");
            }
            String signMessage = stringBuilder.toString();
            byte[] message = signMessage.getBytes();

            Signature signature = Signature.getInstance("SHA256WithRSA");
            signature.initSign(PemUtil.loadPrivateKey(new FileInputStream(new File(weChatProperties.getPrivateKeyFilePath()))));
            signature.update(message);
            String packageSign = Base64.getEncoder().encodeToString(signature.sign());

            //构造数据给微信小程序,用于调起微信支付
            JSONObject jo = new JSONObject();
            jo.put("timeStamp", timeStamp);
            jo.put("nonceStr", nonceStr);
            jo.put("package", "prepay_id=" + prepayId);
            jo.put("signType", "RSA");
            jo.put("paySign", packageSign);

            return jo;
        }
        return jsonObject;
    }
    /**
     * 发送get方式请求
     *
     * @param url
     * @return
     */
    private String get(String url) throws Exception {
        CloseableHttpClient httpClient = getClient();

        HttpGet httpGet = new HttpGet(url);
        httpGet.addHeader(HttpHeaders.ACCEPT, ContentType.APPLICATION_JSON.toString());
        httpGet.addHeader(HttpHeaders.CONTENT_TYPE, ContentType.APPLICATION_JSON.toString());
        httpGet.addHeader("Wechatpay-Serial", weChatProperties.getMchSerialNo());

        CloseableHttpResponse response = httpClient.execute(httpGet);
        try {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            return bodyAsString;
        } finally {
            httpClient.close();
            response.close();
        }
    }


    /**
     * 申请退款
     *
     * @param outTradeNo    商户订单号
     * @param outRefundNo   商户退款单号
     * @param refund        退款金额
     * @param total         原订单金额
     * @return
     */
    public String refund(String outTradeNo, String outRefundNo, BigDecimal refund, BigDecimal total) throws Exception {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("out_trade_no", outTradeNo);
        jsonObject.put("out_refund_no", outRefundNo);

        JSONObject amount = new JSONObject();
        amount.put("refund", refund.multiply(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).intValue());
        amount.put("total", total.multiply(new BigDecimal(100)).setScale(2, BigDecimal.ROUND_HALF_UP).intValue());
        amount.put("currency", "CNY");

        jsonObject.put("amount", amount);
        jsonObject.put("notify_url", weChatProperties.getRefundNotifyUrl());

        String body = jsonObject.toJSONString();

        //调用申请退款接口
        return post(REFUNDS, body);
    }
}

遇到的问题:

  • 应答的状态码不为200-299,商户证书序列号有误。

    你需要找到最近操作的证书序列号

  • 找不到证书序列号对应的证书

    这个地址对应的看一下是否是最新的

  • Java HotSpot(TM) 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appendedranh

    这个需要的是apiclient_key.pem文件,这个是由apiclient_cert.pem文件加序列号生成的文件。

总结

到此这篇关于微信小程序支付Jsapi下单Java版的文章就介绍到这了,更多相关Java微信小程序支付Jsapi下单内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 从try-with-resources到ThreadLocal,优化你的代码编写方式

    从try-with-resources到ThreadLocal,优化你的代码编写方式

    这篇文章主要为大家介绍了从try-with-resources到ThreadLocal,优化代码的编写方式详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-04-04
  • Java基础开发之JDBC操作数据库增删改查,分页查询实例详解

    Java基础开发之JDBC操作数据库增删改查,分页查询实例详解

    这篇文章主要介绍了Java基础开发之JDBC操作数据库增删改查,分页查询实例详解,需要的朋友可以参考下
    2020-02-02
  • mybatis中BigDecimal中的0存为null的坑及解决

    mybatis中BigDecimal中的0存为null的坑及解决

    在使用MyBatis进行数据库操作时,若Java中属性类型为BigDecimal且值为0,插入数据库时可能会变为null,而不是0,这个问题可能是由于MyBatis在处理BigDecimal类型时的弱类型判断导致的,当BigDecimal变量与空字符串进行比较时,MyBatis可能将其视为null
    2024-10-10
  • spring中的BeanFactory与FactoryBean的讲解

    spring中的BeanFactory与FactoryBean的讲解

    今天小编就为大家分享一篇关于spring中的BeanFactory与FactoryBean的讲解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-01-01
  • Java集合的Collection接口和List接口详解

    Java集合的Collection接口和List接口详解

    这篇文章主要为大家详细介绍了Java集合的Collection接口和List接口,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-03-03
  • MyBatis中使用foreach循环的坑及解决

    MyBatis中使用foreach循环的坑及解决

    这篇文章主要介绍了MyBatis中使用foreach循环的坑及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-01-01
  • springboot2.x解决运行顺序及Bean对象注入顺序的问题

    springboot2.x解决运行顺序及Bean对象注入顺序的问题

    这篇文章主要介绍了springboot2.x解决运行顺序及Bean对象注入顺序的问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-01-01
  • Spring容器初始化及问题解决方案

    Spring容器初始化及问题解决方案

    这篇文章主要介绍了Spring容器初始化及问题解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-06-06
  • SpringBoot优化连接数的方法详解

    SpringBoot优化连接数的方法详解

    SpringBoot开发最大的好处是简化配置,内置了Tomcat,下面这篇文章主要给大家介绍了关于SpringBoot优化连接数的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-06-06
  • 使用maven项目pom.xml文件配置打包功能和静态资源文件自带版本号功能

    使用maven项目pom.xml文件配置打包功能和静态资源文件自带版本号功能

    在Maven项目中,通过pom.xml文件配置打包功能,可以控制构建过程,生成可部署的包,同时,为了缓存控制与版本更新,可以在打包时给静态资源文件如JS、CSS添加版本号,这通常通过插件如maven-resources-plugin实现
    2024-09-09

最新评论