应用场景:在订单系统中,用户创建订单,发起支付;收到微信支付成功的回调,触发订单支付完成的处理。当用户发起退款申请时,通过业务控制权限审批,对于审批通过的退款申请,调用微信代理服务进行退款操作。 官方文档: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); }