• 微信支付v3接口的 官方 Java SDK


    啰嗦几句:微信支付v3版接口麻烦吗?在对接微信支付v3接口时,本来是一件很简单的事情,其实微信支付v3接口并不是很复杂,但是微信团队的管理很混乱,给我们开发者带来了巨大的麻烦。

    微信支付v3版接口对接麻烦-问题出在了哪?

    • 其一:微信支付的版本较多,没有形成一个统一管理说明;
    • 其二:微信v3支付,没有一个完整的说明文档,文档都很分散;
    • 其三:微信支付官方文档看似很详细,其实很多关键点都没说明白,新手看着就很头疼;

    下面详细的说一下微信支付v3接口的开发

    这个版本整合微信官方文档,以微信小程序开发为基础,大家按步骤点开链接查看操作。

    对接微信支付API v3

    前提:商户号已注册好,准备就绪。
    • 接入前准备
      注意:操作过程中产生的APIv3秘钥记录下来,有的后面会用到。 例如 API v3秘钥:B3AQsC17C6UFooIRCAaXRUvaq8PInN60
      微信商户平台接入说明文档
      [微信商户平台](https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_8_1.shtml)

      说明:这个是小程序版,如果是其他平台可以在左侧找对应目录
      注意:这个流程走下来你会拿到一个压缩文件类似下图

      图:这个压缩文件里有三个证书(解压后如下图),三个证书文件拷贝到你的开发平台里,使用方式继续往下看

    • 扫码进入商户平台的入口
      微信商户平台
      [微信商户平台](https://pay.weixin.qq.com)
      说明:使用管理者的微信扫码进入,进入后台根据上面的说明进行操作。如果没有注册可以直接走注册流程。注册流程这里不讲解。

    • 扫码小程序后台的入口
      微信小程序后台
      [微信小程序后台](https://mp.weixin.qq.com/)
      注意:如果没有申请小程序可以进入这里看怎么申请
      申请小程序说明

    • 小程序后台开通微信支付并绑定商户平台说明
      小程序后台开通微信支付并绑定商户平台说明
      [小程序后台开通微信支付并绑定商户平台说明](https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_8_1.shtml)
      注意:查看说明中最后的【5.配置应用】进行操作

    • 获取商户API证书序列号(merchantSerialNumber)
      进入证书查看网站
      [进入证书查看网站](https://myssl.com/cert_decode.html)

      说明:上传证书文件:apiclient_cert.pem,点击查看证书,里面会有个序列号,复制出来。下载微信支付平台证书会用到。
      例如:
      证书序列号:54776TTTF8F77EXXX3641FAB5F940FII11C65347

    • 下载微信支付平台证书方法
      微信支付平台证书下载工具
      [微信支付平台证书下载工具](https://github.com/wechatpay-apiv3/CertificateDownloader)
      说明:在这里下载 CertificateDownloader.jar 文件,按照里面的文档操作即可
      注意:我的操作说明,我是把下载好的 CertificateDownloader.jar 文件,放在了解压证书的目录里。这样我取mchPrivateKeyFilePath这个值的时候就可以直接写证书文件名,不用写路径了。

    官方完整命令如:

    java -jar CertificateDownloader.jar -k ${apiV3key} -m ${mchId} -f ${mchPrivateKeyFilePath} -s ${mchSerialNo} -o ${outputFilePath} -c ${wechatpayCertificateFilePath}

    我的操作例如:

    java -jar CertificateDownloader.jar -k B3AQsC17C6UFooIRCAaXRUvaq8PInN60 -m 1901174254 -f apiclient_key.pem -s 54776TTTF8F77EXXX3641FAB5F940FII11C65347 -o file

    注意:1.操作前 CertificateDownloader.jar 和 apiclient_key.pem 文件在同一个目录 。并且在这个目录中进入cmd命令工具。cmd命令工具中显示的目录就是CertificateDownloader.jar所在目录。
    例如:

    文件-CertificateDownloader.jar 路径:D:\WXCertUtil\cert\CertificateDownloader.jar
    文件-apiclient_key.pem 路径:D:\WXCertUtil\cert\apiclient_key.pem
    cmd中路径:D:\WXCertUtil\cert>

    注意:2.命令中 -o file 只是个文件夹,命令执行后会在下面路径中生成一个微信支付平台证书文件。把这个证书也复制到你的平台中,和上面三个证书放在一个位置方便管理。
    例如:
    D:\WXCertUtil\cert\file\wechatpay_3A4AF69999DF01F39BB08C21C1C29B6AA17C074N.pem

    至此,所有微信支付v3的准备工作已就绪,接下来,使用微信官方SDK开发接入微信支付v3。

    Java平台接入微信支付v3接口

    更多平台看官方文档

    微信支付 APIv3 Java SDK
    微信支付 APIv3 Java SDK,里面有详细说明
    github 地址:https://github.com/wechatpay-apiv3/wechatpay-java
    注意:开发可以根据这里的说明操作就好了,下面我写一下可能大家不理解的点。

    com.github.wechatpay-apiv3:wechatpay-java

    代码中的配置-例如:

    /** 商户号 */
    public static String merchantId = "1901174254";
    /** 商户API私钥路径 */
    public static String privateKeyPath = "apiclient_key.pem";
    /** 商户证书序列号 */
    public static String merchantSerialNumber = "54776TTTF8F77EXXX3641FAB5F940FII11C65347";
    /** 微信支付平台证书路径 */
    public static String wechatPayCertificatePath = "wechatpay_3A4AF69999DF01F39BB08C21C1C29B6AA17C074N.pem";
    /** 微信支付 APIv3 密钥 */
    /** 如果微信支付平台证书,已经下载好了,apiV3Key 就不需要了 */
    public static String apiV3Key = "B3AQsC17C6UFooIRCAaXRUvaq8PInN60";

    如果微信支付平台证书,已经下载好了,github里这个代码就不用看了。

    package com.wechat.pay.java.service;
    import com.wechat.pay.java.core.Config;
    import com.wechat.pay.java.core.RSAConfig;
    import com.wechat.pay.java.service.certificate.CertificateService;
    import java.nio.charset.StandardCharsets;
    import java.security.cert.X509Certificate;
    import java.util.List;
    /** 下载微信支付平台证书为例 */
    public class QuickStart {
    /** 商户号 */
    public static String merchantId = "";
    /** 商户API私钥路径 */
    public static String privateKeyPath = "";
    /** 商户证书序列号 例如-证书序列号:54776TTTF8F77EXXX3641FAB5F940FII11C65347*/
    public static String merchantSerialNumber = "";
    /** 微信支付平台证书路径 例如:wechatpay_3A4AF69999DF01F39BB08C21C1C29B6AA17C074N.pem*/
    public static String wechatPayCertificatePath = "";
    /** 微信支付 APIv3 密钥 */
    public static String apiV3Key = "";
    public static void main(String[] args) {
    Config config =
    new RSAConfig.Builder()
    .merchantId(merchantId)
    .privateKeyFromPath(privateKeyPath)
    .merchantSerialNumber(merchantSerialNumber)
    .wechatPayCertificatesFromPath(wechatPayCertificatePath)
    .build();
    CertificateService certificateService = new CertificateService.Builder().config(config).build();
    List certificates =
    certificateService.downloadCertificate(apiV3Key.getBytes(StandardCharsets.UTF_8));
    }
    }

    支付的第一步:微信支付前需要拿到预支付id(prepayId),才能支付,所有支付的第一步是预支付

    import com.wechat.pay.java.core.Config;
    import com.wechat.pay.java.core.RSAConfig;
    import com.wechat.pay.java.service.payments.jsapi.JsapiService;
    import com.wechat.pay.java.service.payments.jsapi.model.Amount;
    import com.wechat.pay.java.service.payments.jsapi.model.Payer;
    import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;
    import com.wechat.pay.java.service.payments.jsapi.model.PrepayResponse;
    public class JsapiExample {
    public static void main(String[] args) {
    Config config =
    new RSAConfig.Builder()
    //商户ID
    .merchantId(merchantId)
    //商户私钥文件(apiclient_key.pem)路径
    .privateKeyFromPath(privateKeyPath)
    //商户证书序列号 例如-证书序列号:54776TTTF8F77EXXX3641FAB5F940FII11C65347
    .merchantSerialNumber(merchantSerialNumber)
    //微信支付平台证书文件路径 例如:wechatpay_3A4AF69999DF01F39BB08C21C1C29B6AA17C074N.pem
    .wechatPayCertificatesFromPath(wechatPayCertificatePath)
    .build();
    JsapiService service = new JsapiService.Builder().config(config).build();
    PrepayRequest request = new PrepayRequest();
    Amount amount = new Amount();
    amount.setTotal(100);
    request.setAmount(amount);
    request.setAppid("wxa9d9651ae******");
    request.setMchid("190000****");
    request.setDescription("测试商品标题");
    request.setNotifyUrl("https://notify_url");
    request.setOutTradeNo("out_trade_no_001");
    Payer payer = new Payer();
    payer.setOpenid("oLTPCuN5a-nBD4rAL_fa********");
    request.setPayer(payer);
    PrepayResponse response = service.prepay(request);
    System.out.println(response.getPrepayId());
    }
    }
    • 如果你的秘钥文件,跟我一样放在resources目录下,可以使用下面方法获取路径

    import org.springframework.core.io.ClassPathResource;
    ClassPathResource resourcePrivateKey = new ClassPathResource("apiclient_key.pem");
    ClassPathResource resourceWeixinPayCert = new ClassPathResource("wechatpay_3A4AF69999DF01F39BB08C21C1C29B6AA17C074N.pem");
    String privateKeyFromPath = resourcePrivateKey.getFile().getPath();
    String wechatPayCertificatesFromPath = resourceWeixinPayCert.getFile().getPath();
    还需要知道的:微信平台给的用户openid,并不是一个用户的唯一ID,不能多个小程序同时使用。也就是说,同一个用户在小程序A中和在小程序B中拿到的用户openid 是不一样的。

    微信小程序调起支付 wx.requestPayment

    • 先看官方说明文档了解一下基本使用原理
      官方说明文档
      [官方说明文档](https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_4.shtml#menu1)
    • 看完官方文档后知道了,想要成功拉起支付,还是需要一些操作的

      1. 首先需要的就是签名,签名后得到的值参考字段paySign

      2. 签名是在小程序里完成,还是在后台完成,这是第一个疑问

      3. 看下例如对参数的介绍,理解一下每个参数怎么来的。

    例如:

    wx.requestPayment
    (
    {
    //时间戳字段,即当前的时间(后台生成签名时使用的)
    "timeStamp": "1414561699",
    //随机数最长32,这个随机数没有其他规定,随便给一个就可以(后台生成签名时使用的)
    "nonceStr": "5K8264ILTKCH16CQ2502SI8ZNMTM67VS",
    //在后台的预支付接口中获得(后台生成签名时使用的)
    "package": "prepay_id=wx201410272009395522657a690389285100",
    //v3支付固定前面类型RSA
    "signType": "RSA",
    //支付签名,这个签名是在后台生成的,具体操作继续往下看。
    "paySign": "oR9d8PuhnIc+YZ8cBHFCwfgpaK9gd7vaRvkYD7rthRAZ\/X+QBhcCYL21N7cHCTUxbQ+EAt6Uy+lwSN22f5YZvI45MLko8Pfso0jm46v5hqcVwrk6uddkGuT+Cdvu4WBqDzaDjnNa5UK3GfE1Wfl2gHxIIY5lLdUgWFts17D4WuolLLkiFZV+JSHMvH7eaLdT9N5GBovBwu5yYKUR7skR8Fu+LozcSqQixnlEZUfyE55feLOQTUYzLmR9pNtPbPsu6WVhbNHMS3Ss2+AehHvz+n64GDmXxbX++IOBvm2olHu3PsOUGRwhudhVf7UcGcunXt8cqNjKNqZLhLw4jq\/xDg==",
    "success":function(res){},
    "fail":function(res){},
    "complete":function(res){}
    }
    )

    注意:从上面代码的解释中可以看出,下面的这几个字段都是从后台拿到的

    //时间戳字段,即当前的时间(后台生成签名时使用的)
    "timeStamp": "1414561699",
    //随机数最长32,这个随机数没有其他规定,随便给一个就可以(后台生成签名时使用的)
    "nonceStr": "5K8264ILTKCH16CQ2502SI8ZNMTM67VS",
    //在后台的预支付接口中获得(后台生成签名时使用的)
    "package": "prepay_id=wx201410272009395522657a690389285100",
    //支付签名,这个签名是在后台生成的,具体操作继续往下看。
    "paySign": "oR9d8PuhnIc+YZ8cBHFCwfgpaK9gd7vaRvkYD7rthRAZ\/X+QBhcCYL21N7cHCTUxbQ+EAt6Uy+lwSN22f5YZvI45MLko8Pfso0jm46v5hqcVwrk6uddkGuT+Cdvu4WBqDzaDjnNa5UK3GfE1Wfl2gHxIIY5lLdUgWFts17D4WuolLLkiFZV+JSHMvH7eaLdT9N5GBovBwu5yYKUR7skR8Fu+LozcSqQixnlEZUfyE55feLOQTUYzLmR9pNtPbPsu6WVhbNHMS3Ss2+AehHvz+n64GDmXxbX++IOBvm2olHu3PsOUGRwhudhVf7UcGcunXt8cqNjKNqZLhLw4jq\/xDg=="
    • 后台生成微信小程序支付需要的支付签名paySign(Java 后台)

    例如(这里使用的是微信支付官方Java SDK实现的):
    微信支付官方Java SDK入口
    [微信支付官方Java SDK入口](https://github.com/wechatpay-apiv3/wechatpay-java)

    import com.wechat.pay.java.core.RSAConfig;
    import com.wechat.pay.java.core.util.IOUtil;
    import com.wechat.pay.java.core.util.PemUtil;
    import com.wechat.pay.java.core.cipher.RSASigner;
    import com.wechat.pay.java.core.cipher.SignatureResult;
    import com.wechat.pay.java.core.cipher.Signer;
    //注意 SignInfo 是我自己定义的一个实体类,你们也可以自己定义一个
    public SignInfo generateWeixinSigner(String prepayid){
    String privateKeyStr = null;
    try {
    //privateKeyFromPath 是私钥文件(apiclient_key.pem)路径
    //apiclient_key.pem 这个文件路径在你的后台代码里动态获取
    privateKeyStr = IOUtil.loadStringFromPath(privateKeyFromPath);
    } catch (IOException e) {
    e.printStackTrace();
    }
    PrivateKey key = PemUtil.loadPrivateKeyFromString(privateKeyStr);
    //拿到 预支付ID(prepayid)值
    //假如-预支付ID:prepayid = "woLD34lk34lk345l345jl345j3l4534ok"
    //假如-小程序ID:appid= "applasdiasdfljsf"
    //假如-证书序列号:weixinMerchantSerialNumber = "ASDF98SDFAS9D8FASD9F8SAFAS9DF8ASDF98ASDF9S8F98"
    String packageStr = String.format("prepay_id=%s", prepayid);
    SignInfo info = new SignInfo();
    info.setAppId(appid);
    info.setTimeStamp("" + System.currentTimeMillis());
    info.setNonceStr(generateOrderNumber(30));
    info.setPackageStr(packageStr);
    String str = info.toString();
    Signer rsaSigner = new RSASigner(weixinMerchantSerialNumber, key);
    SignatureResult signatureResult = rsaSigner.sign(str);
    info.setPaySign(signatureResult.getSign());
    return info;
    }
    • 我的 SignInfo 实体类实现
    import com.wechat.pay.java.core.util.StringUtil;
    import lombok.Data;
    @Data
    public class SignInfo {
    private String appId;
    private String timeStamp;
    private String nonceStr;
    private String packageStr;
    private String signType="RSA";
    private String paySign;
    @Override
    public String toString() {
    StringBuilder sb = new StringBuilder();
    sb.append(StringUtil.toIndentedString(appId)).append("\n");
    sb.append(StringUtil.toIndentedString(timeStamp)).append("\n");
    sb.append(StringUtil.toIndentedString(nonceStr)).append("\n");
    sb.append(StringUtil.toIndentedString(packageStr)).append("\n");
    return sb.toString();
    }
    }
    • 我生成随机码实现方法
    import org.apache.commons.lang3.RandomStringUtils;
    //这里使用了 org.apache.commonslang3 中的 RandomStringUtils。
    //想要使用的需要加入依赖包
    /*
    org.apache.commons
    commons-lang3
    3.12.0
    */
    public static String generateOrderNumber(int size) {
    String abc = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890";
    //指定长度size = 30
    //指定取值范围 abc 如果不指定取值范围,中文环境下会乱码
    String str = RandomStringUtils.random(size, abc);
    }
    • 如果你的秘钥文件,跟我一样放在resources目录下,可以使用下面方法获取路径
    import org.springframework.core.io.ClassPathResource;
    ClassPathResource resourcePrivateKey = new ClassPathResource("apiclient_key.pem");
    ClassPathResource resourceWeixinPayCert = new ClassPathResource("wechatpay_3A4AF69999DF01F39BB08C21C1C29B6AA17C074N.pem");
    String privateKeyFromPath = resourcePrivateKey.getFile().getPath();
    String wechatPayCertificatesFromPath = resourceWeixinPayCert.getFile().getPath();
  • 相关阅读:
    vue3中两个el-select下拉框选项相互影响
    # Vue3 toRef 和 toRefs 函数
    Cookie和Session
    将HTML页面中的table表格元素转换为矩形,计算出每个单元格的宽高以及左上角坐标点,输出为json数据
    大二Web课程设计 HTML+CSS制作苹果商城网站 Apple商城 8个页面
    小熊派-FreeRTOS-点灯学习过程-20221029
    校园网站毕业设计,校园网站设计与实现,校园网站论文作品参考
    21天打卡挑战学习MySQL——《Window下安装MySql》第一周 第三篇
    【Java刷题进阶】基础入门篇⑦
    微信小程序小汇总
  • 原文地址:https://www.cnblogs.com/ilii/p/16854165.html