记录spring cloud fein编写微信支付client相关

编程入门 行业动态 更新时间:2024-10-07 10:24:16

记录spring <a href=https://www.elefans.com/category/jswz/34/1771354.html style=cloud fein编写微信支付client相关"/>

记录spring cloud fein编写微信支付client相关

记录下遇到的问题和几个关键的点

背景:

cloud版本Dalston.SR5

feign client的注解用的是@RequestMapping的形式

微信那边的接口是使用XML通信的, Request和response都是XML, 接口参数需签名, 部分接口需要双向认证.

遇到的问题:

1. 如何在feign里设置双向认证证书

2. 若使用Bean发送或接收参数, 会自动使用application/json的方式处理数据

3. 使用bean作为发送参数后签名失败

问题解决:

1. 如何在feign里设置双向认证证书?

自定义feign的configuration, 然后@Bean覆盖Client. 在新的构造里添加双向认证.

    @Beanpublic Client feignClient() {return new Client.Default(TrustingSSLSocketFactory.get("MMPayCert"),new NoopHostnameVerifier());}

TrustingSSLSocketFactory类参考自

.java

因为一开始不知道微信支付的证书key是MMPayCert, 对SSLContext的构造部分代码做了改动, 使构造时载入加载p12文件的KeyStore类, 并在SSLContext成功构建后打断点查看内部属性, 找到对应的key. 改动后的类初始化代码:

private TrustingSSLSocketFactory(String serverAlias) {try {KeyStore keyStore =loadKeyStore(new ClassPathResource("/cert/apiclient_cert.p12").getInputStream());SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, KEYSTORE_PASSWORD).build();this.delegate = sslcontext.getSocketFactory();this.serverAlias = serverAlias;if (serverAlias.isEmpty()) {this.privateKey = null;this.certificateChain = null;} else {this.privateKey = (PrivateKey) keyStore.getKey(serverAlias, KEYSTORE_PASSWORD);Certificate[] rawChain = keyStore.getCertificateChain(serverAlias);this.certificateChain = Arrays.copyOf(rawChain, rawChain.length, X509Certificate[].class);}} catch (Exception e) {throw new RuntimeException(e);}}

问题1解决. 这时我写的client method还是纯String通信的, 觉得需要改进, 就写了bean来接收响应, 请求体暂不改, 还是String, 因为涉及签名比较麻烦.

由此遇到了问题2 

2. 若使用Bean发送或接收参数, 会自动使用application/json的方式处理数据

查日志, 微信的接口返回数据跟accept头完全对不上, 编写自定义的docker解决, 虽说是自定义, 但其实只需要选取现成的合适的convert类再继承后重设下可支持的MediaType就好了.

public class WxPayResponseConverter extends MappingJackson2XmlHttpMessageConverter {public WxPayResponseConverter() {List<MediaType> mediaTypes = new ArrayList<>();mediaTypes.add(MediaType.TEXT_HTML);mediaTypes.add(MediaType.TEXT_XML);setSupportedMediaTypes(mediaTypes);}
}

使用方法, 同样写入feign配置类里

    @Beanpublic Decoder decoder() {HttpMessageConverter<?> additional = new WxPayResponseConverter();ObjectFactory<HttpMessageConverters> objectFactory = () -> new HttpMessageConverters(additional);return new ResponseEntityDecoder(new SpringDecoder(objectFactory));}

此时数据的接收已可直接从XML注入bean.

然后再是把请求数据结构也改成bean. 

同样添加自定义encoder, 与上面几乎一样, 换个返回类型和方法名就行. 不再贴代码.

改完后测试, 发现返回提示XML解析错误, 看日志发现过去的还是JSON数据, 且请求头为application/json

在@RequestMapping里添加headers={"content-type=text/xml"}再试. 结果依然不行, 可以看到日志里请求头已经是text/xml 但数据还是json格式.

debug SpringEncoder的encode方法, 里面有重要的一句

Collection<String> contentTypes = (Collection)request.headers().get("Content-Type");

请求头必须是Content-Type............................................................WTF

修改后

@RequestMapping(value = WX_PAY_API_B2C, method = RequestMethod.POST, headers = {"Content-Type=text/xml"})

然后再试, 出现问题3. 提示签名失败.

3 使用bean作为发送参数后签名失败

签名的方法原本使用的是一个给Map<String, String> 签名的静态工具类里的方法, 为了能给bean使用稍作了改动, 时签名之前完成bean -> Map<String, String>的转换

然后微信支付里的参数有许多带下划线, 非驼峰格式, 且我的bean加了驼峰处理, 使用Jackon注解定义转换的参数名. 

问题就出在这里!!!

上述bean2map的转换使用反射完成, 没做判断, 直接使用了bean里field的name来做map的key, 导致签名参数名与传递的不符.

解决, 增加判断逻辑. 保证签名正常, 只贴bean2map的部分, 其余的签名代码就不贴了.

        Map<String, String> data = new HashMap<>();Field[] declaredFields = object.getClass().getDeclaredFields();for (Field field : declaredFields) {field.setAccessible(true);String fieldName = field.isAnnotationPresent(JsonProperty.class) ?field.getAnnotation(JsonProperty.class).value() : field.getName();Object value = field.get(object);if (value == null)continue;data.put(fieldName, value.toString());}
至此实现feign客户端与微信支付API的通信

更多推荐

记录spring cloud fein编写微信支付client相关

本文发布于:2024-02-28 04:09:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1768197.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:cloud   spring   client   fein

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!