赞
踩
<!--微信支付-->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
<version>4.3.9.B</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>5.3.8</version>
</dependency>
@Slf4j
public class Wxpay {
private static final Gson GSON = new GsonBuilder().create();
/**
* 支付
*/
public static WxPayReturnInfoVO prepay(){
final WxPayUnifiedOrderRequest wxPayUnifiedOrderRequest = WxPayUnifiedOrderRequest.newBuilder()
//支付人的openid
.openid("")
//用户生成的唯一订单编号
.outTradeNo("")
//订单金额(单位分)
.totalFee(1)
//商品描述
.body("")
//可以是区分业务的字段,例如userId,也可以不写
.attach("")
//本地ip
.spbillCreateIp(InetAddress.getLoopbackAddress().getHostAddress())
//回调的url地址
.notifyUrl("")
.build();
WxPayUnifiedOrderResult wxPayUnifiedOrderResult = null;
try {
wxPayUnifiedOrderResult = WxConfig.wxPayService().unifiedOrder(wxPayUnifiedOrderRequest);
} catch (WxPayException e) {
e.printStackTrace();
}
//组合参数构建支付
Map<String, String> paySignInfo = new HashMap<>(5);
String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
String nonceStr = String.valueOf(System.currentTimeMillis());
paySignInfo.put("appId", WxConfig.appId);
paySignInfo.put("nonceStr", nonceStr);
paySignInfo.put("timeStamp", timeStamp);
paySignInfo.put("signType", "MD5");
paySignInfo.put("package", "prepay_id=" + wxPayUnifiedOrderResult.getPrepayId());
String[] signInfo = new String[0];
String paySign = SignUtils.createSign(paySignInfo, "MD5", WxConfig.mch_key, signInfo);
//组合支付参数
WxPayReturnInfoVO returnPayInfoVO = new WxPayReturnInfoVO();
returnPayInfoVO.setAppId(WxConfig.appId);
returnPayInfoVO.setNonceStr(nonceStr);
returnPayInfoVO.setPaySign(paySign);
returnPayInfoVO.setSignType("MD5");
returnPayInfoVO.setPrepayId(wxPayUnifiedOrderResult.getPrepayId());
returnPayInfoVO.setTimeStamp(timeStamp);
return returnPayInfoVO;
}
/**
* 退款
*/
public static Map<String, String> rebackPay(String orderNo, String transactionId, BigDecimal amount) throws Exception{
Map<String, String> returnMap = new HashMap<>();
//微信支付-申请退款请求参数
WxPayRefundV3Request request = new WxPayRefundV3Request();
WxPayRefundV3Request.Amount am = new WxPayRefundV3Request.Amount();
//原订单金额
am.setTotal(amount.multiply(new BigDecimal(100)).intValue());
//退款币种,符合ISO 4217标准的三位字母代码,目前只支持人民币:CNY。
am.setCurrency("CNY");
//退款金额 注意:退款金额,单位为分,只能为整数,不能超过原订单支付金额。
am.setRefund(amount.multiply(new BigDecimal(100)).intValue());
//金额信息
request.setAmount(am);
//transaction_id:微信支付订单号
request.setTransactionId(transactionId);
//商户订单号
request.setOutRefundNo("return_"+orderNo);
request.setNotifyUrl(WxConfig.refund_notify_url);
//调用微信V3退款API
WxPayRefundV3Result result = WxConfig.wxPayService().refundV3(request);
String returnMsg = null;
String status = result.getStatus();
switch (status) {
case "SUCCESS":
returnMsg = "退款成功";
break;
case "CLOSED":
returnMsg = "退款关闭";
break;
case "PROCESSING":
returnMsg = "退款处理中";
break;
case "ABNORMAL":
returnMsg = "退款异常";
break;
default:
returnMsg = "受理失败";
break;
}
returnMap.put("status", status);
returnMap.put("returnMsg", returnMsg);
return returnMap;
}
/**
* 企业付款到用户零钱
*/
public static void entPay(String openId, BigDecimal amount) {
String outNo = IdUtil.getSnowflake(0, 0).nextIdStr();
/**
* 系统内部业务逻辑
*/
EntPayRequest wxEntPayRequest = new EntPayRequest();
wxEntPayRequest.setAppid(WxConfig.appId);
wxEntPayRequest.setMchId(WxConfig.mch_id);
// 系统订单id
wxEntPayRequest.setPartnerTradeNo(outNo);
// 付款用户的wxopenid
wxEntPayRequest.setOpenid(openId);
// 实名校验
// NO_CHECK:不校验真实姓名
// FORCE_CHECK:强校验真实姓名(未实名认证的用户会校验失败,无法转账)
// OPTION_CHECK:针对已实名认证的用户才校验真实姓名(未实名认证用户不校验,可以转账成功)
wxEntPayRequest.setCheckName("NO_CHECK");
//付款金额 单位分
wxEntPayRequest.setAmount(amount.multiply(new BigDecimal(100)).intValue());
//wxEntPayRequest.setAmount(amount.multiply(new BigDecimal(100)).intValue());
// 描述
wxEntPayRequest.setDescription("提现");
// 调用接口的机器IP地址
wxEntPayRequest.setSpbillCreateIp("");
try {
EntPayResult wxEntPayResult = WxConfig.wxPayService().getEntPayService().entPay(wxEntPayRequest);
if ("SUCCESS".equals(wxEntPayResult.getResultCode())
&& "SUCCESS".equals(wxEntPayResult.getReturnCode())) {
log.info("企业对个人付款成功!\n付款信息:\n" + wxEntPayResult);
System.out.println("企业对个人付款成功!\n付款信息:\n" + wxEntPayResult);
/**
* 系统内部业务逻辑
*/
} else {
log.error("err_code: " + wxEntPayResult.getErrCode()
+ " err_code_des: " + wxEntPayResult.getErrCodeDes());
System.out.println("提现失败! " + "err_code: " + wxEntPayResult.getErrCode() + " err_code_des: " + wxEntPayResult.getErrCodeDes());
/**
* 系统内部业务逻辑
*/
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("提现失败! " + e.getMessage());
}
}
/**
* 商家转账到零钱
*/
public static String weixinTransferBat(String openId, int amount) throws Exception {
log.info("进入商家转账到零钱, openId:{},金额:{}", openId,amount);
//商户号
String mchid = WxConfig.mch_id;
//申请商户号的appid或商户号绑定的appid(企业号corpid即为此appid)
String appId = WxConfig.appId;
//用户在直连商户应用下的用户标示
//String openId = openId;
//商户证书编号
String wechatPayserialNo = WxConfig.serial_no;
//商户证书路径(在你本机测试时放你本机路径中的就可以)
String privatekeypath = WxConfig.pemUrl;
Map<String, Object> postMap = new HashMap<>();
//商家批次单号 长度 1~32
String outNo = IdUtil.getSnowflake(0, 0).nextIdStr();
postMap.put("appid", appId);
postMap.put("out_batch_no", outNo);
//该笔批量转账的名称
postMap.put("batch_name", "测试转账");
//转账说明,UTF8编码,最多允许32个字符
postMap.put("batch_remark", "测试转账");
//转账金额单位为“分”。 总金额
//金额
postMap.put("total_amount", amount);
//。转账总笔数
postMap.put("total_num", 1);
List<Map> list = new ArrayList<>();
Map<String, Object> subMap = new HashMap<>(4);
//商家明细单号
subMap.put("out_detail_no", outNo);
//转账金额
//金额
subMap.put("transfer_amount", amount);
//转账备注
subMap.put("transfer_remark", "明细备注1");
//用户在直连商户应用下的用户标示
subMap.put("openid", openId);
list.add(subMap);
postMap.put("transfer_detail_list", list);
//发起转账操作
String resStr = HttpUtil.postTransBatRequest(
WxConfig.batches_url,
GSON.toJson(postMap),
wechatPayserialNo,
mchid,
privatekeypath);
log.info("商家转账到零钱END, 返回参数:{}",resStr);
return resStr;
}
}
@Data
public class WxPayReturnInfoVO {
private String appId;
private String timeStamp;
private String nonceStr;
private String prepayId;
private String paySign;
private String signType;
private String id;
}
@Component
public class WxConfig {
/**小程序appId*/
public static String appId = "";
/**小程序的秘钥*/
public static String miyao = "";
/**商户号*/
public static String mch_id = "";
/**商户支付秘钥V2*/
public static String mch_key = "";
/**退款用到*/
public static String keyPath = "";
/**商家转账到零钱*/
public static String pemUrl = "";
public static String sn = "";
/**商户证书序列号*/
public static String serial_no = "";
/**回调通知地址(需内网穿透测试)*/
public static String notify_url = "";
/**交易类型*/
public static String trade_type = "JSAPI";
/**统一下单API接口链接*/
public static String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
/**查询订单API接口链接*/
public static String query_url = "https://api.mch.weixin.qq.com/pay/orderquery";
/**退款接口*/
public static String refund_url = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";
/**退款回调接口*/
public static String refund_notify_url = "";
/**商家转账到零钱*/
public static String batches_url = "https://api.mch.weixin.qq.com/v3/transfer/batches";
public static WxPayService wxPayService() {
WxPayConfig payConfig = new WxPayConfig();
payConfig.setAppId(appId);
payConfig.setMchId(mch_id);
payConfig.setMchKey(mch_key);
payConfig.setKeyPath(keyPath);
payConfig.setTradeType(trade_type);
payConfig.setNotifyUrl(notify_url);
WxPayService wxPayService = new WxPayServiceImpl();
wxPayService.setConfig(payConfig);
return wxPayService;
}
}
@Slf4j
public class HttpUtil {
/**
* 发起批量转账API 批量转账到零钱
*
* @param requestUrl
* @param requestJson 组合参数
* @param wechatPayserialNo 商户证书序列号
* @param mchID4M 商户号
* @param privatekeypath 商户私钥证书路径
* @return
*/
public static String postTransBatRequest(
String requestUrl,
String requestJson,
String wechatPayserialNo,
String mchID4M,
String privatekeypath) {
CloseableHttpClient httpclient = HttpClients.createDefault();
CloseableHttpResponse response = null;
HttpEntity entity = null;
try {
//商户私钥证书
HttpPost httpPost = new HttpPost(requestUrl);
// NOTE: 建议指定charset=utf-8。低于4.4.6版本的HttpCore,不能正确的设置字符集,可能导致签名错误
httpPost.addHeader("Content-Type", "application/json");
httpPost.addHeader("Accept", "application/json");
//"55E551E614BAA5A3EA38AE03849A76D8C7DA735A");
httpPost.addHeader("Wechatpay-Serial", wechatPayserialNo);
//-------------------------核心认证 start-----------------------------------------------------------------
String strToken = VechatPayV3Util.getToken("POST",
"/v3/transfer/batches",
requestJson,mchID4M,wechatPayserialNo, privatekeypath);
log.error("微信转账token "+strToken);
// 添加认证信息
httpPost.addHeader("Authorization",
"WECHATPAY2-SHA256-RSA2048" + " "
+ strToken);
//---------------------------核心认证 end---------------------------------------------------------------
httpPost.setEntity(new StringEntity(requestJson, "UTF-8"));
//发起转账请求
response = httpclient.execute(httpPost);
//获取返回的数据
entity = response.getEntity();
log.info("-----getHeaders.Request-ID:"+response.getHeaders("Request-ID"));
return EntityUtils.toString(entity);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
@Slf4j
public class VechatPayV3Util {
@Autowired
static ResourceLoader resourceLoader;
/**
*
* @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) {
// String canonicalUrl = url.encodedPath();
// if (url.encodedQuery() != null) {
// canonicalUrl += "?" + url.encodedQuery();
// }
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");
// apiclient_key.pem存在到resource/cert下
//Resource resource = resourceLoader.getResource("classpath:/cert/apiclient_key.pem");
ClassPathResource classPathResource = new ClassPathResource(WxConfig.pemUrl);
File file = classPathResource.getFile();
String path = file.getPath();
// 获取私钥key,实际读取apiclient_key.pem文件信息创建PrivateKey 对象
PrivateKey privateKey = getPrivateKey(path);
sign.initSign(privateKey);
sign.update(message);
return Base64.encodeBase64String(sign.sign());
}
/**
* 微信支付-前端唤起支付参数-获取商户私钥
*
* @param filename 私钥文件路径 (required)
* @return 私钥对象
*/
public static PrivateKey getPrivateKey(String filename) throws IOException {
log.error("签名 证书地址是 "+filename);
String content = new String(Files.readAllBytes(Paths.get(filename)), "utf-8");
try {
String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replaceAll("\\s+", "");
//System.out.println("--------privateKey---------:"+privateKey);
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
*/
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();
}
}
一个在学习的开发者,勿喷,欢迎交流
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。