fix: 微信支付,登录接口。

This commit is contained in:
tianyongbao
2026-01-16 14:33:21 +08:00
parent 7efbad95aa
commit d7702ee991
34 changed files with 2581 additions and 145 deletions

View File

@@ -1,20 +1,36 @@
package com.intc.weixin.controller;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.intc.common.core.domain.R;
import com.intc.common.satoken.utils.LoginHelper;
import com.intc.common.web.core.BaseController;
import com.intc.weixin.config.WxMaProperties;
import com.intc.weixin.config.WxPayItemProperties;
import com.intc.weixin.config.WxPayNotifyProperties;
import com.intc.weixin.domain.bo.ReqCreatePayOrder;
import com.intc.weixin.domain.vo.PayItemVo;
import com.intc.weixin.domain.vo.WxPayOrderVo;
import com.intc.weixin.service.PayOrderBusinessService;
import com.intc.weixin.service.WxMaService;
import com.intc.weixin.service.WxMpService;
import com.intc.weixin.service.WxPayService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.constraints.NotBlank;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.bean.result.WxMpUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Optional;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 微信对接控制器
@@ -34,6 +50,21 @@ public class WeixinController extends BaseController {
@Autowired(required = false)
private WxMaService wxMaService;
@Autowired(required = false)
private WxMaProperties wxMaProperties;
@Autowired(required = false)
private WxPayItemProperties wxPayItemProperties;
@Autowired(required = false)
private WxPayNotifyProperties wxPayNotifyProperties;
@Autowired(required = false)
private PayOrderBusinessService payOrderBusinessService;
@Autowired(required = false)
private WxPayService wxPayService;
@Operation(summary = "测试接口")
@GetMapping("/test")
public R<String> test() {
@@ -106,4 +137,278 @@ public class WeixinController extends BaseController {
}
}
@Operation(summary = "获取支付选项列表")
@PostMapping("/pay/get_pay_item")
public R<List<PayItemVo>> getListPayItem() {
// 获取当前登录用户ID
Long userId = LoginHelper.getUserId();
if (userId == null || userId < 0) {
return R.fail("用户未登录");
}
// 检查配置是否存在
if (wxPayItemProperties == null || wxPayItemProperties.getPayItems() == null) {
return R.fail("支付选项配置未启用");
}
// 构建返回列表
List<PayItemVo> listPayItem = new ArrayList<>();
for (WxPayItemProperties.PayItem item : wxPayItemProperties.getPayItems()) {
PayItemVo data = new PayItemVo();
data.setId(item.getId());
data.setAmount(item.getAmount());
data.setAddMonth(item.getAddMonth());
data.setTitle(item.getTitle());
data.setDescription(item.getDescription());
listPayItem.add(data);
}
return R.ok(listPayItem);
}
@Operation(summary = "创建微信JSAPI支付订单")
@PostMapping("/pay/create_order")
public R<WxPayOrderVo> createOrderByJsapi(@Validated @RequestBody ReqCreatePayOrder request) {
try {
// 1. 获取当前登录用户ID
Long userId = LoginHelper.getUserId();
if (userId == null || userId < 0) {
return R.fail("用户未登录");
}
// 2. 检查服务是否可用
if (wxMaService == null) {
return R.fail("小程序配置未启用");
}
if (payOrderBusinessService == null) {
return R.fail("支付服务未启用");
}
if (wxPayService == null) {
return R.fail("微信支付服务未启用");
}
if (wxPayNotifyProperties == null || wxPayNotifyProperties.getNotifyUrl() == null) {
return R.fail("支付回调配置未设置");
}
// 3. 通过jsCode获取openId
String openId;
try {
openId = wxMaService.code2Session(request.getJsCode());
if (openId == null || openId.isEmpty()) {
return R.fail("获取用户openId失败");
}
} catch (WxErrorException e) {
log.error("获取openId失败", e);
return R.fail("获取用户openId失败: " + e.getMessage());
}
// 4. 关闭用户未支付的订单
try {
payOrderBusinessService.closeUnpaidOrders(userId, openId);
} catch (Exception e) {
log.warn("关闭旧订单失败", e);
// 不影响后续流程
}
// 5. 创建支付订单
Long orderId = payOrderBusinessService.createPayOrder(
userId,
openId,
request.getPayId(),
request.getListDeviceId(),
request.getJsCode()
);
if (orderId == null || orderId <= 0) {
return R.fail("创建订单失败");
}
// 6. 查询订单信息
com.intc.fishery.domain.PayOrder order = payOrderBusinessService.queryById(orderId);
if (order == null) {
return R.fail("查询订单信息失败");
}
// 7. 调用微信支付API创建预支付订单
String prepayId = wxPayService.createJsapiOrder(
openId,
order.getOutTradeNumber(),
order.getTotalAmount(),
order.getDescription(),
wxPayNotifyProperties.getNotifyUrl()
);
if (prepayId == null || prepayId.isEmpty()) {
return R.fail("创建微信预支付订单失败");
}
// 8. 生成JSAPI支付参数
String appId = wxMaProperties != null && wxMaProperties.getAppId() != null ?
wxMaProperties.getAppId() : "";
if (appId.isEmpty()) {
return R.fail("小程序appId配置未设置");
}
Map<String, String> payParams = wxPayService.generateJsapiPayParams(prepayId, appId);
// 9. 构建返回结果
WxPayOrderVo result = new WxPayOrderVo();
result.setPrepayId(prepayId);
result.setTotalAmount(order.getTotalAmount());
result.setTimestamp(payParams.get("timeStamp"));
result.setNonceStr(payParams.get("nonceStr"));
result.setPackageValue(payParams.get("package"));
result.setSignatureType(payParams.get("signType"));
result.setSignature(payParams.get("paySign"));
result.setAppId(appId);
return R.ok(result);
} catch (Exception e) {
log.error("创建支付订单失败", e);
return R.fail("创建支付订单失败: " + e.getMessage());
}
}
@Operation(summary = "微信支付回调通知")
@PostMapping("/pay_notify")
public Map<String, String> payNotify(
@RequestBody String requestBody,
@RequestHeader(value = "Wechatpay-Timestamp", required = false) String timestamp,
@RequestHeader(value = "Wechatpay-Nonce", required = false) String nonce,
@RequestHeader(value = "Wechatpay-Signature", required = false) String signature,
@RequestHeader(value = "Wechatpay-Serial", required = false) String serial) {
log.info("接收到微信支付回调: timestamp={}, nonce={}, signature={}, serial={}",
timestamp, nonce, signature, serial);
log.info("回调请求体: {}", requestBody);
Map<String, String> response = new HashMap<>();
try {
// 1. 检查服务是否可用
if (wxPayService == null) {
log.error("微信支付服务未启用");
response.put("code", "FAIL");
response.put("message", "微信支付服务未启用");
return response;
}
if (payOrderBusinessService == null) {
log.error("支付订单业务服务未启用");
response.put("code", "FAIL");
response.put("message", "支付订单业务服务未启用");
return response;
}
// 2. 验证签名
boolean isValid = wxPayService.verifySignature(timestamp, nonce, requestBody, signature, serial);
if (!isValid) {
log.error("微信支付回调签名验证失败");
response.put("code", "FAIL");
response.put("message", "签名验证失败");
return response;
}
// 3. 解析回调数据
JSONObject callbackData = JSONUtil.parseObj(requestBody);
String eventType = callbackData.getStr("event_type");
log.info("微信支付事件类型: {}", eventType);
// 4. 处理支付成功通知
if ("TRANSACTION.SUCCESS".equalsIgnoreCase(eventType)) {
JSONObject resource = callbackData.getJSONObject("resource");
if (resource == null) {
log.error("回调数据resouce为空");
response.put("code", "FAIL");
response.put("message", "解析回调数据失败");
return response;
}
// 5. 解密回调数据
String associatedData = resource.getStr("associated_data");
String nonceValue = resource.getStr("nonce");
String ciphertext = resource.getStr("ciphertext");
String decryptedData = wxPayService.decryptCallbackData(associatedData, nonceValue, ciphertext);
if (decryptedData == null || decryptedData.isEmpty()) {
log.error("解密回调数据失败");
response.put("code", "FAIL");
response.put("message", "解密数据失败");
return response;
}
log.info("解密后的支付数据: {}", decryptedData);
// 6. 解析交易数据
JSONObject transactionData = JSONUtil.parseObj(decryptedData);
// 7. 验证商户号
String mchId = transactionData.getStr("mchid");
if (wxPayNotifyProperties != null && wxPayNotifyProperties.getMchId() != null) {
if (!wxPayNotifyProperties.getMchId().equals(mchId)) {
log.error("商户号不匹配: expected={}, actual={}",
wxPayNotifyProperties.getMchId(), mchId);
response.put("code", "FAIL");
response.put("message", "商户号错误");
return response;
}
}
// 8. 提取交易信息
String outTradeNumber = transactionData.getStr("out_trade_no");
String transactionId = transactionData.getStr("transaction_id");
String tradeState = transactionData.getStr("trade_state");
String tradeStateDesc = transactionData.getStr("trade_state_desc");
String successTime = transactionData.getStr("success_time");
String bankType = transactionData.getStr("bank_type");
// 提取金额信息
JSONObject amountObj = transactionData.getJSONObject("amount");
Integer payerTotal = amountObj.getInt("payer_total");
String payerCurrency = amountObj.getStr("payer_currency");
log.info("处理支付成功通知: outTradeNumber={}, transactionId={}, tradeState={}",
outTradeNumber, transactionId, tradeState);
// 9. 调用业务层处理支付成功
boolean success = payOrderBusinessService.handlePaymentSuccess(
outTradeNumber,
transactionId,
payerTotal,
payerCurrency,
successTime,
tradeState,
tradeStateDesc,
bankType
);
if (success) {
log.info("支付回调处理成功: outTradeNumber={}", outTradeNumber);
response.put("code", "SUCCESS");
response.put("message", "成功");
} else {
log.error("支付回调处理失败: outTradeNumber={}", outTradeNumber);
response.put("code", "FAIL");
response.put("message", "处理失败");
}
return response;
} else {
log.warn("未处理的事件类型: {}", eventType);
response.put("code", "SUCCESS");
response.put("message", "成功");
return response;
}
} catch (Exception e) {
log.error("处理微信支付回调异常", e);
response.put("code", "FAIL");
response.put("message", "处理异常: " + e.getMessage());
return response;
}
}
}