增加订单信息获取接口,支付模块通过feign获取支付的详细信息(订单、用户openid、支付明细),稍后测试支付、退款

This commit is contained in:
jkcdev 2025-05-12 11:07:14 +08:00
parent e76fd4d9e1
commit c64f61560f
16 changed files with 229 additions and 44 deletions

View File

@ -50,6 +50,18 @@ public class ConvertUtil {
return utcZoned.format(formatter);
}
/**
* 格式化LocalDateTime为字符串
* 格式yyyy-MM-dd HH:mm:ss
*
* @param localDateTime 要格式化的LocalDateTime对象
* @return
*/
public static String formatLocalDateTime(LocalDateTime localDateTime) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
return localDateTime.format(formatter);
}
//public static void main(String[] args) {
// LocalDateTime localDateTime = LocalDateTime.now();
// String utcTime = localDateTimeToUTC(localDateTime);

View File

@ -47,6 +47,14 @@ public interface OrdersFeignClient {
@PostMapping("userOrders/payment")
JsonResult<?> payment(@RequestBody ResFeignOrderPaymentDto paymentParam);
/**
* 根据ID获取订单支付详情
* @param orderId 支付id
* @return 订单支付详情
*/
@PostMapping("userOrders/paymentDetailsInfo")
JsonResult<?> getPaymentDetails(@RequestParam("orderId")Long orderId);
/**
* 订单发起退款
* @param orderId 订单ID

View File

@ -1,4 +1,4 @@
package com.cdzy.payment.model.dto;
package com.ebike.feign.model.rsp;
import lombok.AllArgsConstructor;
import lombok.Data;

View File

@ -1,4 +1,4 @@
package com.cdzy.payment.model.dto;
package com.ebike.feign.model.rsp;
import lombok.AllArgsConstructor;
import lombok.Data;

View File

@ -1,4 +1,4 @@
package com.cdzy.payment.model.dto;
package com.ebike.feign.model.rsp;
import lombok.AllArgsConstructor;
import lombok.Data;

View File

@ -1,4 +1,4 @@
package com.cdzy.payment.model.dto;
package com.ebike.feign.model.rsp;
import lombok.AllArgsConstructor;
import lombok.Data;

View File

@ -9,6 +9,7 @@ import com.ebike.feign.model.res.ResFeignOrderPaymentDto;
import com.cdzy.orders.model.dto.res.RspBikeDto;
import com.cdzy.orders.model.entity.EbikeUserOrders;
import com.cdzy.orders.service.UserOrdersService;
import com.ebike.feign.model.rsp.EbikePaymentDto;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import jakarta.annotation.Resource;
@ -193,6 +194,22 @@ public class EbikeUserOrdersController {
return JsonResult.success(info);
}
/**
* 根据ID获取订单支付详情
*
* @param orderId 订单表主键
* @ 订单支付详情
*/
@GetMapping("paymentDetailsInfo")
public JsonResult<?> paymentDetailsInfo(@RequestParam("orderId") Long orderId) {
try {
EbikePaymentDto info = userOrdersService.paymentDetailsInfo(orderId);
return JsonResult.success(info);
} catch (Exception e) {
return JsonResult.failed(e.getMessage());
}
}
/**
* 根据用户订单表主键获取详细信
*

View File

@ -5,6 +5,7 @@ import com.cdzy.orders.model.dto.req.ReqOrderDto;
import com.cdzy.orders.model.dto.res.RspOrderDetailsInfo;
import com.ebike.feign.model.res.ResFeignOrderPaymentDto;
import com.cdzy.orders.model.dto.res.RspBikeDto;
import com.ebike.feign.model.rsp.EbikePaymentDto;
import com.mybatisflex.core.service.IService;
import com.cdzy.orders.model.entity.EbikeUserOrders;
@ -107,4 +108,12 @@ public interface UserOrdersService extends IService<EbikeUserOrders> {
* @return 金额
*/
BigDecimal costOrderCalculation(ReqOrderDto orderDto);
/**
* 获取支付信息
*
* @param orderId 订单ID
* @return 支付信息
*/
EbikePaymentDto paymentDetailsInfo(Long orderId);
}

View File

@ -4,6 +4,7 @@ import com.alibaba.fastjson2.JSONObject;
import com.cdzy.common.enums.Code;
import com.cdzy.common.model.JsonResult;
import com.cdzy.common.model.ResGPSDto;
import com.cdzy.common.utils.ConvertUtil;
import com.cdzy.orders.component.EbikeCoreHandler;
import com.cdzy.orders.enums.*;
import com.cdzy.orders.mapper.EbikeOrderDetailsMapper;
@ -14,7 +15,9 @@ import com.cdzy.orders.model.dto.res.RedisPoint;
import com.cdzy.orders.model.dto.res.RspBikeDto;
import com.cdzy.orders.model.dto.res.RspOrderDetailsInfo;
import com.cdzy.orders.model.entity.EbikeOrderDetails;
import com.cdzy.orders.model.entity.EbikeUser;
import com.cdzy.orders.model.entity.EbikeUserOrders;
import com.cdzy.orders.service.EbikeUserService;
import com.cdzy.orders.service.UserOrdersService;
import com.cdzy.orders.uitls.NumberUtils;
import com.cdzy.orders.uitls.RedisUtil;
@ -25,9 +28,7 @@ import com.ebike.feign.model.res.ResFeignEbikeSysRcostsetDto;
import com.ebike.feign.model.res.ResFeignEbikeSysRcostsetTimePeriodDto;
import com.ebike.feign.model.res.ResFeignEbikeSysRcostsetWeekDto;
import com.ebike.feign.model.res.ResFeignOrderPaymentDto;
import com.ebike.feign.model.rsp.FeignEbikeBikeInfoDto;
import com.ebike.feign.model.rsp.FeignEbikeEcuInfo;
import com.ebike.feign.model.rsp.FeignEbikeRegionDto;
import com.ebike.feign.model.rsp.*;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
@ -72,6 +73,9 @@ public class UserOrdersServiceImpl extends ServiceImpl<UserOrdersMapper, EbikeUs
@Resource
EbikeOrderDetailsMapper orderDetailsMapper;
@Resource
EbikeUserService ebikeUserService;
@Override
@Transactional
public Long saveRide(ReqOrderDto orderDto) {
@ -346,6 +350,54 @@ public class UserOrdersServiceImpl extends ServiceImpl<UserOrdersMapper, EbikeUs
return userOrders.getTotalAmount();
}
@Override
public EbikePaymentDto paymentDetailsInfo(Long orderId) {
RspOrderDetailsInfo orderDetailsInfo = orderDetailsInfo(orderId);
if (orderDetailsInfo == null) {
throw new RuntimeException("订单不存在");
}
// user openid
EbikeUser ebikeUser = ebikeUserService.getById(orderDetailsInfo.getUserId());
if (ebikeUser == null) {
throw new RuntimeException("用户不存在");
}
EbikePaymentDto ebikePaymentDto = new EbikePaymentDto();
ebikePaymentDto.setOpenId(ebikeUser.getOpenId());
ebikePaymentDto.setOrderId(String.valueOf(orderId));
ebikePaymentDto.setDescription(String.format("骑行:%s - %s",
ConvertUtil.formatLocalDateTime(orderDetailsInfo.getStartTime()),
ConvertUtil.formatLocalDateTime(orderDetailsInfo.getEndTime())));
int ot = orderDetailsInfo.getOrderType();
//1-单次骑行 2-骑行卡购买 3-会员卡续费
switch (ot) {
case 1:
ebikePaymentDto.setGoodsTag("单次骑行");
break;
case 2:
ebikePaymentDto.setGoodsTag("骑行卡购买");
break;
case 3:
ebikePaymentDto.setGoodsTag("会员卡续费");
break;
}
AmountDto amountDto = new AmountDto();
amountDto.setTotal(orderDetailsInfo.getActualAmount().doubleValue());
ebikePaymentDto.setAmount(amountDto);
DetailDto detailDto = new DetailDto();
detailDto.setCostPrice(orderDetailsInfo.getTotalAmount().doubleValue());
detailDto.setGoodsDetail(orderDetailsInfo.getDetails().stream().map(orderDetail -> {
PayDetailDto payDetailDto = new PayDetailDto();
payDetailDto.setItemName(orderDetail.getItemName());
payDetailDto.setItemId(String.valueOf(orderDetail.getItemId()));
payDetailDto.setQuantity(1);
payDetailDto.setUnitPrice(orderDetail.getItemAmount().doubleValue());
return payDetailDto;
}).toList());
ebikePaymentDto.setDetail(detailDto);
// client ip 后续如果可以获取到就加上
return ebikePaymentDto;
}
@Override
public void payment(ResFeignOrderPaymentDto paymentDto) {
EbikeUserOrders userOrders = this.mapper.selectOneById(paymentDto.getOrderId());

View File

@ -34,7 +34,7 @@ public class EbikeWxPaymentController {
@PostMapping("/prepay")
public JsonResult<?> prepay(@RequestParam(name = "orderId") String orderId) {
JSONObject r = wxPayService.prepay(orderId);
return r == null?JsonResult.failed("下单失败"):JsonResult.success(r);
return r.containsKey("error")?JsonResult.failed("下单失败: "+ r.getString("error")):JsonResult.success(r);
}
/**
@ -61,18 +61,31 @@ public class EbikeWxPaymentController {
return JsonResult.success(r);
}
// 拆分为两个接口一个是退款申请(生成退款记录)一个是提交退款调用微信退款接口
/**
* 退款申请
*
* @param refundDto 退款信息
* @return 退款成功返回true否则返回false
*/
@PostMapping("/refund")
public JsonResult<?> refund(@RequestBody EbikeRefundDto refundDto) {
String r = wxPayService.refund(refundDto.getOrderId(), refundDto.getReason());
@PostMapping("/refundApply")
public JsonResult<?> refundApply(@RequestBody EbikeRefundDto refundDto) {
String r = wxPayService.refundApply(refundDto.getOrderId(), refundDto.getReason());
return r == null?JsonResult.failed("退款失败"):JsonResult.success(r);
}
/**
* 调用微信退款接口
*
* @param orderId 订单id
* @return 退款成功返回true否则返回false
*/
@PostMapping("/refund")
public JsonResult<?> refund(@RequestParam(name = "orderId") String orderId) {
boolean r = wxPayService.refund(orderId);
return r?JsonResult.failed("退款失败"):JsonResult.success(r);
}
/**
* 通过退款单号查询退款信息
*

View File

@ -80,7 +80,7 @@ public class EbikePayment implements Serializable {
private String currency;
/**
* 金额
* 实际支付金额
*/
private Double total;

View File

@ -30,6 +30,14 @@ public interface EbikeRefundService extends IService<EbikeRefund> {
*/
List<EbikeRefund> getNoSuccessRefundOrderByDuration(int duration);
/**
* 根据订单id查询退款记录
*
* @param orderId 订单id
* @return
*/
EbikeRefund getByOrderId(String orderId);
/**
* 更新退款状态
*

View File

@ -29,7 +29,7 @@ public interface WxPayService {
* JSAPI支付下单
*
* @param outTradeNo 订单id
* @return 下单成功返回true否则返回false
* @return 下单成功返回wx支付请求参数否则返回null
*/
JSONObject prepay(String outTradeNo);
@ -65,7 +65,15 @@ public interface WxPayService {
* @param reason 退款原因
* @return 退款信息id
*/
String refund(String outTradeNo, String reason);
String refundApply(String outTradeNo, String reason);
/**
* 调用微信退款接口
*
* @param outTradeNo 商户(骑行)订单号
* @return 退款信息id
*/
Boolean refund(String outTradeNo);
/**

View File

@ -34,7 +34,7 @@ public class EbikePaymentServiceImpl extends ServiceImpl<EbikePaymentMapper, Ebi
@Override
public List<EbikePayment> getNoPayOrderByDuration(int duration) {
QueryColumn time = QueryMethods.secToTime(String.valueOf(duration * 60));
// trade_state 等于 NOTPAY未支付的 USERPAYING正在支持的 并且创建时间未超过duration分钟的订单
// trade_state 等于 NOTPAY未支付的 并且创建时间未超过duration分钟的订单
QueryWrapper query = QueryWrapper.create()
.where(EBIKE_PAYMENT.TRADE_STATE.eq(Transaction.TradeStateEnum.NOTPAY.ordinal()))
.and(QueryMethods.addTime(EBIKE_PAYMENT.CREATE_TIME, time).lt(QueryMethods.now()));
@ -44,7 +44,7 @@ public class EbikePaymentServiceImpl extends ServiceImpl<EbikePaymentMapper, Ebi
@Override
public List<EbikePayment> getExpireOrderByDuration(int duration) {
QueryColumn time = QueryMethods.secToTime(String.valueOf(duration * 60));
// trade_state 等于 NOTPAY未支付的 USERPAYING正在支持的 并且创建时间超过duration分钟的订单
// trade_state 等于 NOTPAY未支付的 并且创建时间超过duration分钟的订单
QueryWrapper query = QueryWrapper.create()
.where(EBIKE_PAYMENT.TRADE_STATE.eq(Transaction.TradeStateEnum.NOTPAY.ordinal()))
.and(QueryMethods.addTime(EBIKE_PAYMENT.CREATE_TIME, time).ge(QueryMethods.now()));

View File

@ -48,6 +48,16 @@ public class EbikeRefundServiceImpl extends ServiceImpl<EbikeRefundMapper, Ebike
return list(query);
}
@Override
public EbikeRefund getByOrderId(String orderId) {
// create_time到排序取最新的一条
QueryWrapper query = QueryWrapper.create()
.where(EBIKE_REFUND.ORDER_ID.eq(orderId))
.orderBy(EBIKE_REFUND.CREATE_TIME.desc())
.limit(1);
return getOne(query);
}
@Override
public Boolean updateRefundStatus(Refund refund) {
EbikeRefund ebikeRefund = new EbikeRefund();

View File

@ -1,7 +1,9 @@
package com.cdzy.payment.service.impl;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.cdzy.common.model.JsonResult;
import com.cdzy.payment.config.WxPayConfig;
import com.cdzy.payment.model.dto.*;
import com.cdzy.payment.model.entity.EbikePayment;
@ -12,6 +14,10 @@ import com.cdzy.payment.service.EbikeRefundService;
import com.cdzy.payment.service.WxPayService;
import com.cdzy.payment.utils.HttpServletUtils;
import com.cdzy.payment.utils.StringUtils;
import com.ebike.feign.clients.OrdersFeignClient;
import com.ebike.feign.model.rsp.AmountDto;
import com.ebike.feign.model.rsp.DetailDto;
import com.ebike.feign.model.rsp.EbikePaymentDto;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.cipher.RSASigner;
import com.wechat.pay.java.core.exception.HttpException;
@ -64,7 +70,8 @@ public class WxPayServiceImpl implements WxPayService {
private EbikePaymentService ebikePaymentService;
@Resource
private EbikeRefundService ebikeRefundService;
@Resource
private OrdersFeignClient orderFeingClient;
@Override
public boolean closeOrder(String outTradeNo) {
@ -84,40 +91,48 @@ public class WxPayServiceImpl implements WxPayService {
@Override
public JSONObject prepay(String outTradeNo){
JSONObject r = new JSONObject();
// 首先检查订单是否正在付中或者已经支付成功
EbikePayment ebikePayment = ebikePaymentService.getByOrderId(outTradeNo);
if (ebikePayment != null) {
if (String.valueOf(Transaction.TradeStateEnum.SUCCESS.ordinal()).equals(ebikePayment.getTradeState())) {
log.info("订单已经支付成功,订单号:{}", outTradeNo);
return null;
r.put("error", "订单已经支付成功");
return r;
}
if (String.valueOf(Transaction.TradeStateEnum.NOTPAY.ordinal()).equals(ebikePayment.getTradeState())
||String.valueOf(Transaction.TradeStateEnum.USERPAYING.ordinal()).equals(ebikePayment.getTradeState())) {
log.info("订单正在支付中,订单号:{}", outTradeNo);
r.put("error", "订单正在支付中");
return null;
}
}
//查询订单, orderFeingClient.getOrderById(orderId)
//获取当前用户openId
return null;
JsonResult<?> result = orderFeingClient.getPaymentDetails(Long.valueOf(outTradeNo));
if (result.getCode() != 200) {
log.error("查询订单 {} 失败, {}", outTradeNo, result.getMessage());
r.put("error", "查询订单失败, "+result.getMessage());
return r;
}
EbikePaymentDto paymentDto = JSON.parseObject(JSON.toJSONString(result.getData()), EbikePaymentDto.class);
return prepay(paymentDto, ebikePayment);
}
/**
* JSAPI支付下单
*
* @param outTradeNo 商户(骑行)订单号
* @param description 商品描述
* @param goodsTag 商品标记
* @param openId 用户标识
* @param amount 金额
* @param detail 商品详情
* @param paymentDto 支付信息
* @param ebikePayment 原始支付记录
* @return 下单成功返回true否则返回false
* @return 下单成功返回wx支付请求参数否则返回null
*/
private JSONObject prepay(String outTradeNo, String description, String goodsTag, String openId, AmountDto amount, DetailDto detail, EbikePayment ebikePayment) {
private JSONObject prepay(EbikePaymentDto paymentDto, EbikePayment ebikePayment) {
String outTradeNo = paymentDto.getOrderId();
String description = paymentDto.getDescription();
String goodsTag = paymentDto.getGoodsTag();
String openId = paymentDto.getOpenId();
AmountDto amount = paymentDto.getAmount();
DetailDto detail = paymentDto.getDetail();
try {
PrepayRequest request = new PrepayRequest();
request.setAppid(wxPayConfig.getAppId());
@ -258,20 +273,55 @@ public class WxPayServiceImpl implements WxPayService {
}
@Override
public String refund(String outTradeNo, String reason) {
// 雪花算法生成退款单号
public String refundApply(String outTradeNo, String reason) {
// 检查是否重复退款申请
EbikeRefund ebikeRefund = ebikeRefundService.getByOrderId(outTradeNo);
if (ebikeRefund!= null) {
if(String.valueOf(Status.PROCESSING.ordinal()).equals(ebikeRefund.getStatus())){
log.error("订单{}正在退款中", outTradeNo);
return null;
}
if(String.valueOf(Status.SUCCESS.ordinal()).equals(ebikeRefund.getStatus())) {
log.error("订单{}已经退款过", outTradeNo);
return null;
}
}
// 查询支付记录
EbikePayment ebikePayment = ebikePaymentService.getByOrderId(outTradeNo);
String transactionId = ebikePayment.getTransactionId();
// 生成退款记录
String outRefundNo = StringUtils.generateSnowflakeId("refundId");
EbikeRefund ebikeRefund = new EbikeRefund();
ebikeRefund = new EbikeRefund();
ebikeRefund.setCreateTime(LocalDateTime.now());
// 查询支付订单
String transactionId = "";
ebikeRefund.setRefundId(outRefundNo);
ebikeRefund.setOrderId(outTradeNo);
ebikeRefund.setTransactionId(transactionId);
ebikeRefund.setReason(reason);
ebikeRefund.setStatus(String.valueOf(Status.PROCESSING.ordinal()));
ebikeRefundService.saveRefundResult(ebikeRefund);
return outRefundNo;
}
@Override
public Boolean refund(String outTradeNo) {
EbikeRefund ebikeRefund = ebikeRefundService.getByOrderId(outTradeNo);
if (ebikeRefund==null){
log.error("退款refund失败订单 {} 退款申请不存在", outTradeNo);
return false;
}
// 查询支付记录
EbikePayment ebikePayment = ebikePaymentService.getByOrderId(outTradeNo);
String transactionId = ebikePayment.getTransactionId();
AmountRefundDto amount = new AmountRefundDto();
amount.setTotal(ebikePayment.getCostPrice());
amount.setRefund(ebikePayment.getTotal());
amount.setCurrency(ebikePayment.getCurrency());
// 发起退款
CreateRequest request = new CreateRequest();
request.setTransactionId(transactionId);
request.setOutTradeNo(outTradeNo);
request.setOutRefundNo(outRefundNo);
request.setReason(reason);
request.setOutRefundNo(ebikeRefund.getRefundId());
request.setReason(ebikeRefund.getReason());
request.setNotifyUrl(wxPayConfig.getRefundNotifyUrl());
AmountReq amountReq = new AmountReq();
amountReq.setRefund(BigDecimal.valueOf(amount.getRefund() * 100.0).longValue());
@ -279,24 +329,22 @@ public class WxPayServiceImpl implements WxPayService {
amountReq.setCurrency(amount.getCurrency());
request.setAmount(amountReq);
Refund result = wxRefundService.create(request);
// 入库
// 更新退款信息
if (result != null) {
ebikeRefund.setRefundId(outRefundNo);
ebikeRefund.setOrderId(outTradeNo);
ebikeRefund.setTransactionId(transactionId);
ebikeRefund.setTotal(amount.getTotal());
ebikeRefund.setReason(reason);
ebikeRefund.setStatus(String.valueOf(result.getStatus().ordinal()));
ebikeRefund.setCurrency(amount.getCurrency());
if(Status.SUCCESS.equals(result.getStatus())){
ebikeRefund.setRefund(result.getAmount().getRefund().doubleValue() / 100.0);
ebikeRefund.setRefundTime(LocalDateTime.now());
}
ebikeRefundService.saveRefundResult(ebikeRefund);
return outRefundNo;
ebikeRefundService.updateById(ebikeRefund);
return true;
}
log.error("退款申请refund失败订单号{}", outTradeNo);
return null;
log.error("退款refund失败订单号{}", outTradeNo);
return false;
}
@Override