1、微信专用支付类 WxHttpUtil :
import lombok.extern.slf4j.Slf4j;
import org.apache.http.*;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
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 java.io.IOException;
import static org.apache.http.HttpHeaders.*;
import static org.apache.http.entity.ContentType.APPLICATION_JSON;
/**
* 微信支付专用类 请求操作方法
*
*/
@Slf4j
public class WxHttpUtil {
/**
* 发起批量转账API 批量转账到零钱
*
* @param requestUrl 请求地址
* @param requestJson 组合参数
* @param paySerialNo 商户证书序列号
* @param privateKeyPath 商户私钥证书路径
* @return String
*/
public static String postTransBatRequest(
String requestUrl,
String requestJson,
String paySerialNo,
String mchId,
String privateKeyPath, String url) {
CloseableHttpResponse response = null;
HttpEntity entity = null;
CloseableHttpClient httpClient = null;
try {
HttpPost httpPost = createHttpPost(requestUrl, requestJson, paySerialNo, mchId, privateKeyPath, url);
httpClient = HttpClients.createDefault();
// 发起转账请求
response = httpClient.execute(httpPost);
log.info("response:{}", response);
entity = response.getEntity();
// 获取返回的数据
log.info(String.format("-----getHeaders.Request-ID:%s", response.getHeaders("Request-ID")));
return EntityUtils.toString(entity);
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭流
try {
if (httpClient != null) {
httpClient.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
/**
* 账单查询
*
* @param requestUrl 请求完整地址
* @param paySerialNo 商户证书序列号
* @param privateKeyPath 商户私钥证书路径
* @param privateKeyPath 支付秘钥
* @return String
*/
public static String getTransBatRequest(
String requestUrl,
String paySerialNo,
String mchId,
String privateKeyPath, String url) {
CloseableHttpResponse response = null;
HttpEntity entity = null;
CloseableHttpClient httpClient = null;
try {
HttpGet httpPost = createHttpGet(requestUrl, paySerialNo, mchId, privateKeyPath, url);
httpClient = HttpClients.createDefault();
// 发起转账请求
response = httpClient.execute(httpPost);
log.info("response:{}", response);
entity = response.getEntity();
// 获取返回的数据
log.info(String.format("-----getHeaders.Request-ID:%s", response.getHeaders("Request-ID")));
return EntityUtils.toString(entity);
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭流
try {
if (httpClient != null) {
httpClient.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
/**
* @param requestUrl 请求完整地址
* @param requestJson 请求参数
* @param paySerialNo 支付证书序列号
* @param mchId 商户号
* @param privateKeyPath 私钥路径
* @param servletPath 相对路径
* @return HttpPost
*/
private static HttpPost createHttpPost(String requestUrl,
String requestJson,
String paySerialNo,
String mchId,
String privateKeyPath, String servletPath) {
//商户私钥证书
HttpPost httpPost = new HttpPost(requestUrl);
// NOTE: 建议指定charset=utf-8。低于4.4.6版本的HttpCore,不能正确的设置字符集,可能导致签名错误
httpPost.addHeader(ACCEPT, APPLICATION_JSON.toString());
httpPost.addHeader(CONTENT_TYPE, APPLICATION_JSON.toString());
httpPost.addHeader("Wechatpay-Serial", paySerialNo);
//-------------------------核心认证 start-----------------------------------------------------------------
String strToken = null;
try {
log.info("requestJson:{}", requestJson);
strToken = WxPayV3Util.getToken("POST",
servletPath,
requestJson, mchId, paySerialNo, privateKeyPath);
} catch (Exception e) {
log.error("createHttpPost error:", e);
e.printStackTrace();
}
StringEntity reqEntity = new StringEntity(requestJson, APPLICATION_JSON);
log.info("token " + strToken);
// 添加认证信息
httpPost.addHeader("Authorization",
"WECHATPAY2-SHA256-RSA2048" + " "
+ strToken);
//---------------------------核心认证 end---------------------------------------------------------------
httpPost.setEntity(reqEntity);
return httpPost;
}
/**
* 创建get 请求
*
* @param requestUrl 请求完整地址
* @param paySerialNo 支付证书序列号
* @param mchId 商户号
* @param privateKeyPath 私钥路径
* @param servletPath 相对路径 请求地址上如果有参数 则此处需要带上参数
* @return HttpGet
*/
private static HttpGet createHttpGet(String requestUrl,
String paySerialNo,
String mchId,
String privateKeyPath, String servletPath) {
//商户私钥证书
HttpGet httpGet = new HttpGet(requestUrl);
// NOTE: 建议指定charset=utf-8。低于4.4.6版本的HttpCore,不能正确的设置字符集,可能导致签名错误
httpGet.addHeader("Content-Type", "application/json");
httpGet.addHeader("Accept", "application/json");
httpGet.addHeader("Wechatpay-Serial", paySerialNo);
//-------------------------核心认证 start-----------------------------------------------------------------
String strToken = null;
try {
strToken = WxPayV3Util.getToken("GET",
servletPath,
"", mchId, paySerialNo, privateKeyPath);
} catch (Exception e) {
log.error("createHttpGet error:", e);
e.printStackTrace();
}
log.info("token " + strToken);
// 添加认证信息
httpGet.addHeader("Authorization",
"WECHATPAY2-SHA256-RSA2048" + " "
+ strToken);
//---------------------------核心认证 end---------------------------------------------------------------
return httpGet;
}
}
2、微信专用支付类 WxPayV3Util :
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Random;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
@Slf4j
public class WxPayV3Util {
/**
* @param method 请求方法 post
* @param canonicalUrl 请求地址
* @param body 请求参数 GET请求传空字符
* @param merchantId 这里用的商户号
* @param certSerialNo 商户证书序列号
* @param keyPath 私钥商户证书地址
* @return String
* @throws Exception 异常信息
*/
public static String getToken(
String method,
String canonicalUrl,
String body,
String merchantId,
String certSerialNo,
String keyPath) throws Exception {
String signStr = "";
//获取32位随机字符串
String nonceStr = getRandomString(32);
//当前系统运行时间
long timestamp = System.currentTimeMillis() / 1000;
String message = buildMessage(method, canonicalUrl, timestamp, nonceStr, body);
//签名操作
String signature = sign(message.getBytes(StandardCharsets.UTF_8), keyPath);
//组装参数
signStr = "mchid=\"" + merchantId + "\"," +
"timestamp=\"" + timestamp + "\"," +
"nonce_str=\"" + nonceStr + "\"," +
"serial_no=\"" + certSerialNo + "\"," +
"signature=\"" + signature + "\"";
return signStr;
}
public static String buildMessage(String method, String canonicalUrl, long timestamp, String nonceStr, String body) {
return method + "\n" + canonicalUrl + "\n" + timestamp + "\n" + nonceStr + "\n" + body + "\n";
}
public static String sign(byte[] message, String keyPath) throws Exception {
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initSign(getPrivateKey(keyPath));
sign.update(message);
return Base64.encodeBase64String(sign.sign());
}
/**
* 微信支付-前端唤起支付参数-获取商户私钥
*
* @param filename 私钥文件路径 (required)
* @return 私钥对象
*/
public static PrivateKey getPrivateKey(String filename) throws IOException {
String content = new String(Files.readAllBytes(Paths.get(filename)), StandardCharsets.UTF_8);
try {
String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replaceAll("\\s+", "");
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(
new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey)));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("当前Java环境不支持RSA", e);
} catch (InvalidKeySpecException e) {
throw new RuntimeException("无效的密钥格式");
}
}
/**
* 获取随机位数的字符串
*
* @param length 需要的长度
* @return String
*/
public static String getRandomString(int length) {
String base = "abcdefghijklmnopqrstuvwxyz0123456789";
Random random = new Random();
StringBuffer stringBuffer = new StringBuffer();
for (int i = 0; i < length; i++) {
int number = random.nextInt(base.length());
stringBuffer.append(base.charAt(number));
}
return stringBuffer.toString();
}
}
3、接口类 ITransferService :
public interface ITransferService {
/**
* 微信商家转账到零钱
*
* @param dto 请求DTO
* @return String
*/
String wxTransfer(TransferDTO dto);
/**
* 通过商家明细单号查询明细单
*
* @param dto 请求DTO
* @return 返回VO
*/
TransferQueryVO wxTransferQuery(TransferQueryDTO dto);
}
4、获取配置类 WxTransferConfig :
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@Configuration
@Data
public class WxTransferConfig {
/**
* 商户appId
*/
@Value("${wxTransfer.appId}")
private String appId;
/**
* 微信请求基础URL
*/
@Value("${wxTransfer.baseUrl}")
private String baseUrl;
/**
* 发起商家转账URL
*/
@Value("${wxTransfer.transferUrl}")
private String transferUrl;
/**
* 商家转账查询URL
*/
@Value("${wxTransfer.transferInfoUrl}")
private String transferInfoUrl;
/**
* 商家转账查询URL
*/
@Value("${wxTransfer.transferQueryUrl}")
private String transferQueryUrl;
/**
* 商户证书序列号
*/
@Value("${wxTransfer.paySerialNo}")
private String paySerialNo;
/**
* 商户号
*/
@Value("${wxTransfer.mchId}")
private String mchId;
/**
* 商户私钥证书
*/
@Value("${wxTransfer.privateKeyPath}")
private String privateKeyPath;
}
5、参数配置 bootstrap.xml :
wxTransfer:
appId: wx1234567891011121
baseUrl: https://api.mch.weixin.qq.com
transferUrl: /v3/transfer/batches
transferInfoUrl: /v3/transfer/batches/out-batch-no
transferQueryUrl: /v3/transfer/batches/out-batch-no/{0}/details/out-detail-no/{1}
paySerialNo: 1234567891011121314151617181920212223242
mchId: 1234567899
privateKeyPath: src/main/resources/apiclient_key.pem
6、转账枚举类 WxTransferEnum :
public enum WxTransferEnum {
/**
* 明细状态 INIT-初始化 WAIT_PAY-待确认 PROCESSING-转账中 SUCCESS-转账成功 FAIL-转账失败
*/
INIT("PROCESSING", "转账中"),
WAIT_PAY("PROCESSING", "转账中"),
PROCESSING("PROCESSING", "转账中"),
SUCCESS("SUCCESS", "转账成功"),
FAIL("FAIL", "转账失败"),
;
private String code;
private String message;
WxTransferEnum(String code, String message) {
this.code = code;
this.message = message;
}
public static String getMessageByName(String detailName) {
for (WxTransferEnum value : WxTransferEnum.values()) {
if (value.name().equals(detailName)) {
return value.getMessage();
}
}
return null;
}
public static String getCodeByName(String detailName) {
for (WxTransferEnum value : WxTransferEnum.values()) {
if (value.name().equals(detailName)) {
return value.getCode();
}
}
return null;
}
public String getCode() {
return code;
}
public String getMessage() {
return message;
}
}
7、转账失败枚举类 WxTransferFailEnum :
import com.wechat.pay.java.service.transferbatch.model.FailReasonType;
public enum WxTransferFailEnum {
/**
* 明细失败原因
*/
ACCOUNT_FROZEN("该用户账户被冻结"),
REAL_NAME_CHECK_FAIL("收款人未实名认证,需要用户完成微信实名认证"),
NAME_NOT_CORRECT("收款人姓名校验不通过,请核实信息"),
OPENID_INVALID("Openid格式错误或者不属于商家公众账号"),
TRANSFER_QUOTA_EXCEED("超过用户单笔收款额度,核实产品设置是否准确"),
DAY_RECEIVED_QUOTA_EXCEED("超过用户单日收款额度,核实产品设置是否准确"),
MONTH_RECEIVED_QUOTA_EXCEED("超过用户单月收款额度,核实产品设置是否准确"),
DAY_RECEIVED_COUNT_EXCEED("超过用户单日收款次数,核实产品设置是否准确"),
PRODUCT_AUTH_CHECK_FAIL("未开通该权限或权限被冻结,请核实产品权限状态"),
OVERDUE_CLOSE("超过系统重试期,系统自动关闭"),
ID_CARD_NOT_CORRECT("收款人身份证校验不通过,请核实信息"),
ACCOUNT_NOT_EXIST("该用户账户不存在"),
TRANSFER_RISK("该笔转账可能存在风险,已被微信拦截"),
OTHER_FAIL_REASON_TYPE("其它失败原因"),
REALNAME_ACCOUNT_RECEIVED_QUOTA_EXCEED("用户账户收款受限,请引导用户在微信支付查看详情"),
RECEIVE_ACCOUNT_NOT_PERMMIT("未配置该用户为转账收款人,请在产品设置中调整,添加该用户为收款人"),
PAYEE_ACCOUNT_ABNORMAL("用户账户收款异常,请联系用户完善其在微信支付的身份信息以继续收款"),
PAYER_ACCOUNT_ABNORMAL("商户账户付款受限,可前往商户平台获取解除功能限制指引"),
TRANSFER_SCENE_UNAVAILABLE("该转账场景暂不可用,请确认转账场景ID是否正确"),
TRANSFER_SCENE_INVALID("你尚未获取该转账场景,请确认转账场景ID是否正确"),
TRANSFER_REMARK_SET_FAIL("转账备注设置失败, 请调整后重新再试"),
RECEIVE_ACCOUNT_NOT_CONFIGURE("请前往商户平台-商家转账到零钱-前往功能-转账场景中添加"),
BLOCK_B2C_USERLIMITAMOUNT_BSRULE_MONTH("超出用户单月转账收款20w限额,本月不支持继续向该用户付款"),
BLOCK_B2C_USERLIMITAMOUNT_MONTH("用户账户存在风险收款受限,本月不支持继续向该用户付款"),
MERCHANT_REJECT("商户员工(转账验密人)已驳回转账"),
MERCHANT_NOT_CONFIRM("商户员工(转账验密人)超时未验密"),
SYSTEM_ERROR("打款失败"),
;
private String reason;
public String getReason() {
return reason;
}
WxTransferFailEnum(String reason) {
this.reason = reason;
}
public static String getReasonByName(String reasonName) {
for (WxTransferFailEnum value : WxTransferFailEnum.values()) {
if (value.name().equals(reasonName)) {
return value.getReason();
}
}
return null;
}
/**
* 引入 sdk 版
* @param failReasonType 失败枚举类
* @return String
*/
public static String getReasonByReason(FailReasonType failReasonType) {
for (WxTransferFailEnum value : WxTransferFailEnum.values()) {
if (value.name().equals(failReasonType.name())) {
return value.getReason();
}
}
return null;
}
}
8、转账请求参数类 TransferDTO :
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@NoArgsConstructor
public class TransferDTO implements Serializable {
private static final long serialVersionUID = 9152333275186294043L;
public TransferDTO(String openId, Long amount) {
this.openId = openId;
this.amount = amount;
}
/**
* 商户系统内部的商家批次单号,要求此参数只能由数字、大小写字母组成,在商户系统内部唯一
* 可不传,默认格式:2306211424589664124
*/
private String outBatchNo;
/**
* 该笔批量转账的名称
* 可不传,默认:商家转账到零钱
*/
private String batchName;
/**
* 转账说明,UTF8编码,最多允许32个字符
* 可不传,默认:商家转账到零钱
*/
private String batchRemark;
/**
* 商户appId下,某用户的openId
* 必传
*/
private String openId;
/**
* 转账金额单位为“分”
* 必传
*/
private Long amount;
}
9、转账查询请求参数类 TransferQueryDTO :
import lombok.Data;
import java.io.Serializable;
@Data
public class TransferQueryDTO implements Serializable {
private static final long serialVersionUID = -9039158496135815154L;
/**
* 商户系统内部的商家批次单号,要求此参数只能由数字、大小写字母组成,在商户系统内部唯一
* 必传
*/
private String outBatchNo;
}
9、转账查询返回类 TransferQueryVO :
import lombok.Data;
import java.io.Serializable;
@Data
public class TransferQueryVO implements Serializable {
private static final long serialVersionUID = -1724534950699781783L;
/**
* 业务结果:成功-SUCCESS 失败-FAIL 转账中-PROCESSING
*/
private String resCode;
/**
* 业务结果描述
*/
private String resCodeDes;
}
10、实现类 TransferServiceImpl :
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import WxTransferConfig;
import WxHttpUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.text.MessageFormat;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
@Service
@Slf4j
public class TransferServiceImpl implements ITransferService {
@Resource
private WxTransferConfig wxTransferConfig;
private static final String BATCH_REMARK = "商家转账到零钱";
@Override
public String wxTransfer(TransferDTO dto) {
SortedMap<String, Object> parameters = transferParam(dto);
// 微信请求基础URL
String baseUrl = wxTransferConfig.getBaseUrl();
// 发起商家转账请求URL
String transferUrl = wxTransferConfig.getTransferUrl();
// 商户证书序列号
String paySerialNo = wxTransferConfig.getPaySerialNo();
// 商户号
String mchId = wxTransferConfig.getMchId();
// 商户私钥证书
String privateKeyPath = wxTransferConfig.getPrivateKeyPath();
String result = WxHttpUtil.postTransBatRequest(
String.format("%s%s", baseUrl, transferUrl),
JSONObject.toJSONString(parameters),
paySerialNo,
mchId,
privateKeyPath, transferUrl);
JSONObject jsonObject = JSONObject.parseObject(result);
log.info("转账返回信息:{}", JSON.toJSONString(jsonObject));
if (null == jsonObject) {
throw new RuntimeException("打款失败");
}
if (jsonObject.containsKey("code")) {
log.info("转账失败信息:[{},{}]", jsonObject.getString("code"), jsonObject.getString("message"));
throw new RuntimeException(jsonObject.getString("message"));
} else {
if (!jsonObject.containsKey("out_batch_no")) {
throw new RuntimeException("打款失败");
}
}
log.info("转账受理成功-商家转账批次单号:{}", jsonObject.getString("out_batch_no"));
return jsonObject.getString("out_batch_no");
}
@Override
public TransferQueryVO wxTransferQuery(TransferQueryDTO dto) {
TransferQueryVO transferQueryVO = new TransferQueryVO();
//商家转账批次单号
String outBatchNo = dto.getOutBatchNo();
//商家转账明细单号
String outDetailNo = dto.getOutBatchNo();
// 微信请求基础URL
String baseUrl = wxTransferConfig.getBaseUrl();
// 商家转账查询请求URL
String transferQueryUrl = wxTransferConfig.getTransferQueryUrl();
// 商户证书序列号
String paySerialNo = wxTransferConfig.getPaySerialNo();
// 商户号
String mchId = wxTransferConfig.getMchId();
// 商户私钥证书
String privateKeyPath = wxTransferConfig.getPrivateKeyPath();
// 返回信息接收
String params = "{" +
"\"out_batch_no\":\"" + outBatchNo +
"\",\"out_detail_no\":\"" + outDetailNo
+ "\"}";
log.info("转账查询请求参数:{}", JSON.toJSONString(params));
String queryUrl = new MessageFormat(transferQueryUrl).format(new Object[]{outBatchNo, outDetailNo});
String result = WxHttpUtil.getTransBatRequest(
String.format("%s%s", baseUrl, queryUrl),
paySerialNo,
mchId,
privateKeyPath, queryUrl);
JSONObject jsonObject = JSONObject.parseObject(result);
log.info("转账查询返回信息:{}", JSON.toJSONString(jsonObject));
if (null != jsonObject) {
if (jsonObject.containsKey("code")) {
log.info("转账失败信息:[{},{}]", jsonObject.getString("code"), jsonObject.getString("message"));
transferQueryVO.setResCode(jsonObject.getString("code"));
transferQueryVO.setResCodeDes(jsonObject.getString("message"));
} else {
transferQueryVO.setResCode(WxTransferEnum.getCodeByName(jsonObject.getString("detail_status")));
transferQueryVO.setResCodeDes(WxTransferEnum.getMessageByName(jsonObject.getString("detail_status")));
if (WxTransferEnum.FAIL.getCode().equals(jsonObject.getString("detail_status"))) {
transferQueryVO.setResCodeDes(WxTransferFailEnum.getReasonByName(jsonObject.getString("fail_reason")));
}
}
} else {
transferQueryVO.setResCode(WxTransferEnum.FAIL.getCode());
transferQueryVO.setResCodeDes("打款失败");
}
log.info("转账查询返回信息II:{}", transferQueryVO);
return transferQueryVO;
}
/**
* 封装请求参数
*
* @param dto DTO请求
* @return SortedMap
*/
private SortedMap<String, Object> transferParam(TransferDTO dto) {
// 商户appId
String appId = wxTransferConfig.getAppId();
String outBatchNo = "".equals(dto.getOutBatchNo()) || null == dto.getOutBatchNo() ? generateOutNo() : dto.getOutBatchNo();
String batchName = "".equals(dto.getBatchName()) || null == dto.getBatchName() ? BATCH_REMARK : dto.getBatchName();
String batchRemark = "".equals(dto.getBatchRemark()) || null == dto.getBatchRemark() ? BATCH_REMARK : dto.getBatchRemark();
Long totalAmount = dto.getAmount();
Integer totalNum = 1;
String openId = dto.getOpenId();
SortedMap<String, Object> parameters = new TreeMap<>();
// 【商户appid】 申请商户号的appid或商户号绑定的appid
parameters.put("appid", appId);
// 【商家批次单号】 商户系统内部的商家批次单号,要求此参数只能由数字、大小写字母组成,在商户系统内部唯一
parameters.put("out_batch_no", outBatchNo);
// 【批次名称】 该笔批量转账的名称
parameters.put("batch_name", batchName);
// 【批次备注】 转账说明,UTF8编码,最多允许32个字符
parameters.put("batch_remark", batchRemark);
// 【转账总金额】 转账金额单位为“分”。账总金额必须与批次内所有明细转账金额之和保持一致,否则无法发起转账操作
parameters.put("total_amount", totalAmount);
// 【转账总笔数】 一个转账批次单最多发起一千笔转账。转账总笔数必须与批次内所有明细之和保持一致,否则无法发起转账操作
parameters.put("total_num", totalNum);
List<Map> transferDetailList = new ArrayList<>();
Map<String, Object> subMap = new HashMap<>(4);
//商家明细单号 该商家下唯一
subMap.put("out_detail_no", outBatchNo);
//转账金额
subMap.put("transfer_amount", totalAmount);
//转账备注
subMap.put("transfer_remark", batchRemark);
//用户在直连商户应用下的用户标示
subMap.put("openid", openId);
transferDetailList.add(subMap);
// 【转账明细列表】 发起批量转账的明细列表,最多一千笔
parameters.put("transfer_detail_list", transferDetailList);
log.info("转账请求参数:[{}]", JSON.toJSONString(parameters));
return parameters;
}
private String generateOutNo() {
String time = cn.hutool.core.date.DateUtil.format(new Date(), "yyyyMMddHHmmssSSS").substring(2);
int randomInt = ThreadLocalRandom.current().nextInt(1000, 10000);
return time + randomInt;
}
}
11、文件过滤 pom.xml :
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<nonFilteredFileExtensions>
<nonFilteredFileExtension>pem</nonFilteredFileExtension>
</nonFilteredFileExtensions>
</configuration>
</plugin>
</plugins>
</build>
12、文件拷贝 apiclient_key.pem :
将 apiclient_key.pem 拷贝至 resources 目录下。
13、测试类 TransferTest :
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
import java.util.concurrent.ThreadLocalRandom;
@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class TransferTest {
@Resource
TransferServiceImpl transferService;
@Test
//@Ignore
public void wxTransfer() {
TransferDTO dto = new TransferDTO();
Long amount = 10L;
String openId = "1234567";
dto.setOpenId(openId);
//金额单位:分
dto.setAmount(amount);
transferService.wxTransfer(dto);
}
@Test
public void wxTransferQuery() {
TransferQueryDTO dto = new TransferQueryDTO();
dto.setOutBatchNo("12345678910111213");
transferService.wxTransferQuery(dto);
}
}