微信企业账户提现到微信用户钱包零钱(通过openid)
时间:2022-09-15 04:00:00
快速完成开发
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 paraFilter(Map sArray) { Map result = new HashMap(); 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 params) { List keys = new ArrayList(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 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 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 handleInputParamMap(HttpServletRequest request,String openId,String amount,String transactionNo,String msg) throws Exception {
Map packageParams = new HashMap();
//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 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.create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", sslConnectionSocketFactory)
.build(),
null,
null,
null
);
}
else {
connManager = new BasicHttpClientConnectionManager(
RegistryBuilder.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");
}
}
小结:代码需要更换参数 具体位置请自行查找(有点乱,请自行配置修改在同一个位置)
依赖:
org.jdom jdom2 2.0.6