0

微信支付v3接口的 官方 Java SDK - 代码召唤师

 1 year ago
source link: https://www.cnblogs.com/ilii/p/16854165.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

微信支付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)

    428878-20221103121654621-490978601.png
    说明:这个是小程序版,如果是其他平台可以在左侧找对应目录
    注意:这个流程走下来你会拿到一个压缩文件类似下图
    428878-20221103121921570-2077894843.png
    图:这个压缩文件里有三个证书(解压后如下图),三个证书文件拷贝到你的开发平台里,使用方式继续往下看
    428878-20221103122111797-1485334088.png
  • 扫码进入商户平台的入口
    微信商户平台
    [微信商户平台](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)

    428878-20221103124159070-841535606.png
    说明:上传证书文件: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.pemcmd中路径: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<X509Certificate> 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目录下,可以使用下面方法获取路径
428878-20221103204835906-961424325.png
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; @Datapublic 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。//想要使用的需要加入依赖包/*<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.12.0</version></dependency>*/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();

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK