所需资料拿到后接下来集成到项目中
pom文件:
<!-- 支付宝 -->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.13.32.ALL</version>
</dependency>
<!-- 微信 -->
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>
1.工具类
/**
* 微信支付V3版本
*/
@Component
public class WechatPayUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(WechatPayUtil.class);
@Autowired
private WechatPayConfig wechatPayConfig;
/**
* 微信支付V3-APP支付地址
*/
private static final String APP_PAY_URL = "https://api.mch.weixin.qq.com/v3/pay/transactions/app";
/**
* 微信支付V3-小程序支付地址
*/
private static final String JSAPI_PAY_URL = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
/**
* 微信支付V3-申请退款地址
*/
private static final String APP_REFUND_URL = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";
/**
* 微信支付V3-APP商品回调地址
*/
private static final String APP_PAY_NOTIFY_URL_GOODS = "/api/callback/paymentOfGoodsNotifyForWechatPay";
/**
* 商品退款回调地址
*/
private static final String APP_REFUND_NOTIFY_URL_GOODS = "/cloud-order/api/callback/refundOfGoodsNotifyForWechatPay";
/**
* 微信小程序预支付
*
* @param outTradeNo 订单编号
* @param amount 订单金额【分】
*/
public PaymentResultDto wechatPayForJSAPI(PaymentCodeEnum paymentCodeEnum, String outTradeNo, BigDecimal amount, String openId) {
// 绝对接口地址
String canonicalURL = "/v3/pay/transactions/jsapi";
// 订单总金额 元 -> 分
// int totalAmount = amount.multiply(new BigDecimal(100)).setScale(0, RoundingMode.HALF_UP).intValue();
// 构造异步回调地址
String notifyURL = this.buildNotifyURL(paymentCodeEnum);
// 时间戳
long timestamp = System.currentTimeMillis() / 1000;
// 随机字符串
String nonceStr = RandomUtil.randomString(32);
// 加载证书信息
KeyPairFactory.KeyPairCustom pkcs12 = KeyPairFactory.createPKCS12(wechatPayConfig.getMchid());
// 构造请求体body
String jsonBody = WechatFactory.buildPayBodyParamsForMini(wechatPayConfig.getMiniAppid(), wechatPayConfig.getMchid(), outTradeNo, notifyURL, totalAmount, openId);
// 计算签名值
String signature = SignFactory.sign(HttpMethod.POST.name(), canonicalURL, timestamp, nonceStr, jsonBody, pkcs12);
// 构造HTTP头
String authorization = TokenFactory.getAuthorization(wechatPayConfig.getMchid(), nonceStr, timestamp, pkcs12.getSerialNo(), signature);
// 响应结果
String responseBody = HttpRequest.post(JSAPI_PAY_URL).header("Authorization", authorization).body(jsonBody).execute().body();
LOGGER.info("【WechatPay】responseBody = {}", responseBody);
return this.buildMiniPrepayInfo(JSON.parseObject(responseBody).getString("prepay_id"), timestamp, nonceStr, pkcs12);
}
/**
* 微信APP预支付
*
* @param outTradeNo 订单编号
* @param amount 订单金额【分】
*/
public PaymentResultDto wechatPayForAPP(PaymentCodeEnum paymentCodeEnum, String outTradeNo, BigDecimal amount) {
// 绝对接口地址
String canonicalURL = "/v3/pay/transactions/app";
// 订单总金额 元 -> 分
int totalAmount = amount.multiply(new BigDecimal(100)).setScale(0, RoundingMode.HALF_UP).intValue();
System.out.println("totalAmount:"+totalAmount);
// 构造异步回调地址
String notifyURL = this.buildNotifyURL(paymentCodeEnum);
// 时间戳
long timestamp = System.currentTimeMillis() / 1000;
// 随机字符串
String nonceStr = RandomUtil.randomString(32);
//过期时间 RFC 3339格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
//支付订单过期时间
String timeExpire = sdf.format(new Date(System.currentTimeMillis() + 30000));
// 加载证书信息
KeyPairFactory.KeyPairCustom pkcs12 = KeyPairFactory.createPKCS12(wechatPayConfig.getMchid());
// 构造请求体body
String jsonBody = WechatFactory.buildPayBodyParamsForAPP(wechatPayConfig.getAppid(), wechatPayConfig.getMchid(), outTradeNo, notifyURL, totalAmount, timeExpire);
// 计算签名值
String signature = SignFactory.sign(HttpMethod.POST.name(), canonicalURL, timestamp, nonceStr, jsonBody, pkcs12);
// 构造HTTP头
String authorization = TokenFactory.getAuthorization(wechatPayConfig.getMchid(), nonceStr, timestamp, pkcs12.getSerialNo(), signature);
// 响应结果
String responseBody = HttpRequest.post(APP_PAY_URL).header("Authorization", authorization).body(jsonBody).execute().body();
LOGGER.info("【WechatPay】responseBody = {}", responseBody);
return this.buildAppPrepayInfo(JSON.parseObject(responseBody).getString("prepay_id"), timestamp, nonceStr, pkcs12);
}
/**
* 微信支付APP申请退款
*
* @param paymentCodeEnum 支付类型枚举
* @param outTradeNo 原支付订单编号
* @param outRefundNo 退款订单编号
* @param reason 退款原因
* @param refundAmount 退款金额(元)
*/
public void wechatRefundForAPP(PaymentCodeEnum paymentCodeEnum, String outTradeNo, String outRefundNo, String reason, BigDecimal refundAmount, BigDecimal totalAmount) {
// 绝对接口地址
String canonicalURL = "/v3/refund/domestic/refunds";
// 构造退款回调地址
String refundNotifyURL = this.buildRefundNotifyURL(paymentCodeEnum);
// 订单总金额 元 -> 分
// int totalMoney = totalAmount.multiply(new BigDecimal(100)).setScale(0, RoundingMode.HALF_UP).intValue();
// 退款金额 元 -> 分
// int refundMoney = refundAmount.multiply(new BigDecimal(100)).setScale(0, RoundingMode.HALF_UP).intValue();
int totalMoney =1;
int refundMoney =1;
// 时间戳
long timestamp = System.currentTimeMillis() / 1000;
// 随机字符串
String nonceStr = RandomUtil.randomString(32);
// 加载证书信息
KeyPairFactory.KeyPairCustom pkcs12 = KeyPairFactory.createPKCS12(wechatPayConfig.getMchid());
// 构造请求体body参数
String jsonBody = WechatFactory.buildRefundBodyParamsForAPP(outTradeNo, outRefundNo, refundNotifyURL, reason, refundMoney, totalMoney);
LOGGER.info("【WechatRefund】body = {}", jsonBody);
// 计算签名值
String signature = SignFactory.sign(HttpMethod.POST.name(), canonicalURL, timestamp, nonceStr, jsonBody, pkcs12);
// 构造HTTP头
String authorization = TokenFactory.getAuthorization(wechatPayConfig.getMchid(), nonceStr, timestamp, pkcs12.getSerialNo(), signature);
// 响应结果
String responseBody = HttpRequest.post(APP_REFUND_URL).header("Authorization", authorization).body(jsonBody).execute().body();
LOGGER.info("【WechatRefund】responseBody = {}", responseBody);
}
/**
* 构造异步回调地址信息
*
* @param paymentCodeEnum 支付方式
* @return String
*/
private String buildNotifyURL(PaymentCodeEnum paymentCodeEnum) {
String notifyURL = "";
if (Objects.equals(PaymentCodeEnum.WECHAT_PAY_FOR_GOODS.getCode(), paymentCodeEnum.getCode())) {
notifyURL = wechatPayConfig.getDomain() + APP_PAY_NOTIFY_URL_GOODS;
} else if (Objects.equals(PaymentCodeEnum.WECHAT_PAY_FOR_LOCAL_LIFE.getCode(), paymentCodeEnum.getCode())) {
notifyURL = wechatPayConfig.getDomain() + APP_PAY_NOTIFY_URL_LOCAL_LIFE;
}
return notifyURL;
}
/**
* 构造退款回调地址
*
* @param paymentCodeEnum 支付类型枚举
* @return the notify url
*/
private String buildRefundNotifyURL(PaymentCodeEnum paymentCodeEnum) {
String refundNotifyURL = "";
if (Objects.equals(PaymentCodeEnum.WECHAT_PAY_FOR_GOODS.getCode(), paymentCodeEnum.getCode())) {
refundNotifyURL = wechatPayConfig.getDomain() + APP_REFUND_NOTIFY_URL_GOODS;
} else if (Objects.equals(PaymentCodeEnum.WECHAT_PAY_FOR_LOCAL_LIFE.getCode(), paymentCodeEnum.getCode())) {
refundNotifyURL = wechatPayConfig.getDomain() + APP_REFUND_NOTIFY_URL_LOCAL_LIFE;
}
return refundNotifyURL;
}
/**
* 返回APP预支付订单信息,再次封装返回前端
*
* @param prepayId 预支付订单id
* @return PaymentResultDto
*/
private PaymentResultDto buildAppPrepayInfo(String prepayId, long timestamp, String nonceStr, KeyPairFactory.KeyPairCustom pkcs12) {
Map<String, String> paramsMap = new HashMap<>();
paramsMap.put("appid", wechatPayConfig.getAppid());
paramsMap.put("partnerid", wechatPayConfig.getMchid());
paramsMap.put("prepayid", prepayId);
paramsMap.put("packagestr", "Sign=WXPay");
paramsMap.put("noncestr", nonceStr);
paramsMap.put("timestamp", String.valueOf(timestamp));
paramsMap.put("sign", SignFactory.signForAPP(wechatPayConfig.getAppid(), timestamp, nonceStr, prepayId, pkcs12));
return JSON.parseObject(JSON.toJSONString(paramsMap), PaymentResultDto.class);
}
/**
* 返回小程序预支付订单信息,再次封装返回前端
*
* @param prepayId 预支付订单id
* @return PaymentResultDto
*/
private PaymentResultDto buildMiniPrepayInfo(String prepayId, long timestamp, String nonceStr, KeyPairFactory.KeyPairCustom pkcs12) {
timestamp = timestamp/ 1000L;
Map<String, Object> keyValues = new HashMap();
keyValues.put("packagestr", "prepay_id=" + prepayId);
keyValues.put("noncestr", nonceStr);
keyValues.put("timestamp", String.valueOf(timestamp));
keyValues.put("sign", SignFactory.signForAPP(wechatPayConfig.getMiniAppid(), timestamp, nonceStr, "prepay_id=" + prepayId, pkcs12));
keyValues.put("signType", "RSA");
return JSON.parseObject(JSON.toJSONString(keyValues), PaymentResultDto.class);
}
}
paymentCodeEnum参数是我自己设置的枚举,用来判断获取哪个支付地址。
KeyPairFactory:
/**
* 微信支付V3
* 获取密钥对信息
*/
public class KeyPairFactory {
private static KeyStore store;
private static final Object lock = new Object();
/**
* API证书路径【类路径/cert/apiclient_cert.p12】
*/
private static final String KEY_PATH = "cert/apiclient_cert.p12";
/**
* 证书的别名(固定值:Tenpay Certificate)
*/
private static final String KEY_ALIAS = "Tenpay Certificate";
/**
* 获取公私钥.
*
* @param keyPass 证书密码(默认:商户号)
* @return the key pair
*/
public static synchronized KeyPairCustom createPKCS12(String keyPass) {
ClassPathResource resource = new ClassPathResource(KEY_PATH);
char[] pem = keyPass.toCharArray();
try {
synchronized (lock) {
if (store == null) {
synchronized (lock) {
store = KeyStore.getInstance("PKCS12");
store.load(resource.getStream(), pem);
}
}
}
X509Certificate certificate = (X509Certificate) store.getCertificate(KEY_ALIAS);
certificate.checkValidity();
// 证书的序列号 也有用
String serialNumber = certificate.getSerialNumber().toString(16).toUpperCase();
// 证书的 公钥
PublicKey publicKey = certificate.getPublicKey();
// 证书的 私钥
PrivateKey storeKey = (PrivateKey) store.getKey(KEY_ALIAS, pem);
return new KeyPairCustom(serialNumber, publicKey, storeKey);
} catch (Exception e) {
throw new IllegalStateException("Cannot load keys from store: " + resource, e);
}
}
/**
* 自定义返回密钥结果
*/
@Data
public static class KeyPairCustom {
/**
* API证书序列号
*/
private String serialNo;
/**
* 证书的 公钥
*/
private PublicKey publicKey;
/**
* 证书的 私钥
*/
private PrivateKey privateKey;
public KeyPairCustom(String serialNo, PublicKey publicKey, PrivateKey privateKey) {
this.serialNo = serialNo;
this.publicKey = publicKey;
this.privateKey = privateKey;
}
}
}
/**
* 微信支付V3相关配置信息
*/
@Data
@Configuration
public class WechatPayConfig {
/**
* 域名
*/
@Value("${pay.domain}")
private String domain;
/**
* appid
*/
@Value("${pay.wechat.appid}")
private String appid;
/**
* miniAppid
*/
@Value("${pay.wechat.miniAppid}")
private String miniAppid;
/**
* 商户号
*/
@Value("${pay.wechat.mchid}")
private String mchid;
/**
* API V3 密钥
*/
@Value("${pay.wechat.apiV3Key}")
private String apiV3Key;
支付的时候调用WechatPayUtil 中的接口就可以了;
@Component
public class AliPayUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(AliPayUtil.class);
@Autowired
private AliPayConfig aliPayConfig;
// 商品异步通知回调地址
private static final String GOODS_NOTIFY_URL = "/api/callback/paymentOfGoodsNotifyForAliPay";
/**
* APP商品预支付
* @param outTradeNo 订单编号
* @param totalAmount 订单金额【元】
* @param subject 订单标题
* @return 生成预支付响应对象返回前端
*/
public String aliPayPrePaidForGoods(PaymentCodeEnum paymentCodeEnum, String outTradeNo, String totalAmount, String subject) {
try {
// 1. 创建AlipayClient实例
DefaultAlipayClient alipayClient = getClientParams();
// 2. 创建使用的Open API对应的Request请求对象
AlipayTradeAppPayRequest request = getRequest(outTradeNo, totalAmount, subject);
// 2.1 异步回调地址
request.setNotifyUrl(this.buildNotifyURL(paymentCodeEnum));
// 3. 发起请求并处理响应
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
if (response.isSuccess()) return response.getBody();
LOGGER.error("调用失败,原因:" + response.getMsg() + "," + response.getSubMsg());
throw new RuntimeException("调用失败,原因:" + response.getMsg() + "," + response.getSubMsg());
} catch (Exception e) {
LOGGER.error("调用遭遇异常,原因:{}", e.getMessage());
throw new RuntimeException(e.getMessage(), e);
}
}
/**
* 构造异步回调地址信息
*
* @param paymentCodeEnum 支付方式
* @return String
*/
private String buildNotifyURL(PaymentCodeEnum paymentCodeEnum) {
String notifyURL = "";
if (Objects.equals(PaymentCodeEnum.ALI_PAY_FOR_GOODS.getCode(), paymentCodeEnum.getCode())) {
notifyURL = aliPayConfig.getDomain() + GOODS_NOTIFY_URL;
} else if (Objects.equals(PaymentCodeEnum.ALI_PAY_FOR_LOCAL_LIFE.getCode(), paymentCodeEnum.getCode())) {
notifyURL = aliPayConfig.getDomain() + LOCAL_LIFE_NOTIFY_URL;
}
return notifyURL;
}
private DefaultAlipayClient getClientParams() {
return new DefaultAlipayClient("https://openapi.alipay.com/gateway.do", aliPayConfig.getAppid(), aliPayConfig.getMerchantPrivateKey(), "json", "utf-8", aliPayConfig.getAlipayPublicKey(), "RSA2");
}
private AlipayTradeAppPayRequest getRequest(String outTradeNo, String totalAmount, String subject) {
// 初始化Request,并填充Model属性。实际调用时请替换为您想要使用的API对应的Request对象。
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
AlipayTradeAppPayModel payModel = new AlipayTradeAppPayModel();
//订单编号
payModel.setOutTradeNo(outTradeNo);
//订单金额 单位:【元】,精确到小数点后两位
payModel.setTotalAmount(totalAmount);
//订单标题
payModel.setSubject(subject);
request.setBizModel(payModel);
return request;
}
/**
* 服务端验证异步通知信息参数示例
* @param request 请求对象
* @return Map
*/
public Map<String, String> convertRequestParamsToMap(HttpServletRequest request) {
//获取支付宝POST过来反馈信息
Map<String, String> params = new HashMap<String, String>();
Map<String, String[]> requestParams = request.getParameterMap();
for (String name : requestParams.keySet()) {
String[] values = requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
}
//乱码解决,这段代码在出现乱码时使用。
//valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
params.put(name, valueStr);
}
return params;
}
/**
* 验证异步通知信息参数
* @param params 参数集合
* @return boolean
*/
public boolean rsaCertCheckV1(Map<String, String> params) {
//切记alipayPublicCertPath是支付宝公钥证书路径,请去open.alipay.com对应应用下载。
try {
return AlipaySignature.rsaCheckV1(params, aliPayConfig.getAlipayPublicKey(), "utf-8","RSA2");
} catch (AlipayApiException e) {
LOGGER.error("验证异步通知信息参数失败,Message = {}", e.getMessage());
e.printStackTrace();
return false;
}
}
}
@Data
@Configuration
public class AliPayConfig {
/**
* 主域名
*/
@Value("${pay.domain}")
private String domain;
/**
* 应用ID
*/
@Value("${pay.alipay.appid}")
private String appid;
/**
* 商户私钥,您的PKCS8格式RSA2私钥
*/
@Value("${pay.alipay.privateKey}")
private String merchantPrivateKey;
/**
* 公钥
*/
@Value("${pay.alipay.publicKey}")
private String alipayPublicKey;
}