fix: 微信支付,登录接口。
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user