提交 7b568604 authored 作者: hzh's avatar hzh

订单回调功能实现

上级 a2b2f4de
......@@ -109,6 +109,13 @@
<version>2.9.9</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
......
package org.dromara.order.base;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
......@@ -39,6 +40,13 @@ public abstract class AbstractBaseService<V, B, T> implements IBaseService<V, B,
return mapper().selectVoList(lqw);
}
@Override
public V getOne(B bo) {
LambdaQueryWrapper<T> lqw = buildQueryWrapper(bo);
List<V> list = mapper().selectVoList(lqw);
return CollectionUtil.isEmpty(list) ? null : list.get(0);
}
@Override
public Boolean insertByBo(B bo) {
Class<T> clazz = (Class<T>) GenericsUtils.getSuperClassGenricType(this.getClass(), 2);
......
......@@ -34,10 +34,17 @@ public interface IBaseService<V, B, T> {
* 查询符合条件的列表
*
* @param bo 查询条件
* @return 雷彪
* @return 结果
*/
List<V> queryList(B bo);
/**
* 查询符合条件的单个
* @param bo bo
* @return 结果
*/
V getOne(B bo);
/**
* 新增
*
......
package org.dromara.order.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.http.ContentType;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.ImmutableMap;
import com.ijpay.core.kit.HttpKit;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
......@@ -17,11 +23,16 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController;
import org.dromara.order.domain.bo.OrderBo;
import org.dromara.order.domain.vo.OrderVo;
import org.dromara.order.service.IOrderPayService;
import org.dromara.order.service.IOrderService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 订单费用
......@@ -30,6 +41,7 @@ import java.util.List;
* @author hzh
* @date 2024-12-05
*/
@Slf4j
@Validated
@RequiredArgsConstructor
@RestController
......@@ -37,6 +49,7 @@ import java.util.List;
public class OrderController extends BaseController {
private final IOrderService orderService;
private final IOrderPayService orderPayService;
/**
* 查询订单费用列表
......@@ -104,4 +117,45 @@ public class OrderController extends BaseController {
@PathVariable Long[] ids) {
return toAjax(orderService.deleteWithValidByIds(List.of(ids), true));
}
/**
* 回调原函数
*
* @param request request
* @param response response
*/
@RequestMapping(value = "/v3/payNotify", method = {RequestMethod.POST, RequestMethod.GET})
public void payNotify(HttpServletRequest request, HttpServletResponse response) {
Map<String, String> map = new HashMap<>(12);
try {
log.info("微信回调开始");
ImmutableMap.Builder<String, String> builder = ImmutableMap.<String, String>builder()
.put("timestamp", request.getHeader("Wechatpay-Timestamp"))
.put("nonce", request.getHeader("Wechatpay-Nonce"))
.put("serialNo", request.getHeader("Wechatpay-Serial"))
.put("signature", request.getHeader("Wechatpay-Signature"))
.put("result", HttpKit.readData(request));
boolean result = orderPayService.notify(JSON.toJSONString(builder));
if (result) {
response.setStatus(200);
map.put("code", "SUCCESS");
map.put("message", "SUCCESS");
} else {
response.setStatus(500);
map.put("code", "ERROR");
map.put("message", "签名错误");
}
response.setHeader("Content-type", ContentType.JSON.toString());
response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
response.flushBuffer();
} catch (Exception e) {
log.error("系统异常", e);
}
}
}
package org.dromara.order.service;
/**
* @author hzh
* @date 2024-12-09
**/
public interface IOrderPayService {
/**
* 支付回调
*
* @param body 回调参数
* @return 回调结果
*/
boolean notify(String body);
}
package org.dromara.order.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ijpay.core.kit.PayKit;
import com.ijpay.core.kit.WxPayKit;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.dromara.order.api.enums.OrderStatus;
import org.dromara.order.config.WechatPayConfiguration;
import org.dromara.order.domain.bo.OrderBo;
import org.dromara.order.domain.bo.OrderTradeBo;
import org.dromara.order.domain.vo.OrderTradeVo;
import org.dromara.order.domain.vo.OrderVo;
import org.dromara.order.service.IOrderPayService;
import org.dromara.order.service.IOrderService;
import org.dromara.order.service.IOrderTradeService;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.Objects;
/**
* @author hzh
* @date 2024-12-09
**/
@Slf4j
@Service
@RequiredArgsConstructor
public class WeChatOrderPayServiceImpl implements IOrderPayService {
private final WechatPayConfiguration config;
private final IOrderTradeService orderTradeService;
private final IOrderService orderService;
@GlobalTransactional(rollbackFor = Exception.class)
@Override
public boolean notify(String body) {
log.info("微信支付回调参数:{}", body);
JSONObject bodyJson = JSONObject.parseObject(body);
try {
// 需要通过证书序列号查找对应的证书,verifyNotify 中有验证证书的序列号
String plainText = WxPayKit.verifyNotify(
getSerialNumber(),
bodyJson.getString("result"),
bodyJson.getString("signature"),
bodyJson.getString("nonce"),
bodyJson.getString("timestamp"),
config.getApiKey3(),
config.getPlatformCertPath());
log.info("支付通知明文 {}", plainText);
JSONObject resJson = JSON.parseObject(plainText);
//支付订单号
String orderPayNo = resJson.getString("out_trade_no");
String tradeState = resJson.getString("trade_state");
OrderTradeVo orderTrade = orderTradeService.getOne(new OrderTradeBo().setOrderPayNo(orderPayNo));
if (Objects.isNull(orderTrade)) {
throw new RuntimeException("交易订单不存在");
}
if (!StringUtils.equals(tradeState, orderTrade.getTradeState())) {
//更新订单信息
String successTimeStr = resJson.getString("success_time");
//支付完成时间
Date payTime = StringUtils.isEmpty(successTimeStr) ? null : DateUtil.parse(successTimeStr);
//微信支付订单号
String transactionId = resJson.getString("transaction_id");
JSONObject amount = JSON.parseObject(resJson.getString("amount"));
//支付金额
int actualPayAmount = amount.getInteger("payer_total");
//获取openId
String payOpenId = JSON.parseObject(resJson.getString("payer")).getString("openid");
orderTrade.setPayOpenId(payOpenId);
orderTrade.setPayTime(payTime);
orderTrade.setTransactionId(transactionId);
orderTrade.setActualPayAmount(new BigDecimal(actualPayAmount).divide(new BigDecimal(100), 2, RoundingMode.CEILING));
orderTrade.setTradeState(tradeState);
orderTrade.setTradeInfo(JSON.toJSONString(resJson));
orderTradeService.updateByBo(BeanUtil.copyProperties(orderTrade, OrderTradeBo.class));
//跟新主表信息
OrderVo order = orderService.getOne(new OrderBo().setOrderNo(orderTrade.getOrderNo()));
order.setOrderPayNo(orderPayNo);
order.setActualPayAmount(orderTrade.getActualPayAmount());
order.setPayTime(new Date());
order.setStatus(OrderStatus.PAYED.getCode());
orderService.updateByBo(BeanUtil.copyProperties(order, OrderBo.class));
}
return true;
} catch (Exception e) {
log.error("系统异常", e);
return false;
}
}
/**
* 商户 API 证书序列号
*/
private String serialNo;
/**
* 获取证书序列号
*
* @return 证书序列号
*/
private String getSerialNumber() {
if (StringUtils.isEmpty(serialNo)) {
// 获取证书序列号
X509Certificate certificate = PayKit.getCertificate(config.getCertPath());
if (null != certificate) {
serialNo = certificate.getSerialNumber().toString(16).toUpperCase();
// 提前两天检查证书是否有效
boolean isValid = PayKit.checkCertificateIsValid(certificate, config.getMchId(), -2);
log.info("证书是否可用 {} 证书有效期为 {}", isValid, DateUtil.format(certificate.getNotAfter(), DatePattern.NORM_DATETIME_PATTERN));
}
}
return serialNo;
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论