JAVA 微信小程序v3最新版本0.2.17(保姆级)-微信支付回调(基于NotificationParser )修复版

前言:
上一个微信支付回调(基于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
}

已测试通过。。。。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值