java基础教程栏目介绍Java中的微信支付,实现API V3版本签名。
1、 前言
最近在折腾微信支付,证书还是比较烦人的,所以有必要分享一些经验,减少你在开发微信支付时的踩坑。目前微信支付的API已经发展到V3版本,采用了流行的Restful风格。
今天来分享微信支付的难点——签名,虽然有很多好用的SDK但是如果你想深入了解微信支付还是需要了解一下的。
2. API证书
为了保证资金敏感数据的安全性,确保我们业务中的资金往来交易万无一失。目前微信支付第三方签发的权威的CA证书(API证书)中提供的私钥来进行签名。通过商户平台你可以设置并获取API证书。
切记在第一次设置的时候会提示下载,后面就不再提供下载了,具体参考说明。
设置后找到zip
压缩包解压,里面有很多文件,对于JAVA开发来说只需要关注apiclient_cert.p12
这个证书文件就行了,它包含了公私钥
,我们需要把它放在服务端并利用Java解析.p12
文件获取公钥私钥。
务必保证证书在服务器端的安全,它涉及到资金安全。
解析API证书
接下来就是证书的解析了,证书的解析有网上很多方法,这里我使用比较“正规”的方法来解析,利用JDK安全包的java.security.KeyStore
来解析。
微信支付API证书使用了PKCS12
算法,我们通过KeyStore
来获取公私钥对的载体KeyPair
以及证书序列号serialNumber
,我封装了工具类:
import org.springframework.core.io.ClassPathResource;import java.security.KeyPair;import java.security.KeyStore;import java.security.PrivateKey;import java.security.PublicKey;import java.security.cert.X509Certificate;/** * KeyPairFactory * * @author dax * @since 13:41 **/public class KeyPairFactory { private KeyStore store; private final Object lock = new Object(); /** * 获取公私钥. * * @param keyPath the key path * @param keyAlias the key alias * @param keyPass password * @return the key pair */ public KeyPair createPKCS12(String keyPath, String keyAlias, String keyPass) { ClassPathResource resource = new ClassPathResource(keyPath); char[] pem = keyPass.toCharArray(); try { synchronized (lock) { if (store == null) { synchronized (lock) { store = KeyStore.getInstance("PKCS12"); store.load(resource.getInputStream(), pem); } } } X509Certificate certificate = (X509Certificate) store.getCertificate(keyAlias); certificate.checkValidity(); // 证书的序列号 也有用 String serialNumber = certificate.getSerialNumber().toString(16).toUpperCase(); // 证书的 公钥 PublicKey publicKey = certificate.getPublicKey(); // 证书的私钥 PrivateKey storeKey = (PrivateKey) store.getKey(keyAlias, pem); return new KeyPair(publicKey, storeKey); } catch (Exception e) { throw new IllegalStateException("Cannot load keys from store: " + resource, e); } } }复制代码
眼熟的可以看出是胖哥Spring Security教程中JWT用的公私钥提取方法的修改版本,你可以对比下不同之处。
这个方法中有三个参数,这里必须要说明一下:
-
keyPath
API证书apiclient_cert.p12
的classpath
路径,一般我们会放在resources
路径下,当然你可以修改获取证书输入流的方式。 -
keyAlias
证书的别名,这个微信的文档是没有的,胖哥通过加载证书时进行DEBUG获取到该值固定为Tenpay Certificate
。 -
keyPass
证书密码,这个默认就是商户号,在其它配置中也需要使用就是mchid
,就是你用超级管理员登录微信商户平台在个人资料中的一串数字。
3. V3签名
微信支付V3版本的签名是我们在调用具体的微信支付的API时在HTTP请求头中携带特定的编码串供微信支付服务器进行验证请求来源,确保请求是真实可信的。
签名格式
签名串的具体格式,一共五行一行也不能少,每一行以换行符\n
结束。
HTTP请求方法\n URL\n 请求时间戳\n 请求随机串\n 请求报文主体\n复制代码
-
HTTP请求方法 你调用的微信支付API所要求的请求方法,比如APP支付为
POST
。 -
URL 比如APP支付文档中为
https://api.mch.weixin.qq.com/v3/pay/transactions/app
,除去域名部分得到参与签名的URL。如果请求中有查询参数,URL末尾应附加有'?'和对应的查询字符串。这里为/v3/pay/transactions/app
。 -
请求时间戳 服务器系统时间戳,保证服务器时间正确并利用
System.currentTimeMillis() / 1000
获取即可。 -
请求随机串 找个工具类生成类似
593BEC0C930BF1AFEB40B4A08C8FB242
的字符串就行了。 -
请求报文主体 如果是GET请求直接为空字符
""
;当请求方法为POST
或PUT
时,请使用真实发送的JSON
报文。图片上传API,请使用meta
对应的JSON
报文。
生成签名
然后我们使用商户私钥对按照上面格式的待签名串进行SHA256 with RSA签名,并对签名结果进行Base64编码得到签名值。对应的核心Java代码为:
/** * V3 SHA256withRSA 签名. * * @param method 请求方法 GET POST PUT DELETE 等 * @param canonicalUrl 例如 https://api.mch.weixin.qq.com/v3/pay/transactions/app?version=1 ——> /v3/pay/transactions/app?version=1 * @param timestamp 当前时间戳 因为要配置到TOKEN 中所以 签名中的要跟TOKEN 保持一致 * @param nonceStr 随机字符串 要和TOKEN中的保持一致 * @param body 请求体 GET 为 "" POST 为JSON * @param keyPair 商户API 证书解析的密钥对 实际使用的是其中的私钥 * @return the string */@SneakyThrowsString sign(String method, String canonicalUrl, long timestamp, String nonceStr, String body, KeyPair keyPair) { String signatureStr = Stream.of(method, canonicalUrl, String.valueOf(timestamp), nonceStr, body) .collect(Collectors.joining("\n", "", "\n")); Signature sign = Signature.getInstance("SHA256withRSA"); sign.initSign(keyPair.getPrivate()); sign.update(signatureStr.getBytes(StandardCharsets.UTF_8)); return Base64Utils.encodeToString(sign.sign()); }复制代码
4. 使用签名
签名生成后会同一些参数组成一个Token
放置到对应HTTP请求的Authorization
请求头中,格式为:
Authorization: WECHATPAY2-SHA256-RSA2048 {Token}复制代码
Token
由以下五部分组成:
发起请求的商户(包括直连商户、服务商或渠道商)的商户号
mchid
商户API证书序列号
serial_no
,用于声明所使用的证书请求随机串
nonce_str
时间戳
timestamp
签名值
signature
Token
生成的核心代码:
/** * 生成Token. * * @param mchId 商户号 * @param nonceStr 随机字符串 * @param timestamp 时间戳 * @param serialNo 证书序列号 * @param signature 签名 * @return the string */String token(String mchId, String nonceStr, long timestamp, String serialNo, String signature) { final String TOKEN_PATTERN = "mchid=\"%s\",nonce_str=\"%s\",timestamp=\"%d\",serial_no=\"%s\",signature=\"%s\""; // 生成token return String.format(TOKEN_PATTERN, wechatPayProperties.getMchId(), nonceStr, timestamp, serialNo, signature); }复制代码
将生成的Token
按照上述格式放入请求头中即可完成签名的使用。
5. 总结
本文我们对微信支付V3版本的难点签名以及签名的使用进行了完整的分析,同时对API证书的解析也进行了讲解,相信能够帮助你在支付开发中解决一些具体的问题。
相关免费学习推荐:java基础教程
相关文章介绍:如何实现小程序支付功能
以上是详解Java中的微信支付(1):API V3版本签名的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

VSCode Windows 64位 下载
微软推出的免费、功能强大的一款IDE编辑器

DVWA
Damn Vulnerable Web App (DVWA) 是一个PHP/MySQL的Web应用程序,非常容易受到攻击。它的主要目标是成为安全专业人员在合法环境中测试自己的技能和工具的辅助工具,帮助Web开发人员更好地理解保护Web应用程序的过程,并帮助教师/学生在课堂环境中教授/学习Web应用程序安全。DVWA的目标是通过简单直接的界面练习一些最常见的Web漏洞,难度各不相同。请注意,该软件中

EditPlus 中文破解版
体积小,语法高亮,不支持代码提示功能

mPDF
mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),

禅工作室 13.0.1
功能强大的PHP集成开发环境