<think>我们使用Java实现微信小程序的支付和退款功能,主要涉及微信支付API V3版本(当前推荐版本)。下面将分步骤详细说明,并提供关键代码示例。
### 1. 准备工作
- 申请小程序AppID和商户号(MCHID),并完成绑定。
- 在微信支付商户平台配置API密钥(APIv3密钥和API证书)。
- 小程序端获取用户openid(通过`wx.login`)。
### 2. 微信支付流程(JSAPI支付)
#### 步骤:
1. 小程序调用后端预下单接口(创建支付订单)
2. 后端调用微信支付统一下单接口(V3版)生成预支付交易会话标识(prepay_id)
3. 后端将生成的支付参数(包括prepay_id等)签名后返回小程序
4. 小程序调用`wx.requestPayment`发起支付
5. 支付成功后,微信服务器异步通知(回调)后端支付结果
6. 后端处理回调,进行业务逻辑处理(如更新订单状态)
#### 关键代码(后端):
##### 创建预支付订单
```java
public Map<String, String> createJsapiPrepayOrder(String openid, String orderNo, BigDecimal amount) throws Exception {
// 构造请求体
Map<String, Object> bodyMap = new HashMap<>();
bodyMap.put("appid", appId); // 小程序appid
bodyMap.put("mchid", mchId); // 商户号
bodyMap.put("description", "商品描述");
bodyMap.put("out_trade_no", orderNo); // 商户订单号
bodyMap.put("notify_url", notifyUrl); // 支付结果回调地址
bodyMap.put("amount", Map.of(
"total", amount.multiply(BigDecimal.valueOf(100)).intValue(), // 金额(分)
"currency", "CNY"
));
bodyMap.put("payer", Map.of("openid", openid));
// 调用微信支付统一下单接口(V3)
String url = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
String response = wxPayRequest.post(url, bodyMap); // 使用微信支付SDK或自己封装(需处理签名和证书)
// 解析响应,获取prepay_id
String prepayId = parsePrepayId(response);
// 生成小程序调起支付所需的参数(需签名)
Map<String, String> payParams = new HashMap<>();
payParams.put("appId", appId);
payParams.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));
payParams.put("nonceStr", generateNonceStr());
payParams.put("package", "prepay_id=" + prepayId);
payParams.put("signType", "RSA"); // V3使用RSA签名
// 计算签名
String sign = generateSignature(payParams);
payParams.put("paySign", sign);
return payParams;
}
```
##### 支付结果回调处理
```java
@PostMapping("/wxpay/notify")
public String handleWxpayNotify(HttpServletRequest request) {
// 读取请求体
String body = readRequestBody(request);
// 验证签名(确保请求来自微信)
if (!verifySignature(request, body)) {
return "FAIL";
}
// 解析回调数据
Map<String, Object> resultMap = parseCallbackBody(body);
String orderNo = (String) resultMap.get("out_trade_no");
String tradeNo = (String) resultMap.get("transaction_id");
String tradeState = (String) resultMap.get("trade_state");
// 处理业务:更新订单状态
if ("SUCCESS".equals(tradeState)) {
orderService.updateOrderStatus(orderNo, "PAID", tradeNo);
}
// 返回成功响应(必须)
return "<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>";
}
```
### 3. 微信退款流程
#### 步骤:
1. 商户调用微信支付退款接口(需证书)
2. 微信处理退款请求
3. 微信发送退款结果通知(异步)
#### 关键代码(后端):
```java
public boolean refund(String orderNo, BigDecimal totalAmount, BigDecimal refundAmount) throws Exception {
// 构造退款请求体
Map<String, Object> bodyMap = new HashMap<>();
bodyMap.put("out_trade_no", orderNo); // 原支付订单号
bodyMap.put("out_refund_no", generateRefundNo()); // 退款单号
bodyMap.put("reason", "退款原因");
bodyMap.put("notify_url", refundNotifyUrl); // 退款结果回调地址
bodyMap.put("amount", Map.of(
"refund", refundAmount.multiply(BigDecimal.valueOf(100)).intValue(),
"total", totalAmount.multiply(BigDecimal.valueOf(100)).intValue(),
"currency", "CNY"
));
// 调用退款接口(需要证书)
String url = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";
String response = wxPayRequest.postWithCert(url, bodyMap); // 使用证书进行请求
// 解析响应,判断是否受理成功(注意:退款成功是异步的)
String status = parseRefundStatus(response);
return "PROCESSING".equals(status) || "SUCCESS".equals(status);
}
// 退款结果回调处理(类似支付回调)
```
### 注意事项
1. **签名验证**:所有请求和回调都需要验证签名,确保安全性。
2. **金额单位**:微信支付使用分作为单位,注意转换。
3. **幂等性**:支付和退款接口都要保证幂等性(使用商户订单号去重)。
4. **证书管理**:退款需要API证书,妥善保管证书并注意更新。
### 安全建议
- 在支付回调中,一定要验证签名和金额,防止伪造回调。
- 退款操作应设置权限控制,避免越权操作。
- 退款金额不能超过原订单金额。
以上代码为示例,实际开发中需要根据微信支付官方文档进行调整,并处理异常情况。
§§相关问题§§
1. Java实现微信小程序微信支付时,如何保证支付回调的安全性?
2. 微信退款接口调用需要哪些必要条件?
3. 如何处理微信支付中的高并发下单请求?
</think>
### Java实现微信小程序微信支付和退款方法
#### 一、微信支付实现流程(JSAPI支付)
1. **创建预支付订单**
后端需调用微信支付统一下单接口生成预支付交易会话标识(prepay_id)
```java
public Map<String, String> createPrepayOrder(String openid, String orderNo, BigDecimal amount) throws Exception {
// 构造请求体
Map<String, Object> body = new HashMap<>();
body.put("appid", APP_ID);
body.put("mchid", MCH_ID);
body.put("description", "订单描述");
body.put("out_trade_no", orderNo);
body.put("notify_url", "https://yourdomain.com/pay/notify");
body.put("amount", Map.of("total", amount.multiply(BigDecimal.valueOf(100)).intValue(), "currency", "CNY"));
body.put("payer", Map.of("openid", openid));
// 调用微信接口
String response = HttpUtil.post("https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi", body);
Map<String, String> result = JSON.parseObject(response, Map.class);
return Map.of("prepay_id", result.get("prepay_id"));
}
```
2. **小程序端发起支付**
小程序使用预支付ID调起支付窗口
```java
// 后端返回给小程序的数据结构
{
"timeStamp": "1629781200",
"nonceStr": "5K8264ILTKCH16CQ",
"package": "prepay_id=wx1234567890",
"signType": "RSA",
"paySign": "加密签名"
}
```
3. **支付回调处理**
微信支付成功后主动回调notify_url
```java
@PostMapping("/pay/notify")
public String handleNotify(@RequestBody String data) {
// 1. 验证签名确保请求来自微信
if (!verifySignature(data)) return "FAIL";
// 2. 解析回调数据
Map<String, String> params = parseXml(data);
String orderNo = params.get("out_trade_no");
// 3. 更新订单状态(示例伪代码)
orderService.updateStatus(orderNo, "PAID");
return "<xml><return_code>SUCCESS</return_code></xml>";
}
```
#### 二、微信退款实现流程
```java
public boolean refund(String orderNo, BigDecimal refundAmount) throws Exception {
// 构造退款请求
Map<String, Object> body = new HashMap<>();
body.put("out_trade_no", orderNo);
body.put("out_refund_no", "REFUND_" + System.currentTimeMillis());
body.put("amount", Map.of(
"refund", refundAmount.multiply(BigDecimal.valueOf(100)).intValue(),
"total", 10000, // 原订单金额(分)
"currency", "CNY"
));
// 需要加载商户证书(退款必须)
String response = HttpUtil.postWithCert(
"https://api.mch.weixin.qq.com/v3/refund/domestic/refunds",
body,
CERT_PATH
);
// 解析响应
return "SUCCESS".equals(JSON.parseObject(response).getString("status"));
}
```
#### 三、关键注意事项
1. **安全验证**
- 支付回调必须验证签名防止伪造请求
- 退款操作需使用商户API证书(PKCS12格式)
2. **金额处理**
- 微信接口金额单位为分(需将元转换为分)
- 退款金额≤原订单金额
3. **幂等性控制**
- 使用商户订单号(out_trade_no)保证重复请求不会重复扣款
4. **错误处理**
```java
try {
// 支付/退款操作
} catch (WxPayException e) {
log.error("微信接口错误: code={}, msg={}", e.getErrCode(), e.getErrMsg());
// 处理网络超时、证书失效等异常
}
```
> 完整实现需参考[微信支付官方文档](https://pay.weixin.qq.com/wiki/doc/apiv3/apis/index.shtml),重点章节:JSAPI支付、退款申请、回调通知[^1][^2]。