springboot对接完微信支付V3详细 步骤

195 阅读4分钟

Spring Boot 对接微信支付 V3 版本主要涉及环境准备、核心配置、支付与退款接口实现、以及回调处理等关键步骤。下面是一个清晰的流程指南。

🚀 Spring Boot 对接微信支付V3详细步骤

下面表格汇总了对接微信支付V3的主要阶段和核心任务,帮助您快速把握整体流程。

阶段核心任务关键配置/操作
​1. 环境准备​获取商户信息、配置API密钥、安装证书商户号(mchId)、APIv3密钥、商户证书序列号
​2. 项目配置​添加SDK依赖、配置支付参数、初始化支付客户端pom.xml依赖、application.yml配置、WechatPayConfig配置类
​3. 支付功能​实现统一下单、生成支付签名、处理支付回调调用JSAPI/Native接口、签名生成、回调验签解密
​4. 退款功能​实现退款申请、处理退款回调调用退款接口、退款结果异步通知
​5. 安全处理​签名验证、敏感信息加密解密验证微信签名、处理加密回调数据

1. 环境准备与配置

1.1 获取商户平台关键信息

在开始编码前,您需要从微信商户平台获取以下关键信息:

  • ​商户号(mchId)​​:微信支付分配的商户标识
  • ​APPID​​:公众账号ID或应用APPID
  • ​APIv3密钥​​:用于加密解密和签名验证的32位密钥
  • ​商户证书序列号​​:从商户平台下载的API证书序列号
  • ​商户私钥​​:从商户平台下载的私钥文件内容

1.2 添加SDK依赖

在pom.xml中添加微信支付Java SDK依赖(根据实际情况选择最新版本):

<dependency>
    <groupId>com.github.wechatpay-apiv3</groupId>
    <artifactId>wechatpay-java</artifactId>
    <version>0.4.12</version>
</dependency>

或者使用Apache HttpClient版本的SDK:

<dependency>
    <groupId>com.github.wechatpay-apiv3</groupId>
    <artifactId>wechatpay-apache-httpclient</artifactId>
    <version>0.4.8</version>
</dependency>

1.3 配置支付参数

在application.yml/application.properties中配置支付参数:

# application.yml配置示例
wechat:
  pay:
    appid: your_appid
    mchid: your_mch_id
    api-v3-key: your_api_v3_key
    private-key-path: classpath:apiclient_key.pem
    certificate-path: classpath:apiclient_cert.pem
    notify-url: your_notify_url

2. 支付客户端配置

2.1 初始化支付客户端

创建配置类初始化微信支付客户端,这是与微信支付API交互的基础:

@Configuration
public class WechatPayConfig {
    
    @Value("${wechat.pay.appid}")
    private String appid;
    
    @Value("${wechat.pay.mchid}")
    private String mchid;
    
    @Value("${wechat.pay.api-v3-key}")
    private String apiV3Key;
    
    @Bean
    public WechatPayService wechatPayService() throws IOException {
        // 加载商户私钥
        PrivateKey privateKey = loadPrivateKey(privateKeyPath);
        
        // 构建配置
        Config config = new RSAAutoCertificateConfig.Builder()
            .merchantId(mchid)
            .privateKeyFromPath(privateKeyPath)
            .merchantSerialNumber(merchantSerialNumber)
            .apiV3Key(apiV3Key)
            .build();
        
        return new WechatPayService.Builder().config(config).build();
    }
    
    private PrivateKey loadPrivateKey(String path) throws IOException {
        // 实现私钥加载逻辑
        String privateKeyContent = new String(Files.readAllBytes(Paths.get(path)));
        return PemUtil.loadPrivateKey(privateKeyContent);
    }
}

3. 支付功能实现

3.1 统一下单接口实现

创建支付服务类处理统一下单逻辑:

@Service
public class WechatPayService {
    
    @Autowired
    private WechatPayService wechatPayService;
    
    @Value("${wechat.pay.appid}")
    private String appid;
    
    @Value("${wechat.pay.notify-url}")
    private String notifyUrl;
    
    public Map<String, String> jsapiPay(String outTradeNo, Integer totalAmount, 
                                       String description, String openid) throws IOException {
        
        // 构建支付请求
        PrepayRequest request = new PrepayRequest();
        request.setAppid(appid);
        request.setMchid(mchid);
        request.setDescription(description);
        request.setOutTradeNo(outTradeNo);
        request.setNotifyUrl(notifyUrl);
        
        // 设置金额
        Amount amount = new Amount();
        amount.setTotal(totalAmount); // 单位:分
        amount.setCurrency("CNY");
        request.setAmount(amount);
        
        // 设置支付者
        Payer payer = new Payer();
        payer.setOpenid(openid);
        request.setPayer(payer);
        
        // 调用下单接口
        PrepayResponse response = wechatPayService.prepay(request);
        
        // 生成前端支付参数
        return generatePaySignature(response.getPrepayId());
    }
    
    private Map<String, String> generatePaySignature(String prepayId) {
        String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
        String nonceStr = UUID.randomUUID().toString().replace("-", "");
        String packageStr = "prepay_id=" + prepayId;
        
        // 生成签名
        String sign = generateSign(appid, timeStamp, nonceStr, packageStr);
        
        Map<String, String> result = new HashMap<>();
        result.put("appId", appid);
        result.put("timeStamp", timeStamp);
        result.put("nonceStr", nonceStr);
        result.put("package", packageStr);
        result.put("signType", "RSA");
        result.put("paySign", sign);
        
        return result;
    }
}

3.2 支付回调处理

支付回调接口用于接收微信支付的异步通知:

@RestController
@RequestMapping("/wechat/pay")
public class WechatPayController {
    
    @Autowired
    private WechatPayService wechatPayService;
    
    @PostMapping("/callback")
    public String payCallback(HttpServletRequest request) {
        try {
            // 读取回调数据
            String body = readRequestBody(request);
            
            // 获取微信回调头信息
            String signature = request.getHeader("Wechatpay-Signature");
            String nonce = request.getHeader("Wechatpay-Nonce");
            String timestamp = request.getHeader("Wechatpay-Timestamp");
            String serial = request.getHeader("Wechatpay-Serial");
            
            // 验证签名
            if (!verifySignature(signature, nonce, timestamp, body, serial)) {
                return buildErrorResponse("签名验证失败");
            }
            
            // 解密回调数据
            String plainText = decryptBody(body);
            
            // 处理业务逻辑(更新订单状态等)
            wechatPayService.processPaymentResult(plainText);
            
            return buildSuccessResponse();
            
        } catch (Exception e) {
            return buildErrorResponse("处理失败: " + e.getMessage());
        }
    }
    
    private String decryptBody(String encryptedBody) throws GeneralSecurityException {
        JSONObject jsonObject = JSON.parseObject(encryptedBody);
        JSONObject resource = jsonObject.getJSONObject("resource");
        
        String ciphertext = resource.getString("ciphertext");
        String associatedData = resource.getString("associated_data");
        String nonce = resource.getString("nonce");
        
        // 使用AES-GCM解密
        AesUtil aesUtil = new AesUtil(apiV3Key.getBytes(StandardCharsets.UTF_8));
        return aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8), 
                                      nonce.getBytes(StandardCharsets.UTF_8), ciphertext);
    }
}

4. 退款功能实现

4.1 退款接口实现

在支付服务类中添加退款方法:

@Service
public class WechatPayService {
    
    public RefundResponse refund(String outTradeNo, String outRefundNo, 
                                Integer refundFee, Integer totalFee) throws IOException {
        
        RefundRequest request = new RefundRequest();
        request.setOutTradeNo(outTradeNo);
        request.setOutRefundNo(outRefundNo);
        
        // 设置退款金额
        AmountRefund amount = new AmountRefund();
        amount.setRefund(refundFee);
        amount.setTotal(totalFee);
        amount.setCurrency("CNY");
        request.setAmount(amount);
        
        // 调用退款接口
        return wechatPayService.refund(request);
    }
}

4.2 退款回调处理

退款结果也需要异步回调通知:

@PostMapping("/refund/callback")
public String refundCallback(HttpServletRequest request) {
    try {
        // 处理逻辑与支付回调类似
        String body = readRequestBody(request);
        
        // 验证签名
        if (!verifyRefundSignature(request, body)) {
            return buildErrorResponse("退款回调签名验证失败");
        }
        
        // 解密退款结果
        String plainText = decryptBody(body);
        
        // 处理退款业务逻辑
        wechatPayService.processRefundResult(plainText);
        
        return buildSuccessResponse();
        
    } catch (Exception e) {
        return buildErrorResponse("退款处理失败: " + e.getMessage());
    }
}

5. 关键注意事项

5.1 安全性处理

  • ​签名验证​​:所有回调必须验证微信签名,防止伪造请求
  • ​数据解密​​:回调中的敏感信息使用AEAD_AES_256_GCM算法加密,需要解密处理
  • ​幂等性处理​​:支付和退款回调需要处理重复通知,确保业务幂等性

5.2 异常处理

  • ​网络超时​​:设置合理的超时时间,实现重试机制
  • ​证书管理​​:定期更新平台证书,处理证书过期异常
  • ​金额一致性​​:确保支付金额、退款金额与订单一致,避免金额不一致错误

5.3 回调接口要求

  • ​公网可访问​​:回调地址必须是HTTPS且公网可访问
  • ​快速响应​​:处理完成后需要立即返回success,否则微信会重试通知
  • ​跳过拦截器​​:确保回调接口不被权限拦截器拦截

以上步骤涵盖了Spring Boot对接微信支付V3的核心流程。实际开发中,请结合微信支付官方文档和业务需求进行适当调整。如果您在具体实现过程中遇到问题,可以参考微信支付官方文档或社区资源获取更多帮助。