Java中的微信支付(1):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*/
@SneakyThrows
String 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\"";// 生成tokenreturn String.format(TOKEN_PATTERN,wechatPayProperties.getMchId(),nonceStr, timestamp, serialNo, signature);
}
将生成的Token
按照上述格式放入请求头中即可完成签名的使用。
5. 总结
本文我们对微信支付 V3 版本的难点签名以及签名的使用进行了完整的分析,同时对 API 证书的解析也进行了讲解,相信能够帮助你在支付开发中解决一些具体的问题。后面有时间我还将对签名的验证进行讲解,关注下方公众号,及时获取系列知识。
往期推荐
10个你可能不曾用过却很有用的 LINUX 命令
分享一个Java开发都用得到的密码摘要算法包
程序员编码时都戴耳机?到底在听什么?
Spring Data 发布更改版本管理方案之后的第一个版本:2020.0.0
终于还是对“带薪拉SHI”出手了...
Spring 5.3 正式GA,维护至2024年,4.3版本年末结束维护
﹀
﹀
﹀
深度内容
推荐加入
最近热门内容回顾 #社会人系列
Java中的微信支付(1):API V3版本签名详解相关推荐
- Java中的微信支付:API V3对微信服务器响应进行签名验证3
前言 牢记一句话:公钥加密,私钥解密:私钥加签,公钥验签. 微信支付V3版本前两篇分别讲了如何对请求做签名和如何获取并刷新微信平台公钥,本篇将继续展开如何对微信支付响应结果的验签. 2. 为什么要对响 ...
- Java中的微信支付(2):API V3 微信平台证书的获取与刷新
1. 前言 在Java 中的微信支付(1):API V3 版本签名详解一文中胖哥讲解了微信支付 V3 版本 API 的签名,当我方(你自己的服务器)请求微信支付服务器时需要根据我方的API 证书对参数 ...
- Java中创建String的两道面试题及详解
转载自 Java中创建String的两道面试题及详解 我们知道创建一个String类型的变量一般有以下两种方法: String str1 = "abcd";String str2 ...
- java显示参数,Java中的隐式参数和显示参数实例详解
在学习java的过程中,我们会遇到许多的问题.下面我们就来看看什么是隐式参数和显示参数. 显式参数,就是平时见到的在方法名括号中间的参数,就是所谓能看得见的参数www.cppcns.com. 隐式参数 ...
- java 中实现微信支付退款功能案例
微信支付功能做了太多,今天又做了支付.退款.查询.提现等等,顺便把支付和退款代码贴出来,希望对初学者有点帮助. 首先调用微信支付退款 API 地址 https://pay.weixin.qq.com/ ...
- android V1,V2,V3,V4签名详解
前言 最近帮测试做了一点关于签名的需求,今天就和各位同学简单聊一聊关于签名的那些事儿. 如果问到 Android 为什么需要签名?大家都可能想到官网的解释: ❝ Android 系统要求所有 APK ...
- Java中的微信支付: 微信API-V3签名生成工具类
微信支付API v3简介 微信官方文档地址 为了在 保证支付 安全的前提下,带给商户 简单.一致且易用的开发体验,我们推出了全新的微信支付API v3. 相较于之前的微信支付API,主要区别是: 遵循 ...
- 微信实现APP下单接口(微信支付开发API V3接口调用)
准备工作 : 有微信商户平台 : mchid:微信商户id appid:商户appid 商户证书:商户可登录微信商户平台,在[账户中心]->[API安全]目录下载证书 证书文件名 :apicli ...
- java微信支付必要参数_微信支付 开发账号体系各参数详解
商户在微信公众平台提交申请资料以及银行账户资料,资料审核通过并签约后,可以获得表6-4所示帐户(包含财付通的相关支付资金账户),用于公众帐号支付. 帐号及作用: appid :公众帐号身份的唯一标识. ...
最新文章
- python自动化测试难不难_Python测试自动化好学还是Pythonweb开发好学?
- poj 3662 Telephone Lines spfa算法灵活运用
- 那些有用但不为大家所熟知的 Java 特性
- 创建c语言编译错误,创建C语言项目时,无法编译成*.exe文件,提示系统找不到指定的文件...
- 如何衡量机器与人类的智能关系,AI智商评测标准专家研讨会邀请
- 常用的正则表达式(持续更新。。)
- BZOJ 3208: 花神的秒题计划Ⅰ
- js 浅拷贝直接赋值_JS中的赋值、浅拷贝与深拷贝
- Java,使用泛型构建自己的工具包——包装System.out
- GDB调试之ptrace实现原理
- 80211n标准建链速率计算
- redis使用sysc超时_优雅的处理Redis访问超时
- 又是整数划分(poj1032)
- Python爬取网上文章并发表到微信公众号
- yaahp层次分析法步骤_层次模型构造
- [工业互联-6]:PLC工业控制系统快速概览
- ps去水印(操作流程)
- 个人博客网站编写(01)
- Apsara Clouder基础技能认证-阿里巴巴编程规范考试流程
- 大学计算机基础ppt重点,大学计算机基础.ppt
热门文章
- linux cat 合并文本 中间添加分隔符
- kerberos 身份认证 简介
- python SSL error: [SSL: TLSV1_ALERT_PROTOCOL_VERSION] tlsv1 alert protocol version (_ssl.c:590) 解决方法
- linux 路由跟踪表满错误 nf_conntrack: table full, dropping packet 原理解决方法
- linux gnome虚拟文件系统 gvfs和gnome-vfs 简介
- linux c 线程的创建、线程等待、线程终止、线程分离
- linux c 内核 ISO C90 forbids mixed declarations and code 警告
- linux 日志文件utmp、wtmp、lastlog、messages介绍
- linux c 中 当前函数名 文件名 可变参 不定参 宏使用
- KeUserModeCallback用法详解