mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2024-11-26 17:21:53 +08:00
支付宝退款申请通知
This commit is contained in:
parent
444ba79822
commit
dfde260ebb
@ -1,12 +1,7 @@
|
||||
package cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayRefundDO;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
|
||||
@ -17,5 +12,7 @@ import org.apache.ibatis.annotations.Mapper;
|
||||
@Mapper
|
||||
public interface PayRefundMapper extends BaseMapperX<PayRefundDO> {
|
||||
|
||||
|
||||
default PayRefundDO selectByReqNo(String reqNo) {
|
||||
return selectOne("req_no", reqNo);
|
||||
}
|
||||
}
|
||||
|
@ -47,8 +47,8 @@ public interface PayErrorCodeCoreConstants {
|
||||
ErrorCode PAY_REFUND_AMOUNT_EXCEED = new ErrorCode(1007006000, "退款金额超过订单可退款金额");
|
||||
ErrorCode PAY_REFUND_ALL_REFUNDED = new ErrorCode(1007006001, "订单已经全额退款");
|
||||
ErrorCode PAY_REFUND_CHN_ORDER_NO_IS_NULL = new ErrorCode(1007006002, "该订单的渠道订单为空");
|
||||
ErrorCode PAY_REFUND_POST_HANDLER_NOT_FOUND = new ErrorCode(1007006002, "未找到对应的退款后置处理类");
|
||||
|
||||
ErrorCode PAY_REFUND_POST_HANDLER_NOT_FOUND = new ErrorCode(1007006003, "未找到对应的退款后置处理类");
|
||||
ErrorCode PAY_REFUND_NOT_FOUND = new ErrorCode(1007006004, "支付退款单不存在");
|
||||
// TODO @aquan:下面还两个要合并上去哈。另外一般中英文之间要有空格。例如说, 新建一个 order 数据;这样可读性更好。
|
||||
|
||||
/**
|
||||
|
@ -2,6 +2,7 @@ package cn.iocoder.yudao.coreservice.modules.pay.service.order;
|
||||
|
||||
import cn.iocoder.yudao.coreservice.modules.pay.service.order.bo.PayRefundReqBO;
|
||||
import cn.iocoder.yudao.coreservice.modules.pay.service.order.bo.PayRefundRespBO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.PayNotifyDataDTO;
|
||||
|
||||
/**
|
||||
* 退款单 Core Service
|
||||
@ -17,4 +18,17 @@ public interface PayRefundCoreService {
|
||||
* @return 退款申请返回信息
|
||||
*/
|
||||
PayRefundRespBO refund(PayRefundReqBO reqDTO);
|
||||
|
||||
|
||||
/**
|
||||
* 渠道的退款通知
|
||||
* @param channelId 渠道编号
|
||||
* @param channelCode 渠道编码
|
||||
* @param notifyData 通知数据
|
||||
* @throws Exception 退款通知异常
|
||||
*/
|
||||
void notifyPayRefund(Long channelId, String channelCode, PayNotifyDataDTO notifyData) throws Exception;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -10,7 +10,11 @@ import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayRefundDO
|
||||
import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayOrderCoreMapper;
|
||||
import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayOrderExtensionCoreMapper;
|
||||
import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayRefundMapper;
|
||||
import cn.iocoder.yudao.coreservice.modules.pay.enums.notify.PayNotifyTypeEnum;
|
||||
import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayOrderNotifyStatusEnum;
|
||||
import cn.iocoder.yudao.coreservice.modules.pay.service.notify.PayNotifyCoreService;
|
||||
import cn.iocoder.yudao.coreservice.modules.pay.service.notify.dto.PayNotifyTaskCreateReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.PayRefundNotifyDTO;
|
||||
import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayRefundTypeEnum;
|
||||
import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayOrderStatusEnum;
|
||||
import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayRefundStatusEnum;
|
||||
@ -24,8 +28,10 @@ import cn.iocoder.yudao.coreservice.modules.pay.service.order.bo.PayRefundRespBO
|
||||
import cn.iocoder.yudao.coreservice.modules.pay.util.PaySeqUtils;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.PayClient;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.PayNotifyDataDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.PayRefundUnifiedReqDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.PayRefundUnifiedRespDTO;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.PayNotifyRefundStatusEnum;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.PayChannelRespEnum;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -66,6 +72,9 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService {
|
||||
@Resource
|
||||
private List<PayRefundChannelPostHandler> handlerList;
|
||||
|
||||
@Resource
|
||||
private PayNotifyCoreService payNotifyCoreService;
|
||||
|
||||
|
||||
private final EnumMap<PayChannelRespEnum, PayRefundChannelPostHandler> mapHandler = new EnumMap<>(PayChannelRespEnum.class);
|
||||
|
||||
@ -84,7 +93,6 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public PayRefundRespBO refund(PayRefundReqBO reqBO) {
|
||||
@ -173,6 +181,64 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void notifyPayRefund(Long channelId, String channelCode, PayNotifyDataDTO notifyData) {
|
||||
log.info("[notifyPayRefund][channelId({}) 回调数据({})]", channelId, notifyData.getBody());
|
||||
// 校验支付渠道是否有效
|
||||
PayChannelDO channel = payChannelCoreService.validPayChannel(channelId);
|
||||
// 校验支付客户端是否正确初始化
|
||||
PayClient client = payClientFactory.getPayClient(channel.getId());
|
||||
if (client == null) {
|
||||
log.error("[notifyPayOrder][渠道编号({}) 找不到对应的支付客户端]", channel.getId());
|
||||
throw exception(PAY_CHANNEL_CLIENT_NOT_FOUND);
|
||||
}
|
||||
//解析渠道退款通知数据, 统一处理
|
||||
PayRefundNotifyDTO refundNotify = client.parseRefundNotify(notifyData);
|
||||
|
||||
if(Objects.equals(PayNotifyRefundStatusEnum.SUCCESS,refundNotify.getStatus())){
|
||||
//退款成功。 支付宝只有退款成功才会发通知
|
||||
PayRefundDO refundDO = payRefundMapper.selectByReqNo(refundNotify.getReqNo());
|
||||
if (refundDO == null) {
|
||||
log.error("不存在 seqNo 为{} 的支付退款单",refundNotify.getReqNo());
|
||||
throw exception(PAY_REFUND_NOT_FOUND);
|
||||
}
|
||||
Long refundAmount = refundDO.getRefundAmount();
|
||||
Integer type = refundDO.getType();
|
||||
PayOrderStatusEnum orderStatus = PayOrderStatusEnum.SUCCESS;
|
||||
if(PayRefundTypeEnum.ALL.getStatus().equals(type)){
|
||||
orderStatus = PayOrderStatusEnum.CLOSED;
|
||||
}
|
||||
//更新支付订单
|
||||
PayOrderDO payOrderDO = payOrderCoreMapper.selectById(refundDO.getOrderId());
|
||||
//需更新已退金额
|
||||
Long refundedAmount = payOrderDO.getRefundAmount();
|
||||
PayOrderDO updateOrderDO = new PayOrderDO();
|
||||
updateOrderDO.setId(refundDO.getOrderId())
|
||||
.setRefundAmount(refundedAmount + refundAmount)
|
||||
.setStatus(orderStatus.getStatus())
|
||||
.setRefundStatus(type);
|
||||
payOrderCoreMapper.updateById(updateOrderDO);
|
||||
|
||||
//跟新退款订单
|
||||
PayRefundDO updateRefundDO = new PayRefundDO();
|
||||
updateRefundDO.setId(refundDO.getId())
|
||||
.setSuccessTime(refundNotify.getRefundSuccessTime())
|
||||
.setChannelRefundNo(refundNotify.getChannelOrderNo())
|
||||
.setTradeNo(refundNotify.getTradeNo())
|
||||
.setNotifyTime(new Date())
|
||||
.setStatus(PayRefundStatusEnum.SUCCESS.getStatus());
|
||||
payRefundMapper.updateById(updateRefundDO);
|
||||
|
||||
//插入退款通知记录
|
||||
// TODO 通知商户成功或者失败. 现在通知似乎没有实现, 只是回调
|
||||
payNotifyCoreService.createPayNotifyTask(PayNotifyTaskCreateReqDTO.builder()
|
||||
.type(PayNotifyTypeEnum.REFUND.getType()).dataId(refundDO.getId()).build());
|
||||
}else{
|
||||
//TODO 退款失败
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验是否进行退款
|
||||
* @param reqBO 退款申请信息
|
||||
@ -197,7 +263,5 @@ public class PayRefundCoreServiceImpl implements PayRefundCoreService {
|
||||
throw exception(PAY_REFUND_CHN_ORDER_NO_IS_NULL);
|
||||
}
|
||||
//TODO 退款的期限 退款次数的控制
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -42,4 +42,11 @@ public interface PayClient {
|
||||
*/
|
||||
PayRefundUnifiedRespDTO unifiedRefund(PayRefundUnifiedReqDTO reqDTO);
|
||||
|
||||
/**
|
||||
* 解析支付退款通知数据
|
||||
* @param notifyData 支付退款通知请求数据
|
||||
* @return 支付退款通知的Notify DTO
|
||||
*/
|
||||
PayRefundNotifyDTO parseRefundNotify(PayNotifyDataDTO notifyData);
|
||||
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* 支付订单回调,渠道的统一通知请求数据
|
||||
* 支付订单,退款订单回调,渠道的统一通知请求数据
|
||||
*/
|
||||
@Data
|
||||
@ToString
|
||||
|
@ -0,0 +1,63 @@
|
||||
package cn.iocoder.yudao.framework.pay.core.client.dto;
|
||||
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.PayNotifyRefundStatusEnum;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 从渠道返回数据中解析得到的支付退款通知的Notify DTO
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
@Data
|
||||
@ToString
|
||||
@Builder
|
||||
public class PayRefundNotifyDTO {
|
||||
|
||||
/**
|
||||
* 支付渠道编号
|
||||
*/
|
||||
private String channelOrderNo;
|
||||
|
||||
|
||||
/**
|
||||
* 交易订单号,根据规则生成
|
||||
* 调用支付渠道时,使用该字段作为对接的订单号。
|
||||
* 1. 调用微信支付 https://api.mch.weixin.qq.com/pay/unifiedorder 时,使用该字段作为 out_trade_no
|
||||
* 2. 调用支付宝 https://opendocs.alipay.com/apis 时,使用该字段作为 out_trade_no
|
||||
* 这里对应 pay_extension 里面的 no
|
||||
* 例如说,P202110132239124200055
|
||||
*/
|
||||
private String tradeNo;
|
||||
|
||||
/**
|
||||
* https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_refund_no
|
||||
* https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_request_no
|
||||
* 退款请求号。
|
||||
* 标识一次退款请求,需要保证在交易号下唯一,如需部分退款,则此参数必传。
|
||||
* 注:针对同一次退款请求,如果调用接口失败或异常了,重试时需要保证退款请求号不能变更,
|
||||
* 防止该笔交易重复退款。支付宝会保证同样的退款请求号多次请求只会退一次。
|
||||
* 退款单请求号,根据规则生成
|
||||
*
|
||||
* 例如说,RR202109181134287570000
|
||||
*/
|
||||
private String reqNo;
|
||||
|
||||
|
||||
/**
|
||||
* 退款是否成功
|
||||
*/
|
||||
private PayNotifyRefundStatusEnum status;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 退款成功时间
|
||||
*/
|
||||
private Date refundSuccessTime;
|
||||
|
||||
|
||||
}
|
@ -83,6 +83,12 @@ public class AlipayQrPayClient extends AbstractPayClient<AlipayPayClientConfig>
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public PayRefundNotifyDTO parseRefundNotify(PayNotifyDataDTO notifyData) {
|
||||
//TODO 需要实现
|
||||
throw new UnsupportedOperationException("需要实现");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
|
||||
//TODO 需要实现
|
||||
|
@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.dto.*;
|
||||
import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.PayNotifyRefundStatusEnum;
|
||||
import cn.iocoder.yudao.framework.pay.core.enums.PayChannelRespEnum;
|
||||
import com.alipay.api.AlipayApiException;
|
||||
import com.alipay.api.AlipayConfig;
|
||||
@ -103,7 +104,6 @@ public class AlipayWapPayClient extends AbstractPayClient<AlipayPayClientConfig>
|
||||
.data(data.getBody()).build();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
|
||||
AlipayTradeRefundModel model=new AlipayTradeRefundModel();
|
||||
@ -119,11 +119,10 @@ public class AlipayWapPayClient extends AbstractPayClient<AlipayPayClientConfig>
|
||||
AlipayTradeRefundResponse response = client.execute(refundRequest);
|
||||
log.info("[doUnifiedRefund][response({}) 发起退款 渠道返回", toJsonString(response));
|
||||
if (response.isSuccess()) {
|
||||
//退款成功
|
||||
//退款成功,更新为PROCESSING_NOTIFY, 而不是 SYNC_SUCCESS 通过支付宝回调接口处理。退款导致触发的异步通知,
|
||||
//退款导致触发的异步通知是发送到支付接口中设置的notify_url
|
||||
//TODO 沙箱环境 返回 的tradeNo(渠道退款单号) 和 订单的tradNo 是一个值,是不是理解不对?
|
||||
respDTO.setRespEnum(PayChannelRespEnum.SYNC_SUCCESS)
|
||||
.setChannelRefundNo(response.getTradeNo())
|
||||
.setPayTradeNo(response.getOutTradeNo());
|
||||
respDTO.setRespEnum(PayChannelRespEnum.PROCESSING_NOTIFY);
|
||||
}else{
|
||||
//特殊处理 sub_code ACQ.SYSTEM_ERROR(系统错误), 需要调用重试任务
|
||||
//沙箱环境返回的貌似是”aop.ACQ.SYSTEM_ERROR“, 用contain
|
||||
@ -153,12 +152,20 @@ public class AlipayWapPayClient extends AbstractPayClient<AlipayPayClientConfig>
|
||||
.setChannelErrMsg(e.getErrMsg())
|
||||
.setRespEnum(PayChannelRespEnum.CALL_EXCEPTION);
|
||||
}
|
||||
|
||||
return respDTO;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public PayRefundNotifyDTO parseRefundNotify(PayNotifyDataDTO notifyData) {
|
||||
Map<String, String> params = notifyData.getParams();
|
||||
PayRefundNotifyDTO notifyDTO = PayRefundNotifyDTO.builder().channelOrderNo(params.get("trade_no"))
|
||||
.tradeNo(params.get("out_trade_no"))
|
||||
.reqNo(params.get("out_biz_no"))
|
||||
.status(PayNotifyRefundStatusEnum.SUCCESS)
|
||||
.refundSuccessTime(DateUtil.parse(params.get("gmt_refund"), "yyyy-MM-dd HH:mm:ss"))
|
||||
.build();
|
||||
return notifyDTO;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -141,6 +141,12 @@ public class WXPubPayClient extends AbstractPayClient<WXPayClientConfig> {
|
||||
.data(data.getBody()).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PayRefundNotifyDTO parseRefundNotify(PayNotifyDataDTO notifyData) {
|
||||
//TODO 需要实现
|
||||
throw new UnsupportedOperationException("需要实现");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
|
||||
|
@ -0,0 +1,20 @@
|
||||
package cn.iocoder.yudao.framework.pay.core.enums;
|
||||
|
||||
/**
|
||||
* 退款通知, 统一的渠道退款状态
|
||||
*
|
||||
* @author jason
|
||||
*/
|
||||
public enum PayNotifyRefundStatusEnum {
|
||||
/**
|
||||
* 支付宝 中 全额退款 trade_status=TRADE_CLOSED, 部分退款 trade_status=TRADE_SUCCESS
|
||||
* 退款成功
|
||||
*/
|
||||
SUCCESS,
|
||||
|
||||
/**
|
||||
* 支付宝退款通知没有这个状态
|
||||
* 退款异常
|
||||
*/
|
||||
ABNORMAL;
|
||||
}
|
@ -3,6 +3,7 @@ package cn.iocoder.yudao.userserver.modules.pay.controller.order;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO;
|
||||
import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayOrderCoreService;
|
||||
import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayRefundCoreService;
|
||||
import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayOrderSubmitReqDTO;
|
||||
import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayOrderSubmitRespDTO;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
@ -34,6 +35,9 @@ public class PayOrderController {
|
||||
@Resource
|
||||
private PayOrderCoreService payOrderCoreService;
|
||||
|
||||
@Resource
|
||||
private PayRefundCoreService payRefundCoreService;
|
||||
|
||||
@PostMapping("/submit")
|
||||
@ApiOperation("提交支付订单")
|
||||
// @PreAuthenticated // TODO 暂时不加登陆验证,前端暂时没做好
|
||||
@ -85,11 +89,21 @@ public class PayOrderController {
|
||||
public String notifyAliPayWapPayOrder(@PathVariable("channelId") Long channelId,
|
||||
@RequestParam Map<String, String> params,
|
||||
@RequestBody String originData) throws Exception {
|
||||
//TODO @jason 校验 是否支付宝调用。 payclient 中加一个校验方法
|
||||
payOrderCoreService.notifyPayOrder(channelId, PayChannelEnum.ALIPAY_WAP.getCode(), PayNotifyDataDTO.builder().params(params).body(originData).build());
|
||||
//TODO 校验是否支付宝调用。 payclient 中加一个校验方法
|
||||
//支付宝退款交易也会触发支付回调接口
|
||||
//参考 https://opensupport.alipay.com/support/helpcenter/193/201602484851
|
||||
//判断是否为支付宝的退款交易
|
||||
if(isAliPayRefund(params)) {
|
||||
//退款通知
|
||||
payRefundCoreService.notifyPayRefund(channelId,PayChannelEnum.ALIPAY_WAP.getCode(), PayNotifyDataDTO.builder().params(params).body(originData).build());
|
||||
}else{
|
||||
//支付通知
|
||||
payOrderCoreService.notifyPayOrder(channelId, PayChannelEnum.ALIPAY_WAP.getCode(), PayNotifyDataDTO.builder().params(params).body(originData).build());
|
||||
}
|
||||
return "success";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* https://opendocs.alipay.com/open/203/105285#%E5%89%8D%E5%8F%B0%E5%9B%9E%E8%B7%B3%E5%8F%82%E6%95%B0%E8%AF%B4%E6%98%8E
|
||||
* @param channelId 渠道id
|
||||
@ -102,6 +116,19 @@ public class PayOrderController {
|
||||
return "支付成功";
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是支付宝的退款交易
|
||||
* @param params http content-type application/x-www-form-urlencoded 的参数
|
||||
* @return
|
||||
*/
|
||||
private boolean isAliPayRefund(Map<String, String> params) {
|
||||
if (params.containsKey("refund_fee")) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping("/notify/test")
|
||||
@ApiOperation("通知的测试接口")
|
||||
public String notifyTest() {
|
||||
|
@ -37,6 +37,7 @@ public class PayRefundController {
|
||||
reqBO.setUserIp(getClientIP());
|
||||
//TODO 测试暂时模拟生成商户退款订单
|
||||
reqBO.setMerchantRefundNo(PaySeqUtils.genMerchantRefundNo());
|
||||
//reqBO.setMerchantRefundNo("MO202111210814084370000");
|
||||
return CommonResult.success( PayRefundConvert.INSTANCE.convert(payRefundCoreService.refund(reqBO)));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user