退款通知签名错误
GongZhengMe opened this issue · 16 comments
你好,我使用这个sdk在接受微信退款通知的时候发现错误。接下来我描述下具体情况
首先我在支付通知这里,是没有任何错误的,无论是validate(request, verifier)还是 validateNotification(notification)都没有任何报错。但是在我使用退款通知时, validate(request, verifier)这一步出现了报错,报错代码段为
if (!verifier.verify(serialNumber, message, signature)) {
val errorMessage =
String.format("验签失败:serial=[%s] message=[%s] sign=[%s]", serialNumber, String(message), signature)
throw ValidationException(errorMessage)
}
我复写了这段代码,将 validate(request, verifier)这个方法注释掉,直接调用validateNotification,与setDecryptData都是可以正常使用的,请问你们是否有遇见这个bug
是否有报错的退款通知的具体数据,我来看看
是否有报错的退款通知的具体数据,我来看看
你好,这些参数是我们的正式支付里面携带的参数,请问我是否方便加你的微信将参数告知你
如果方便的话,可以再发一次请求,然后将报错的退款通知的通知ID
发到这儿
Contributor
如果方便的话,可以再发一次请求,然后将报错的退款通知的
通知ID
发到这儿
这是刚刚发送的退款通知id:f4cadfdb-821b-524b-93c6-9e49e861efc4
我使用自己的测试信息试了一下,没有复现验签失败的场景,你可以先按照下面的思路检查一下:
- 确认用于验证签名的平台证书序列号和回调通知的序列号是否一致,不一致会导致验签失败。可以对比回调通知请求头的Wechatpay-Serial跟你设置的
verifier
中的证书序列号是否一致。 - 如果确认verifier中的平台证书无误,确认用于验签的信息是否设置错误,或者是否有编码问题。
好的哈,我以为有bug的原因是因为,微信支付的回调,执行验签,解密都没问题。目前我只有退款回调遇到了这个问题,只有验签有问题,但是解密是ok的。
两个通知的回调,我是公用了一个方法,应该不存在参数设置错误或者编码问题。我回头对一下,你说的情况1,如果实在不行,希望你能给我个邮箱,我把我的所有参数都给你下,你看可以吗
不用给我参数的,咱们在保证隐私安全的情况下一起来解决问题。
我验证了f4cadfdb-821b-524b-93c6-9e49e861efc4
这个通知的回调签名,是可以验证通过的。你可以贴下调用代码吗?
我们公司用的语言是kotlin,这里是我的调用代码段,因为公司common包里有jackson所以我自己用了gson来解析,并将下划线字段强转驼峰,来实现的
val nonce = headers["wechatpay-nonce"]
val timestamp = headers["wechatpay-timestamp"]
val signature = headers["wechatpay-signature"]
val verifier = getWechatVerifier(
wechatPayParam.merchantId!!,
wechatPayParam.merchantSerialNumber!!,
wechatPayParam.apiV3Key!!
)
val request: NotificationRequest = NotificationRequest.Builder()
.withSerialNumber(wechatPayParam.wechatPaySerial)
.withNonce(nonce)
.withTimestamp(timestamp)
.withSignature(signature)
.withBody(Gson().toJson(wxCallbackReqVo))
.build();
validate(request, verifier)
// 因为微信使用的jackson版本过高导致readValue会报错,所以不采用微信sdk里面的方法,而是使用自定的Gson解析
val gsonBuilder: GsonBuilder = GsonBuilder()
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES);
val notification = gsonBuilder.create().fromJson(request.body, Notification::class.java)
validateNotification(notification)
setDecryptData(notification, wechatPayParam.apiV3Key!!.toByteArray(StandardCharsets.UTF_8))
这里是我的调用代码段,这个代码段在对微信支付回调是ok的
getWechatVerifier
这段代码可以贴下吗?
verifier
是用什么方式生成的呢,new CertificatesVerifier()
还是使用CertificatesManager
获得的?
getWechatVerifier
这段代码可以贴下吗?verifier
是用什么方式生成的呢,new CertificatesVerifier()
还是使用CertificatesManager
获得的?
是根据demo使用的CertificatesManager
private fun getWechatVerifier(
merchantId: String,
merchantSerialNumber: String,
apiV3Key: String,
): Verifier {
val certificatesManager = CertificatesManager.getInstance();
// 向证书管理器增加需要自动更新平台证书的商户信息
certificatesManager.putMerchant(
merchantId, WechatPay2Credentials(
merchantId,
PrivateKeySigner(merchantSerialNumber, getPrivateKeyByFile())
), apiV3Key.toByteArray(StandardCharsets.UTF_8)
);
return certificatesManager.getVerifier(merchantId);
}
上面的代码我没有看出问题。验签失败,我能想到两个方向:
- verifier有问题。你的业务代码中是否设置了日志打印?日志打印级别设置为debug,可以看到是否有异常日志输出,例如verifier获取为空,下载失败等。另外,你可以尝试将verifier改为
new CertificatesVerifier()
的方式,看下是否能验签通过。以此来确定是否为verifier的问题。 - 验签使用的数据有问题。因为你之前提及可以验证通过支付成功回调通知,所以我在想是否跟验签使用的数据有关。可以对比下
Gson().toJson(wxCallbackReqVo)
生成的body是否和微信支付回调通知的原始报文中的body一致。
上面的代码我没有看出问题。验签失败,我能想到两个方向:
- verifier有问题。你的业务代码中是否设置了日志打印?日志打印级别设置为debug,可以看到是否有异常日志输出,例如verifier获取为空,下载失败等。另外,你可以尝试将verifier改为
new CertificatesVerifier()
的方式,看下是否能验签通过。以此来确定是否为verifier的问题。- 验签使用的数据有问题。因为你之前提及可以验证通过支付成功回调通知,所以我在想是否跟验签使用的数据有关。可以对比下
Gson().toJson(wxCallbackReqVo)
生成的body是否和微信支付回调通知的原始报文中的body一致。
嗯我今天想了下,可能是这个我绕过jackson用Gson解析导致的问题,现在公司网络有问题,我后续准备检查下body。至于你说的1里面verifier我打断点看过,是有值且有mchid的,应该不是1的问题
不对我刚刚看了下我代码,我是在validateSign后使用的Gson,目前报错的方法是validateSign,还没有到Gson解析那一步。可能我最后只能使用折中的办法,无法好的定位具体问题。而是收到退款回调后,跳过验签,直接解析,将解析得到的退款单号,去调用 单笔退款查询接口,以此来进行我的后续业务操作。这样做我认为也是安全的吧
你在构造NotificationRequest的时候,使用了withBody(Gson().toJson(wxCallbackReqVo))。在NotificationRequest里面,会使用传入的body来生成验签的message信息。所以,如果body有问题,验签会失败的。
另外,如果业务需要上线,我们建议是要解决问题,不要跳过验签,以防请求信息被篡改,会有安全风险。
你在构造NotificationRequest的时候,使用了withBody(Gson().toJson(wxCallbackReqVo))。在NotificationRequest里面,会使用传入的body来生成验签的message信息。所以,如果body有问题,验签会失败的。 另外,如果业务需要上线,我们建议是要解决问题,不要跳过验签,以防请求信息被篡改,会有安全风险。
好的,谢谢提醒,我之后仔细比较排查
谢谢支持,问题我已经解决了,就是Gson的问题,因为退款的resource密文里面有=号,gson默认把=号转成了\u003d,导致签名失败,已解决测试。谢谢你的支持,目前我已经全流程跑通了。