最近由于开发需要,进行了微信支付--企业支付到零钱的功能开发,期间也参考了部分网上的开发思路,以及微信支付的开发问题,在这里做一下总结和源码分享。
腾讯的开发文档:https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=14_1
主要逻辑代码是这样的:
public class WxPayUtil {
private static final Logger logger = LoggerFactory.getLogger(WxPayUtil.class);
// 接口链接
private static final String URL = WxPayDO.getUrl();
private static final String appid = WxPayDO.getAppID();
// 微信支付分配的商户号
private static final String mchid = WxPayDO.getMch_id();
public static Map<String, Object> wxPay(String openid, Integer amount,String userName) throws Exception {
Map<String, Object> map = new HashMap<String, Object>();
// logger.info("开始微信提现操作");
//主机ip地址
String IP = InetAddress.getLocalHost().getHostAddress();
// 随机获取随机字符串
String nonce_str = RandCharsUtil.getRandomString(16);
//商户订单号
String partner_trade_no = "partner_trade_no"+System.currentTimeMillis();
//真实姓名
String re_user_name = userName;
// 企业付款描述信息
String desc = "你的描述";
if (!openid.equals("") || !openid.equals(null)) {
// 设置支付参数
SortedMap<Object, Object> signParams = new TreeMap<Object, Object>();
signParams.put("mch_appid", appid); // 微信分配的公众账号ID
signParams.put("mchid", mchid);// 微信支付分配的商户号
signParams.put("nonce_str", nonce_str); // 随机字符串,不长于32位
signParams.put("partner_trade_no", partner_trade_no); // 商户订单号,需保持唯一性
signParams.put("openid", openid); // 商户appid下,某用户的openid
signParams.put("check_name", "FORCE_CHECK"); // 校验真实姓名,也可以选择不做真实姓名校验
signParams.put("re_user_name",re_user_name);
signParams.put("amount", amount); // 企业付款金额,单位为分
signParams.put("desc", desc); // 企业付款操作说明信息。必填。
signParams.put("spbill_create_ip", IP); // 调用接口的机器Ip地址
// 生成支付签名,要采用URLENCODER的原始值进行MD5算法!
String sign = WXSignUtil.createSign("UTF-8", signParams);
// logger.info("签名:"+sign);
String data = "<xml><mch_appid>";
data += appid + "</mch_appid><mchid>"; // APPID
data += mchid + "</mchid><nonce_str>"; // 商户ID
data += nonce_str + "</nonce_str><partner_trade_no>"; // 随机字符串
data += partner_trade_no + "</partner_trade_no><openid>"; // 订单号
data += openid + "</openid><check_name>FORCE_CHECK</check_name><re_user_name>"; // 是否强制实名验证
data += re_user_name + "</re_user_name><amount>"; //用户真实姓名
data += amount + "</amount><desc>"; // 企业付款金额,单位为分
data += desc + "</desc><spbill_create_ip>"; // 企业付款操作说明信息。必填。
data += IP + "</spbill_create_ip><sign>";// 调用接口的机器Ip地址
data += sign + "</sign></xml>";// 签名
// logger.info("xml数据:"+data);
// 获取证书,发送POST请求;
KeyStore keyStore = KeyStore.getInstance("PKCS12");
// 从配置文件里读取证书的路径信息
FileInputStream instream = new FileInputStream(
new File(WxPayDO.getClient().replaceAll("\\\\", "/")));
keyStore.load(instream, mchid.toCharArray());// 证书密码是商户ID
instream.close();
SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, mchid.toCharArray()).build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext,
new String[] { "TLSv1" }, null,
SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
HttpPost httpost = new HttpPost(URL); //
httpost.addHeader("Connection", "keep-alive");
httpost.addHeader("Accept", "*/*");
httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
httpost.addHeader("Host", "api.mch.weixin.qq.com");
httpost.addHeader("X-Requested-With", "XMLHttpRequest");
httpost.addHeader("Cache-Control", "max-age=0");
httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
httpost.setEntity(new StringEntity(data, "UTF-8"));
CloseableHttpResponse response = httpclient.execute(httpost);
HttpEntity entity = response.getEntity();
String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
logger.info("提现返回的数据为:" + jsonStr);
EntityUtils.consume(entity);
// 把返回的字符串解释成DOM节点
Document dom = DocumentHelper.parseText(jsonStr);
Element root = dom.getRootElement();
String returnCode = root.element("result_code").getText(); // 获取返回代码
// logger.info("返回状态码:"+returnCode);
if (StringUtils.equals(returnCode, "SUCCESS")) { // 判断返回码为成功还是失败
String payment_no = root.element("payment_no").getText(); // 获取支付流水号
String payment_time = root.element("payment_time").getText(); // 获取支付时间
//返回信息
map.put("state", true);
map.put("time", String.valueOf(System.currentTimeMillis()));
logger.info(payment_no +" 支付成功 "+payment_time);
return map;
} else {
String err_code = root.element("err_code").getText(); // 获取错误代码
String err_code_des = root.element("err_code_des").getText();// 获取错误描述
logger.info(err_code+" 支付失败 "+err_code_des);
//返回错误信息
map.put("state", false);
// map.put("Message", utils);
map.put("time", String.valueOf(System.currentTimeMillis()));
return map;
}
} else {
logger.info("没有获得openid");
map.put("state", false);
map.put("message", "抱歉没有获得openid");
map.put("time", String.valueOf(System.currentTimeMillis()));
return map;
}
}
}
代码有需要调用的类:
public class WXSignUtil {
private static final Logger logger = LoggerFactory.getLogger(WXSignUtil.class);
private static final String key = WxPayDO.getKey();
public static String createSign(String characterEncoding,SortedMap<Object,Object> parameters){
StringBuffer sb = new StringBuffer();
//所有参与传参的参数按照accsii排序(升序)
Set es = parameters.entrySet();
Iterator it = es.iterator();
while(it.hasNext()) {
Map.Entry entry = (Map.Entry)it.next();
String k = (String)entry.getKey();
Object object = entry.getValue();
if(null != object && !"".equals(object) && !"sign".equals(k) && !"key".equals(k)) {
sb.append(k + "=" + object + "&");
}
}
sb.append("key=").append(key);
String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
logger.info("sign >>>> "+sign);
return sign;
}
}
public class WxPayDO implements Serializable {
// 商户API密钥
public static String key = "商户API密钥";
private static String appID="appid";
private static String mch_id="mch_id";
//1.本地测试 (这里写了3个client 根据你本身的环境来定 这里配置的是你的证书路径 )
//private static String client = "\\apiclient_cert.p12";
//2.这是window环境上的测试路径
// private static String client = "F:\\client\\apiclient_cert.p12";
//3.这是linux环境下测试的路径
private static String client = "\\opt\\client\\apiclient_cert.p12";
private static String url = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
public static String getKey() {
return key;
}
public static void setKey(String key) {
WxPayDO.key = key;
}
public static String getAppID() {
return appID;
}
public static void setAppID(String appID) {
WxPayDO.appID = appID;
}
public static String getMch_id() {
return mch_id;
}
public static void setMch_id(String mch_id) {
WxPayDO.mch_id = mch_id;
}
public static String getClient() {
return client;
}
public static void setClient(String client) {
WxPayDO.client = client;
}
public static String getUrl() {
return url;
}
public static void setUrl(String url) {
WxPayDO.url = url;
}
}