feat(商城): 完善退款功能

master
wayn 1 year ago
parent fd6cb4b285
commit 64a35eaf71

@ -31,4 +31,5 @@ public class OrderVO {
* 1 2
*/
private Integer payType;
private String returnUrl;
}

@ -102,15 +102,6 @@ public class AdminOrderServiceImpl extends ServiceImpl<AdminOrderMapper, Order>
}
}
// 返还优惠券
// List<LitemallCouponUser> couponUsers = couponUserService.findByOid(orderId);
// for (LitemallCouponUser couponUser: couponUsers) {
// // 优惠券状态设置为可使用
// couponUser.setStatus(CouponUserConstant.STATUS_USABLE);
// couponUser.setUpdateTime(LocalDateTime.now());
// couponUserService.update(couponUser);
// }
// 退款成功通知用户, 例如“您申请的订单退款 [ 单号:{1} ] 已成功,请耐心等待到账。”
// 注意订单号只发后6位
String email = iMemberService.getById(order.getUserId()).getEmail();
@ -118,7 +109,6 @@ public class AdminOrderServiceImpl extends ServiceImpl<AdminOrderMapper, Order>
iMailService.sendEmail("订单已经退款", order.getOrderSn().substring(8, 14), email,
WaynConfig.getAdminUrl() + "/callback/email");
}
// logHelper.logOrderSucceed("退款", "订单编号 " + order.getOrderSn());
return R.success();
}
@ -169,6 +159,4 @@ public class AdminOrderServiceImpl extends ServiceImpl<AdminOrderMapper, Order>
data.put("user", member);
return R.success().add("data", data);
}
}

@ -1,8 +1,11 @@
package com.wayn.common.enums;
import lombok.Getter;
/**
*
*/
@Getter
public enum PayTypeEnum {
// 微信
WX(1),
@ -17,10 +20,6 @@ public enum PayTypeEnum {
this.code = code;
}
public int getCode() {
return code;
}
public static PayTypeEnum of(int code) {
for (PayTypeEnum value : PayTypeEnum.values()) {
if (code == value.getCode()) {

@ -43,6 +43,7 @@ public enum ReturnCodeEnum {
ORDER_USER_NOT_SAME_ERROR(5012, "订单用户和当前登陆用户不一致"),
ORDER_ERROR_CART_EMPTY_ERROR(5013, "下单失败,当前购物车无数据"),
ORDER_ERROR_ADDRESS_ERROR(50143, "下单失败,当前地址错误"),
ORDER_REFUND_ERROR(50144, "退款失败"),
// 用户错误
USER_NOT_EXISTS_ERROR(5101, "用户不存在"),

@ -61,11 +61,6 @@ public class OrderController extends BaseController {
return iOrderService.cancel(orderId);
}
@PostMapping("refund/{orderId}")
public R refund(@PathVariable Long orderId) throws UnsupportedEncodingException {
return iOrderService.refund(orderId);
}
@PostMapping("confirm/{orderId}")
public R confirm(@PathVariable Long orderId) {
return iOrderService.confirm(orderId);

@ -0,0 +1,28 @@
package com.wayn.mobile.api.controller;
import com.alipay.api.AlipayApiException;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.wayn.common.base.controller.BaseController;
import com.wayn.common.core.domain.vo.OrderVO;
import com.wayn.common.util.R;
import com.wayn.mobile.api.service.IPayService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.io.UnsupportedEncodingException;
@Slf4j
@RestController
@AllArgsConstructor
@RequestMapping("order/refund")
public class OrderRefundController extends BaseController {
private IPayService payService;
@PostMapping("{orderId}")
public R refund(@PathVariable Long orderId) throws UnsupportedEncodingException, AlipayApiException, WxPayException {
return payService.refund(orderId);
}
}

@ -4,7 +4,6 @@ package com.wayn.mobile.api.controller;
import com.wayn.common.base.controller.BaseController;
import com.wayn.common.core.domain.vo.OrderVO;
import com.wayn.common.util.R;
import com.wayn.mobile.api.service.IOrderService;
import com.wayn.mobile.api.service.IPayService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -42,7 +41,7 @@ public class PayController extends BaseController {
*/
@PostMapping("h5pay")
public R h5pay(@RequestBody OrderVO orderVO) throws UnsupportedEncodingException {
return payService.h5pay(orderVO.getOrderSn(), orderVO.getPayType(), request);
return payService.h5pay(orderVO);
}
}

@ -67,18 +67,6 @@ public interface IOrderService extends IService<Order> {
*/
R cancel(Long orderId);
/**
* 退
* <p>
* 1. 退
* 2. 退
*
* @param orderId ID
* @return r
*/
R refund(Long orderId) throws UnsupportedEncodingException;
/**
*
* <p>

@ -1,12 +1,9 @@
package com.wayn.mobile.api.service;
import com.alipay.api.AlipayApiException;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.wayn.common.core.domain.shop.Order;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.wayn.common.core.domain.vo.OrderVO;
import com.wayn.common.util.R;
import com.wayn.message.core.dto.OrderDTO;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@ -20,15 +17,6 @@ import java.io.UnsupportedEncodingException;
*/
public interface IPayService {
/**
* H5
*
* @param orderSn
* @param request
* @return r
*/
R h5pay(String orderSn, Integer payType, HttpServletRequest request) throws UnsupportedEncodingException;
/**
*
* <p>
@ -42,6 +30,25 @@ public interface IPayService {
*/
R prepay(String orderSn, Integer payType, HttpServletRequest request);
/**
* H5
*
* @param orderVO VO
* @return r
*/
R h5pay(OrderVO orderVO) throws UnsupportedEncodingException;
/**
* 退
* <p>
* 1. 退
* 2. 退
*
* @param orderId ID
* @return r
*/
R refund(Long orderId) throws UnsupportedEncodingException, AlipayApiException, WxPayException;
String wxPayNotify(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException;
String aliPayNotify(HttpServletRequest request, HttpServletResponse response) throws AlipayApiException, UnsupportedEncodingException;

@ -417,35 +417,6 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
return R.success();
}
@Override
public R refund(Long orderId) throws UnsupportedEncodingException {
Order order = getById(orderId);
ReturnCodeEnum returnCodeEnum = checkOrderOperator(order);
if (!ReturnCodeEnum.SUCCESS.equals(returnCodeEnum)) {
return R.error(returnCodeEnum);
}
OrderHandleOption handleOption = OrderUtil.build(order);
if (!handleOption.isRefund()) {
return R.error(ReturnCodeEnum.ORDER_CANNOT_REFUND_ERROR);
}
// 设置订单申请退款状态
order.setOrderStatus(OrderUtil.STATUS_REFUND);
order.setUpdateTime(new Date());
updateById(order);
// 有用户申请退款,邮件通知运营人员
String email = iMemberService.getById(order.getUserId()).getEmail();
if (StringUtils.isNotEmpty(email)) {
if (StringUtils.isNotBlank(email)) {
iMailService.sendEmail("订单正在退款", order.toString(), email, WaynConfig.getMobileUrl() + "/callback/email");
}
}
return R.success();
}
@Override
@Transactional(rollbackFor = Exception.class)
public R delete(Long orderId) {

@ -6,28 +6,37 @@ import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.request.AlipayTradeWapPayRequest;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
import com.github.binarywang.wxpay.bean.request.WxPayRefundV3Request;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.BaseWxPayResult;
import com.github.binarywang.wxpay.bean.result.WxPayRefundV3Result;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.wayn.common.config.AlipayConfig;
import com.wayn.common.config.WaynConfig;
import com.wayn.common.core.domain.shop.*;
import com.wayn.common.core.service.shop.*;
import com.wayn.common.core.domain.shop.Member;
import com.wayn.common.core.domain.shop.Order;
import com.wayn.common.core.domain.vo.OrderVO;
import com.wayn.common.core.service.shop.IMailService;
import com.wayn.common.core.service.shop.IMemberService;
import com.wayn.common.core.util.OrderHandleOption;
import com.wayn.common.core.util.OrderUtil;
import com.wayn.common.enums.PayTypeEnum;
import com.wayn.common.enums.ReturnCodeEnum;
import com.wayn.common.util.R;
import com.wayn.common.util.ServletUtils;
import com.wayn.common.util.ip.IpUtils;
import com.wayn.mobile.api.service.IOrderService;
import com.wayn.mobile.api.service.IPayService;
import com.wayn.mobile.api.util.OrderSnGenUtil;
import com.wayn.mobile.framework.security.util.MobileSecurityUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@ -38,11 +47,13 @@ import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
*
@ -60,6 +71,7 @@ public class PayServiceImpl implements IPayService {
private AlipayConfig alipayConfig;
private IOrderService orderService;
private WxPayService wxPayService;
private OrderSnGenUtil orderSnGenUtil;
@Override
@Transactional(rollbackFor = Exception.class)
@ -88,7 +100,7 @@ public class PayServiceImpl implements IPayService {
orderRequest.setOutTradeNo(order.getOrderSn());
orderRequest.setOpenid(openid);
orderRequest.setBody("商城订单:" + order.getOrderSn());
String url = WaynConfig.getMobileUrl() + request.getContextPath();
String url = WaynConfig.getMobileUrl();
orderRequest.setNotifyUrl(url + "/pay/callback/weixinPayNotify");
// 元转成分
int fee;
@ -107,7 +119,9 @@ public class PayServiceImpl implements IPayService {
@Override
@Transactional(rollbackFor = Exception.class)
public R h5pay(String orderSn, Integer payType, HttpServletRequest request) throws UnsupportedEncodingException {
public R h5pay(OrderVO orderVO) throws UnsupportedEncodingException {
String orderSn = orderVO.getOrderSn();
Integer payType = orderVO.getPayType();
// 获取订单详情
Order order = orderService.getOne(new QueryWrapper<Order>().eq("order_sn", orderSn));
Long userId = order.getUserId();
@ -132,7 +146,7 @@ public class PayServiceImpl implements IPayService {
WxPayMpOrderResult result;
try {
WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
String url = WaynConfig.getMobileUrl() + request.getContextPath();
String url = WaynConfig.getMobileUrl();
orderRequest.setOutTradeNo(order.getOrderSn());
orderRequest.setTradeType(WxPayConstants.TradeType.MWEB);
orderRequest.setNotifyUrl(url + "/pay/callback/weixinPayNotify");
@ -142,7 +156,7 @@ public class PayServiceImpl implements IPayService {
BigDecimal actualPrice = order.getActualPrice();
fee = actualPrice.multiply(new BigDecimal(100)).intValue();
orderRequest.setTotalFee(fee);
orderRequest.setSpbillCreateIp(IpUtils.getIpAddr(request));
orderRequest.setSpbillCreateIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
result = wxPayService.createOrder(orderRequest);
return R.success().add("result", result);
@ -159,8 +173,10 @@ public class PayServiceImpl implements IPayService {
// 创建API对应的request使用手机网站支付request
AlipayTradeWapPayRequest alipayRequest = new AlipayTradeWapPayRequest();
// 在公共参数中设置回跳和通知地址
String url = WaynConfig.getMobileUrl() + request.getContextPath();
alipayRequest.setReturnUrl(url + "/returnOrders/" + orderSn + "/" + userId);
String url = WaynConfig.getMobileUrl();
if (StringUtils.isNotBlank(orderVO.getReturnUrl())) {
alipayRequest.setReturnUrl(orderVO.getReturnUrl());
}
alipayRequest.setNotifyUrl(url + "/pay/callback/aliPayNotify");
// 填充业务参数
@ -190,6 +206,7 @@ public class PayServiceImpl implements IPayService {
// 请求
String form;
try {
log.info("alipayRequest:{}", JSON.toJSONString(alipayRequest));
// 需要自行申请支付宝的沙箱账号、申请appID并在配置文件中依次配置AppID、密钥、公钥否则这里会报错。
form = alipayClient.pageExecute(alipayRequest).getBody();// 调用SDK生成表单
return R.success().add("form", form);
@ -220,6 +237,81 @@ public class PayServiceImpl implements IPayService {
}
}
@Override
public R refund(Long orderId) throws UnsupportedEncodingException, AlipayApiException, WxPayException {
Order order = orderService.getById(orderId);
ReturnCodeEnum returnCodeEnum = orderService.checkOrderOperator(order);
if (!ReturnCodeEnum.SUCCESS.equals(returnCodeEnum)) {
return R.error(returnCodeEnum);
}
OrderHandleOption handleOption = OrderUtil.build(order);
if (!handleOption.isRefund()) {
return R.error(ReturnCodeEnum.ORDER_CANNOT_REFUND_ERROR);
}
// 设置订单申请退款状态
order.setOrderStatus(OrderUtil.STATUS_REFUND);
order.setUpdateTime(new Date());
if (!orderService.updateById(order)) {
return R.error(ReturnCodeEnum.ERROR);
}
Integer payType = order.getPayType();
switch (Objects.requireNonNull(PayTypeEnum.of(payType))) {
case WX -> {
WxPayRefundV3Request refundV3Request = new WxPayRefundV3Request();
String refundSn = orderSnGenUtil.generateRefundOrderSn();
refundV3Request.setTransactionId(order.getPayId());
refundV3Request.setOutRefundNo(refundSn);
refundV3Request.setReason("商城退款refund{}" + refundSn);
WxPayRefundV3Request.Amount amount = new WxPayRefundV3Request.Amount();
amount.setRefund(order.getActualPrice().multiply(new BigDecimal("100")).intValue());
amount.setCurrency("CNY");
amount.setTotal(order.getActualPrice().multiply(new BigDecimal("100")).intValue());
refundV3Request.setAmount(amount);
WxPayRefundV3Result refundV3Result = wxPayService.refundV3(refundV3Request);
log.info("response:{}", JSON.toJSONString(refundV3Result));
String status = refundV3Result.getStatus();
if (!"SUCCESS".equals(status)) {
return R.error(ReturnCodeEnum.ORDER_REFUND_ERROR);
}
break;
}
case ALI -> {
AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig.getGateway(), alipayConfig.getAppId(),
alipayConfig.getRsaPrivateKey(), alipayConfig.getFormat(), alipayConfig.getCharset(), alipayConfig.getAlipayPublicKey(),
alipayConfig.getSigntype());
AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
JSONObject bizContent = new JSONObject();
String refundSn = orderSnGenUtil.generateRefundOrderSn();
bizContent.put("trade_no", order.getPayId());
bizContent.put("refund_amount", order.getActualPrice());
bizContent.put("out_request_no", refundSn);
bizContent.put("refund_reason", "商城退款refund{}" + refundSn);
request.setBizContent(bizContent.toString());
AlipayTradeRefundResponse response = alipayClient.execute(request);
log.info("response:{}", JSON.toJSONString(response));
if (!response.isSuccess()) {
return R.error(ReturnCodeEnum.ORDER_REFUND_ERROR);
}
break;
}
default -> {
}
}
// 有用户申请退款,邮件通知运营人员
String email = iMemberService.getById(order.getUserId()).getEmail();
if (StringUtils.isNotEmpty(email)) {
if (StringUtils.isNotBlank(email)) {
iMailService.sendEmail("订单正在退款", order.toString(), email, WaynConfig.getMobileUrl() + "/callback/email");
}
}
return R.success();
}
@Override
public String wxPayNotify(HttpServletRequest request, HttpServletResponse response) {
try {

@ -21,7 +21,7 @@ public class OrderSnGenUtil {
private RedisCache redisCache;
/**
* + ID +
* + id
*
* @return
*/
@ -34,4 +34,19 @@ public class OrderSnGenUtil {
}
return now + String.format("%06d", incrKey);
}
/**
* 退 + id
*
* @return
*/
public String generateRefundOrderSn() {
long now = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
Integer orderSnIncrLimit = Constants.ORDER_SN_INCR_LIMIT;
Long incrKey = redisCache.luaIncrKey(CacheConstants.ORDER_SN_INCR_KEY, orderSnIncrLimit);
if (incrKey > (long) orderSnIncrLimit) {
throw new BusinessException("订单编号生成失败");
}
return "RE" + now + String.format("%06d", incrKey);
}
}

@ -61,8 +61,8 @@ wayn:
version: 1.3.0
email: 166738430@qq.com
uploadDir: ${UPLOAD_DIR:D:/waynshop/webp}
adminUrl: http://localhost:81
mobileUrl: http://localhost:82
adminUrl: ${ADMIN_URL:http://localhost:81}
mobileUrl: ${MOBILE_URL:http://localhost:82}
freightLimit: "88" # 运费限额
freightPrice: "10" # 运费
unpaidOrderCancelDelayTime: 30 # 未支付订单超时时间
@ -70,7 +70,7 @@ wayn:
# wx支付配置
shop:
# 开发者应该设置成自己的wx相关信息
wx:
wxpay:
app-id:
app-secret:
mch-id:

Loading…
Cancel
Save