较快完成开发
1. apiclient_cert.p12:微信商户支付证书,这个是一个文件,配置在项目里或者服务器
2.工具类
import org.apache.commons.codec.digest.DigestUtils;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.util.*;
public class CommonUtil {
/**
* 获取一定长度的随机字符串,范围0-9,a-z
* @param length:指定字符串长度
* @return 一定长度的随机字符串
*/
public static String getRandomStringByLength(int length) {
String base = "abcdefghijklmnopqrstuvwxyz0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
/**
* 除去数组中的空值和签名参数
*
* @param sArray 签名参数组
* @return 去掉空值与签名参数后的新签名参数组
*/
public static Map<String, String> paraFilter(Map<String, String> sArray) {
Map<String, String> result = new HashMap<String, String>();
if (sArray == null || sArray.size() <= 0) {
return result;
}
for (String key : sArray.keySet()) {
String value = sArray.get(key);
if (value == null || value.equals("") || key.equalsIgnoreCase("sign")
|| key.equalsIgnoreCase("sign_type")) {
continue;
}
result.put(key, value);
}
return result;
}
/**
* 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串
*
* @param params 需要排序并参与字符拼接的参数组
* @return 拼接后字符串
*/
public static String createLinkString(Map<String, String> params) {
List<String> keys = new ArrayList<String>(params.keySet());
Collections.sort(keys);
String prestr = "";
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
String value = params.get(key);
if (i == keys.size() - 1) {// 拼接时,不包括最后一个&字符
prestr = prestr + key + "=" + value;
} else {
prestr = prestr + key + "=" + value + "&";
}
}
return prestr;
}
/**
* 签名字符串
*
* @param text 需要签名的字符串
* @param key 密钥
* @param input_charset 编码格式
* @return 签名结果
*/
public static String sign(String text, String key, String input_charset) {
text = text + "&key=" + key;
return DigestUtils.md5Hex(getContentBytes(text, input_charset));
}
public static byte[] getContentBytes(String content, String charset) {
if (charset == null || "".equals(charset)) {
return content.getBytes();
}
try {
return content.getBytes(charset);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset);
}
}
/**
* 将Map转换为XML格式的字符串
*
* @param data Map类型数据
* @return XML格式的字符串
* @throws Exception
*/
public static String mapToXml(Map<String, String> data) throws Exception {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder= documentBuilderFactory.newDocumentBuilder();
org.w3c.dom.Document document = documentBuilder.newDocument();
org.w3c.dom.Element root = document.createElement("xml");
document.appendChild(root);
for (String key: data.keySet()) {
String value = data.get(key);
if (value == null) {
value = "";
}
value = value.trim();
org.w3c.dom.Element filed = document.createElement(key);
filed.appendChild(document.createTextNode(value));
root.appendChild(filed);
}
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
DOMSource source = new DOMSource(document);
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
transformer.transform(source, result);
String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");
try {
writer.close();
}
catch (Exception ex) {
}
return output;
}
/**
* 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
*
* @param strxml
* @return
* @throws IOException
*/
public static Map doXMLParse(String strxml) throws Exception {
if (null == strxml || "".equals(strxml)) {
return null;
}
Map m = new HashMap();
InputStream in = String2Inputstream(strxml);
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(in);
Element root = doc.getRootElement();
List list = root.getChildren();
Iterator it = list.iterator();
while (it.hasNext()) {
Element e = (Element) it.next();
String k = e.getName();
String v = "";
List children = e.getChildren();
if (children.isEmpty()) {
v = e.getTextNormalize();
} else {
v = getChildrenText(children);
}
m.put(k, v);
}
//关闭流
in.close();
return m;
}
}
3.结果实体
import lombok.Data;
/**
* @author ljchen
*/
@Data
public class TransferResultDto{
/**
* 转账结果:成功或失败(SUCCESS/FAIL)
*/
private String resultCode;
/**
* 商户转账订单号
*/
private String partnerTradeNo;
/**
* 微信订单号
*/
private String paymentNo;
/**
* 微信支付成功时间
*/
private String paymentTime;
/**
* 错误代码
*/
private String errorCode;
/**
* 错误代码描述
*/
private String errorCodeDes;
}
4.实现代码
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import org.springframework.util.CollectionUtils;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.security.*;
import java.util.*;
/**
* 微信提现
* @author
*/
public class WxPayServiceImpl{
/**
* 企业微信提现到用户
* @param amount 提现金额 整数 单位分
* @param openId 微信用户 openid
* @param transactionNo 自定义 提现记录全局唯一标识
* @param request
* @param msg 企业付款操作说明
* @throws Exception
*/
public TransferResultDto wxWithdrawalTransferSample(HttpServletRequest request, String openId,String amount,String transactionNo,String msg) throws Exception {
//封装提现请求参数map
Map<String, String> packageParams = handleInputParamMap(request,openId, amount, transactionNo,msg);
//将map转成xml
String mapToXml = CommonUtil.mapToXml(packageParams);
//调用接口
String result = requestOnce("https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers", mapToXml, 30000, 30000, true);
//调用提现请求接口,返回结果是xml格式
log.info("提现接口{}",result);
//处理微信提现接口返回结果
return handleTransferResultDto(result);
}
private Map<String, String> handleInputParamMap(HttpServletRequest request,String openId,String amount,String transactionNo,String msg) throws Exception {
Map<String, String> packageParams = new HashMap<String, String>();
//1.0 拼凑企业支付需要的参数
//微信小程序的appid
String mchAppId ="微信小程序的appid";
//商户号
String mchId = "商户号";
//生成随机数
String nonceStr =CommonUtil.getRandomStringByLength(32);
//是否验证真实姓名呢:NO_CHECK:不校验真实姓名; FORCE_CHECK:强校验真实姓名
String checkName = "NO_CHECK";
//企业付款操作说明信息, 默认为提现到微信零钱
//获取调用接口的机器ip
String spbillCreateIp = request.getRemoteHost();
//微信小程序的appid
packageParams.put("mch_appid", mchAppId);
//商户号
packageParams.put("mchid", mchId);
//随机生成后数字,保证安全性
packageParams.put("nonce_str", nonceStr);
//生成商户订单号
packageParams.put("partner_trade_no", transactionNo);
// 支付给用户openid
packageParams.put("openid", openId);
//是否验证真实姓名呢
packageParams.put("check_name", checkName);
//企业付款金额,单位为分
packageParams.put("amount",amount);
//企业付款操作说明信息。必填。
packageParams.put("desc", msg);
//调用接口的机器Ip地址
packageParams.put("spbill_create_ip", spbillCreateIp);
//3.0 生成自己的签名
// 把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
packageParams=CommonUtil.paraFilter(packageParams);
String prestr = CommonUtil.createLinkString(packageParams);
//签名
String sign = CommonUtil.sign(prestr,mchAppId,"utf-8").toUpperCase();
//封装入参map
packageParams.put("sign", sign);
return packageParams;
}
private TransferResultDto handleTransferResultDto(String resultXml) throws Exception {
TransferResultDto resultDto = new TransferResultDto();
Map<String, String> resultMap = CommonUtil.doXMLParse(resultXml);
if (!CollectionUtils.isEmpty(resultMap) && "SUCCESS".equals(resultMap.get("result_code"))&&"SUCCESS".equals(resultMap.get("return_code"))) {
log.info("转账成功");
resultDto.setResultCode(resultMap.get("result_code"));
// 商户转账订单号
resultDto.setPartnerTradeNo(resultMap.get("partner_trade_no"));
// 微信订单号
resultDto.setPaymentNo(resultMap.get("payment_no"));
// 微信支付成功时间
resultDto.setPaymentTime(resultMap.get("payment_time"));
} else {
resultDto.setResultCode(resultMap.get("result_code"));
//错误代码
resultDto.setErrorCode(resultMap.get("err_code"));
//错误代码描述
resultDto.setErrorCodeDes(resultMap.get("err_code_des"));
}
return resultDto;
}
/**
* @param data
* @param connectTimeoutMs
* @param readTimeoutMs
* @param useCert 是否使用证书,针对退款、撤销等操作
* @return
* @throws Exception
*/
private String requestOnce(String url,String data, int connectTimeoutMs, int readTimeoutMs, boolean useCert) throws Exception {
//证书路径
String path = "你的证书路径";
BasicHttpClientConnectionManager connManager;
if (useCert) {
//商户id
char[] password = "小程序商户id".toCharArray();
//加载证书
FileInputStream certStream = new FileInputStream(new File(path));
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(certStream, password);
// 实例化密钥库 & 初始化密钥工厂
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, password);
// 创建 SSLContext
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(), null, new SecureRandom());
SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(
sslContext,
new String[]{"TLSv1"},
null,
new DefaultHostnameVerifier());
connManager = new BasicHttpClientConnectionManager(
RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", sslConnectionSocketFactory)
.build(),
null,
null,
null
);
}
else {
connManager = new BasicHttpClientConnectionManager(
RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", SSLConnectionSocketFactory.getSocketFactory())
.build(),
null,
null,
null
);
}
HttpClient httpClient = HttpClientBuilder.create()
.setConnectionManager(connManager)
.build();
HttpPost httpPost = new HttpPost(url);
RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(readTimeoutMs).setConnectTimeout(connectTimeoutMs).build();
httpPost.setConfig(requestConfig);
StringEntity postEntity = new StringEntity(data, "UTF-8");
httpPost.addHeader("Content-Type", "text/xml");
httpPost.addHeader("User-Agent", "WXPaySDK/3.0.9" +
" (" + System.getProperty("os.arch") + " " + System.getProperty("os.name") + " " + System.getProperty("os.version") +
") Java/" + System.getProperty("java.version") + " HttpClient/" + HttpClient.class.getPackage().getImplementationVersion() + " " + path);
httpPost.setEntity(postEntity);
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
return EntityUtils.toString(httpEntity, "UTF-8");
}
}
小结:代码需要更换参数 具体位置请自行查找(有点乱,请自行配置修改在同一个位置)
依赖:
<dependency> <groupId>org.jdom</groupId> <artifactId>jdom2</artifactId> <version>2.0.6</version> </dependency>