2024.09.24更新说明
许多人对两个微信支付依赖不清楚是什么关系区别
这里解释一下
<!-- 微信官方封装SDK -->
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.14</version>
</dependency>
官方原封maven依赖
<!-- 微信官方封装的Apache HttpClient扩展SDK-->
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.4.9</version>
</dependency>
官方扩展封装maven依赖 使用HttpClient构造内部请求
如果你是使用Apache HttpClient的商户开发者
可以使用它构造HttpClient。得到的HttpClient在执行请求时将自动携带身份认证信息,
并检查应答的微信支付签名。
上述为微信支付github库原述
所以有人疑惑微信要求传入认证信息
我这里不需要填写因为全部已经封装进HttpClient里了
这里我又通过官方封装的源码实例分离出验证方法简化验证过程
只要商户证书秘钥等正确一致 基本上直接通
这里放上微信maven库可仔细查看各个不同支付封装的解释 wechatpay-apiv3 · GitHub
为了你们能看懂我操碎了心!
2024.05.09更新方法
更新WXPaySignatureCertificateUtil.java 工具类 详细看3.1
许多人对AutoUpdateCertificatesVerifier验证有疑问
所以此次更新使用Verifier验证
两者自选使用
不废话 如何开通微信转账我就不发了 网上一大堆 直接上主代码
主打一个复制粘贴直接用 依然没有其它依赖
加入Maven依赖
<!-- 微信支付V3 目前新版本-->
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.4.9</version>
</dependency>
创建 WxV3PayConfig.java
/**
* implements WXPayConfig
*/
@Data
public class WxV3PayConfig {
//平台证书序列号
public static String mchSerialNo = "xxxxxxxxxxxxxxxxxxxxxx";
//appID
public static String APP_ID = "xxxxxxxxxxx";
//商户id
public static String Mch_ID = "xxxxxxxxxxxx";
// API V3密钥
public static String apiV3Key = "xxxxxxxxxxxxxxxxxxxxxx";
}
创建WXPayConstants.java
/**
* 常量
*/
public class WXPayConstants {
public static final String DOMAIN_API = "https://api.mch.weixin.qq.com/v3";
//微信转账
public static final String REFUND_DOMESTIC_REFUNDS = "/refund/domestic/refunds";
}
2.创建WXPaySignatureCertificateUtil.java 工具类
复制粘贴即可
/***
*
*包都在这
*
*
*
*/
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.SneakyThrows;
import org.apache.http.impl.client.CloseableHttpClient;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class WXPaySignatureCertificateUtil {
/**
* 证书验证
* 自动更新的签名验证器
*/
public static CloseableHttpClient checkSign() throws IOException {
//验签
CloseableHttpClient httpClient = null;
PrivateKey merchantPrivateKey = WXPaySignatureCertificateUtil.getPrivateKey();
httpClient = WechatPayHttpClientBuilder.create()
.withMerchant(WxV3PayConfig.Mch_ID, WxV3PayConfig.mchSerialNo, merchantPrivateKey)
.withValidator(new WechatPay2Validator(WXPaySignatureCertificateUtil.getVerifier(WxV3PayConfig.mchSerialNo)))
.build();
return httpClient;
}
/**
* 保存微信平台证书
*/
private static final ConcurrentHashMap<String, AutoUpdateCertificatesVerifier> verifierMap = new ConcurrentHashMap<>();
/**
* 功能描述:获取平台证书,自动更新
* 注意:这个方法内置了平台证书的获取和返回值解密
*/
static AutoUpdateCertificatesVerifier getVerifier() {
String mchSerialNo = WxV3PayConfig.mchSerialNo;
AutoUpdateCertificatesVerifier verifier = null;
if (verifierMap.isEmpty() || !verifierMap.containsKey(mchSerialNo)) {
verifierMap.clear();
try {
//传入证书
PrivateKey privateKey = getPrivateKey();
//刷新
PrivateKeySigner signer = new PrivateKeySigner(mchSerialNo, privateKey);
WechatPay2Credentials credentials = new WechatPay2Credentials(WxV3PayConfig.Mch_ID, signer);
verifier = new AutoUpdateCertificatesVerifier(credentials
, WxV3PayConfig.apiV3Key.getBytes("utf-8"));
verifierMap.put(verifier.getValidCertificate().getSerialNumber()+"", verifier);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
} else {
verifier = verifierMap.get(mchSerialNo);
}
return verifier;
}
/**
* app生成带签名支付信息
*
* @param timestamp 时间戳
* @param nonceStr 随机数
* @param prepayId 预付单
* @return 支付信息
* @throws Exception
*/
public static String appPaySign(String timestamp, String nonceStr, String prepayId) throws Exception {
//上传私钥
PrivateKey privateKey = getPrivateKey();
String signatureStr = Stream.of(WxV3PayConfig.APP_ID, timestamp, nonceStr, prepayId)
.collect(Collectors.joining("\n", "", "\n"));
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initSign(privateKey);
sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(sign.sign());
}
/**
* 小程序及其它支付生成带签名支付信息
*
* @param timestamp 时间戳
* @param nonceStr 随机数
* @param prepayId 预付单
* @return 支付信息
* @throws Exception
*/
public static String jsApiPaySign(String timestamp, String nonceStr, String prepayId) throws Exception {
//上传私钥
PrivateKey privateKey = getPrivateKey();
String signatureStr = Stream.of(WxV3PayConfig.APP_ID, timestamp, nonceStr, "prepay_id="+prepayId)
.collect(Collectors.joining("\n", "", "\n"));
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initSign(privateKey);
sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(sign.sign());
}
/**
* 获取私钥。
* 证书路径 本地使用如: D:\\微信平台证书工具\\7.9\\apiclient_key.pem
* 证书路径 线上使用如: /usr/apiclient_key.pem
* String filename 私钥文件路径 (required)
* @return 私钥对象
*/
public static PrivateKey getPrivateKey() throws IOException {
String content = new String(Files.readAllBytes(Paths.get("D:\\微信平台证书工具\\7.9\\apiclient_key.pem")), "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.getDecoder().decode(privateKey)));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("当前Java环境不支持RSA", e);
} catch (InvalidKeySpecException e) {
throw new RuntimeException("无效的密钥格式");
}
}
}
3.1.创建WXPaySignatureCertificateUtil.java
/***
*
*包都在这
*
*
*
*/
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.SneakyThrows;
import org.apache.http.impl.client.CloseableHttpClient;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class WXPaySignatureCertificateUtil {
private static final ConcurrentHashMap<String, Verifier> verifierMaps = new ConcurrentHashMap<>();
/**
* 证书验证
* 自动更新的签名验证器
*/
public static CloseableHttpClient checkSign() throws IOException {
//验签
CloseableHttpClient httpClient = null;
PrivateKey merchantPrivateKey = WXPaySignatureCertificateUtil.getPrivateKey();
httpClient = WechatPayHttpClientBuilder.create()
.withMerchant(WxV3PayConfig.Mch_ID, WxV3PayConfig.mchSerialNo, merchantPrivateKey)
.withValidator(new WechatPay2Validator(WXPaySignatureCertificateUtil.getVerifier(WxV3PayConfig.mchSerialNo)))
.build();
return httpClient;
}
/**
* 功能描述:获取平台证书,自动更新
* 注意:这个方法内置了平台证书的获取和返回值解密
*/
public static Verifier getVerifiers(String mchSerialNo) {
Verifier verifier = null;
if (verifierMaps.isEmpty() || !verifierMaps.containsKey(mchSerialNo)) {
verifierMaps.clear();
try {
PrivateKey privateKey = getPrivateKey();
//刷新
PrivateKeySigner signer = new PrivateKeySigner(mchSerialNo, privateKey);
WechatPay2Credentials credentials = new WechatPay2Credentials(WxV3PayConfig.Mch_ID, signer);
verifier = new AutoUpdateCertificatesVerifier(credentials
, apiV3Key.getBytes("utf-8"));
verifierMaps.put(verifier.getValidCertificate().getSerialNumber()+"", verifier);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
throw new RuntimeException(e);
}
} else {
verifier = verifierMaps.get(mchSerialNo);
}
return verifier;
}
/**
* app生成带签名支付信息
*
* @param timestamp 时间戳
* @param nonceStr 随机数
* @param prepayId 预付单
* @return 支付信息
* @throws Exception
*/
public static String appPaySign(String timestamp, String nonceStr, String prepayId) throws Exception {
//上传私钥
PrivateKey privateKey = getPrivateKey();
String signatureStr = Stream.of(WxV3PayConfig.APP_ID, timestamp, nonceStr, prepayId)
.collect(Collectors.joining("\n", "", "\n"));
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initSign(privateKey);
sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(sign.sign());
}
/**
* 小程序及其它支付生成带签名支付信息
*
* @param timestamp 时间戳
* @param nonceStr 随机数
* @param prepayId 预付单
* @return 支付信息
* @throws Exception
*/
public static String jsApiPaySign(String timestamp, String nonceStr, String prepayId) throws Exception {
//上传私钥
PrivateKey privateKey = getPrivateKey();
String signatureStr = Stream.of(WxV3PayConfig.APP_ID, timestamp, nonceStr, "prepay_id="+prepayId)
.collect(Collectors.joining("\n", "", "\n"));
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initSign(privateKey);
sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(sign.sign());
}
/**
* 获取私钥。
* 证书路径 本地使用如: D:\\微信平台证书工具\\7.9\\apiclient_key.pem
* 证书路径 线上使用如: /usr/apiclient_key.pem
* String filename 私钥文件路径 (required)
* @return 私钥对象
*/
public static PrivateKey getPrivateKey() throws IOException {
String content = new String(Files.readAllBytes(Paths.get("D:\\微信平台证书工具\\7.9\\apiclient_key.pem")), "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.getDecoder().decode(privateKey)));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("当前Java环境不支持RSA", e);
} catch (InvalidKeySpecException e) {
throw new RuntimeException("无效的密钥格式");
}
}
}
创建WechatPaymentService.java
/**
* 微信支付 WechatPaymentService
* @author 影子
*/
public interface WechatPaymentService{
/**
* 微信转账提现
* @param payParam
* @return
*/
public Map<String, Object> weChatWithdraw();
}
创建WeChatPaymentServiceImpl.java
/**
* 微信支付 WeChatPaymentServiceImpl
*/
@Service
@Slf4j
public class WeChatPaymentServiceImpl implements WechatPaymentService {
/**
* 微信提现转账
* @param
* @return 影子
*/
@Override
public Map<String, Object> weChatWithdraw() {
Map<String,Object> ajaxResult = new HashMap<>();
try {
//支付总金额
BigDecimal totalPrice = BigDecimal.ZERO;
totalPrice = totalPrice.add(BigDecimal.valueOf(500));
//转换金额
Integer money = new BigDecimal(String.valueOf(totalPrice)).movePointRight(2).intValue();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode rootNode = objectMapper.createObjectNode();
//微信商户批次号订单号
rootNode.put("appid", "appid")
//微信商户批次号订单号
.put("out_batch_no","51213215612")
.put("batch_name","分佣提现")
.put("batch_remark","商品订单分佣奖励")
.put("total_amount",money)
.put("total_num",1);
ArrayNode transferDetailList = objectMapper.createArrayNode();
ObjectNode transferDetail = objectMapper.createObjectNode();
//微信批次号
transferDetail.put("out_detail_no","55312168545132")
.put("transfer_amount",money)
.put("transfer_remark","商品订单分佣奖励")
.put("openid","1223115612");
transferDetailList.add(transferDetail);
rootNode.set("transfer_detail_list", transferDetailList);
objectMapper.writeValue(bos, rootNode);
//验证证书
CloseableHttpClient httpClient = WXPaySignatureCertificateUtil.checkSign();
//申请退款接口
HttpPost httpPost = new HttpPost(DOMAIN_API + REFUND_DOMESTIC_REFUNDS);
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-type", "application/json");
httpPost.addHeader("Wechatpay-Serial", WxV3PayConfig.mchSerialNo);
httpPost.setEntity(new StringEntity(bos.toString(), "UTF-8"));
CloseableHttpResponse response = httpClient.execute(httpPost);
//退款成功返回消息
String bodyAsString = EntityUtils.toString(response.getEntity());
JSONObject jsonObject = JSONObject.parseObject(bodyAsString);
if (jsonObject.containsKey("batch_status")) {
String batchStatus = jsonObject.getString("batch_status");
if ("ACCEPTED".equals(batchStatus)) {
// 退款成功
ajaxResult.put("code", "200");
ajaxResult.put("msg", "提现成功");
return ajaxResult;
}
} else if (jsonObject.containsKey("code")) {
String code = jsonObject.getString("code");
if ("NOT_ENOUGH".equals(code)) {
ajaxResult.put("code", "500");
ajaxResult.put("msg", "资金不足");
return ajaxResult;
} else {
ajaxResult.put("code", "500");
ajaxResult.put("msg", "申请提现失败");
return ajaxResult;
}
}
}catch (Exception e) {
e.printStackTrace();
}
ajaxResult.put("code", "500");
ajaxResult.put("msg", "申请提现失败");
return ajaxResult;
}
}