需要准备:
需要在微信商户凭他开通企业付款到零钱
微信APPID, 商户id, 证书
下面直接附上代码
/**
* 企业转账到零钱 微信
*
* @param chatTransferParam 微信转账参数类
* @return
*/
@Override
public Map<String, String> weChatPaymentToChange(WeChatTransferParam chatTransferParam) throws Exception {
String partnerTradeNo = String.valueOf(snowflake.nextId());
chatTransferParam.setPartnerTradeNo(partnerTradeNo);
Map<String, String> params = TransferModel.builder()
.mch_appid(WxPayConfig.APP_ID)
.mchid(WxPayConfig.MCH_ID)
.nonce_str(WxPayKit.generateStr())
.partner_trade_no(chatTransferParam.getPartnerTradeNo())
.openid(chatTransferParam.getOpenId())
.check_name("NO_CHECK")
.amount(String.valueOf(chatTransferParam.getAmount().multiply(new BigDecimal(100)).intValue()))
.desc(chatTransferParam.getDesc())
.build()
.createSign(WxPayConfig.MCH_KEY, SignType.MD5, false);
log.info("params-->{}", params);
//获取证书路径
File file = ResourceUtils.getFile(WxPayConfig.CERT_PATH);
//文件输入流
//FileInputStream certStream = new FileInputStream(file);
//提现
String transfers = WxPayApi.transfers(params, file.toString(), WxPayConfig.MCH_ID);
log.info("提现结果:" + transfers);
Map<String, String> map = WXPayUtil.xmlToMap(transfers);
String returnCode = map.get("return_code");
String resultCode = map.get("result_code");
if (WxPayKit.codeIsOk(returnCode) && WxPayKit.codeIsOk(resultCode)) {
// 提现成功
} else {
// 提现失败
}
return map;
}
/**
* 证书地址:resource下
*/
public static final String CERT_PATH = "classpath:cert/apiclient_cert.p12";
WxPayApi 工具类代码
/**
* 企业付款到零钱
* <p>
* 证书以路径形式
*
* @param params 请求参数
* @param certPath 证书文件目录
* @param certPass 证书密码
* @return {@link String} 请求返回的结果
*/
public static String transfers(Map<String, String> params, String certPath, String certPass) {
return execution(getReqUrl(WxApiType.TRANSFER, null, false), params, certPath, certPass);
}
/**
* 企业付款到零钱
* <p>
* 证书以文件形式
*
* @param params 请求参数
* @param certFile 证书文件的 InputStream
* @param certPass 证书密码
* @return {@link String} 请求返回的结果
*/
public static String transfers(Map<String, String> params, InputStream certFile, String certPass) {
return execution(getReqUrl(WxApiType.TRANSFER, null, false), params, certFile, certPass);
}
/**
* 获取接口请求的 URL
*
* @param wxApiType {@link WxApiType} 支付 API 接口枚举
* @param wxDomain {@link WxDomain} 支付 API 接口域名枚举
* @param isSandBox 是否是沙箱环境
* @return {@link String} 返回完整的接口请求URL
*/
public static String getReqUrl(WxApiType wxApiType, WxDomain wxDomain, boolean isSandBox) {
if (wxDomain == null) {
wxDomain = WxDomain.CHINA;
}
return wxDomain.getType()
.concat(isSandBox ? WxApiType.SAND_BOX_NEW.getType() : "")
.concat(wxApiType.getType());
}
/**
* 发起请求
*
* @param apiUrl 接口 URL
* 通过 {@link WxPayApi #getReqUrl(WxApiType)}
* 或者 {@link WxPayApi #getReqUrl(WxApiType, WxDomain, boolean)} 来获取
* @param params 接口请求参数
* @param certPath 证书文件目录
* @param certPass 证书密码
* @return {@link String} 请求返回的结果
*/
public static String execution(String apiUrl, Map<String, String> params, String certPath, String certPass) {
return doPostSSL(apiUrl, params, certPath, certPass);
}
/**
* 发起请求
*
* @param apiUrl 接口 URL
* 通过 {@link WxPayApi #getReqUrl(WxApiType)}
* 或者 {@link WxPayApi #getReqUrl(WxApiType, WxDomain, boolean)} 来获取
* @param params 接口请求参数
* @param certFile 证书文件输入流
* @param certPass 证书密码
* @return {@link String} 请求返回的结果
*/
public static String execution(String apiUrl, Map<String, String> params, InputStream certFile, String certPass) {
return doPostSSL(apiUrl, params, certFile, certPass);
}
public static String doPostSSL(String url, Map<String, String> params, String certPath, String certPass) {
return HttpKit.getDelegate().post(url, WxPayKit.toXml(params), certPath, certPass);
}
public static String doPostSSL(String url, Map<String, String> params, InputStream certFile, String certPass) {
return HttpKit.getDelegate().post(url, WxPayKit.toXml(params), certFile, certPass);
}
HttpayKit
package com.cainaer.common.core.utils.http;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
/**
* Http 工具类
*
* @author serence
* @date 2021/8/15 9:59
*/
public class HttpKit {
private static AbstractHttpDelegate delegate = new DefaultHttpKit();
public static AbstractHttpDelegate getDelegate() {
return delegate;
}
public static void setDelegate(AbstractHttpDelegate delegate) {
HttpKit.delegate = delegate;
}
public static String readData(HttpServletRequest request) {
BufferedReader br = null;
try {
StringBuilder result = new StringBuilder();
br = request.getReader();
for (String line; (line = br.readLine()) != null; ) {
if (result.length() > 0) {
result.append("
");
}
result.append(line);
}
return result.toString();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
/**
* 使用 huTool 实现的 Http 工具类
*
* @author Javen
*/
class DefaultHttpKit extends AbstractHttpDelegate {
}
AbstractHttpDelegate
package com.cainaer.common.core.utils.http;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.ssl.SSLSocketFactoryBuilder;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.Map;
/**
* Http 代理类
*
* @author serence
* @date 2021/8/15 9:59
*/
public class AbstractHttpDelegate {
/**
* get 请求
*
* @param url 请求url
* @return {@link String} 请求返回的结果
*/
public String get(String url) {
return HttpUtil.get(url);
}
/**
* get 请求
*
* @param url 请求url
* @param paramMap 请求参数
* @return {@link String} 请求返回的结果
*/
public String get(String url, Map<String, Object> paramMap) {
return HttpUtil.get(url, paramMap);
}
/**
* post 请求
*
* @param url 请求url
* @param data 请求参数
* @return {@link String} 请求返回的结果
*/
public String post(String url, String data) {
return HttpUtil.post(url, data);
}
/**
* post 请求
*
* @param url 请求url
* @param paramMap 请求参数
* @return {@link String} 请求返回的结果
*/
public String post(String url, Map<String, Object> paramMap) {
return HttpUtil.post(url, paramMap);
}
/**
* post 请求
*
* @param url 请求url
* @param data 请求参数
* @param certPath 证书路径
* @param certPass 证书密码
* @return {@link String} 请求返回的结果
*/
public String post(String url, String data, String certPath, String certPass) {
try {
return HttpRequest.post(url)
.setSSLSocketFactory(SSLSocketFactoryBuilder
.create()
.setProtocol(SSLSocketFactoryBuilder.TLSv1)
.setKeyManagers(getKeyManager(certPass, certPath, null))
.setSecureRandom(new SecureRandom())
.build()
)
.body(data)
.execute()
.body();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* post 请求
*
* @param url 请求url
* @param data 请求参数
* @param certFile 证书文件输入流
* @param certPass 证书密码
* @return {@link String} 请求返回的结果
*/
public String post(String url, String data, InputStream certFile, String certPass) {
try {
return HttpRequest.post(url)
.setSSLSocketFactory(SSLSocketFactoryBuilder
.create()
.setProtocol(SSLSocketFactoryBuilder.TLSv1)
.setKeyManagers(getKeyManager(certPass, null, certFile))
.setSecureRandom(new SecureRandom())
.build()
)
.body(data)
.execute()
.body();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private KeyManager[] getKeyManager(String certPass, String certPath, InputStream certFile) throws Exception {
KeyStore clientStore = KeyStore.getInstance("PKCS12");
if (certFile != null) {
clientStore.load(certFile, certPass.toCharArray());
} else {
clientStore.load(new FileInputStream(certPath), certPass.toCharArray());
}
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(clientStore, certPass.toCharArray());
return kmf.getKeyManagers();
}
}
WxPayKit
package com.cainaer.common.core.wx.utils;
import cn.hutool.core.util.StrUtil;
import com.cainaer.common.core.wx.enums.SignType;
import java.util.HashMap;
import java.util.Map;
import static cn.hutool.crypto.SecureUtil.md5;
import static com.cainaer.common.core.wx.sdk.WXPayConstants.FIELD_SIGN;
import static com.cainaer.common.core.wx.sdk.WXPayConstants.FIELD_SIGN_TYPE;
/**
* @author serence
* @date 2021/8/13 17:27
*/
public class WxPayKit {
/**
* 针对支付的 xml,没有嵌套节点的简单处理
*
* @param xmlStr xml 字符串
* @return 转化后的 Map
*/
public static Map<String, String> xmlToMap(String xmlStr) {
return PayKit.xmlToMap(xmlStr);
}
/**
* 微信下单 map to xml
*
* @param params Map 参数
* @return xml 字符串
*/
public static String toXml(Map<String, String> params) {
return PayKit.toXml(params);
}
/**
* 判断接口返回的 code
*
* @param codeValue code 值
* @return 是否是 SUCCESS
*/
public static boolean codeIsOk(String codeValue) {
return StrUtil.isNotEmpty(codeValue) && "SUCCESS".equals(codeValue);
}
/**
* 构建签名
*
* @param params 需要签名的参数
* @param partnerKey 密钥
* @param signType 签名类型
* @param haveSignType 签名是否包含 sign_type 字段
* @return 签名后的 Map
*/
public static Map<String, String> buildSign(Map<String, String> params, String partnerKey, SignType signType, boolean haveSignType) {
if (haveSignType) {
params.put(FIELD_SIGN_TYPE, signType.getType());
}
String sign = createSign(params, partnerKey, signType);
params.put(FIELD_SIGN, sign);
return params;
}
/**
* 生成签名
*
* @param params 需要签名的参数
* @param partnerKey 密钥
* @param signType 签名类型
* @return 签名后的数据
*/
public static String createSign(Map<String, String> params, String partnerKey, SignType signType) {
if (signType == null) {
signType = SignType.MD5;
}
// 生成签名前先去除sign
params.remove(FIELD_SIGN);
String tempStr = PayKit.createLinkString(params);
String stringSignTemp = tempStr + "&key=" + partnerKey;
if (signType == SignType.MD5) {
return md5(stringSignTemp).toUpperCase();
} else {
return hmacSha256(stringSignTemp, partnerKey).toUpperCase();
}
}
public static String hmacSha256(String data, String key) {
return PayKit.hmacSha256(data, key);
}
/**
* <p>APP 支付-预付订单再次签名</p>
* <p>注意此处签名方式需与统一下单的签名类型一致</p>
*
* @param appId 应用编号
* @param partnerId 商户号
* @param prepayId 预付订单号
* @param partnerKey API Key
* @param signType 签名方式
* @return 再次签名后的 Map
*/
public static Map<String, String> appPrepayIdCreateSign(String appId, String partnerId, String prepayId, String partnerKey, SignType signType) {
Map<String, String> packageParams = new HashMap<String, String>(8);
packageParams.put("appid", appId);
packageParams.put("partnerid", partnerId);
packageParams.put("prepayid", prepayId);
packageParams.put("package", "Sign=WXPay");
packageParams.put("noncestr", String.valueOf(System.currentTimeMillis()));
packageParams.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000));
if (signType == null) {
signType = SignType.MD5;
}
String packageSign = createSign(packageParams, partnerKey, signType);
packageParams.put("sign", packageSign);
return packageParams;
}
/**
* 获取随机字符串
*
* @return
*/
public static String generateStr() {
return PayKit.generateStr();
}
}
查询转账订单
/**
* 查询企业付款到零钱
*
* @param chatTransferParam 微信转账参数类
* @return
*/
@Override
public String queryBusinessPaymentToChange(WeChatTransferParam chatTransferParam) {
try {
Map<String, String> params = GetTransferInfoModel.builder()
.nonce_str(WxPayKit.generateStr())
.partner_trade_no(chatTransferParam.getPartnerTradeNo())
.mch_id(WxPayConfig.MCH_ID)
.appid(WxPayConfig.APP_ID)
.build()
.createSign(WxPayConfig.MCH_KEY, SignType.HMACSHA256, false);
//获取证书路径
File file = ResourceUtils.getFile("classpath:cert/apiclient_cert.p12");
FileInputStream certStream = new FileInputStream(file);
return WxPayApi.getTransferInfo(params, certStream, WxPayConfig.MCH_ID);
} catch (Exception e) {
e.printStackTrace();
}
throw new CustomException("系统繁忙,请稍后重试");
}
/**
* 查询企业付款到零钱
*
* @param params 请求参数
* @param certFile 证书文件的 InputStream
* @param certPass 证书密码
* @return {@link String} 请求返回的结果
*/
public static String getTransferInfo(Map<String, String> params, InputStream certFile, String certPass) {
return execution(getReqUrl(WxApiType.GET_TRANSFER_INFO, null, false), params, certFile, certPass);
}
用到的代码都贴上了,如有漏掉的请下方留言......