作者遇到的一些问题,分享。
发起预支付的时候时间戳和随机字符串(32位以内)要和后续返回前端时paySin加密签名保持一致,v3只支持RSA。
其他问题导致签名验证失败,可以使用官方签名工具进行验签。
使用工具时要注意换行符n/否则会展示验签失败 (或使用官方文档示例以及test.pem生成) 如果和官方文档所示一致,则证明加密过程没有问题,为加密时所使用的参数问题。
其余注意,引入包要确认与相关程序一致(partnerpayments包为子商户(特约商户 / 二级商户)payments包为直连商户等),如app,小程序,公众号,所使用的包不一致。加密参数也不一致。
小程序为
"package":"prepay_id=wx00000000000000000000000000",
具体对应官方文档。
微信沙箱问题
相关技术论坛说v3有沙箱。不确定。
作者直接申请金额测试,不想研究沙箱了。
推荐方式。(无需关注加密等情况。)
// 调用prepayWithRequestPayment方法,直接返回包含调起支付所需的所有参数
com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse response =
service.prepayWithRequestPayment(request);
// 从response中获取SDK生成的所有参数(包含timeStamp和nonceStr)
String timeStamp = response.getTimeStamp();
String nonceStr = response.getNonceStr();
String packageVal = response.getPackageVal();
String signType = response.getSignType();
String paySign = response.getPaySign(); // SDK已经完成签名,无需二次签名
加密具体为
/**
* 构造微信支付V3签名串
* @param appId 应用ID
* @param timeStamp 10位秒级时间戳
* @param nonceStr 随机字符串(建议32位)
* @param prepayId 预支付交易会话标识
* @return 符合要求的签名串
*/
public static String buildSignStr(String appId, String timeStamp, String nonceStr, String prepayId) {
// 校验核心参数
...略过
// 严格按格式拼接(末尾保留\n)
String signStr = String.format("%s\n%s\n%s\nprepay_id=%s\n",
appId.trim(), timeStamp.trim(), nonceStr.trim(), prepayId.trim());
log.info("构造完成的签名串:\n{}", signStr);
return signStr;
}
/**
* 生成支付签名(满足SHA256withRSA加密条件)
* @param signStr 待签名串
* @param privateKeyPath 商户私钥文件绝对路径(PKCS#8格式)
* @return Base64编码后的签名
* @throws Exception 签名过程异常
*/
public static String generatePaySign(String signStr, String privateKeyPath) throws Exception {
// 1. 读取并清理私钥(核心:兼容PKCS#8格式)
String privateKeyContent = new String(Files.readAllBytes(Paths.get(privateKeyPath)), StandardCharsets.UTF_8);
String cleanPrivateKey = cleanPrivateKey(privateKeyContent);
log.info("清理后私钥长度:{}", cleanPrivateKey.length());
// 2. 校验私钥格式(Base64合法性)
validateBase64(cleanPrivateKey);
// 3. 解析私钥(PKCS#8格式)
PrivateKey rsaPrivateKey = getPrivateKey(cleanPrivateKey);
// 4. 执行SHA256withRSA签名
byte[] signBytes = signWithSHA256RSA(signStr, rsaPrivateKey);
// 5. Base64编码返回
String paySign = Base64.getEncoder().encodeToString(signBytes);
log.info("生成的支付签名:{}", paySign);
return paySign;
}
/**
* 清理私钥字符串(移除标识头、尾、空白字符)
*/
private static String cleanPrivateKey(String privateKeyContent) {
return privateKeyContent
.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replaceAll("\r\n", "")
.replaceAll("\n", "")
.replaceAll("\s+", "");
}
/**
* 清理私钥字符串(移除标识头、尾、空白字符)
*/
private static String cleanPublicKey(String publicKeyContent) {
return publicKeyContent
.replace("-----BEGIN PUBLIC KEY-----", "")
.replace("-----END PUBLIC KEY-----", "")
.replaceAll("\r\n", "")
.replaceAll("\n", "")
.replaceAll("\s+", "");
}
/**
* 执行SHA256withRSA签名
*/
private static byte[] signWithSHA256RSA(String signStr, PrivateKey privateKey) throws Exception {
Signature signature = Signature.getInstance(SIGN_ALGORITHM);
signature.initSign(privateKey);
// 必须使用UTF-8编码,微信支付固定要求
signature.update(signStr.getBytes(StandardCharsets.UTF_8));
byte[] signBytes = signature.sign();
log.info("SHA256withRSA签名完成,签名字节长度:{}", signBytes.length);
return signBytes;
}
具体使用maven依赖为
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.14</version>
</dependency>
题外话,感觉对接微信支付要比支付宝要麻烦一些(起码支付宝沙箱环境很明确)。官方微信文档v2/v3比较穿插。
注:本文只是接入v3注意事项。