前言:
上一个微信支付回调(基于NotificationParser ) 有些问题,映射错了,这个是改正过并测试通过
官方依赖:
<!-- 微信官方支付SDK -->
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.17</version>
</dependency>
yml:
# 微信配置
wechat:
v3:
#小程序appid
app_id: wx203dc1a67c
#小程序secret
app_secret: 60e689794f471db7dce1e80
#商户号
mch_id: 173555563
#商户APIv3密钥
api_v3_key: 60e689794f471db7dce1e80
#商户证书序列号
mch_cert_serial_number: 2C4D370207CD59845123064D9F31DB8D3BD3B966
#商户API私钥文件路径
private_key_path: classpath:merchant_cert/apiclient_key.pem
#微信支付回调地址
notify_url: http://223.113.59.183:16193/api/payCallback
#退款结果回调地址
refund_notify_url: http://223.113.59.183:16193/api/refundCallback
支付配置类:
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.refund.RefundService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.StreamUtils;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
@Slf4j
@Data
@Configuration
@ConfigurationProperties(prefix = "wechat.v3")
public class WechatConfig {
//小程序appid
private String appId;
//小程序secret
private String appSecret;
//商户号
private String mchId;
//商户APIv3密钥
private String apiV3Key;
//商户证书序列号
private String mchCertSerialNumber;
//商户API私钥文件路径
private String privateKeyPath;
//微信支付回调地址
private String notifyUrl;
//退款结果回调地址
private String refundNotifyUrl;
//获取私钥内容
public String getPrivateKeyContent() {
try {
ClassPathResource resource = new ClassPathResource(privateKeyPath.replace("classpath:", ""));
try (InputStream inputStream = resource.getInputStream()) {
return StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
}
} catch (IOException e) {
log.error("读取私钥文件失败: {}", privateKeyPath, e);
return null;
}
}
//创建微信支付配置Bean
@Bean
public Config wechatPayConfig() {
return new RSAAutoCertificateConfig.Builder()
.merchantId(mchId)
.privateKey(getPrivateKeyContent())
.merchantSerialNumber(mchCertSerialNumber)
.apiV3Key(apiV3Key)
.build();
}
//构建JSAPI服务
@Bean
public JsapiServiceExtension jsapiService(Config wechatPayConfig) {
return new JsapiServiceExtension.Builder()
.config(wechatPayConfig)
.build();
}
//构建Refund服务
@Bean
public RefundService refundService(Config wechatPayConfig) {
return new RefundService.Builder()
.config(wechatPayConfig)
.build();
}
//自动验签 + 解密
@Bean
public NotificationParser notificationParser(Config wechatPayConfig) {
return new NotificationParser((RSAAutoCertificateConfig) wechatPayConfig);
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
实体类:
//对resource中ciphertext进行解密后的资源对象实体类
@Data
public class TransactionResourceVO implements Serializable {
//商户订单号
private String out_trade_no;
//微信支付订单号
private String transaction_id;
//交易状态
private String trade_state;
//支付完成时间
private String success_time;
}
controller:
//微信支付成功回调V3
@PostMapping("/payCallback")
public String payCallback(HttpServletRequest request) {
return apiService.payCallback(request);
}
service:
@Resource
private WxCartMapper cartMapper;
@Resource
private OrderBaseInfoMapper baseInfoMapper;
@Resource
private NotificationParser notificationParser;
//微信支付成功回调V3
public String payCallback(HttpServletRequest request) {
try {
//验证签名并返回结果
TransactionResourceVO vo = verifyWechatCallback(request);
if (StringUtils.isNull(vo)) {
log.warn("微信支付回调验签失败");
return this.buildFailXml("验签失败");
}
log.info("返回支付成功结果: {}", vo);
//检查事件类型
if (!"SUCCESS".equals(vo.getTrade_state())) {
log.warn("非支付成功事件,忽略: {}", vo.getTrade_state());
return buildSuccessXml(); // 仍然返回 success,避免重试
}
//根据 outTradeNo 查询你的订单,并更新为已支付
OrderBaseInfo baseInfo = baseInfoMapper.selectBaseInfoByOutTradeNo(vo.getOut_trade_no());
//清除购物车
cartMapper.clearCart(baseInfo.getUserId());
if (baseInfo.getStatus() == 1) {
return this.buildSuccessXml();
}
Date successTime = toDate(vo.getSuccess_time());
baseInfoMapper.updateOrderBaseInfo(new OrderBaseInfo() {{
this.setId(baseInfo.getId());
this.setTransactionId(vo.getTransaction_id());
this.setStatus(1);
this.setPayTime(successTime);
}});
return this.buildSuccessXml();
} catch (Exception e) {
log.error("微信支付回调处理异常", e);
return this.buildFailXml("系统异常");
}
}
//转换时间
private Date toDate(String date) {
OffsetDateTime offsetDateTime = OffsetDateTime.parse(date);
return Date.from(offsetDateTime.toInstant());
}
//获取请求体
private static String getRequestBody(HttpServletRequest request) {
StringBuilder requestBody = new StringBuilder();
try {
requestBody.append(IoUtil.readUtf8(request.getInputStream()));
} catch (IOException e) {
log.error("读取请求体发生了异常: ", e);
}
return requestBody.toString();
}
//验签逻辑
private TransactionResourceVO verifyWechatCallback(HttpServletRequest request) {
try {
//提取请求头中的验签信息
String serial = request.getHeader("Wechatpay-Serial");
String nonce = request.getHeader("Wechatpay-Nonce");
String timestamp = request.getHeader("Wechatpay-Timestamp");
String signature = request.getHeader("Wechatpay-Signature");
String requestBody = getRequestBody(request);
//构建微信回调的请求参数对象
RequestParam requestParam = new RequestParam.Builder()
.serialNumber(serial)
.timestamp(timestamp)
.nonce(nonce)
.signature(signature)
.body(requestBody)
.build();
// 尝试解析,如果验签失败,parser.parse 会抛出异常
return notificationParser.parse(requestParam, TransactionResourceVO.class);
} catch (Exception e) {
log.error("微信支付回调验签异常", e);
return null;
}
}
返回支付成功结果:
TransactionResourceVO {
out_trade_no=1958783457915772928,
transaction_id=42000027562568882542529903,
trade_state=SUCCESS,
success_time=2025-08-22T14:49:16+08:00
}
已测试通过。。。。。。