diff --git a/ebike-common/src/main/java/com/cdzy/common/utils/ConvertUtil.java b/ebike-common/src/main/java/com/cdzy/common/utils/ConvertUtil.java index 37dba411..e47ddcf9 100644 --- a/ebike-common/src/main/java/com/cdzy/common/utils/ConvertUtil.java +++ b/ebike-common/src/main/java/com/cdzy/common/utils/ConvertUtil.java @@ -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); diff --git a/ebike-feign/src/main/java/com/ebike/feign/clients/OrdersFeignClient.java b/ebike-feign/src/main/java/com/ebike/feign/clients/OrdersFeignClient.java index 57a06c2e..07bddcd9 100644 --- a/ebike-feign/src/main/java/com/ebike/feign/clients/OrdersFeignClient.java +++ b/ebike-feign/src/main/java/com/ebike/feign/clients/OrdersFeignClient.java @@ -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 diff --git a/ebike-payment/src/main/java/com/cdzy/payment/model/dto/AmountDto.java b/ebike-feign/src/main/java/com/ebike/feign/model/rsp/AmountDto.java similarity index 92% rename from ebike-payment/src/main/java/com/cdzy/payment/model/dto/AmountDto.java rename to ebike-feign/src/main/java/com/ebike/feign/model/rsp/AmountDto.java index 6a322432..7de4bb4d 100644 --- a/ebike-payment/src/main/java/com/cdzy/payment/model/dto/AmountDto.java +++ b/ebike-feign/src/main/java/com/ebike/feign/model/rsp/AmountDto.java @@ -1,4 +1,4 @@ -package com.cdzy.payment.model.dto; +package com.ebike.feign.model.rsp; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/ebike-payment/src/main/java/com/cdzy/payment/model/dto/DetailDto.java b/ebike-feign/src/main/java/com/ebike/feign/model/rsp/DetailDto.java similarity index 94% rename from ebike-payment/src/main/java/com/cdzy/payment/model/dto/DetailDto.java rename to ebike-feign/src/main/java/com/ebike/feign/model/rsp/DetailDto.java index 90172c84..24a39395 100644 --- a/ebike-payment/src/main/java/com/cdzy/payment/model/dto/DetailDto.java +++ b/ebike-feign/src/main/java/com/ebike/feign/model/rsp/DetailDto.java @@ -1,4 +1,4 @@ -package com.cdzy.payment.model.dto; +package com.ebike.feign.model.rsp; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/ebike-payment/src/main/java/com/cdzy/payment/model/dto/EbikePaymentDto.java b/ebike-feign/src/main/java/com/ebike/feign/model/rsp/EbikePaymentDto.java similarity index 95% rename from ebike-payment/src/main/java/com/cdzy/payment/model/dto/EbikePaymentDto.java rename to ebike-feign/src/main/java/com/ebike/feign/model/rsp/EbikePaymentDto.java index 180114b5..bbcfc4ac 100644 --- a/ebike-payment/src/main/java/com/cdzy/payment/model/dto/EbikePaymentDto.java +++ b/ebike-feign/src/main/java/com/ebike/feign/model/rsp/EbikePaymentDto.java @@ -1,4 +1,4 @@ -package com.cdzy.payment.model.dto; +package com.ebike.feign.model.rsp; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/ebike-payment/src/main/java/com/cdzy/payment/model/dto/PayDetailDto.java b/ebike-feign/src/main/java/com/ebike/feign/model/rsp/PayDetailDto.java similarity index 94% rename from ebike-payment/src/main/java/com/cdzy/payment/model/dto/PayDetailDto.java rename to ebike-feign/src/main/java/com/ebike/feign/model/rsp/PayDetailDto.java index 00151b91..78634cdc 100644 --- a/ebike-payment/src/main/java/com/cdzy/payment/model/dto/PayDetailDto.java +++ b/ebike-feign/src/main/java/com/ebike/feign/model/rsp/PayDetailDto.java @@ -1,4 +1,4 @@ -package com.cdzy.payment.model.dto; +package com.ebike.feign.model.rsp; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/ebike-orders/src/main/java/com/cdzy/orders/controller/EbikeUserOrdersController.java b/ebike-orders/src/main/java/com/cdzy/orders/controller/EbikeUserOrdersController.java index 2aceed62..07021bd8 100644 --- a/ebike-orders/src/main/java/com/cdzy/orders/controller/EbikeUserOrdersController.java +++ b/ebike-orders/src/main/java/com/cdzy/orders/controller/EbikeUserOrdersController.java @@ -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()); + } + } + /** * 根据用户订单表主键获取详细信。 * diff --git a/ebike-orders/src/main/java/com/cdzy/orders/service/UserOrdersService.java b/ebike-orders/src/main/java/com/cdzy/orders/service/UserOrdersService.java index caefe99c..2106bedb 100644 --- a/ebike-orders/src/main/java/com/cdzy/orders/service/UserOrdersService.java +++ b/ebike-orders/src/main/java/com/cdzy/orders/service/UserOrdersService.java @@ -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 { * @return 金额 */ BigDecimal costOrderCalculation(ReqOrderDto orderDto); + + /** + * 获取支付信息 + * + * @param orderId 订单ID + * @return 支付信息 + */ + EbikePaymentDto paymentDetailsInfo(Long orderId); } diff --git a/ebike-orders/src/main/java/com/cdzy/orders/service/impl/UserOrdersServiceImpl.java b/ebike-orders/src/main/java/com/cdzy/orders/service/impl/UserOrdersServiceImpl.java index 910dc887..311a7202 100644 --- a/ebike-orders/src/main/java/com/cdzy/orders/service/impl/UserOrdersServiceImpl.java +++ b/ebike-orders/src/main/java/com/cdzy/orders/service/impl/UserOrdersServiceImpl.java @@ -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 { + 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()); diff --git a/ebike-payment/src/main/java/com/cdzy/payment/controller/EbikeWxPaymentController.java b/ebike-payment/src/main/java/com/cdzy/payment/controller/EbikeWxPaymentController.java index 2ee7a209..da4b0b65 100644 --- a/ebike-payment/src/main/java/com/cdzy/payment/controller/EbikeWxPaymentController.java +++ b/ebike-payment/src/main/java/com/cdzy/payment/controller/EbikeWxPaymentController.java @@ -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); + } + /** * 通过退款单号查询退款信息 * diff --git a/ebike-payment/src/main/java/com/cdzy/payment/model/entity/EbikePayment.java b/ebike-payment/src/main/java/com/cdzy/payment/model/entity/EbikePayment.java index 6920f336..e2ee5841 100644 --- a/ebike-payment/src/main/java/com/cdzy/payment/model/entity/EbikePayment.java +++ b/ebike-payment/src/main/java/com/cdzy/payment/model/entity/EbikePayment.java @@ -80,7 +80,7 @@ public class EbikePayment implements Serializable { private String currency; /** - * 总金额 + * 实际支付金额 */ private Double total; diff --git a/ebike-payment/src/main/java/com/cdzy/payment/service/EbikeRefundService.java b/ebike-payment/src/main/java/com/cdzy/payment/service/EbikeRefundService.java index a793a2fd..ddb512f6 100644 --- a/ebike-payment/src/main/java/com/cdzy/payment/service/EbikeRefundService.java +++ b/ebike-payment/src/main/java/com/cdzy/payment/service/EbikeRefundService.java @@ -30,6 +30,14 @@ public interface EbikeRefundService extends IService { */ List getNoSuccessRefundOrderByDuration(int duration); + /** + * 根据订单id查询退款记录 + * + * @param orderId 订单id + * @return + */ + EbikeRefund getByOrderId(String orderId); + /** * 更新退款状态 * diff --git a/ebike-payment/src/main/java/com/cdzy/payment/service/WxPayService.java b/ebike-payment/src/main/java/com/cdzy/payment/service/WxPayService.java index 88db80f1..30ab79c2 100644 --- a/ebike-payment/src/main/java/com/cdzy/payment/service/WxPayService.java +++ b/ebike-payment/src/main/java/com/cdzy/payment/service/WxPayService.java @@ -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); /** diff --git a/ebike-payment/src/main/java/com/cdzy/payment/service/impl/EbikePaymentServiceImpl.java b/ebike-payment/src/main/java/com/cdzy/payment/service/impl/EbikePaymentServiceImpl.java index 65a3b886..fe01d420 100644 --- a/ebike-payment/src/main/java/com/cdzy/payment/service/impl/EbikePaymentServiceImpl.java +++ b/ebike-payment/src/main/java/com/cdzy/payment/service/impl/EbikePaymentServiceImpl.java @@ -34,7 +34,7 @@ public class EbikePaymentServiceImpl extends ServiceImpl 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 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())); diff --git a/ebike-payment/src/main/java/com/cdzy/payment/service/impl/EbikeRefundServiceImpl.java b/ebike-payment/src/main/java/com/cdzy/payment/service/impl/EbikeRefundServiceImpl.java index 541ac01a..6d2bda12 100644 --- a/ebike-payment/src/main/java/com/cdzy/payment/service/impl/EbikeRefundServiceImpl.java +++ b/ebike-payment/src/main/java/com/cdzy/payment/service/impl/EbikeRefundServiceImpl.java @@ -48,6 +48,16 @@ public class EbikeRefundServiceImpl extends ServiceImpl 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