Розробка системи комісій біржі

Проєктуємо та розробляємо блокчейн-рішення повного циклу: від архітектури смарт-контрактів до запуску DeFi-протоколів, NFT-маркетплейсів та криптобірж. Аудит безпеки, токеноміка, інтеграція з наявною інфраструктурою.
Показано 1 з 1Усі 1306 послуг
Розробка системи комісій біржі
Середній
~3-5 днів
Часті запитання

Напрямки блокчейн-розробки

Етапи блокчейн-розробки

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1306
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1218
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    920
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1147
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    610
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    885

Система управління ключами API криптобіржи

API-ключі — це програмний аналог логіну/пароля. Трейдер створює ключ з певними правами, передає його боту або стороннему сервісу, і той торгує від його імені. Правильна система API-ключів означає гнучкі дозволи, безпечне зберігання та детальний журнал аудиту.

Модель даних API-ключів

type APIKey struct {
    ID          string    // публічний ключ (наприклад: "ak_prod_a1b2c3d4...")
    Secret      string    // хеш секрету (НІКОЛИ не зберігати простий текст)
    UserID      int64
    Label       string    // "Trading Bot", "Portfolio Tracker"
    
    // Дозволи
    Permissions APIPermissions
    
    // Обмеження
    IPWhitelist []string  // якщо порожній — будь-яка IP
    ExpiresAt   *time.Time
    
    // Статус
    IsActive    bool
    LastUsedAt  *time.Time
    CreatedAt   time.Time
}

type APIPermissions struct {
    // Торгівля
    SpotTrade     bool
    MarginTrade   bool
    FuturesTrade  bool
    
    // Рахунок
    ReadAccount   bool  // баланси, історія
    Withdraw      bool  // ПОПЕРЕДЖЕННЯ: високий ризик
    
    // Ринкові дані
    ReadMarketData bool  // завжди ввімкнено для безплатного доступу
}

Дозвіл на виведення — найнебезпечніший. Рекомендація: окреме підтвердження при ввімкненні, окремий whitelist адрес для цього ключа, сповіщення електронною поштою.

Генерація та зберігання ключів

import (
    "crypto/rand"
    "encoding/hex"
    "golang.org/x/crypto/bcrypt"
)

func GenerateAPIKey() (publicKey, secretKey string, err error) {
    // Публічний ключ: 32 байти, hex-кодований
    pubBytes := make([]byte, 16)
    if _, err = rand.Read(pubBytes); err != nil {
        return
    }
    publicKey = "ak_" + hex.EncodeToString(pubBytes)
    
    // Секрет: 32 байти, hex-кодований
    secBytes := make([]byte, 32)
    if _, err = rand.Read(secBytes); err != nil {
        return
    }
    secretKey = hex.EncodeToString(secBytes)
    
    return
}

func HashSecret(secret string) (string, error) {
    // bcrypt для зберігання — повільний хеш, стійкий до brute force
    hash, err := bcrypt.GenerateFromPassword([]byte(secret), bcrypt.DefaultCost)
    return string(hash), err
}

func VerifySecret(secret, hash string) bool {
    return bcrypt.CompareHashAndPassword([]byte(hash), []byte(secret)) == nil
}

Критично: секретний ключ показується користувачу ОДИН РАЗ при створенні. В базі зберігається лише bcrypt хеш. Якщо користувач втратив секрет — потрібно створити новий ключ.

Middleware аутентифікації

func APIKeyAuthMiddleware(db *DB) func(http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            apiKeyID := r.Header.Get("X-API-Key")
            signature := r.Header.Get("X-Signature")
            timestamp := r.Header.Get("X-Timestamp")
            
            if apiKeyID == "" || signature == "" {
                writeError(w, 401, "Missing authentication headers")
                return
            }
            
            // 1. Знаходимо ключ по публічному ID
            apiKey, err := db.GetAPIKey(apiKeyID)
            if err != nil || !apiKey.IsActive {
                writeError(w, 401, "Invalid API key")
                return
            }
            
            // 2. Перевірка часової мітки (анти-replay, ±5 сек)
            ts, _ := strconv.ParseInt(timestamp, 10, 64)
            if abs(time.Now().UnixMilli()-ts) > 5000 {
                writeError(w, 401, "Timestamp out of range")
                return
            }
            
            // 3. Верифікація підпису (HMAC-SHA256)
            body, _ := io.ReadAll(r.Body)
            r.Body = io.NopCloser(bytes.NewBuffer(body))
            
            message := r.Method + r.URL.RequestURI() + timestamp + string(body)
            // Використовуємо секрет з кеша (вилучення хешу неможливе — потрібен окремий кеш)
            if !verifyHMAC(message, apiKey.SecretForVerification, signature) {
                writeError(w, 401, "Invalid signature")
                return
            }
            
            // 4. IP whitelist
            if len(apiKey.IPWhitelist) > 0 {
                clientIP := getClientIP(r)
                if !contains(apiKey.IPWhitelist, clientIP) {
                    writeError(w, 403, "IP not whitelisted")
                    return
                }
            }
            
            // 5. Оновлюємо last_used_at асинхронно
            go db.UpdateLastUsed(apiKey.ID)
            
            // Передаємо контекст
            ctx := context.WithValue(r.Context(), "api_key", apiKey)
            next.ServeHTTP(w, r.WithContext(ctx))
        })
    }
}

Важливе зауваження: HMAC верифікація вимагає знання секрету, але ми зберігаємо лише bcrypt хеш. Рішення: при створенні ключа зберігаємо секрет у зашифрованому вигляді (AES-256 з ключем із HSM) лише для HMAC верифікації, а не для повторного показу користувачу.

Перевірки дозволів

func RequirePermission(perm string) func(http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            apiKey := r.Context().Value("api_key").(APIKey)
            
            hasPermission := false
            switch perm {
            case "spot_trade":
                hasPermission = apiKey.Permissions.SpotTrade
            case "withdraw":
                hasPermission = apiKey.Permissions.Withdraw
            case "read_account":
                hasPermission = apiKey.Permissions.ReadAccount
            }
            
            if !hasPermission {
                writeError(w, 403, fmt.Sprintf("Permission denied: %s required", perm))
                return
            }
            
            next.ServeHTTP(w, r)
        })
    }
}

// Використання:
router.POST("/api/v1/orders", 
    APIKeyAuthMiddleware(db),
    RequirePermission("spot_trade"),
    handler.PlaceOrder)

router.POST("/api/v1/withdrawals",
    APIKeyAuthMiddleware(db),
    RequirePermission("withdraw"),
    handler.CreateWithdrawal)

UI управління ключами

// Сторінка управління API ключами
function APIKeysManager() {
  const [keys, setKeys] = useState<APIKey[]>([]);
  const [showCreateModal, setShowCreateModal] = useState(false);
  
  return (
    <div>
      <Button onClick={() => setShowCreateModal(true)}>Create New API Key</Button>
      
      <table>
        {keys.map(key => (
          <tr key={key.id}>
            <td>{key.label}</td>
            <td><code>{key.id}</code></td>
            <td><PermissionBadges permissions={key.permissions} /></td>
            <td>{key.ipWhitelist.length > 0 ? key.ipWhitelist.join(', ') : 'All IPs'}</td>
            <td>{key.lastUsedAt ? formatRelative(key.lastUsedAt) : 'Never'}</td>
            <td>
              <ToggleButton active={key.isActive} onToggle={() => toggleKey(key.id)} />
              <DeleteButton onClick={() => deleteKey(key.id)} />
            </td>
          </tr>
        ))}
      </table>
      
      {showCreateModal && <CreateAPIKeyModal onCreated={handleKeyCreated} />}
    </div>
  );
}

// Після створення — показуємо секрет ОДИН РАЗ
function SecretRevealModal({ secret }: { secret: string }) {
  const [copied, setCopied] = useState(false);
  
  return (
    <Modal>
      <Alert type="warning">
        Скопіюйте секретний ключ зараз. Він більше не буде показаний.
      </Alert>
      <CodeBlock value={secret} />
      <CopyButton value={secret} onCopy={() => setCopied(true)} />
      <Button disabled={!copied} onClick={closeModal}>
        Я скопіював ключ
      </Button>
    </Modal>
  );
}

Журнал аудиту

Кожен запит API логується для безпеки:

CREATE TABLE api_access_log (
    id          BIGSERIAL PRIMARY KEY,
    api_key_id  VARCHAR(64) NOT NULL,
    user_id     BIGINT NOT NULL,
    method      VARCHAR(10) NOT NULL,
    path        VARCHAR(255) NOT NULL,
    ip_address  INET NOT NULL,
    status_code SMALLINT NOT NULL,
    latency_ms  INTEGER NOT NULL,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()
) PARTITION BY RANGE (created_at);

-- Зберігаємо 90 днів, старі видаляємо
CREATE INDEX idx_api_log_key_time ON api_access_log(api_key_id, created_at DESC);

Розробка повної системи управління API-ключами з дозволами, IP whitelist, журналом аудиту та UI: 3–4 тижні.