Перейти к содержанию

Withdrawal Flow Architecture

Детальный поток вывода средств с админской авторизацией через HD Wallet мастер-адрес.

Withdrawal Process Flow

sequenceDiagram
    participant User as 👤 User
    participant UserApp as 📱 User App
    participant API as 🌐 API
    participant WithdrawalService as 💸 Withdrawal Service
    participant BalanceService as 💰 Balance Service
    participant Admin as 🔧 Admin
    participant AdminApp as 🛠️ Admin App
    participant HDService as 🏦 HD Wallet Service
    participant MetaMask as 🦊 MetaMask
    participant Blockchain as ⛓️ Blockchain
    participant Database as 🗄️ Database

    Note over User,Database: Step 1: User Requests Withdrawal
    User->>UserApp: Enter withdrawal details
    UserApp->>API: POST /user/withdrawal/request
    API->>WithdrawalService: ValidateWithdrawalRequest()
    WithdrawalService->>BalanceService: CheckUserBalance()
    BalanceService->>Database: Query user balance
    Database->>BalanceService: Balance data
    BalanceService->>WithdrawalService: Balance sufficient
    WithdrawalService->>Database: Create withdrawal request
    Database->>WithdrawalService: Request created
    WithdrawalService->>BalanceService: LockFunds()
    BalanceService->>Database: Update balance (lock amount)
    WithdrawalService->>API: Request submitted
    API->>UserApp: Withdrawal request pending
    UserApp->>User: Show pending status

    Note over User,Database: Step 2: Admin Reviews Request
    Admin->>AdminApp: Open withdrawal management
    AdminApp->>API: GET /admin/withdrawal/pending
    API->>Database: Query pending withdrawals
    Database->>API: Pending withdrawal list
    API->>AdminApp: Display pending requests
    AdminApp->>Admin: Show requests for review

    Admin->>AdminApp: Review and approve request
    AdminApp->>API: POST /admin/withdrawal/approve
    API->>WithdrawalService: ApproveWithdrawal()
    WithdrawalService->>Database: Update status to "approved"

    Note over User,Database: Step 3: Admin Processes Withdrawal
    Admin->>AdminApp: Click "Process Withdrawal"
    AdminApp->>API: POST /admin/withdrawal/process
    API->>HDService: PrepareWithdrawalTransaction()
    HDService->>HDService: Generate transaction to user address
    HDService->>API: Return transaction details
    API->>AdminApp: Transaction details

    Note over User,Database: Step 4: Admin Signs Transaction
    AdminApp->>MetaMask: Request transaction signature
    MetaMask->>Admin: Show transaction details
    Admin->>MetaMask: Approve transaction
    MetaMask->>AdminApp: Signed transaction
    AdminApp->>API: POST /admin/withdrawal/submit
    API->>Blockchain: Submit signed transaction
    Blockchain->>API: Transaction hash
    API->>Database: Update withdrawal with tx_hash
    Database->>API: Updated

    Note over User,Database: Step 5: Transaction Monitoring
    API->>API: Monitor transaction status
    API->>Blockchain: Check transaction status
    Blockchain->>API: Transaction confirmed
    API->>WithdrawalService: TransactionConfirmed()
    WithdrawalService->>Database: Update status to "completed"
    WithdrawalService->>BalanceService: UnlockFunds()
    BalanceService->>Database: Update balance (remove locked amount)

    Note over User,Database: Step 6: User Notification
    API->>UserApp: WebSocket notification
    UserApp->>User: Withdrawal completed notification

Withdrawal State Machine

stateDiagram-v2
    [*] --> Requested: User submits withdrawal request

    Requested --> Pending: Initial validation passed
    Requested --> Rejected: Validation failed

    Pending --> Approved: Admin approval
    Pending --> Rejected: Admin rejection

    Approved --> Executing: Admin initiates transaction
    Executing --> Submitted: Transaction submitted to blockchain
    Executing --> Failed: Transaction submission failed

    Submitted --> Confirmed: Transaction confirmed on blockchain
    Submitted --> Failed: Transaction failed on blockchain

    Confirmed --> Completed: Funds unlocked, user notified

    Failed --> Approved: Admin retry
    Rejected --> [*]: Final rejection
    Completed --> [*]: Successful completion

Admin Withdrawal Management Dashboard

graph TB
    subgraph "Admin Dashboard"
        PENDING[📋 Pending Requests]
        APPROVED[✅ Approved Requests]
        EXECUTING[⚙️ Executing Requests]
        HISTORY[📚 Withdrawal History]
    end

    subgraph "Withdrawal Actions"
        REVIEW[👀 Review Request]
        APPROVE[✅ Approve]
        REJECT[❌ Reject]
        EXECUTE[⚡ Execute]
        RETRY[🔄 Retry Failed]
    end

    subgraph "Validation Checks"
        BALANCE[💰 Balance Check]
        AMOUNT[💵 Amount Validation]
        ADDRESS[📍 Address Validation]
        LIMITS[📏 Limits Check]
    end

    PENDING --> REVIEW
    REVIEW --> BALANCE
    REVIEW --> AMOUNT
    REVIEW --> ADDRESS
    REVIEW --> LIMITS

    BALANCE --> APPROVE
    AMOUNT --> APPROVE
    ADDRESS --> APPROVE
    LIMITS --> APPROVE

    BALANCE --> REJECT
    AMOUNT --> REJECT
    ADDRESS --> REJECT
    LIMITS --> REJECT

    APPROVE --> APPROVED
    REJECT --> HISTORY
    APPROVED --> EXECUTE
    EXECUTE --> EXECUTING
    EXECUTING --> RETRY

    classDef dashboard fill:#E8F5E8,stroke:#4CAF50,color:#000
    classDef actions fill:#FFF3E0,stroke:#FF9800,color:#000
    classDef validation fill:#F3E5F5,stroke:#9C27B0,color:#000

    class PENDING,APPROVED,EXECUTING,HISTORY dashboard
    class REVIEW,APPROVE,REJECT,EXECUTE,RETRY actions
    class BALANCE,AMOUNT,ADDRESS,LIMITS validation

Balance Management During Withdrawal

graph TB
    subgraph "User Balance States"
        AVAILABLE[💰 Available Balance]
        LOCKED[🔒 Locked Balance]
        PENDING[⏳ Pending Balance]
    end

    subgraph "Withdrawal Process"
        REQUEST[📝 Withdrawal Request]
        LOCK[🔒 Lock Funds]
        EXECUTE[⚡ Execute Transaction]
        COMPLETE[✅ Complete Withdrawal]
        FAIL[❌ Transaction Failed]
    end

    AVAILABLE --> REQUEST
    REQUEST --> LOCK
    LOCK --> LOCKED
    LOCKED --> EXECUTE
    EXECUTE --> COMPLETE
    EXECUTE --> FAIL

    COMPLETE --> AVAILABLE
    FAIL --> AVAILABLE

    REQUEST -.-> PENDING
    PENDING -.-> AVAILABLE

    classDef balance fill:#E3F2FD,stroke:#2196F3,color:#000
    classDef process fill:#FFF8E1,stroke:#FFC107,color:#000

    class AVAILABLE,LOCKED,PENDING balance
    class REQUEST,LOCK,EXECUTE,COMPLETE,FAIL process

Security and Validation Flow

flowchart TD
    START[📝 Withdrawal Request] --> BASIC_VAL[✅ Basic Validation]

    BASIC_VAL --> USER_AUTH{User Authenticated?}
    USER_AUTH -->|No| AUTH_ERROR[❌ Authentication Error]
    USER_AUTH -->|Yes| AMOUNT_VAL[💵 Amount Validation]

    AMOUNT_VAL --> AMOUNT_OK{Amount Valid?}
    AMOUNT_OK -->|No| AMOUNT_ERROR[❌ Invalid Amount]
    AMOUNT_OK -->|Yes| BALANCE_CHECK[💰 Balance Check]

    BALANCE_CHECK --> BALANCE_OK{Sufficient Balance?}
    BALANCE_OK -->|No| BALANCE_ERROR[❌ Insufficient Balance]
    BALANCE_OK -->|Yes| ADDRESS_VAL[📍 Address Validation]

    ADDRESS_VAL --> ADDRESS_OK{Valid Address?}
    ADDRESS_OK -->|No| ADDRESS_ERROR[❌ Invalid Address]
    ADDRESS_OK -->|Yes| LIMITS_CHECK[📏 Limits Check]

    LIMITS_CHECK --> LIMITS_OK{Within Limits?}
    LIMITS_OK -->|No| LIMITS_ERROR[❌ Exceeds Limits]
    LIMITS_OK -->|Yes| ADMIN_QUEUE[📋 Admin Review Queue]

    ADMIN_QUEUE --> ADMIN_REVIEW[👨‍💼 Admin Review]
    ADMIN_REVIEW --> ADMIN_DECISION{Admin Decision}
    ADMIN_DECISION -->|Reject| ADMIN_REJECT[❌ Admin Rejection]
    ADMIN_DECISION -->|Approve| ADMIN_APPROVE[✅ Admin Approval]

    ADMIN_APPROVE --> EXECUTE_TX[⚡ Execute Transaction]
    EXECUTE_TX --> TX_SUCCESS{Transaction Success?}
    TX_SUCCESS -->|No| TX_ERROR[❌ Transaction Failed]
    TX_SUCCESS -->|Yes| SUCCESS[✅ Withdrawal Completed]

    %% Error handling
    AUTH_ERROR --> RETRY[🔄 User Can Retry]
    AMOUNT_ERROR --> RETRY
    BALANCE_ERROR --> RETRY
    ADDRESS_ERROR --> RETRY
    LIMITS_ERROR --> RETRY
    ADMIN_REJECT --> FINAL_REJECT[❌ Final Rejection]
    TX_ERROR --> ADMIN_RETRY[🔄 Admin Can Retry]
    ADMIN_RETRY --> EXECUTE_TX

    classDef validation fill:#E8F5E8,stroke:#4CAF50,color:#000
    classDef error fill:#FFEBEE,stroke:#F44336,color:#000
    classDef success fill:#E0F2F1,stroke:#4CAF50,color:#000
    classDef process fill:#FFF3E0,stroke:#FF9800,color:#000

    class BASIC_VAL,AMOUNT_VAL,BALANCE_CHECK,ADDRESS_VAL,LIMITS_CHECK validation
    class AUTH_ERROR,AMOUNT_ERROR,BALANCE_ERROR,ADDRESS_ERROR,LIMITS_ERROR,ADMIN_REJECT,TX_ERROR,FINAL_REJECT error
    class SUCCESS success
    class START,ADMIN_QUEUE,ADMIN_REVIEW,EXECUTE_TX,RETRY,ADMIN_RETRY process

Database Schema for Withdrawals

erDiagram
    USERS {
        uuid id PK
        string email
        string status
        timestamp created_at
        timestamp updated_at
    }

    TRANSACTION_REQUESTS {
        uuid id PK
        uuid user_id FK
        uuid transaction_id FK
        string type
        string from_address
        string to_address
        decimal amount
        string currency
        string status
        jsonb metadata
        timestamp created_at
        timestamp processed_at
    }

    TRANSACTIONS {
        uuid id PK
        uuid user_id FK
        integer network_id
        string type
        string from_address
        string to_address
        decimal amount
        string currency
        string tx_hash
        string status
        integer confirmations
        timestamp created_at
        timestamp confirmed_at
    }

    USERS ||--o{ TRANSACTION_REQUESTS : requests
    TRANSACTION_REQUESTS ||--o| TRANSACTIONS : creates
    USERS ||--o{ TRANSACTIONS : has

АРХИТЕКТУРНЫЙ ПРИНЦИП: Балансы рассчитываются динамически из transactions таблицы (единый источник истины). Кеширование финансовых данных в отдельных таблицах запрещено согласно CLAUDE.md принципам.

Конфигурация лимитов

Все лимиты withdrawal хранятся в unified_config.go:

financial:
  limits:
    min_withdrawal: "0.01"
    max_withdrawal: "100000.00"
    daily_withdrawal_limit: "10000.00"
    weekly_withdrawal_limit: "50000.00"
    monthly_withdrawal_limit: "200000.00"

Implementation Details

Withdrawal Request Validation

func (s *WithdrawalService) ValidateWithdrawalRequest(req *WithdrawalRequest) error {
    // Basic validation
    if req.Amount.IsZero() || req.Amount.IsNegative() {
        return errors.New("invalid withdrawal amount")
    }

    // Address validation
    if !s.validateAddress(req.ToAddress) {
        return errors.New("invalid withdrawal address")
    }

    // Balance check (calculated from transactions)
    currentBalance, err := s.balanceService.CalculateUserBalance(req.UserID, req.Currency)
    if err != nil {
        return err
    }

    if currentBalance.LessThan(req.Amount) {
        return errors.New("insufficient balance")
    }

    // Limits check using unified config
    cfg := s.config
    if req.Amount.LessThan(cfg.GetMinWithdrawal()) {
        return errors.New("amount below minimum withdrawal limit")
    }

    if req.Amount.GreaterThan(cfg.GetMaxWithdrawal()) {
        return errors.New("amount exceeds maximum withdrawal limit")
    }

    // Check temporal limits (daily/weekly/monthly)
    if err := s.checkTemporalLimits(req.UserID, req.Amount, req.Currency); err != nil {
        return err
    }

    return nil
}

Fund Locking

func (s *BalanceService) LockFunds(userID string, amount decimal.Decimal, currency string) error {
    return s.db.Transaction(func(tx *sql.Tx) error {
        // Calculate current balance from transactions (single source of truth)
        currentBalance, err := s.calculateUserBalance(tx, userID, currency)
        if err != nil {
            return err
        }

        if currentBalance.LessThan(amount) {
            return errors.New("insufficient available balance")
        }

        // Create a "lock" transaction to reserve funds
        lockTransaction := &models.Transaction{
            UserID:   userID,
            Type:     "lock",
            Amount:   amount.Neg(), // Negative amount to reduce available balance
            Currency: currency,
            Status:   "confirmed",
            Metadata: map[string]interface{}{"lock_type": "withdrawal_pending"},
        }

        return s.createTransaction(tx, lockTransaction)
    })
}

// Calculate user balance from transactions table (CLAUDE.md compliant)
func (s *BalanceService) calculateUserBalance(tx *sql.Tx, userID string, currency string) (decimal.Decimal, error) {
    query := `
        SELECT COALESCE(SUM(amount), 0) as balance
        FROM transactions
        WHERE user_id = $1 AND currency = $2 AND status = 'confirmed'
    `
    var balance decimal.Decimal
    err := tx.QueryRow(query, userID, currency).Scan(&balance)
    return balance, err
}

Transaction Processing

func (s *WithdrawalService) ProcessWithdrawal(requestID string, adminID string) error {
    // Get withdrawal request from transaction_requests
    request, err := s.getTransactionRequest(requestID)
    if err != nil {
        return err
    }

    if request.Type != "withdrawal" {
        return errors.New("invalid request type")
    }

    // Prepare transaction
    tx, err := s.hdService.PrepareWithdrawalTransaction(
        request.ToAddress,
        request.Amount,
        request.Currency,
    )
    if err != nil {
        return err
    }

    // Update request status
    request.Status = "executing"
    request.ProcessedAt = time.Now()

    // Create transaction record
    transaction := &models.Transaction{
        UserID:      request.UserID,
        NetworkID:   s.config.GetDefaultNetworkID(),
        Type:        "withdrawal",
        FromAddress: s.config.GetHDWalletMasterAddress(),
        ToAddress:   request.ToAddress,
        Amount:      request.Amount,
        Currency:    request.Currency,
        Status:      "pending",
    }

    // Save both records
    if err := s.updateTransactionRequest(request); err != nil {
        return err
    }

    if err := s.createTransaction(transaction); err != nil {
        return err
    }

    return nil
}

Security Considerations

  1. Multi-level Approval: Требует админского одобрения для всех withdrawal
  2. Balance Locking: Средства блокируются во время обработки
  3. Address Validation: Проверка корректности адреса получателя
  4. Unified Config Limits: Все лимиты централизованы в unified_config.go
  5. Минимальные и максимальные суммы withdrawal
  6. Дневные, недельные и месячные лимиты
  7. Комиссии за операции

  8. Audit Trail: Полная история в transaction_requests и transactions

  9. HD Wallet Security: Только мастер-адрес может отправлять средства
  10. Confirmation Requirements: Ожидание подтверждений блокчейна
  11. Retry Logic: Автоматическое повторение при временных ошибках
  12. Admin Authentication: Проверка админских прав через wallet signature
  13. Real-time Monitoring: Мониторинг статуса транзакций в реальном времени

Error Recovery Scenarios

Failed Transaction Recovery

flowchart TD
    TX_FAIL[❌ Transaction Failed] --> ANALYZE[🔍 Analyze Failure]
    ANALYZE --> TEMP_FAIL{Temporary Failure?}

    TEMP_FAIL -->|Yes| AUTO_RETRY[🔄 Auto Retry]
    AUTO_RETRY --> MAX_RETRIES{Max Retries Reached?}
    MAX_RETRIES -->|No| RETRY_TX[⚡ Retry Transaction]
    MAX_RETRIES -->|Yes| ADMIN_MANUAL[👨‍💼 Admin Manual Review]

    TEMP_FAIL -->|No| PERM_FAIL[❌ Permanent Failure]
    PERM_FAIL --> UNLOCK_FUNDS[🔓 Unlock Funds]
    UNLOCK_FUNDS --> NOTIFY_USER[📢 Notify User]

    RETRY_TX --> TX_SUCCESS{Success?}
    TX_SUCCESS -->|Yes| COMPLETE[✅ Complete Withdrawal]
    TX_SUCCESS -->|No| TX_FAIL

    ADMIN_MANUAL --> ADMIN_DECISION{Admin Decision}
    ADMIN_DECISION -->|Retry| RETRY_TX
    ADMIN_DECISION -->|Cancel| UNLOCK_FUNDS

    classDef fail fill:#FFEBEE,stroke:#F44336,color:#000
    classDef process fill:#FFF3E0,stroke:#FF9800,color:#000
    classDef success fill:#E0F2F1,stroke:#4CAF50,color:#000

    class TX_FAIL,PERM_FAIL fail
    class ANALYZE,AUTO_RETRY,RETRY_TX,ADMIN_MANUAL,UNLOCK_FUNDS,NOTIFY_USER process
    class COMPLETE success