Spring Boot 调用微信支付 V3 API 的全套指南

93 阅读3分钟

沉默是金,总会发光

大家好,我是沉默

“支付这件事,就像点外卖——用户只看到‘付款成功’,

背后其实跑了几十个接口、几道签名验证、还有一堆安全校验。”

今天,我们用 Spring Boot + 微信支付 V3,从 0 到 1 打造一个可落地的支付系统,完整覆盖下单、回调、退款、查询全流程。

不废话,干货直接上。

**-**01-

每个系统都绕不开“支付”?

电商下单、会员充值、票务系统、小程序商城……
只要涉及钱,支付模块就是灵魂。

但开发者最怕的就是:

文档晦涩、签名复杂、回调老失败。

于是今天这篇文章,帮你一口气搞清楚:

  • 如何发起微信统一下单(生成二维码)

  • 如何正确处理微信回调

  • 如何实现退款与查询逻辑

  • 如何封装成模块,方便复用

图片

- 02-

微信支付接入前的准备

就像打游戏前先打补丁,微信支付也得先办好“通行证”。

1️⃣ 注册商户号

前往 微信商户平台(pay.weixin.qq.com/)
完成认证后,你将拿到四个关键凭证:

项目说明
mchid商户号
apiV3KeyAPI V3 密钥
privateKey商户私钥
certSerialNo证书序列号

2️⃣ 官方文档入口

微信支付 API V3 文档(pay.weixin.qq.com/wiki/doc/ap…

图片

- 03-

项目准备

核心支付流程图

图片

项目配置与依赖

Maven 依赖

<dependencies>    <!-- Spring Boot Web -->    <dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-web</artifactId>    </dependency>    <!-- 微信支付 SDK -->    <dependency>        <groupId>com.github.wechatpay-apiv3</groupId>        <artifactId>wechatpay-java</artifactId>        <version>0.2.11</version>    </dependency>    <!-- 日志 -->    <dependency>        <groupId>org.slf4j</groupId>        <artifactId>slf4j-api</artifactId>    </dependency></dependencies>

application.yml 配置

wechat:  mchid: '1234567890'  appid: 'wx1234567890abcdef'  apiV3Key: 'your_api_v3_key'  privateKeyPath: 'classpath:/cert/apiclient_key.pem'  certSerialNo: 'XXXXXX'  notifyUrl: 'https://yourdomain.com/wechat/pay/notify'  refundNotifyUrl: 'https://yourdomain.com/wechat/refund/notify'

**-**04-

实战代码详解

统一下单接口(Native 模式)

@Servicepublic class WechatPayService {    @Autowired    private WechatPayHttpClient wechatClient;    @Value("${wechat.notifyUrl}")    private String notifyUrl;    public String unifiedOrder(String orderNo, BigDecimal amount, String description) throws Exception {        Map<String, Object> params = new HashMap<>();        params.put("appid", wechatClient.getAppId());        params.put("mchid", wechatClient.getMchId());        params.put("description", description);        params.put("out_trade_no", orderNo);        params.put("notify_url", notifyUrl);        Map<String, Object> amountMap = new HashMap<>();        amountMap.put("total", amount.multiply(new BigDecimal(100)).intValue());        amountMap.put("currency""CNY");        params.put("amount", amountMap);        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/native");        httpPost.setEntity(new StringEntity(new ObjectMapper().writeValueAsString(params), "UTF-8"));        httpPost.setHeader("Content-Type""application/json");        HttpResponse response = wechatClient.getClient().execute(httpPost);        String body = EntityUtils.toString(response.getEntity());        JsonNode jsonNode = new ObjectMapper().readTree(body);        return jsonNode.get("code_url").asText();    }}



支付回调接口
@RestController@RequestMapping("/wechat/pay")public class WechatPayCallbackController {    @PostMapping("/notify")    public ResponseEntity<String> payNotify(@RequestBody String body, @RequestHeader Map<String, String> headers) {        System.out.println("支付回调数据:" + body);        // TODO: 验签、解密、订单状态更新        return ResponseEntity.ok("{"code":"SUCCESS","message":"成功"}");    }}



回调逻辑流程图




退款与查询接口
public String refund(String orderNo, String refundNo, BigDecimal refundAmount) throws Exception {    Map<StringObject> params = new HashMap<>();    params.put("out_trade_no", orderNo);    params.put("out_refund_no", refundNo);    params.put("notify_url""https://yourdomain.com/wechat/refund/notify");    Map<StringObject> amountMap = new HashMap<>();    amountMap.put("refund", refundAmount.multiply(new BigDecimal(100)).intValue());    amountMap.put("total"100);    amountMap.put("currency""CNY");    params.put("amount", amountMap);    HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/refund/domestic/refunds");    httpPost.setEntity(new StringEntity(new ObjectMapper().writeValueAsString(params), "UTF-8"));    httpPost.setHeader("Content-Type""application/json");    HttpResponse response = wechatClient.getClient().execute(httpPost);    return EntityUtils.toString(response.getEntity());}



系统架构视图



常见问题排查表
问题	解决方案回调未触发	检查 notify_url 是否公网可访问且为 HTTPS
签名失败	确认 V3 密钥、私钥加载正确
退款失败	确认订单状态为已支付、退款金额 ≤ 原金额
商户号无效	检查 appid 与 mchid 是否匹配













-05-

总结

支付接入不难,难的是稳定与信任

支付系统的本质是「信任传递」——
从用户到平台,从平台到银行,从银行再到商户。




你写的每一行代码,其实都在参与一次真实的金钱流转。



所以,别怕麻烦,多一步验证、多一层日志、多一个签名,
都是在为用户的“信任安全”买保险。




进阶建议

支持小程序 / H5 / APP 多端支付

用 MQ 异步化处理支付回调

持久化支付日志,防止重复通知

封装成 SDK 模块供多个项目共用

图片

**-**05-

粉丝福利

点点关注,送你互联网大厂面试题库,如果你正在找工作,又或者刚准备换工作。可以仔细阅读一下,或许对你有所帮助!

image.png

image.pngimage.png