一.准备工作
注册微信商户平台
-
获取商户号(
mchid
)、API密钥(API_KEY
)。 -
-
下载API证书(
apiclient_cert.pem
和apiclient_key.pem
)。
-
二.核心接口实现
1.生成预支付订单(Native支付)
public class WxPayService {
private String mchId;
private String apiKey;
private String notifyUrl;
// 初始化参数(通过@Value注入或配置文件读取)
/**
* 生成Native支付二维码链接
*/
public String createNativeOrder(String orderId, int amount) throws Exception {
String url = "https://api.mch.weixin.qq.com/v3/pay/transactions/native";
Map<String, Object> params = new HashMap<>();
params.put("mchid", mchId);
params.put("appid", "你的AppID"); // 如果是公众号/小程序支付
params.put("description", "订单描述");
params.put("out_trade_no", orderId);
params.put("notify_url", notifyUrl);
params.put("amount", Map.of("total", amount, "currency", "CNY"));
// 生成签名并发送请求
String body = JSON.toJSONString(params);
String authorization = generateSignature("POST", url, body);
// 使用OkHttp或RestTemplate发送请求
String response = sendPostRequest(url, body, authorization);
return JSON.parseObject(response).getString("code_url");
}
/**
* 生成V3接口的Authorization头
*/
private String generateSignature(String method, String url, String body) {
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
String nonceStr = UUID.randomUUID().toString().replace("-", "");
String message = method + "\n" + url + "\n" + timestamp + "\n" + nonceStr + "\n" + body + "\n";
String signature = signWithSHA256RSA(message, apiKey); // 使用私钥签名
return "WECHATPAY2-SHA256-RSA2048 "
+ "mchid=\"" + mchId + "\","
+ "nonce_str=\"" + nonceStr + "\","
+ "timestamp=\"" + timestamp + "\","
+ "serial_no=\"你的证书序列号\","
+ "signature=\"" + signature + "\"";
}
}
2.处理微信支付回调
@RestController
@RequestMapping("/api/wxpay")
public class WxPayCallbackController {
@PostMapping("/notify")
public String handleNotify(@RequestBody String requestBody,
@RequestHeader("Wechatpay-Signature") String signature,
@RequestHeader("Wechatpay-Timestamp") String timestamp,
@RequestHeader("Wechatpay-Nonce") String nonce) {
// 1. 验证签名(防止伪造请求)
String message = timestamp + "\n" + nonce + "\n" + requestBody + "\n";
boolean isValid = verifySignature(message, signature, publicKey); // 使用微信平台公钥验证
if (!isValid) {
return "<xml><return_code>FAIL</return_code></xml>";
}
// 2. 解析回调数据
Map<String, String> result = parseXml(requestBody);
String orderId = result.get("out_trade_no");
String transactionId = result.get("transaction_id");
// 3. 幂等性处理:检查订单是否已处理
if (orderService.isOrderPaid(orderId)) {
return "<xml><return_code>SUCCESS</return_code></xml>";
}
// 4. 更新订单状态
orderService.updateOrderToPaid(orderId, transactionId);
// 5. 返回成功响应(必须!否则微信会重试)
return "<xml><return_code>SUCCESS</return_code></xml>";
}
}
3.查询订单状态
public class WxPayService {
public Map<String, String> queryOrder(String orderId) throws Exception {
String url = "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/"
+ orderId + "?mchid=" + mchId;
String authorization = generateSignature("GET", url, "");
String response = sendGetRequest(url, authorization);
return JSON.parseObject(response, Map.class);
}
}
三.注意事项
签名验证
-
所有回调必须验证签名,防止伪造请求。
-
使用微信提供的平台证书验证。
-
密等性设计
-
通过数据库唯一索引或Redis锁防止重复处理订单。
证书管理
推荐使用WechatPay Apache HttpClient
简化证书处理:
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.4.7</version>
</dependency>
日志记录
-
记录所有微信请求和回调,方便排查问题。
四.完整调用事例(Spring Boot)
@RestController
public class PaymentController {
@Autowired
private WxPayService wxPayService;
@GetMapping("/createOrder")
public String createOrder(@RequestParam String orderId, @RequestParam int amount) {
try {
String codeUrl = wxPayService.createNativeOrder(orderId, amount);
return "<img src=\"https://example.com/qr?data=" + codeUrl + "\">";
} catch (Exception e) {
return "支付创建失败: " + e.getMessage();
}
}
}
五.常见问题
-
证书加载失败:检查证书路径和格式(必须为PEM格式)。
-
签名错误:使用微信官方验签工具调试。
-
回调未触发:检查
notify_url
是否外网可访问,且返回SUCCESS
。