快速入门
1、微信支付官方在线API入口:
https://pay.weixin.qq.com/wiki/doc/api/index.html
2、微信支付能力介绍:
http://action.weixin.qq.com/payact/readtemplate?t=mobile/merchant/ability_tmpl
3、DEMO下载:
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1
4、微信支付相关平台:
需要关注点:
1、随机字符串的生成方式;
2、sign签名的生成方式;
3、请求报文的组装方式;
4、httpclient的应用方式,特别是退款申请要使用双向证书验证(ssl);
5、对于返回报文的解析,比如对账接口情况失败情况是xml格式,而成功则是文本表格格式;
以下是工具类;需要用到httpclient4.3.4
package sven.util; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Random; import org.apache.http.client.ClientProtocolException; /** * 微信支付工具类 * * @author 蔡政滦 * @version 2015年8月2日 */ public class WxUtils { /** * 随机字符串,不长于32 位 * * @return * @author 蔡政滦 modify by 2015年8月2日 */ public static String randomStr() { String template = "abcdefghijklmnopqrstuvwxyz0123456789"; StringBuffer buffer = new StringBuffer(); Random random = new Random(); while (buffer.length() < 32) { int index = random.nextInt(36); char c = template.charAt(index); buffer.append(c); } return buffer.toString(); } /** * 签名 * * @param map 数据 * @param password 密钥 * @return * @author 蔡政滦 modify by 2015年8月2日 * @throws Exception */ public static String getSign(Map<String, Object> map, String password) throws Exception { return getSign(map, password, ""); } /** * 签名 * * @param map 数据 * @param password 密钥 * @param ignore_keys 忽略的key * @return * @author 蔡政滦 modify by 2015年8月2日 * @throws Exception */ public static String getSign(Map<String, Object> map, String password, String... ignore_keys) throws Exception { String result = ""; ArrayList<String> list = new ArrayList<String>(); for (Map.Entry<String, Object> entry : map.entrySet()) { /*if (StringUtils.isNotEmpty(ignore_key) && ignore_key.equals(entry.getKey())) { continue; }*/ if (ignore_keys != null) { for (int i = 0; i < ignore_keys.length; i++) { if (ignore_keys[i].equals(entry.getKey())) { continue; } } } if (entry.getValue() != null && !"".equals(entry.getValue())) { list.add(entry.getKey() + "=" + entry.getValue() + "&"); } } int size = list.size(); if (size > 0) { String[] arrayToSort = list.toArray(new String[size]); Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER); StringBuilder sb = new StringBuilder(); for (int i = 0; i < size; i++) { sb.append(arrayToSort[i]); } result = sb.toString(); result += "key=" + password; result = PayUtils.md5Digest(result); result = result.toUpperCase(); } return result; } /** * 组装请求数据字符串 * * @param postData 请求数据 * @return * @author 蔡政滦 modify by 2015年8月2日 */ public static String toXml(Map<String, String> postData) { return HttpsUtils.toXml("xml", postData); } /** * 提交http请求,获取响应数据字符串 * * @param url 请求URL * @param xml 请求数据字符串 * @return * @throws ClientProtocolException * @throws IOException * @author 蔡政滦 modify by 2015年8月2日 */ public static String postXml(String url, String xml) throws Exception { Map<String, String> headerInfo = new HashMap<String, String>(); headerInfo.put("Content-Type", "text/xml"); headerInfo.put("Connection", "keep-alive"); headerInfo.put("Accept", "*/*"); headerInfo.put("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); headerInfo.put("Host", "api.mch.weixin.qq.com"); headerInfo.put("X-Requested-With", "XMLHttpRequest"); headerInfo.put("Cache-Control", "max-age=0"); headerInfo.put("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) "); return HttpsUtils.postXml(url, headerInfo, xml); } /** * 提交https请求,获取响应数据字符串 * * @param url 请求URL * @param xml 请求数据字符串 * @keyStorePath 证书存放路径 * @keysecret 证书密码 * @return * @throws ClientProtocolException * @throws IOException * @author 蔡政滦 modify by 2015年8月2日 */ public static String postXmlSSL(String url, String xml, String keyStorePath, String keysecret) throws IllegalStateException, IOException, Exception { Map<String, String> headerInfo = new HashMap<String, String>(); headerInfo.put("Content-Type", "text/xml"); headerInfo.put("Connection", "keep-alive"); headerInfo.put("Accept", "*/*"); headerInfo.put("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); headerInfo.put("Host", "api.mch.weixin.qq.com"); headerInfo.put("X-Requested-With", "XMLHttpRequest"); headerInfo.put("Cache-Control", "max-age=0"); headerInfo.put("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) "); return HttpsUtils.postXmlSSL(url, headerInfo, xml, keyStorePath, keysecret); } }
https工具类:
package sven.util; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.security.KeyStore; import java.util.Iterator; import java.util.Map; import javax.net.ssl.SSLContext; import org.apache.http.HeaderElement; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.SSLContexts; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import org.dom4j.Document; import org.dom4j.DocumentHelper; import org.dom4j.Element; /** * https工具类 * * @author SvenAugustus(蔡政滦) e-mail: [email protected] * @version 2015年8月13日 */ public class HttpsUtils { /** * 组装请求数据字符串 * * @param rootElement 根元素名称 * @param postData 请求数据 * @return * @author SvenAugustus(蔡政滦) e-mail: [email protected] modify by 2015年8月2日 */ public static String toXml(String rootElement, Map<String, String> postData) { Element root = DocumentHelper.createElement(rootElement); Document document = DocumentHelper.createDocument(root); Iterator it = postData.keySet().iterator(); while (it.hasNext()) { String key = (String) it.next(); String value = postData.get(key); Element element = root.addElement(key); element.addCDATA(StringUtils.isEmpty(value) ? "" : value); } return document.asXML(); } /** * 提交http请求,获取响应数据字符串 * * @param url 请求URL * @headerInfo 请求头信息 * @param xml 请求数据字符串 * @return * @throws ClientProtocolException * @throws IOException * @author SvenAugustus(蔡政滦) e-mail: [email protected] modify by 2015年8月2日 */ public static String postXml(String url, Map<String, String> headerInfo, String xml) throws Exception { // httpclient 4.2.2 // String result = ""; // // HttpClient httpclient = new DefaultHttpClient(); // HttpPost pmethod = new HttpPost(url); // 设置响应头信息 // pmethod.addHeader("Connection", "keep-alive"); // pmethod.addHeader("Accept", "*/*"); // pmethod.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); // pmethod.addHeader("Host", "api.mch.weixin.qq.com"); // pmethod.addHeader("X-Requested-With", "XMLHttpRequest"); // pmethod.addHeader("Cache-Control", "max-age=0"); // pmethod.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) "); // pmethod.setEntity(new StringEntity(xml, "UTF-8")); // // HttpResponse response = httpclient.execute(pmethod); // if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { // result = EntityUtils.toString(response.getEntity(), "UTF-8"); // } // return result; //return HttpsUtils.httpRequest(url, "POST", xml, "utf-8"); // httpclient 4.3.4 String result = null; HttpEntity postEntity = new StringEntity(xml, "utf-8"); HttpPost httpPost = new HttpPost(url); // httpPost.addHeader("Content-Type", "text/xml"); // httpPost.addHeader("Connection", "keep-alive"); // httpPost.addHeader("Accept", "*/*"); // httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); // httpPost.addHeader("Host", "api.mch.weixin.qq.com"); // httpPost.addHeader("X-Requested-With", "XMLHttpRequest"); // httpPost.addHeader("Cache-Control", "max-age=0"); // httpPost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) "); if (headerInfo != null) { Iterator it = headerInfo.keySet().iterator(); while (it.hasNext()) { String name = (String) it.next(); String value = headerInfo.get(name); httpPost.addHeader(name, value); } } httpPost.setEntity(postEntity); CloseableHttpClient httpclient = HttpClients.custom().build(); try { HttpResponse response = httpclient.execute(httpPost); int statusCode = response.getStatusLine().getStatusCode(); /*if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || statusCode == HttpStatus.SC_MOVED_TEMPORARILY) { String location = response.getFirstHeader("Location").getValue(); return get(location); } HttpEntity entity = response.getEntity(); if (entity != null) { result = EntityUtils.toString(entity, "utf-8"); }*/ if (statusCode == HttpStatus.SC_OK) { HttpEntity entity = response.getEntity(); if (entity != null) { result = EntityUtils.toString(entity, "utf-8"); } } } finally { httpclient.close(); } return result; } /** * 提交https请求,获取响应数据字符串 * * @param url 请求URL * @headerInfo 请求头信息 * @param xml 请求数据字符串 * @keyStorePath 证书存放路径 * @keysecret 证书密码 * @return * @throws ClientProtocolException * @throws IOException * @author SvenAugustus(蔡政滦) e-mail: [email protected] modify by 2015年8月2日 */ public static String postXmlSSL(String url, Map<String, String> headerInfo, String xml, String keyStorePath, String keysecret) throws IllegalStateException, IOException, Exception { // httpclient 4.3.4 支持ssl / https String result = null; if (com.ztesoft.common.util.StringUtils.isNotEmpty(keyStorePath) && com.ztesoft.common.util.StringUtils.isNotEmpty(keysecret)) { HttpEntity postEntity = new StringEntity(xml, "utf-8"); HttpPost httpPost = new HttpPost(url); // httpPost.addHeader("Content-Type", "text/xml"); // httpPost.addHeader("Connection", "keep-alive"); // httpPost.addHeader("Accept", "*/*"); // httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); // httpPost.addHeader("Host", "api.mch.weixin.qq.com"); // httpPost.addHeader("X-Requested-With", "XMLHttpRequest"); // httpPost.addHeader("Cache-Control", "max-age=0"); // httpPost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) "); if (headerInfo != null) { Iterator it = headerInfo.keySet().iterator(); while (it.hasNext()) { String name = (String) it.next(); String value = headerInfo.get(name); httpPost.addHeader(name, value); } } httpPost.setEntity(postEntity); KeyStore keyStore = KeyStore.getInstance("PKCS12"); //File keyStoreFile = new File("D:/10016225.p12"); File keyStoreFile = new File(keyStorePath); FileInputStream instream = new FileInputStream(keyStoreFile); try { keyStore.load(instream, keysecret.toCharArray()); } finally { instream.close(); } // Trust own CA and all self-signed certs SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, keysecret.toCharArray()).build(); // Allow TLSv1 protocol only SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); org.apache.http.impl.client.HttpClientBuilder httpClientBuilder = HttpClients.custom(); httpClientBuilder.setSSLSocketFactory(sslsf); CloseableHttpClient httpclient = httpClientBuilder.build(); try { HttpResponse response = httpclient.execute(httpPost); int statusCode = response.getStatusLine().getStatusCode(); /*if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || statusCode == HttpStatus.SC_MOVED_TEMPORARILY) { String location = response.getFirstHeader("Location").getValue(); return get(location); } HttpEntity entity = response.getEntity(); if (entity != null) { result = EntityUtils.toString(entity, "utf-8"); }*/ if (statusCode == HttpStatus.SC_OK) { HttpEntity entity = response.getEntity(); if (entity != null) { result = EntityUtils.toString(entity, "utf-8"); } } } finally { httpclient.close(); } } return result; } public static String get(String url) throws Exception { String result = ""; HttpGet request = new HttpGet(url); CloseableHttpClient httpclient = HttpClients.custom().build(); HttpResponse response = httpclient.execute(request); int statusCode = response.getStatusLine().getStatusCode(); if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || statusCode == HttpStatus.SC_MOVED_TEMPORARILY) { String location = response.getFirstHeader("Location").getValue(); return get(location); } HttpEntity entity = response.getEntity(); HeaderElement[] hes = entity.getContentType().getElements(); String encode = "utf-8"; if (hes != null && hes.length > 0) { for (HeaderElement he : hes) { encode = he.getParameterByName("charset") == null ? "utf-8" : he.getParameterByName("charset").getValue(); } } if (entity != null) { result = EntityUtils.toString(entity, encode); } return result; } }