问题发现

项目中有微信支付功能,也可以微信退款,因为自己写支付代码比较臃肿,所以用了第三方包IJPay来实现支付和退款功能,它封装了一些第三方支付的方法,比如支付宝、微信、银联,使用了一年多没有问题,前端时间突然使用微信退款功能时报错:

cn.hutool.core.io.IORuntimeException: SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate)

问题研究

上网搜索发现微信社区看到也有人出同样的错:微信社区链接

百度上搜索也有人说是jdk版本问题的,但是我这个一开始没错所以我觉得不关jdk的问题,也没有往这方面研究,然后我找到IJPay的码云Issues:IJPay码云链接

里面也有人提到这个问题,IJPay作者也发现了,提供了版本升级,之前退款用的方法是orderRefund,现在加了一个orderRefundByProtocol方法,可以自己手动输入协议,我想这个问题肯定立马解决了,测试orderRefundByProtocol方法的协议填null,报空指针异常,填“”、“SSLv3”、“SSLv2”均报以上同样的错误(No appropriate protocol)

就觉得很奇怪,然后追溯IJPay的源码:

// 1.自己的业务代码
    String refundStr = WxPayApi.orderRefundByProtocol(false, params, certPath, account.getMchid(),"");
// 2
    public static String orderRefundByProtocol(boolean isSandbox, Map<String, String> params, String certPath, String certPass, String protocol) {
        return execution(getReqUrl(WxApiType.REFUND, (WxDomain)null, isSandbox), params, certPath, certPass, protocol);
    }
// 3.此方法中的参数filePath为上一步中的参数protocol
    public static String execution(String apiUrl, Map<String, String> params, String certPath, String certPass, String filePath) {
        return doUploadSsl(apiUrl, params, certPath, certPass, filePath);
    }
// 4. 此方法参数filePath实际为protocol
    public static String doUploadSsl(String url, Map<String, String> params, String certPath, String certPass, String filePath) {
        return HttpKit.getDelegate().upload(url, WxPayKit.toXml(params), certPath, certPass, filePath);
    }
// 5.
    public String upload(String url, String data, String certPath, String certPass, String filePath) {
        return this.upload(url, data, certPath, certPass, filePath, "TLSv1");
    }
// 6.此方法中的protocol为5步骤中的代码写死的“TLSv1”,那么是否实际执行的时候还是用的TLSv1协议,而不是自己参数里面写的协议
    public String upload(String url, String data, String certPath, String certPass, String filePath, String protocol) {
        try {
            File file = FileUtil.newFile(filePath);
            return ((HttpRequest)HttpRequest.post(url).setSSLSocketFactory(SSLSocketFactoryBuilder.create().setProtocol(protocol).setKeyManagers(this.getKeyManager(certPass, certPath, (InputStream)null)).setSecureRandom(new SecureRandom()).build()).header("Content-Type", "multipart/form-data;boundary=\"boundary\"")).form("file", file).form("meta", data).execute().body();
        } catch (Exception var8) {
            throw new RuntimeException(var8);
        }
    }

以上是orderRefundByProtocol方法的调用链路,我发现可能是这个方法根本没有用上自己填写的协议,一筹莫展,如果抛弃IJPay重写代码的话,花费的时间较多,因为项目已发布在线上也怕出错,所以只好先在IJPay的Issues里面提了自己的问题,希望作者能给与解决办法,正当我想要放弃,等待作者解决办法的时候,我想到用IJPay里面封装的底层方法。

解决方案

通过以上源码追溯,orderRefund方法中调用的AbstractHttpDelegate这个抽象类的post方法,IJPay是写死的“TLSv1”,我们只需要单独调用这个方法,把里面的协议直接填写""就行了,贴上代码:

// 自己的业务代码
    String refundStr = HttpKit.getDelegate().post(WxPayApi.getReqUrl(WxApiType.REFUND, (WxDomain) null, false), WxPayKit.toXml(params), certPath, account.getMchid(), "");

// 实际调用AbstractHttpDelegate类中的方法
    public String post(String url, String data, String certPath, String certPass, String protocol) {
        try {
            return HttpRequest.post(url).setSSLSocketFactory(SSLSocketFactoryBuilder.create().setProtocol(protocol).setKeyManagers(this.getKeyManager(certPass, certPath, (InputStream)null)).setSecureRandom(new SecureRandom()).build()).body(data).execute().body();
        } catch (Exception var7) {
            throw new RuntimeException(var7);
        }
    }

没有悬念,一下就成功了!

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐