快速入门
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、微信支付相关平台:
商户平台 https://pay.weixin.qq.com/ 开放平台 https://open.weixin.qq.com/ 公众平台 https://mp.weixin.qq.com/
需要关注点:
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: SvenAugustus@outlook.com
* @version 2015年8月13日
*/
public class HttpsUtils {
/**
* 组装请求数据字符串
*
* @param rootElement 根元素名称
* @param postData 请求数据
* @return
* @author SvenAugustus(蔡政滦) e-mail: SvenAugustus@outlook.com 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: SvenAugustus@outlook.com 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: SvenAugustus@outlook.com 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;
}
}