目录
-
第一部分:准备工作
(图片来源网络,侵删)- 1 注册微信商户平台
- 2 获取必要参数
- 3 配置支付目录
- 4 下载API证书
- 5 开发环境准备
-
第二部分:后端项目搭建 (Spring Boot)
- 1 创建Spring Boot项目
- 2 添加依赖
- 3 配置文件
-
第三部分:核心代码实现
- 1 统一下单 (创建订单)
- 2 生成前端调起支付所需的参数
- 3 查询订单
- 4 退款
- 5 退款查询
-
第四部分:前端页面集成
- 1 调用后端下单接口
- 2 使用微信JS-SDK调起支付
-
第五部分:重要注意事项
(图片来源网络,侵删)- 1 签名机制详解
- 2 回调通知处理
- 3 错误码与调试
第一部分:准备工作
在开始编码之前,你必须完成以下准备工作,这是所有微信支付功能的基础。
1 注册微信商户平台
- 访问 微信商户平台。
- 使用你的企业或个体工商户账号登录,如果你还没有,需要先完成入驻。
- 完成账户验证和产品开通,确保你的账户状态正常。
2 获取必要参数
登录商户平台后,在 "产品中心" -> "产品大全" 中,确保你已经开通了 "JSAPI支付" 和 "Native支付"(用于生成二维码)。
在 "账户中心" -> "API安全" 页面,找到并记录以下关键信息:
- APIv3密钥 (APIv3 Key): 用于生成和验证签名。非常重要,请妥善保管!
- API证书序列号: 下载证书后可以看到。
- API证书密钥: 下载证书后可以看到。
- 商户号 (mchid): 你的商户平台ID。
3 配置支付目录
在 "产品中心" -> "开发配置" 中,设置你的支付授权目录。

- JSAPI支付: 填写你的H5网站域名或小程序的AppID,如果你的前端页面是
https://www.yourdomain.com/pay.html,你就需要把这个域名添加进去。不配置将无法调起支付。 - Native支付: 填写你的服务器域名。
4 下载API证书
在 "账户中心" -> "API安全" -> "API证书" 中,下载 "商户API证书",下载后你会得到一个 .p12 文件,一个证书序列号和一个证书密钥,在Java代码中,你需要使用 .p12 文件和证书密钥来加载证书。
5 开发环境准备
- JDK: 1.8 或更高版本。
- IDE: IntelliJ IDEA 或 Eclipse。
- 构建工具: Maven 或 Gradle。
- Spring Boot: 2.x 或 3.x 版本。
第二部分:后端项目搭建 (Spring Boot)
我们将使用Spring Boot来快速搭建项目。
1 创建Spring Boot项目
你可以通过 Spring Initializr 快速创建一个项目,选择以下依赖:
- Spring Web
- Spring Boot DevTools (可选,用于热部署)
- Lombok (可选,简化代码)
2 添加依赖
在 pom.xml 文件中,添加微信支付官方提供的SDK依赖和HTTP客户端依赖。
<dependencies>
<!-- Spring Boot Web Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 微信支付V3 SDK -->
<dependency>
<groupId>com.github.wechatpay-java</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.12</version> <!-- 请使用最新版本 -->
</dependency>
<!-- 微信支付V3服务核心库 -->
<dependency>
<groupId>com.github.wechatpay-java</groupId>
<artifactId>wechatpay-java-service</artifactId>
<version>0.2.12</version> <!-- 请使用最新版本 -->
</dependency>
</dependencies>
3 配置文件
在 application.yml 或 application.properties 中配置微信支付的相关参数。
application.yml
server: port: 8080 # 微信支付配置 wechat: # 商户号 mchid: your_mchid # APIv3密钥 api-v3-key: your_api_v3_key # API证书序列号 cert_serial_no: your_cert_serial_no # API证书密钥 (p12文件密码) cert-key: your_cert_key # 证书文件路径 (将p12文件放在resources目录下) cert-path: classpath:apiclient_cert.p12
第三部分:核心代码实现
1 统一下单 (创建订单)
这是支付流程的核心,后端需要调用微信的统一下单接口,创建一个预支付交易单。
创建配置类
// src/main/java/com/example/config/WechatPayConfig.java
package com.example.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Data
@Configuration
@ConfigurationProperties(prefix = "wechat")
public class WechatPayConfig {
private String mchid;
private String apiV3Key;
private String certSerialNo;
private String certKey;
private String certPath;
}
创建支付服务类
// src/main/java/com/example/service/WechatPayService.java
package com.example.service;
import com.example.config.WechatPayConfig;
import com.github.wechatpay.java.core.RSAAutoCertificateConfig;
import com.github.wechatpay.java.service.payments.jsapi.JsapiService;
import com.github.wechatpay.java.service.payments.jsapi.model.PrepayRequest;
import com.github.wechatpay.java.service.payments.jsapi.model.PrepayResponse;
import org.springframework.stereotype.Service;
import java.io.InputStream;
import java.security.PrivateKey;
import java.util.UUID;
@Service
public class WechatPayService {
private final WechatPayConfig config;
private final JsapiService jsapiService;
public WechatPayService(WechatPayConfig config) throws Exception {
this.config = config;
// 加载商户API证书
InputStream certStream = this.getClass().getClassLoader().getResourceAsStream(config.getCertPath());
PrivateKey merchantPrivateKey = RSAAutoCertificateConfig.loadPrivateKeyFromPKCS12(certStream, config.getCertKey());
// 初始化配置
RSAAutoCertificateConfig rsaConfig = new RSAAutoCertificateConfig.Builder()
.merchantId(config.getMchid())
.privateKey(merchantPrivateKey)
.apiV3Key(config.getApiV3Key())
.build();
// 创建JsapiService实例
this.jsapiService = new JsapiService.Builder().config(rsaConfig).build();
}
/**
* 创建预支付订单
* @param openid 用户的OpenID (通过微信登录获取)
* @param description 商品描述
* @param amount 总金额,单位为分
* @return 预支付订单ID
*/
public String createPrepayOrder(String openid, String description, Integer amount) {
// 生成商户订单号
String outTradeNo = UUID.randomUUID().toString().replace("-", "");
// 构建请求体
PrepayRequest request = new PrepayRequest();
request.setAppid("你的AppID"); // 你的小程序或公众号的AppID
request.setMchid(config.getMchid());
request.setDescription(description);
request.setOutTradeNo(outTradeNo);
request.setNotifyUrl("http://your-domain.com/api/pay/notify"); // 回调通知地址
request.setAmount(new PrepayRequest.Amount().setTotal(amount));
request.setPayer(new PrepayRequest.Payer().setOpenid(openid));
try {
// 发起请求
PrepayResponse response = jsapiService.prepay(request);
// 返回预支付会话标识
return response.getPrepayId();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("创建预支付订单失败: " + e.getMessage());
}
}
}
2 生成前端调起支付所需的参数
微信JSAPI支付需要特定的参数才能在前端调起,我们需要创建一个Controller来提供这些参数。
// src/main/java/com/example/controller/PayController.java
package com.example.controller;
import com.example.service.WechatPayService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/pay")
@RequiredArgsConstructor
public class PayController {
private final WechatPayService wechatPayService;
/**
* 创建订单并返回前端调起支付所需参数
*/
@PostMapping("/create-order")
public Map<String, Object> createOrder(@RequestBody CreateOrderRequest request) {
// 1. 调用统一下单接口
String prepayId = wechatPayService.createPrepayOrder(request.getOpenid(), request.getDescription(), request.getAmount());
// 2. 生成前端调起支付所需参数
Map<String, Object> payParams = new HashMap<>();
payParams.put("appId", "你的AppID");
payParams.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));
payParams.put("nonceStr", java.util.UUID.randomUUID().toString().replace("-", ""));
payParams.put("package", "prepay_id=" + prepayId);
payParams.put("signType", "RSA");
// 3. 生成paySign (签名)
// 注意:签名逻辑需要根据微信官方文档实现,这里简化处理
// 实际项目中,应该使用SDK提供的签名工具
String paySign = generatePaySign(payParams);
payParams.put("paySign", paySign);
return payParams;
}
// 这是一个简化的签名方法,实际请参考官方SDK
private String generatePaySign(Map<String, Object> params) {
// 1. 将参数按ASCII码从小到大排序
// 2. 使用URL键值对的格式拼接成字符串 stringA
// 3. 在stringA最后拼接上key(APIv3密钥)
// 4. 对stringA进行SHA256加密,得到sign
// 5. 将sign所有字符转换为大写
// 这里为了演示,直接返回一个假签名,请务必替换为正确的签名逻辑!
return "SIMULATED_PAY_SIGN";
}
}
// 请求DTO
class CreateOrderRequest {
private String openid;
private String description;
private Integer amount; // 单位:分
// Getters and Setters
public String getOpenid() { return openid; }
public void setOpenid(String openid) { this.openid = openid; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public Integer getAmount() { return amount; }
public void setAmount(Integer amount) { this.amount = amount; }
}
3 查询订单
// 在 WechatPayService.java 中添加
import com.github.wechatpay.java.service.payments.jsapi.model.QueryOrderByOutTradeNoRequest;
import com.github.wechatpay.java.service.payments.jsapi.model.QueryOrderResponse;
public QueryOrderResponse queryOrder(String outTradeNo) {
QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
request.setMchid(config.getMchid());
request.setOutTradeNo(outTradeNo);
try {
return jsapiService.queryOrderByOutTradeNo(request);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("查询订单失败: " + e.getMessage());
}
}
4 退款
// 在 WechatPayService.java 中添加
import com.github.wechatpay.java.service.payments.model.RefundRequest;
import com.github.wechatpay.java.service.payments.model.RefundResponse;
import java.math.BigDecimal;
public RefundResponse refund(String outTradeNo, String outRefundNo, BigDecimal refundAmount) {
RefundRequest request = new RefundRequest();
request.setOutTradeNo(outTradeNo);
request.setOutRefundNo(outRefundNo);
request.setNotifyUrl("http://your-domain.com/api/pay/refund-notify");
request.setAmount(new RefundRequest.Amount()
.setTotal(new BigDecimal("100")) // 原订单总金额
.setRefund(refundAmount) // 退款金额
.setCurrency("CNY"));
try {
return jsapiService.refund(request);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("退款失败: " + e.getMessage());
}
}
5 退款查询
// 在 WechatPayService.java 中添加
import com.github.wechatpay.java.service.payments.model.QueryRefundRequest;
import com.github.wechatpay.java.service.payments.model.QueryRefundResponse;
public QueryRefundResponse queryRefund(String outRefundNo) {
QueryRefundRequest request = new QueryRefundRequest();
request.setOutRefundNo(outRefundNo);
try {
return jsapiService.queryRefund(request);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("退款查询失败: " + e.getMessage());
}
}
第四部分:前端页面集成
这里我们模拟一个简单的HTML页面,通过JavaScript调起支付。
pay.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">微信支付测试</title>
<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
</head>
<body>
<h1>微信支付测试</h1>
<button onclick="handlePay()">立即支付</button>
<script>
function handlePay() {
// 1. 调用后端接口获取支付参数
fetch('/api/pay/create-order', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
openid: '用户的OpenID', // 必须通过微信登录获取
description: '测试商品',
amount: 1 // 1分钱
})
})
.then(response => response.json())
.then(data => {
if (data.appId) {
// 2. 调用微信JS-SDK调起支付
wx.chooseWXPay({
appId: data.appId,
timestamp: data.timeStamp,
nonceStr: data.nonceStr,
package: data.package,
signType: data.signType,
paySign: data.paySign,
success: function (res) {
alert('支付成功!');
console.log('支付成功', res);
},
fail: function (res) {
alert('支付失败,请重试。');
console.error('支付失败', res);
},
cancel: function (res) {
alert('您已取消支付。');
console.log('取消支付', res);
}
});
} else {
alert('获取支付参数失败: ' + JSON.stringify(data));
}
})
.catch(error => {
console.error('Error:', error);
alert('请求失败,请检查网络。');
});
}
</script>
</body>
</html>
第五部分:重要注意事项
1 签名机制详解
微信支付V3使用 SHA256withRSA 签名算法,签名流程如下:
- 构造签名串: 将请求URL(不含query)、请求方法(如POST)、请求时间戳、请求随机串和请求体(按参数名ASCII从小到大排序后拼接)组合成一个字符串。
- 计算签名: 使用商户的私钥对签名串进行SHA256withRSA签名。
- 发送签名: 将签名值放在HTTP请求头的
Wechatpay-Timestamp、Wechatpay-Nonce和Wechatpay-Signature中。
强烈建议:直接使用官方提供的SDK,它已经封装了所有复杂的签名逻辑,避免自己实现出错。
2 回调通知处理
当支付状态改变(成功、失败)或退款完成后,微信服务器会主动向你在统一下单时设置的 notify_url 发送一个POST请求,你的服务器必须处理这个回调。
回调Controller
// PayController.java
@PostMapping("/notify")
public String payNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 1. 获取微信服务器发送的请求体
String body = IOUtils.toString(request.getInputStream(), "UTF-8");
// 2. 获取请求头中的签名信息
String wechatpaySignature = request.getHeader("Wechatpay-Signature");
String wechatpayTimestamp = request.getHeader("Wechatpay-Timestamp");
String wechatpayNonce = request.getHeader("Wechatpay-Nonce");
// 3. 验证签名
// 你的WechatPayConfig实例需要暴露出来或者注入
// 这里假设你已经有了rsaConfig
if (!rsaConfig.verifySignature(wechatpayTimestamp, wechatpayNonce, body, wechatpaySignature)) {
response.setStatus(400);
return "FAIL";
}
// 4. 解析通知数据
JSONObject jsonObject = JSON.parseObject(body);
String resourceType = jsonObject.getString("resource_type");
if ("encrypt-resource".equals(resourceType)) {
String ciphertext = jsonObject.getJSONObject("resource").getString("ciphertext");
String associatedData = jsonObject.getJSONObject("resource").getString("associated_data");
String nonce = jsonObject.getJSONObject("resource").getString("nonce");
// 使用APIv3Key解密
String decryptedData = AesUtils.decryptToString(associatedData, nonce, ciphertext, config.getApiV3Key());
JSONObject decryptedJson = JSON.parseObject(decryptedJson);
// 5. 根据业务逻辑处理
String outTradeNo = decryptedJson.getString("out_trade_no");
String transactionId = decryptedJson.getString("transaction_id");
String tradeState = decryptedJson.getString("trade_state");
if ("SUCCESS".equals(tradeState)) {
// 业务处理:更新订单状态为已支付
System.out.println("订单支付成功: " + outTradeNo);
response.setStatus(200);
return "SUCCESS";
} else {
// 处理其他状态
System.out.println("订单支付状态: " + tradeState);
response.setStatus(200);
return "SUCCESS";
}
}
response.setStatus(400);
return "FAIL";
}
注意:你需要引入 commons-io 和 fastjson 等依赖来处理IO和JSON。
3 错误码与调试
- 日志: 在你的代码中添加详细的日志,特别是调用微信API前后的请求和响应,这是排查问题的最有效手段。
- 微信商户平台: 在商户平台的 "交易" -> "订单查询" 或 "资金" -> "退款订单" 中可以查到订单的详细状态。
- 官方文档: 遇到问题时,第一时间查阅 微信支付官方文档,文档是最权威的信息来源。
这份教程为你提供了一个完整的微信支付Java开发流程,从环境搭建到核心功能实现,再到前端集成和关键注意事项,请务必仔细阅读官方文档,并根据你的实际业务需求进行调整。
