微信支付
1. 引入依赖
<!-- 微信支付V3 目前新版本-->
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.4.9</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
2. 更新创建WxV3PayConfig.java配置类
WxV3PayConfig
package com.zxd.Pay.wxPay;
import lombok.Data;
/**
* implements WXPayConfig
*/
@Data
public class WxV3PayConfig {
//平台证书序列号
public static String mchSerialNo = "6B89237B3DD11A5D18960E08EBD41D";
//appID
public static String APP_ID = "wxe7534820";
//商户id
public static String Mch_ID = "165005";
// API V3密钥
public static String apiV3Key = "rw44t35hu36u6u64665u6j3";
// api证书路径
// 本地使用如: C:/Users/D/Desktop/apiclient_key.pem
// 线上使用如: /usr/apiclient_key.pem
public static String path ="C:/Users/D/Desktop/apiclient_key.pem";
}
3. 更新创建WXPaySignatureCertificateUtil.java 工具类 使用Verifier验证
WXPaySignatureCertificateUtil
package com.zxd.Pay.wxPay;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.*;
import org.apache.http.impl.client.CloseableHttpClient;
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.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.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.getVerifiers(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
, WxV3PayConfig.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(WxV3PayConfig.path)), "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("无效的密钥格式");
}
}
}
4. 创建WXPayConstants.java类
WXPayConstants
package com.zxd.Pay.wxPay;
/**
* 常量
*/
public class WXPayConstants {
public static final String DOMAIN_API = "https://api.mch.weixin.qq.com/v3";
//app下单
public static final String PAY_TRANSACTIONS_APP = "/pay/transactions/app";
//公众号下单
public static final String PAY_TRANSACTIONS_JSAPI = "/pay/transactions/jsapi";
//扫码下单
public static final String PAY_TRANSACTIONS_NATIVE = "/pay/transactions/native";
// h5下单
public static final String PAY_TRANSACTIONS_H5 = "/pay/transactions/h5";
//微信支付回调
public static final String WECHAT_PAY_NOTIFY_URL =
"https://jingxi.com.cn/deal/api/appPayment/weChatPayNotify";
//申请退款
public static final String REFUND_DOMESTIC_REFUNDS = "/refund/domestic/refunds";
//微信退款回调
public static final String WECHAT_REFUNDS_NOTIFY_URL = "https://xxx.xxxx.com/api/appPayment/weChatPayRefundsNotify";
//关闭订单
public static final String PAY_TRANSACTIONS_OUT_TRADE_NO = "/pay/transactions/out-trade-no/{}/close";
}
5. 这里以APP支付和退款为例 创建WechatPaymentService.java
WechatPaymentService
package com.zxd.Pay.wxPay;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.util.Map;
public interface WechatPaymentService
{
/**
* 微信商品支付
* @param
* @return
*/
public Map<String, Object>weChatDoUnifiedOrder() throws Exception;
/**
* 微信支付回调通知
* @param
* @return
*/
public Map<String, Object> weChatNotificationHandler(HttpServletRequest request, HttpServletResponse response);
/**
*微信关闭订单
* @param outTradeNo
* @return
*/
public Map<String, Object> closeOrder(String outTradeNo);
/**
* 申请退款
* @param
* @return
*/
public Map<String, Object> weChatPayRefundsNotify(HttpServletRequest request);
/**
* 微信退款
* @param outTradeNo 订单号
* @return
*/
public Map<String, Object> weChatRefunds(String outTradeNo);
}
6. 创建WeChatPaymentServiceImpl.java
WeChatPaymentServiceImpl
package com.zxd.Pay.wxPay;
import cn.dev33.satoken.util.StrFormatter;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.wechat.pay.contrib.apache.httpclient.notification.Notification;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationHandler;
import com.wechat.pay.contrib.apache.httpclient.notification.NotificationRequest;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Service;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
@Service
public class WeChatPaymentServiceImpl implements WechatPaymentService {
/**
* V3微信支付统一下单
*
* @param
* @return
*
*/
@Override
public Map<String, Object> weChatDoUnifiedOrder() {
Map<String, Object> map = new HashMap<>();
// 支付总金额
BigDecimal totalPrice = BigDecimal.valueOf(0.01);
// 转换金额为分(保留两位小数点后乘以 100 转为整数)
Integer money = totalPrice.movePointRight(2).intValue(); // 修改1:简化金额转换逻辑
try {
// 验证证书
CloseableHttpClient httpClient = WXPaySignatureCertificateUtil.checkSign();
// 构造 HTTP 请求
HttpPost httpPost = new HttpPost(WXPayConstants.DOMAIN_API + WXPayConstants.PAY_TRANSACTIONS_APP);
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-type", "application/json; charset=utf-8");
// 构造请求 JSON 参数
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode rootNode = objectMapper.createObjectNode();
rootNode.put("appid", WxV3PayConfig.APP_ID)
.put("mchid", WxV3PayConfig.Mch_ID)
.put("description", "下单")
.put("out_trade_no", PayUtils.getPaySoleNum())
.put("notify_url", WXPayConstants.WECHAT_PAY_NOTIFY_URL); // 回调 URL
rootNode.putObject("amount")
.put("total", money) // 修改2:使用转换后的金额
.put("currency", "CNY"); // 人民币
// 设置请求体
httpPost.setEntity(new StringEntity(rootNode.toString(), "UTF-8"));
// 执行请求并获取响应
CloseableHttpResponse response = httpClient.execute(httpPost);
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200) { // 处理成功
String result = EntityUtils.toString(response.getEntity(), "UTF-8");
JSONObject object = JSONObject.parseObject(result);
// 获取预付单
String prepayId = object.getString("prepay_id");
Long timestamp = System.currentTimeMillis() / 1000;
String nonceStr = RandomStringUtils.randomAlphanumeric(32);
// 生成带签名支付信息
String paySign = WXPaySignatureCertificateUtil.appPaySign(String.valueOf(timestamp), nonceStr, prepayId);
Map<String, String> param = new HashMap<>();
param.put("appid", WxV3PayConfig.APP_ID);
param.put("partnerid", WxV3PayConfig.Mch_ID);
param.put("prepayid", prepayId);
param.put("package", "Sign=WXPay");
param.put("noncestr", nonceStr);
param.put("timestamp", String.valueOf(timestamp));
param.put("sign", paySign);
map.put("code", 200);
map.put("message", "下单成功");
map.put("data", param); // 返回预付单信息
return map;
}
// 修改3:处理非 200 状态码
String errorMessage = EntityUtils.toString(response.getEntity(), "UTF-8");
map.put("code", statusCode);
map.put("message", "下单失败:" + errorMessage);
map.put("data", null); // 避免直接返回 response 对象
return map;
} catch (Exception e) {
// 修改4:记录详细错误信息
e.printStackTrace();
map.put("code", 500);
map.put("message", "系统异常:" + e.getMessage());
map.put("data", null);
}
return map;
}
/**
* 微信支付回调通知
* @return
*/
@Override
public Map<String, Object> weChatNotificationHandler(HttpServletRequest request, HttpServletResponse response){
Map<String,Object> map = new HashMap<>();
try {
BufferedReader br = request.getReader();
String str = null;
StringBuilder sb = new StringBuilder();
while ((str = br.readLine())!=null) {
sb.append(str);
}
// 构建request,传入必要参数
NotificationRequest requests = new NotificationRequest.Builder()
.withSerialNumber(request.getHeader("Wechatpay-Serial"))
.withNonce(request.getHeader("Wechatpay-Nonce"))
.withTimestamp(request.getHeader("Wechatpay-Timestamp"))
.withSignature(request.getHeader("Wechatpay-Signature"))
.withBody(String.valueOf(sb))
.build();
//验签
NotificationHandler handler = new NotificationHandler(WXPaySignatureCertificateUtil.getVerifiers(WxV3PayConfig.mchSerialNo), WxV3PayConfig.apiV3Key.getBytes(StandardCharsets.UTF_8));
//解析请求体
Notification notification = handler.parse(requests);
String decryptData = notification.getDecryptData();
//解析
JSONObject jsonObject = JSONObject.parseObject(decryptData);
//支付状态交易状态,枚举值: SUCCESS:支付成功 REFUND:转入退款 NOTPAY:未支付 CLOSED:已关闭 REVOKED:已撤销(付款码支付)
// USERPAYING:用户支付中(付款码支付) PAYERROR:支付失败(其他原因,如银行返回失败)
String trade_state = String.valueOf(jsonObject.get("trade_state"));
if (trade_state.equals("SUCCESS")) {
//订单号
String orderNumber = String.valueOf(jsonObject.get("out_trade_no"));
//微信支付微信生成的订单号
String transactionId = String.valueOf(jsonObject.get("transaction_id"));
//省略查询订单
//此处处理业务
map.put("code","SUCCESS");
map.put("message","成功");
//消息推送成功
return map;
}
map.put("code","RESOURCE_NOT_EXISTS");
map.put("message", "订单不存在");
return map;
}catch (Exception e) {
e.printStackTrace();
}
map.put("code","FAIL");
map.put("message", "失败");
return map;
}
/**
* 关闭订单
* @param outTradeNo 订单号
* @return
*/
@Override
public Map<String, Object> closeOrder(String outTradeNo) {
Map<String,Object> map = new HashMap<>();
try {
//验证证书
CloseableHttpClient httpClient = WXPaySignatureCertificateUtil.checkSign();
//关闭订单
String url = StrFormatter.format(WXPayConstants.DOMAIN_API+WXPayConstants.PAY_TRANSACTIONS_OUT_TRADE_NO, outTradeNo);
HttpPost httpPost = new HttpPost(url);
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-type", "application/json; charset=utf-8");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
//2.添加商户id
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode rootNode = objectMapper.createObjectNode();
rootNode.put("mchid", WxV3PayConfig.Mch_ID);
objectMapper.writeValue(bos, rootNode);
//3.调起微信关单接口
httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
//完成签名并执行请求
CloseableHttpResponse response = httpClient.execute(httpPost);
System.out.println(response.getStatusLine().getStatusCode() == 204);
//无数据(Http状态码为204) 微信返回结果无数据 状态码为204 成功
if (response.getStatusLine().getStatusCode() == 204) {
//code 退款码请前往微信支付文档查询
map.put("code",200);
map.put("message", "关闭订单成功!");
return map;
}
} catch (Exception e) {
System.out.println("关单失败:" + outTradeNo + e);
}
return null;
}
/**
* 微信退款
* @param outTradeNo 订单号
* @return
*/
@Override
public Map<String, Object> weChatRefunds(String outTradeNo) {
Map<String, Object> map = new HashMap<>();
//退款总金额
BigDecimal totalPrice = BigDecimal.ZERO;
totalPrice = totalPrice.add(BigDecimal.valueOf(600));
//转换金额
Integer money = new BigDecimal(String.valueOf(totalPrice)).movePointRight(2).intValue();
JSONObject jsonObject = null;
try {
//验证证书
CloseableHttpClient httpClient = WXPaySignatureCertificateUtil.checkSign();
//申请退款接口
HttpPost httpPost = new HttpPost(WXPayConstants.DOMAIN_API + WXPayConstants.REFUND_DOMESTIC_REFUNDS);
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-type", "application/json; charset=utf-8");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode rootNode = objectMapper.createObjectNode();
//微信支付订单号
rootNode.put("transaction_id", "微信支付订单号")
//退款订单号
.put("out_refund_no", "生成退款订单号")
.put("notify_url", "退款回调");
//退款金额
rootNode.putObject("amount")
.put("refund", "100.00")
//原订单金额
.put("total", "100.00")
.put("currency", "CNY");
objectMapper.writeValue(bos, rootNode);
httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
CloseableHttpResponse response = httpClient.execute(httpPost);
//退款成功返回消息
String bodyAsString = EntityUtils.toString(response.getEntity());
jsonObject = JSONObject.parseObject(bodyAsString);
if (jsonObject.get("status").equals("SUCCESS") || jsonObject.get("status").equals("PROCESSING")) {
//code返回
map.put("code", 200);
map.put("message", "退款成功");
return map;
}
} catch (Exception e) {
e.printStackTrace();
}
map.put("code", 500);
map.put("message", "申请退款失败!");
map.put("data", jsonObject);
return map;
}
/**
* 申请退款回调
* @param request
* @return
*/
@Override
public Map<String,Object> weChatPayRefundsNotify(HttpServletRequest request) {
Map<String,Object> map = new HashMap<>();
try {
BufferedReader br = request.getReader();
String str = null;
StringBuilder sb = new StringBuilder();
while ((str = br.readLine())!=null) {
sb.append(str);
}
// 构建request,传入必要参数
NotificationRequest requests = new NotificationRequest.Builder()
.withSerialNumber(request.getHeader("Wechatpay-Serial"))
.withNonce(request.getHeader("Wechatpay-Nonce"))
.withTimestamp(request.getHeader("Wechatpay-Timestamp"))
.withSignature(request.getHeader("Wechatpay-Signature"))
.withBody(String.valueOf(sb))
.build();
//验签
NotificationHandler handler = new NotificationHandler(WXPaySignatureCertificateUtil.getVerifiers(WxV3PayConfig.mchSerialNo), WxV3PayConfig.apiV3Key.getBytes(StandardCharsets.UTF_8));
//解析请求体
Notification notification = handler.parse(requests);
String decryptData = notification.getDecryptData();
//解析
JSONObject jsonObject = JSONObject.parseObject(decryptData);
String refund_status = String.valueOf(jsonObject.get("refund_status"));
if (refund_status.equals("SUCCESS")) {
//订单号
String orderNumber = String.valueOf(jsonObject.get("out_trade_no"));
//微信支付订单号
String transactionId = String.valueOf(jsonObject.get("transaction_id"));
//这里是处理业务逻辑
//code 退款码请前往微信支付文档查询
map.put("code","RESOURCE_NOT_EXISTS");
map.put("message", "订单不存在");
return map;
}
}catch (Exception e) {
e.printStackTrace();
}
map.put("code","USER_ACCOUNT_ABNORMAL");
map.put("message", "退款请求失败");
return map;
}
}
如果更换支付类型如:APP、二维码支付、扫码支付、JSAPI支付
请看以下示例二维码支付代码
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3"+"/pay/transactions/native");
HttpPost httpPost = new HttpPost("这里更换")
整体不变只需要微信返回正确状态码内处理即可 如以下返回为 二维码支付参数
微信返回正确状态码内处理
//完成签名并执行请求
CloseableHttpResponse response = httpClient.execute(httpPost);
//获取返回状态
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200) { //处理成功
String result = EntityUtils.toString(response.getEntity(), "UTF-8");
JSONObject object = JSONObject.parseObject(result);
map.put("code",200);
map.put("message", "下单成功");
map.put("data", object);
return map;
}
map.put("code",500);
map.put("message", "下单失败");
map.put("data", response);
return map;
官方文档:https://pay.weixin.qq.com/doc/v3/merchant/4013070347
参考链接:https://blog.csdn.net/qq_37544675/article/details/126014142
支付宝支付
1.
官方文档:https://opendocs.alipay.com/open-v3/429e4d75_alipay.trade.app.pay?scene=20&pathHash=a51ed054
参考链接:https://blog.csdn.net/qq_37544675/article/details/130661483?spm=1001.2014.3001.5502
第三方支付IJPay
官方文档:https://ijpay.dreamlu.net/ijpay/guide/
参考链接:
标签:map,String,支付宝,微信,new,支付,put,import,public From: https://www.cnblogs.com/xd99/p/18623529