优化结构
This commit is contained in:
@@ -7,7 +7,8 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"kra/internal/biz/system"
|
||||
integrationbiz "kra/internal/biz/integration"
|
||||
bizpayment "kra/internal/biz/payment"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -21,13 +22,13 @@ import (
|
||||
|
||||
type paymentRepo struct{ data Provider }
|
||||
|
||||
func NewPaymentRepo(data Provider) system.PaymentRepo { return &paymentRepo{data: data} }
|
||||
func NewPaymentRepo(data Provider) bizpayment.PaymentRepo { return &paymentRepo{data: data} }
|
||||
|
||||
func ensurePaymentIntegrationConfigs(db *gorm.DB) error {
|
||||
for _, provider := range system.SupportedPaymentProviders {
|
||||
for _, provider := range bizpayment.SupportedPaymentProviders {
|
||||
var row integrationConfigPO
|
||||
err := db.Where("kind = ? AND provider = ?", integrationKindPayment, provider).First(&row).Error
|
||||
defaults := system.DefaultIntegrationConfig(integrationKindPayment, provider)
|
||||
defaults := integrationbiz.DefaultIntegrationConfig(integrationKindPayment, provider)
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
encoded, _ := json.Marshal(defaults)
|
||||
if err := db.Create(&integrationConfigPO{Kind: integrationKindPayment, Provider: provider, Enabled: false, Config: string(encoded)}).Error; err != nil {
|
||||
@@ -61,7 +62,7 @@ func (r *paymentRepo) row(ctx context.Context, provider string) (*integrationCon
|
||||
var row integrationConfigPO
|
||||
if err := r.data.DB().WithContext(ctx).Where("kind = ? AND provider = ?", integrationKindPayment, provider).First(&row).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, nil, system.ErrPaymentProviderNotFound
|
||||
return nil, nil, bizpayment.ErrPaymentProviderNotFound
|
||||
}
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -84,11 +85,11 @@ func (r *paymentRepo) adapter(ctx context.Context, provider string) (datapayment
|
||||
return adapter, values, err
|
||||
}
|
||||
|
||||
func (r *paymentRepo) TestProvider(ctx context.Context, provider string) (*system.PaymentTestResult, error) {
|
||||
func (r *paymentRepo) TestProvider(ctx context.Context, provider string) (*bizpayment.PaymentTestResult, error) {
|
||||
started := time.Now()
|
||||
test := &system.PaymentTestResult{Provider: provider, TradeNo: "", Passed: false, Stages: []system.PaymentTestStage{}}
|
||||
test := &bizpayment.PaymentTestResult{Provider: provider, TradeNo: "", Passed: false, Stages: []bizpayment.PaymentTestStage{}}
|
||||
add := func(name, status, message, tradeNo string, since time.Time) {
|
||||
test.Stages = append(test.Stages, system.PaymentTestStage{Name: name, Status: status, Message: message, TradeNo: tradeNo, Duration: time.Since(since).Milliseconds()})
|
||||
test.Stages = append(test.Stages, bizpayment.PaymentTestStage{Name: name, Status: status, Message: message, TradeNo: tradeNo, Duration: time.Since(since).Milliseconds()})
|
||||
}
|
||||
values, err := r.testRow(ctx, provider)
|
||||
if err != nil {
|
||||
@@ -97,7 +98,7 @@ func (r *paymentRepo) TestProvider(ctx context.Context, provider string) (*syste
|
||||
}
|
||||
test.Mode = strings.ToLower(strings.TrimSpace(text(values, "environment")))
|
||||
configStart := time.Now()
|
||||
if err = system.ValidateIntegrationConfig(system.IntegrationKindPayment, provider, values); err != nil {
|
||||
if err = integrationbiz.ValidateIntegrationConfig(integrationbiz.IntegrationKindPayment, provider, values); err != nil {
|
||||
add("config", "failed", err.Error(), "", configStart)
|
||||
return test, err
|
||||
}
|
||||
@@ -117,11 +118,11 @@ func (r *paymentRepo) TestProvider(ctx context.Context, provider string) (*syste
|
||||
orders := &paymentOrderRepo{data: r.data}
|
||||
extra, _ := json.Marshal(req.Extra)
|
||||
localStart := time.Now()
|
||||
order, _, err := orders.CreatePaymentOrder(ctx, &system.PaymentOrder{
|
||||
order, _, err := orders.CreatePaymentOrder(ctx, &bizpayment.PaymentOrder{
|
||||
TradeNo: req.TradeNo, Provider: provider, BusinessType: req.BusinessType, BusinessID: req.BusinessID,
|
||||
Subject: req.Subject, PaymentMode: system.PaymentModeExternal, OriginalAmount: req.Amount, Amount: req.Amount,
|
||||
Currency: req.Currency, PaymentStatus: system.PaymentStatusInitialized, FulfillmentStatus: system.FulfillmentStatusPending,
|
||||
RefundStatus: system.RefundStatusNone, ConfirmationID: uuid.NewString(), RequestFingerprint: paymentTestFingerprint(req), Extra: extra,
|
||||
Subject: req.Subject, PaymentMode: bizpayment.PaymentModeExternal, OriginalAmount: req.Amount, Amount: req.Amount,
|
||||
Currency: req.Currency, PaymentStatus: bizpayment.PaymentStatusInitialized, FulfillmentStatus: bizpayment.FulfillmentStatusPending,
|
||||
RefundStatus: bizpayment.RefundStatusNone, ConfirmationID: uuid.NewString(), RequestFingerprint: paymentTestFingerprint(req), Extra: extra,
|
||||
})
|
||||
if err != nil {
|
||||
add("local_order", "failed", err.Error(), req.TradeNo, localStart)
|
||||
@@ -159,7 +160,7 @@ func (r *paymentRepo) TestProvider(ctx context.Context, provider string) (*syste
|
||||
if queryID == "" {
|
||||
queryID = req.TradeNo
|
||||
}
|
||||
if provider == system.PaymentApple {
|
||||
if provider == bizpayment.PaymentApple {
|
||||
queryID = strings.TrimSpace(text(values, "test_transaction_id"))
|
||||
if queryID == "" {
|
||||
err = errors.New("Apple 连通性测试需要配置 test_transaction_id(沙箱交易 ID)")
|
||||
@@ -179,7 +180,7 @@ func (r *paymentRepo) TestProvider(ctx context.Context, provider string) (*syste
|
||||
add("query", "failed", err.Error(), req.TradeNo, queryStart)
|
||||
return test, err
|
||||
}
|
||||
if provider != system.PaymentApple {
|
||||
if provider != bizpayment.PaymentApple {
|
||||
if order, err = orders.ApplyPaymentResult(ctx, provider, req.TradeNo, paymentTestProviderUpdate(queried)); err != nil {
|
||||
add("local_order", "failed", "回写测试查单结果失败: "+err.Error(), req.TradeNo, queryStart)
|
||||
return test, err
|
||||
@@ -193,9 +194,9 @@ func (r *paymentRepo) TestProvider(ctx context.Context, provider string) (*syste
|
||||
test.Result = queried
|
||||
add("query", "passed", "测试订单查询成功,状态: "+queried.Status, req.TradeNo, queryStart)
|
||||
|
||||
if queried.Status != "success" || provider == system.PaymentApple {
|
||||
if queried.Status != "success" || provider == bizpayment.PaymentApple {
|
||||
message := "订单尚未支付成功,已完成配置、下单和查单连通性测试;请在沙箱完成付款后重试"
|
||||
if provider == system.PaymentApple {
|
||||
if provider == bizpayment.PaymentApple {
|
||||
message = "Apple 退款由 App Store 管理,已完成配置、下单和交易查询测试"
|
||||
}
|
||||
add("refund", "skipped", message, req.TradeNo, time.Now())
|
||||
@@ -208,7 +209,7 @@ func (r *paymentRepo) TestProvider(ctx context.Context, provider string) (*syste
|
||||
add("refund", "failed", beginErr.Error(), req.TradeNo, refundStart)
|
||||
return test, beginErr
|
||||
}
|
||||
refund, refundErr := adapter.Refund(ctx, &system.PaymentRefundRequest{Provider: provider, TradeNo: req.TradeNo, ProviderTradeNo: order.ProviderTradeNo, QueryID: order.QueryID, RefundNo: order.RefundNo, Amount: req.Amount, TotalAmount: req.Amount, Currency: req.Currency}, values)
|
||||
refund, refundErr := adapter.Refund(ctx, &bizpayment.PaymentRefundRequest{Provider: provider, TradeNo: req.TradeNo, ProviderTradeNo: order.ProviderTradeNo, QueryID: order.QueryID, RefundNo: order.RefundNo, Amount: req.Amount, TotalAmount: req.Amount, Currency: req.Currency}, values)
|
||||
if refundErr != nil {
|
||||
recordPaymentTestError(ctx, r.data, provider, req.TradeNo, refundErr)
|
||||
add("refund", "failed", refundErr.Error(), req.TradeNo, refundStart)
|
||||
@@ -230,17 +231,17 @@ func (r *paymentRepo) TestProvider(ctx context.Context, provider string) (*syste
|
||||
return test, nil
|
||||
}
|
||||
|
||||
func paymentTestFingerprint(req *system.PaymentRequest) string {
|
||||
func paymentTestFingerprint(req *bizpayment.PaymentRequest) string {
|
||||
raw, _ := json.Marshal(req)
|
||||
hash := sha256.Sum256(raw)
|
||||
return hex.EncodeToString(hash[:])
|
||||
}
|
||||
|
||||
func paymentTestProviderUpdate(result *system.PaymentResult) *system.PaymentProviderUpdate {
|
||||
func paymentTestProviderUpdate(result *bizpayment.PaymentResult) *bizpayment.PaymentProviderUpdate {
|
||||
if result == nil {
|
||||
return nil
|
||||
}
|
||||
return &system.PaymentProviderUpdate{
|
||||
return &bizpayment.PaymentProviderUpdate{
|
||||
Status: result.Status, ProviderStatus: result.Status, ProviderTradeNo: result.ProviderTradeNo, QueryID: result.QueryID,
|
||||
Amount: result.Amount, PayerPaidAmount: result.PayerPaidAmount, CashPaidAmount: result.CashPaidAmount,
|
||||
PointPaidAmount: result.PointPaidAmount, DiscountAmount: result.DiscountAmount,
|
||||
@@ -250,21 +251,21 @@ func paymentTestProviderUpdate(result *system.PaymentResult) *system.PaymentProv
|
||||
}
|
||||
}
|
||||
|
||||
func validatePaymentTestResult(provider, tradeNo string, result *system.PaymentResult) error {
|
||||
func validatePaymentTestResult(provider, tradeNo string, result *bizpayment.PaymentResult) error {
|
||||
if result == nil {
|
||||
return errors.New("支付渠道响应为空")
|
||||
}
|
||||
if strings.TrimSpace(result.Provider) != provider {
|
||||
return errors.New("支付渠道响应的 provider 不匹配")
|
||||
}
|
||||
if value := strings.TrimSpace(result.TradeNo); provider != system.PaymentApple && value != "" && value != tradeNo {
|
||||
if value := strings.TrimSpace(result.TradeNo); provider != bizpayment.PaymentApple && value != "" && value != tradeNo {
|
||||
return errors.New("支付渠道响应的商户订单号不匹配")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func queryPaymentTest(ctx context.Context, adapter datapayment.Adapter, queryID string, values map[string]any) (*system.PaymentResult, error) {
|
||||
var result *system.PaymentResult
|
||||
func queryPaymentTest(ctx context.Context, adapter datapayment.Adapter, queryID string, values map[string]any) (*bizpayment.PaymentResult, error) {
|
||||
var result *bizpayment.PaymentResult
|
||||
var err error
|
||||
for attempt := 0; attempt < 3; attempt++ {
|
||||
result, err = adapter.Query(ctx, queryID, values)
|
||||
@@ -300,7 +301,7 @@ func (r *paymentRepo) testRow(ctx context.Context, provider string) (map[string]
|
||||
var row integrationConfigPO
|
||||
if err := r.data.DB().WithContext(ctx).Where("kind = ? AND provider = ?", integrationKindPayment, provider).First(&row).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, system.ErrPaymentProviderNotFound
|
||||
return nil, bizpayment.ErrPaymentProviderNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
@@ -311,17 +312,17 @@ func (r *paymentRepo) testRow(ctx context.Context, provider string) (map[string]
|
||||
return values, nil
|
||||
}
|
||||
|
||||
func paymentTestRequest(provider string, values map[string]any) *system.PaymentRequest {
|
||||
func paymentTestRequest(provider string, values map[string]any) *bizpayment.PaymentRequest {
|
||||
tradeNo := "kra-test-" + time.Now().UTC().Format("20060102150405.000000000")
|
||||
amount := configuredInt64(values, "test_amount", 1)
|
||||
if amount <= 0 {
|
||||
amount = 1
|
||||
}
|
||||
req := &system.PaymentRequest{Provider: provider, TradeNo: strings.ReplaceAll(tradeNo, ".", ""), Subject: "Kra 支付渠道连通性测试", Amount: amount, Currency: strings.ToUpper(firstAny(values, "test_currency", "currency", "fee_type")), NotifyURL: text(values, "notify_url"), ReturnURL: text(values, "return_url"), BusinessType: "system_payment_test", BusinessID: uuid.NewString(), Extra: map[string]any{}}
|
||||
req := &bizpayment.PaymentRequest{Provider: provider, TradeNo: strings.ReplaceAll(tradeNo, ".", ""), Subject: "Kra 支付渠道连通性测试", Amount: amount, Currency: strings.ToUpper(firstAny(values, "test_currency", "currency", "fee_type")), NotifyURL: text(values, "notify_url"), ReturnURL: text(values, "return_url"), BusinessType: "system_payment_test", BusinessID: uuid.NewString(), Extra: map[string]any{}}
|
||||
if req.Currency == "" {
|
||||
req.Currency = "CNY"
|
||||
}
|
||||
if provider == system.PaymentApple {
|
||||
if provider == bizpayment.PaymentApple {
|
||||
req.TradeNo = uuid.NewString()
|
||||
req.Extra["product_id"] = firstAny(values, "product_id", "test_product_id")
|
||||
}
|
||||
@@ -351,10 +352,10 @@ func validatePaymentTestSettings(provider string, values map[string]any) error {
|
||||
return fmt.Errorf("test_extra 必须是 JSON 对象: %w", err)
|
||||
}
|
||||
}
|
||||
if provider == system.PaymentApple && strings.TrimSpace(text(values, "test_transaction_id")) == "" {
|
||||
if provider == bizpayment.PaymentApple && strings.TrimSpace(text(values, "test_transaction_id")) == "" {
|
||||
return errors.New("Apple 测试需要 test_transaction_id(沙箱交易 ID)")
|
||||
}
|
||||
if provider == system.PaymentApple && strings.TrimSpace(firstAny(values, "test_product_id", "product_id")) == "" {
|
||||
if provider == bizpayment.PaymentApple && strings.TrimSpace(firstAny(values, "test_product_id", "product_id")) == "" {
|
||||
return errors.New("Apple 测试需要 test_product_id(沙箱商品 ID)")
|
||||
}
|
||||
return nil
|
||||
@@ -377,7 +378,7 @@ func testModeEnabled(values map[string]any) bool {
|
||||
}
|
||||
}
|
||||
|
||||
func (r *paymentRepo) Create(ctx context.Context, req *system.PaymentRequest) (*system.PaymentResult, error) {
|
||||
func (r *paymentRepo) Create(ctx context.Context, req *bizpayment.PaymentRequest) (*bizpayment.PaymentResult, error) {
|
||||
if req == nil {
|
||||
return nil, errors.New("支付下单请求为空")
|
||||
}
|
||||
@@ -396,7 +397,7 @@ func (r *paymentRepo) Create(ctx context.Context, req *system.PaymentRequest) (*
|
||||
|
||||
func paymentProviderRequiresNotifyURL(provider string) bool {
|
||||
switch provider {
|
||||
case system.PaymentApple, system.PaymentAllinPay, system.PaymentSaobei, system.PaymentPayPal:
|
||||
case bizpayment.PaymentApple, bizpayment.PaymentAllinPay, bizpayment.PaymentSaobei, bizpayment.PaymentPayPal:
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
@@ -413,15 +414,15 @@ func paymentCreateRequiresNotifyURL(provider string, extra, config map[string]an
|
||||
}
|
||||
keys := []string{"method", "pay_method", "trade_type", "pay_type", "channel"}
|
||||
switch provider {
|
||||
case system.PaymentAlipay, system.PaymentAlipayV3:
|
||||
case bizpayment.PaymentAlipay, bizpayment.PaymentAlipayV3:
|
||||
keys = []string{"method", "pay_method", "trade_type", "channel"}
|
||||
case system.PaymentWechatV2:
|
||||
case bizpayment.PaymentWechatV2:
|
||||
keys = []string{"trade_type", "pay_type", "method", "pay_method", "channel"}
|
||||
case system.PaymentWechatV3:
|
||||
case bizpayment.PaymentWechatV3:
|
||||
keys = []string{"trade_type", "pay_type", "method"}
|
||||
case system.PaymentQQ:
|
||||
case bizpayment.PaymentQQ:
|
||||
keys = []string{"trade_type", "pay_type", "method", "pay_method"}
|
||||
case system.PaymentLakala:
|
||||
case bizpayment.PaymentLakala:
|
||||
keys = []string{"method", "pay_method", "trade_type"}
|
||||
}
|
||||
value := firstAny(extra, keys...)
|
||||
@@ -430,28 +431,28 @@ func paymentCreateRequiresNotifyURL(provider string, extra, config map[string]an
|
||||
}
|
||||
normalized := strings.NewReplacer(".", "_", "-", "_", " ", "_").Replace(strings.ToLower(strings.TrimSpace(value)))
|
||||
switch provider {
|
||||
case system.PaymentAlipay, system.PaymentAlipayV3:
|
||||
case bizpayment.PaymentAlipay, bizpayment.PaymentAlipayV3:
|
||||
return !contains([]string{"pay", "trade_pay", "alipay_trade_pay", "barcode", "barcode_pay", "micropay", "face_to_face"}, normalized)
|
||||
case system.PaymentWechatV2:
|
||||
case bizpayment.PaymentWechatV2:
|
||||
return !contains([]string{"micropay", "micro_pay", "barcode", "barcode_pay", "pay_code", "payment_code"}, normalized)
|
||||
case system.PaymentWechatV3:
|
||||
case bizpayment.PaymentWechatV3:
|
||||
return !contains([]string{"micropay", "micro_pay", "codepay", "code_pay", "barcode", "barcode_pay", "facepay", "face_pay"}, normalized)
|
||||
case system.PaymentQQ:
|
||||
case bizpayment.PaymentQQ:
|
||||
return !contains([]string{"micropay", "micro_pay", "barcode", "barcode_pay"}, normalized)
|
||||
case system.PaymentLakala:
|
||||
case bizpayment.PaymentLakala:
|
||||
return !contains([]string{"retail", "retail_pay", "micropay", "barcode"}, normalized)
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
func (r *paymentRepo) Query(ctx context.Context, provider, tradeNo string) (*system.PaymentResult, error) {
|
||||
func (r *paymentRepo) Query(ctx context.Context, provider, tradeNo string) (*bizpayment.PaymentResult, error) {
|
||||
a, c, err := r.adapter(ctx, provider)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return a.Query(ctx, tradeNo, c)
|
||||
}
|
||||
func (r *paymentRepo) Refund(ctx context.Context, req *system.PaymentRefundRequest) (*system.PaymentResult, error) {
|
||||
func (r *paymentRepo) Refund(ctx context.Context, req *bizpayment.PaymentRefundRequest) (*bizpayment.PaymentResult, error) {
|
||||
if req == nil {
|
||||
return nil, errors.New("支付退款请求为空")
|
||||
}
|
||||
@@ -461,7 +462,7 @@ func (r *paymentRepo) Refund(ctx context.Context, req *system.PaymentRefundReque
|
||||
}
|
||||
return a.Refund(ctx, req, c)
|
||||
}
|
||||
func (r *paymentRepo) HandleCallback(ctx context.Context, callback *system.PaymentCallback) (*system.PaymentResult, error) {
|
||||
func (r *paymentRepo) HandleCallback(ctx context.Context, callback *bizpayment.PaymentCallback) (*bizpayment.PaymentResult, error) {
|
||||
if callback == nil {
|
||||
return nil, errors.New("支付回调为空")
|
||||
}
|
||||
@@ -471,23 +472,23 @@ func (r *paymentRepo) HandleCallback(ctx context.Context, callback *system.Payme
|
||||
}
|
||||
result, err := a.Callback(ctx, callback, c)
|
||||
if err != nil {
|
||||
return nil, &system.PaymentCallbackError{Cause: err, Ack: paymentCallbackAck(callback.Provider, c, false)}
|
||||
return nil, &bizpayment.PaymentCallbackError{Cause: err, Ack: paymentCallbackAck(callback.Provider, c, false)}
|
||||
}
|
||||
if result == nil {
|
||||
err = errors.New("支付回调解析结果为空")
|
||||
return nil, &system.PaymentCallbackError{Cause: err, Ack: paymentCallbackAck(callback.Provider, c, false)}
|
||||
return nil, &bizpayment.PaymentCallbackError{Cause: err, Ack: paymentCallbackAck(callback.Provider, c, false)}
|
||||
}
|
||||
result.SuccessAck = paymentCallbackAck(callback.Provider, c, true)
|
||||
result.FailureAck = paymentCallbackAck(callback.Provider, c, false)
|
||||
if result.Provider != callback.Provider {
|
||||
err = errors.New("支付回调渠道不匹配")
|
||||
return nil, &system.PaymentCallbackError{Cause: err, Ack: result.FailureAck}
|
||||
return nil, &bizpayment.PaymentCallbackError{Cause: err, Ack: result.FailureAck}
|
||||
}
|
||||
result.EventID = paymentCallbackEventID(callback, result)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func paymentCallbackEventID(callback *system.PaymentCallback, result *system.PaymentResult) string {
|
||||
func paymentCallbackEventID(callback *bizpayment.PaymentCallback, result *bizpayment.PaymentResult) string {
|
||||
if result != nil {
|
||||
if eventID := strings.TrimSpace(result.EventID); eventID != "" {
|
||||
return eventID
|
||||
@@ -504,8 +505,8 @@ func paymentCallbackEventID(callback *system.PaymentCallback, result *system.Pay
|
||||
return hex.EncodeToString(hash[:])
|
||||
}
|
||||
|
||||
func paymentCallbackAck(provider string, values map[string]any, success bool) system.PaymentCallbackAck {
|
||||
ack := system.DefaultPaymentCallbackAck(provider, success)
|
||||
func paymentCallbackAck(provider string, values map[string]any, success bool) bizpayment.PaymentCallbackAck {
|
||||
ack := bizpayment.DefaultPaymentCallbackAck(provider, success)
|
||||
prefix := "callback_success_"
|
||||
if !success {
|
||||
prefix = "callback_failure_"
|
||||
@@ -524,7 +525,7 @@ func paymentCallbackAck(provider string, values map[string]any, success bool) sy
|
||||
return ack
|
||||
}
|
||||
|
||||
func callbackFields(callback *system.PaymentCallback) map[string]string {
|
||||
func callbackFields(callback *bizpayment.PaymentCallback) map[string]string {
|
||||
fields := map[string]string{}
|
||||
for key, value := range callback.Query {
|
||||
fields[key] = value
|
||||
@@ -569,5 +570,5 @@ func contains(values []string, value string) bool {
|
||||
}
|
||||
|
||||
func validatePaymentConfig(provider string, values map[string]any) error {
|
||||
return system.ValidateIntegrationConfig(system.IntegrationKindPayment, provider, values)
|
||||
return integrationbiz.ValidateIntegrationConfig(integrationbiz.IntegrationKindPayment, provider, values)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user