微信退款操作总结
应用场景:在订单系统中,用户创建订单,发起支付;收到微信支付成功的回调,触发订单支付完成的处理。当用户发起退款申请时,通过业务控制权限审批,对于审批通过的退款申请,调用微信代理服务进行退款操作。
官方文档:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=9_4。
要点:请求需要双向证书。
相比支付(预支付)退款操作需要双向证书,即客户端也需要提供证书(openssl)。
实现:
- 加载API证书文件
private SSLContext getSSLContext() throws Exception {
File file1 = new File(this.getClass().getClassLoader().getResource(WxConfig.Merch_Cert_Filename).getFile());
if(!file1.exists()) { //检测证书文件是否存在
this.logger.D(Module_Tag, "加载API证书文件异常!");
return (null);
}
//商户双向证书(PKCS12证书)
KeyStore keyStore = KeyStore.getInstance("PKCS12");
//加载文件
FileInputStream fis1 = new FileInputStream(file1);
try {
//加载证书
keyStore.load(fis1, WxConfig.Merch_ID.toCharArray() );
} finally { //善后
fis1.close();
}
//加载证书
return SSLContextBuilder.create().loadKeyMaterial(keyStore, WxConfig.Merch_ID.toCharArray()).build();
}
- 发起https请求并获得反馈报文
/**
* 带证书推送XML内容
* @param url 请求url
* @param xmlText 提交xml报文
* @return 返回报文
* @throws Exception 异常信息
* @see 使用证书推送XML内容https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=4_3
*/
public String doPost_WithsCert(String url, String xmlText) throws Exception {
SSLContext ctx = getSSLContext();
if(null == ctx) {
return (null);
}
//指定TLSv1协议
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
ctx,
new String[] { "TLSv1" },
null,
NoopHostnameVerifier.INSTANCE);
CloseableHttpClient httpclient = HttpClients.custom()
.setSSLSocketFactory(sslsf)
.build();
try {
HttpPost post = new HttpPost(url);
//
ByteArrayEntity contents = new ByteArrayEntity(xmlText.getBytes(FsSpec.Charset_Default));
contents.setContentType("application/x-www-form-urlencoded");
post.setEntity(contents);
//执行POST方法
HttpResponse response = httpclient.execute(post);
//返回状态
int statusCode = response.getStatusLine().getStatusCode();
StringBuffer sb = new StringBuffer();
if (statusCode == HttpStatus.SC_OK) {
HttpEntity entity = response.getEntity();
if (entity != null) {
//指定内容的读取编码为UTF-8(服务方必须为UTF-8编码)
InputStreamReader isr = new InputStreamReader(entity.getContent(), FsSpec.Charset_Default);
BufferedReader br = new BufferedReader(isr);
String line=null;
while ((line = br.readLine()) != null) { //读取服务端反馈
sb.append(line);
}
}
post.abort();
} else {
post.abort();
return (null);
}
return (sb.toString() );
} catch( ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return (null);
}
说明:适用HttpClient 4.5.x版本库。
构造SSLConnectionSocketFactory的方法使用的是非废除的方法,其原型为:
/**
* @since 4.4
*/
public SSLConnectionSocketFactory(
final SSLContext sslContext,
final String[] supportedProtocols,
final String[] supportedCipherSuites,
final HostnameVerifier hostnameVerifier);
其中hostnameVerifier用的是NoopHostnameVerifier.INSTANCE
其描述为:
/**
* The NO_OP HostnameVerifier essentially turns hostname verification
* off. This implementation is a no-op, and never throws the SSLException.
*
* @since 4.4
*/
表明其是一个简单的、实际上不进行验证的验证器。
已废除的构造SSLConnectionSocketFactory的方法原型为:
/**
* @deprecated (4.4) Use {@link #SSLConnectionSocketFactory(javax.net.ssl.SSLContext,
* String[], String[], javax.net.ssl.HostnameVerifier)}
*/
@Deprecated
public SSLConnectionSocketFactory(
final SSLContext sslContext,
final String[] supportedProtocols,
final String[] supportedCipherSuites,
final X509HostnameVerifier hostnameVerifier);
}