提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
微信商户批量转账
一、微信商户批量转账到零钱
1.引入库
代码如下(示例):
/**
* 微信支付专用类 请求操作方法
*
* @author Administrator
*/
public class WeChatNewUtil {
private static Logger log = Logger.getLogger(WeChatNewUtil.class);
public static String transferBatch="/v3/transfer/batches";
public static String domain="https://api.mch.weixin.qq.com";
public static int connectionTimeout=10000;
public static int readTimeout=10000;
public static String privatekeypath = ClassUtils.getDefaultClassLoader().getResource("").getPath()+"/weixin/apiclient_key.pem";//从微信商户平台下载的密钥存放的路径
/**
* 发起批量转账API 批量转账到零钱
* @param url 全路径地址
* @param requestJson 请求body
* @param mchID4M 商户id
* @param method 请求方法(get,post)
* @param requestUrl (域名后面的请求url)
* @return
*/
public static String postTransBatRequest(String url,
String requestJson,
String mchID,String method,String requestUrl) {
CloseableHttpClient httpclient = HttpClients.createDefault();
CloseableHttpResponse response = null;
HttpEntity entity = null;
try {
String payserialNo= PemUtil.loadCertificate().getSerialNumber().toString(16).toUpperCase();
//商户私钥证书
HttpPost httpPost = new HttpPost(url);
httpPost.addHeader("Content-Type", "application/json");
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Wechatpay-Serial", payserialNo);
//-------------------------核心认证 start-----------------------------------------------------------------
String strToken = V3Util.getToken(method,
requestUrl,requestJson,mchID,payserialNo, privatekeypath);
// 添加认证信息
httpPost.addHeader("Authorization",
"WECHATPAY2-SHA256-RSA2048" + " "
+ strToken);
//---------------------------核心认证 end---------------------------------------------------------------
RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(readTimeout)
.setConnectTimeout(connectionTimeout)
.build();
httpPost.setConfig(requestConfig);
if(requestJson!=null)httpPost.setEntity(new StringEntity(requestJson, "UTF-8"));
//发起转账请求
response = httpclient.execute(httpPost);
entity = response.getEntity();//获取返回的数据
return EntityUtils.toString(entity);
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭流
}
return null;
}
/**
* get查询订单状态
* @param url 全路径地址
* @param requestJson 请求body
* @param mchID4M 商户id
* @param method 请求方法(get,post)
* @param requestUrl (域名后面的请求url)
* @return
*/
public static String getTransBatRequest(String url,
String requestJson,
String mchID,String method,String requestUrl) {
CloseableHttpClient httpclient = HttpClients.createDefault();
CloseableHttpResponse response = null;
HttpEntity entity = null;
try {
String payserialNo= PemUtil.loadCertificate().getSerialNumber().toString(16).toUpperCase();
//商户私钥证书
HttpGet httpGet = new HttpGet(url);
httpGet.addHeader("Content-Type", "application/json");
httpGet.addHeader("Accept", "application/json");
httpGet.addHeader("Wechatpay-Serial", payserialNo);
//-------------------------核心认证 start-----------------------------------------------------------------
String strToken = V3Util.getToken(method,
requestUrl,
requestJson,mchID,payserialNo, privatekeypath);
// 添加认证信息
httpGet.addHeader("Authorization",
"WECHATPAY2-SHA256-RSA2048" + " "
+ strToken);
//---------------------------核心认证 end---------------------------------------------------------------
RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(readTimeout)
.setConnectTimeout(connectionTimeout)
.build();
httpGet.setConfig(requestConfig);
//发起转账请求
response = httpclient.execute(httpGet);
entity = response.getEntity();//获取返回的数据
return EntityUtils.toString(entity);
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭流
}
return null;
}
}
2.获取商户序列号密钥
代码如下(示例):
public class PemUtil {
public static PrivateKey loadPrivateKey(String privateKey) {
privateKey = privateKey
.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replaceAll("\\s+", "");
try {
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(new PKCS8EncodedKeySpec(new Base64().decode(privateKey)));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("当前Java环境不支持RSA", e);
} catch (InvalidKeySpecException e) {
throw new RuntimeException("无效的密钥格式");
}
}
public static PrivateKey loadPrivateKey() throws Exception {
String certPath1 = ClassUtils.getDefaultClassLoader().getResource("").getPath()+"/weixin/apiclient_key.pem";//从微信商户平台下载的密钥存放的路径
File file1 = new File(certPath1);
InputStream inputStream = new FileInputStream(file1);
ByteArrayOutputStream os = new ByteArrayOutputStream(2048);
byte[] buffer = new byte[1024];
String privateKey;
try {
for (int length; (length = inputStream.read(buffer)) != -1; ) {
os.write(buffer, 0, length);
}
privateKey = os.toString("UTF-8");
} catch (IOException e) {
throw new IllegalArgumentException("无效的密钥", e);
}
return loadPrivateKey(privateKey);
}
public static X509Certificate loadCertificate() throws Exception {
try {
String certPath1 = ClassUtils.getDefaultClassLoader().getResource("").getPath()+"/weixin/apiclient_cert.pem";//从微信商户平台下载的序列号存放的路径
File file1 = new File(certPath1);
InputStream inputStream = new FileInputStream(file1);
CertificateFactory cf = CertificateFactory.getInstance("X509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(inputStream);
cert.checkValidity();
return cert;
} catch (CertificateExpiredException e) {
throw new RuntimeException("证书已过期", e);
} catch (CertificateNotYetValidException e) {
throw new RuntimeException("证书尚未生效", e);
} catch (CertificateException e) {
throw new RuntimeException("无效的证书", e);
}
}
private static final String TRANSFORMATION = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding";
public static String encryptOAEP(String message, X509Certificate certificate) throws Exception {
return encrypt(message, certificate, TRANSFORMATION);
}
/**
* 敏感信息加密
*/
public static String encrypt(String message, X509Certificate certificate, String transformation) throws Exception {
try {
Cipher cipher = Cipher.getInstance(transformation);
cipher.init(Cipher.ENCRYPT_MODE, certificate.getPublicKey());
byte[] data = message.getBytes(StandardCharsets.UTF_8);
byte[] ciphertext = cipher.doFinal(data);
return new Base64().encodeToString(ciphertext);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
throw new RuntimeException("当前Java环境不支持RSA v1.5/OAEP", e);
} catch (InvalidKeyException e) {
throw new IllegalArgumentException("无效的证书", e);
} catch (IllegalBlockSizeException | BadPaddingException e) {
throw new IllegalBlockSizeException("加密原串的长度不能超过214字节");
}
}
}
2.获取签名
代码如下(示例):
public class V3Util {
private static Logger log = Logger.getLogger(V3Util.class);
/**
*
* @param method 请求方法 post
* @param canonicalUrl 请求地址
* @param body 请求参数
* @param merchantId 这里用的商户号
* @param certSerialNo 商户证书序列号
* @param keyPath 商户证书地址
* @return
* @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;
if (StringUtils.isEmpty(body)) {
body = "";
}
//签名操作
String message = buildMessage(method, canonicalUrl, timestamp, nonceStr, body);
//签名操作
String signature = sign(message.getBytes("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 私钥对象
* @throws Exception
*/
public static PrivateKey getPrivateKey(String filename) throws Exception {
return PemUtil.loadPrivateKey();
}
/**
* 获取随机位数的字符串
* @param length
* @return
*/
public static String getRandomString(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();
}
}
2.RFC3339日期转换
代码如下(示例):
public static String getDateByRFC3339(String time){
DateTime dateTime = new DateTime(time);
long timeInMillis = dateTime.toCalendar(Locale.getDefault()).getTimeInMillis();
Date date = new Date(timeInMillis);
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmsss");
String time = format.format(date);
return time;
}
总结
这里对文章进行总结:
以上就是今天要讲的内容,本文仅仅简单介绍了微信商户批量转账到零钱,需要注意的是敏感信息需要进行加密处理,请求时需要获取序列号和签名