zoujiajian 1 dienu atpakaļ
vecāks
revīzija
0005f1c394

+ 4 - 0
netflix-dao/src/main/java/com/cyksj/mapper/special/SpecialMemberEntitlementMapper.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.cyksj.model.entity.SpecialMemberEntitlement;
 import org.apache.ibatis.annotations.Insert;
 import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Update;
 
 public interface SpecialMemberEntitlementMapper extends BaseMapper<SpecialMemberEntitlement> {
 
@@ -15,4 +16,7 @@ public interface SpecialMemberEntitlementMapper extends BaseMapper<SpecialMember
 	int extend(@Param("userId") Long userId,
 			   @Param("durationDays") Integer durationDays,
 			   @Param("orderId") Long orderId);
+
+	@Update("update special_member_entitlement set expire_time = date_sub(expire_time, interval #{durationDays} day), last_order_id = 0, update_time = now() where user_id = #{userId} and last_order_id = #{orderId}")
+	int rollbackIfLastOrder(@Param("userId") Long userId, @Param("durationDays") Integer durationDays, @Param("orderId") Long orderId);
 }

+ 3 - 0
netflix-dao/src/main/java/com/cyksj/mapper/special/SpecialPayOrderMapper.java

@@ -29,4 +29,7 @@ public interface SpecialPayOrderMapper extends BaseMapper<SpecialPayOrder> {
 	@Update("update special_pay_order set fulfill_status = 'SUCCESS', fulfill_time = now(), " +
 			"update_time = now() where id = #{orderId} and status = 'PAID' and fulfill_status = 'INIT'")
 	int markFulfilled(@Param("orderId") Long orderId);
+
+	@Update("update special_pay_order set status = 'REFUNDED', update_time = now() where id = #{orderId} and status = 'PAID'")
+	int markRefunded(@Param("orderId") Long orderId);
 }

+ 2 - 0
netflix-service/src/main/java/com/cyksj/service/special/SpecialPayService.java

@@ -19,5 +19,7 @@ public interface SpecialPayService {
 
 	SpecialMemberEntitlement getMemberEntitlement(Long userId);
 
+	void refundOrder(Long userId, String orderNo) throws Exception;
+
 	void handleAlipayNotify(Map<String, String> params) throws Exception;
 }

+ 35 - 3
netflix-service/src/main/java/com/cyksj/service/special/impl/SpecialPayServiceImpl.java

@@ -3,6 +3,10 @@ package com.cyksj.service.special.impl;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.StrUtil;
 import com.alipay.api.internal.util.AlipaySignature;
+import com.alipay.api.AlipayClient;
+import com.alipay.api.domain.AlipayTradeRefundModel;
+import com.alipay.api.request.AlipayTradeRefundRequest;
+import com.alipay.api.response.AlipayTradeRefundResponse;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.cyksj.common.EnvCommonService;
 import com.cyksj.common.exception.BusinessRuntimeException;
@@ -10,6 +14,7 @@ import com.cyksj.common.snowflake.Sequence;
 import com.cyksj.common.util.Codec;
 import com.cyksj.common.util.Jsons;
 import com.cyksj.config.zfb.BaseZfbConfig;
+import com.cyksj.config.zfb.AliPayClientFactory;
 import com.cyksj.config.zfb.ZfbProductCode;
 import com.cyksj.mapper.channel.ShopConfigMapper;
 import com.cyksj.mapper.special.SpecialMemberEntitlementMapper;
@@ -26,7 +31,6 @@ import com.cyksj.model.response.AliPayTradeStatus;
 import com.cyksj.model.response.SpecialPayCreateRep;
 import com.cyksj.service.pay.AliPayCommonService;
 import com.cyksj.service.special.SpecialPayService;
-import com.cyksj.service.sys.SysConfigService;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
@@ -46,15 +50,39 @@ public class SpecialPayServiceImpl implements SpecialPayService {
 
 	private static final Sequence SEQUENCE = new Sequence(0);
 	private static final int ORDER_EXPIRE_MINUTES = 5;
+	private static final String YING_WANG_FEI_ALIPAY_APP_ID = "2021006115648946";
 
 	private final SpecialPayProductMapper productMapper;
 	private final SpecialPayOrderMapper orderMapper;
 	private final SpecialMemberEntitlementMapper entitlementMapper;
 	private final ShopConfigMapper shopConfigMapper;
-	private final SysConfigService sysConfigService;
 	private final AliPayCommonService aliPayCommonService;
 	private final EnvCommonService envCommonService;
 
+	@Override
+	@Transactional(rollbackFor = Throwable.class)
+	public void refundOrder(Long userId, String orderNo) throws Exception {
+		SpecialPayOrder order = orderMapper.selectOne(Wrappers.lambdaQuery(SpecialPayOrder.class)
+				.eq(SpecialPayOrder::getOrderNo, orderNo).eq(SpecialPayOrder::getUserId, userId).last("limit 1 for update"));
+		if (order == null) throw BusinessRuntimeException.getInstance("订单不存在");
+		if (order.getStatus() == SpecialPayOrder.Status.REFUNDED) return;
+		if (order.getStatus() != SpecialPayOrder.Status.PAID || StrUtil.isBlank(order.getAlipayTradeNo())) throw BusinessRuntimeException.getInstance("当前订单状态不支持退款");
+		SpecialMemberEntitlement entitlement = entitlementMapper.selectOne(Wrappers.lambdaQuery(SpecialMemberEntitlement.class)
+				.eq(SpecialMemberEntitlement::getUserId, userId).last("limit 1 for update"));
+		if (entitlement != null && !order.getId().equals(entitlement.getLastOrderId())) throw BusinessRuntimeException.getInstance("存在后续会员订单,暂不支持自动退款");
+		AlipayTradeRefundModel model = new AlipayTradeRefundModel();
+		model.setTradeNo(order.getAlipayTradeNo());
+		model.setRefundAmount(order.getAmount().divide(BigDecimal.valueOf(100)).setScale(2, RoundingMode.UNNECESSARY).toPlainString());
+		model.setRefundReason("特殊会员订单退款");
+		model.setOutRequestNo("SR" + order.getOrderNo());
+		AlipayTradeRefundRequest refundRequest = new AlipayTradeRefundRequest();
+		refundRequest.setBizModel(model);
+		AlipayTradeRefundResponse response = AliPayClientFactory.getAlipayClient(order.getShopId()).execute(refundRequest);
+		if (response == null || !response.isSuccess()) throw BusinessRuntimeException.getInstance("支付宝退款失败");
+		if (orderMapper.markRefunded(order.getId()) != 1) throw BusinessRuntimeException.getInstance("退款状态更新失败");
+		if (entitlement != null) entitlementMapper.rollbackIfLastOrder(userId, order.getDurationDays(), order.getId());
+	}
+
 	@Override
 	public List<SpecialPayProduct> getEnabledProducts() {
 		return productMapper.selectList(Wrappers.lambdaQuery(SpecialPayProduct.class)
@@ -77,6 +105,10 @@ public class SpecialPayServiceImpl implements SpecialPayService {
 			throw BusinessRuntimeException.getInstance("支付套餐配置异常");
 		}
 		String alipayProductCode = getAlipayProductCode(request.getPayType());
+		ShopConfig shopConfig = shopConfigMapper.getByAppId(YING_WANG_FEI_ALIPAY_APP_ID);
+		if (shopConfig == null || !StrUtil.equals("zfb", shopConfig.getType())) {
+			throw BusinessRuntimeException.getInstance("未配置影网飞支付宝商户");
+		}
 		Date now = new Date();
 		SpecialPayOrder order = new SpecialPayOrder();
 		order.setOrderNo("SP" + SEQUENCE.nextId());
@@ -87,7 +119,7 @@ public class SpecialPayServiceImpl implements SpecialPayService {
 		order.setDurationDays(product.getDurationDays());
 		order.setStatus(SpecialPayOrder.Status.CREATED);
 		order.setFulfillStatus(SpecialPayOrder.FulfillStatus.INIT);
-		order.setShopId(sysConfigService.getDefaultShopConfigByType("zfb"));
+		order.setShopId(YING_WANG_FEI_ALIPAY_APP_ID);
 		order.setExpireTime(DateUtil.offsetMinute(now, ORDER_EXPIRE_MINUTES));
 		order.setCreatedTime(now);
 		order.setUpdateTime(now);

+ 6 - 0
netflix-web/src/main/java/com/cyksj/web/controller/special/SpecialPayController.java

@@ -51,6 +51,12 @@ public class SpecialPayController {
 		return GatewayResponse.SUCCESS.newBuilder().toResult(specialPayService.getMemberEntitlement(userId));
 	}
 
+	@PostMapping("/orders/{orderNo}/refund")
+	public Result refundOrder(@PathVariable String orderNo) throws Exception {
+		specialPayService.refundOrder(StpUserUtil.getLoginIdAsLong(), orderNo);
+		return GatewayResponse.SUCCESS.newBuilder().toResult();
+	}
+
 	@PostMapping("/alipay/notify")
 	public String alipayNotify(HttpServletRequest request) {
 		Map<String, String> params = convertRequestParamsToMap(request);