Розробка системи білінгу Node-as-a-Service

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

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

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

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

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

Розробка системи біллінгу Node-as-a-Service

Запустити ноду просто. Правильно рахувати деньги — особливо коли unit of consumption це не запрос, а час роботи ноди, тип мережі, версія клієнта та тариф, який вибрав користувач три тижні тому — складно. NaaS біллінг провалюється в більшості молодих провайдерів не тому що завдання технічно важке, а тому що між «рахувати деньги приблизно правильно» та «рахувати деньги точно та прозоро» — пропасть в кілька місяців інженерної роботи.

Моделі тарифікації: що реально використовується

Pay-per-request — класика для RPC провайдерів (Alchemy, Infura, QuickNode). Рахуємо кількість JSON-RPC викликів з ваговими коефіцієнтами за методами:

Метод Compute Units
eth_blockNumber 10
eth_getBalance 19
eth_call 26
eth_getLogs 75
trace_transaction 150
debug_traceTransaction 500

eth_getLogs з широким діапазоном блоків — атака на ноду. Без вагових коефіцієнтів користувач може зробити один запрос вартістю тисяч «звичайних». Alchemy називає це Compute Units, QuickNode — Credits. Названия різні, смисл один.

Time-based (subscription) — виділена нода фіксованої мощності оплачується помісячно. Зрозуміліше для користувача, передбачуваний revenue для провайдера. Мінус: користувач переплачує при низькому навантаженню.

Hybrid — базовий план з місячним включеним об'ємом, overage billing зверху. Використовують більшість зрілих провайдерів.

Архітектура біллінгової системи

Слой сбору метрик

Критичний шлях: кожен RPC запрос має бути залогований до відповіді користувачу — інакше при краху втратимо дані про використання. Архітектура:

Client Request
     ↓
API Gateway (Nginx / Envoy / Kong)
     ↓ [access log + request metadata]
Billing Proxy (sidecar) — async write to queue
     ↓
RPC Node Cluster
     ↓
Response → Client

Billing proxy пише в Apache Kafka або NATS JetStream — обидва дають at-least-once delivery. Синхронна запис в базу на кожен запрос убиває latency (додаємо 100–500мс до кожного RPC виклику, неприйнятно).

// Async metric emission — не блокує запрос
func (b *BillingMiddleware) RecordUsage(ctx context.Context, event UsageEvent) {
    select {
    case b.eventChan <- event:
        // успішно поставлено в буфер
    default:
        // буфер повний — метрика втрачена, логуємо як sampling loss
        b.metrics.IncSamplingLoss()
    }
}

Допустимий відсоток sampling loss для біллінгу: <0.01%. Якщо втрачаємо більше — backpressure або нода умирає під навантаженням.

Агрегація та rating engine

Raw события з Kafka → rating pipeline → billable records в PostgreSQL.

Rating — це застосування тарифних правил до raw usage. Для NaaS:

class RatingEngine:
    def rate_event(self, event: UsageEvent, plan: Plan) -> Decimal:
        method_weight = self.compute_unit_table.get(
            event.method, DEFAULT_WEIGHT
        )
        
        # Застосовуємо тарифний план
        if plan.type == "included_pool":
            remaining = plan.included_units - plan.used_units
            if remaining > 0:
                billable = max(0, method_weight - remaining)
                plan.used_units += method_weight
            else:
                billable = method_weight
        elif plan.type == "pay_per_use":
            billable = method_weight
            
        return Decimal(billable) * plan.unit_price

Агрегація відбувається за часовими вікнами (5-хвилинні buckets), фінальна запис — в кінці розрахункового періоду. Це створює billing lag — користувач витратив деньги, але баланс оновлюється через 5 хвилин. Це норма для NaaS.

Зберігання даних

PostgreSQL — правильний вибір для біллінгу. Не ClickHouse, не MongoDB. Біллінг потребує ACID при списанні. Схема:

-- Immutable usage log
CREATE TABLE usage_events (
    id          BIGSERIAL PRIMARY KEY,
    account_id  UUID NOT NULL,
    node_id     UUID NOT NULL,
    method      VARCHAR(64),
    chain_id    INTEGER,
    weight      INTEGER,
    occurred_at TIMESTAMPTZ NOT NULL,
    billed_at   TIMESTAMPTZ
) PARTITION BY RANGE (occurred_at);

-- Billing periods
CREATE TABLE billing_records (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    account_id      UUID NOT NULL,
    period_start    TIMESTAMPTZ NOT NULL,
    period_end      TIMESTAMPTZ NOT NULL,
    total_units     BIGINT,
    total_amount    NUMERIC(20, 8),
    currency        VARCHAR(10),  -- 'USD', 'USDC', 'ETH'
    status          VARCHAR(20),  -- 'pending', 'invoiced', 'paid', 'overdue'
    created_at      TIMESTAMPTZ DEFAULT NOW()
);

usage_events партиціонується по даті — інакше через рік таблиця не вміститься в індекси оперативної пам'яті. Retention: raw события 90 днів, агрегати — бессрочно.

Crypto-native біллінг: специфіка

Prepaid баланс у стейблкоінах

Більшість NaaS для Web3 працюють у prepaid моделі: користувач поповнює баланс в USDC/USDT, списання відбувається з нього. Простіше ніж підписка через кредитну карту та немає chargeback ризиків.

contract NaaSBilling {
    IERC20 public immutable usdc;
    mapping(address => uint256) public balances;
    address public billingOracle; // multisig or oracle service
    
    event Deposit(address indexed account, uint256 amount);
    event Deduction(address indexed account, uint256 amount, string invoiceId);
    
    function deposit(uint256 amount) external {
        usdc.transferFrom(msg.sender, address(this), amount);
        balances[msg.sender] += amount;
        emit Deposit(msg.sender, amount);
    }
    
    // Тільки billingOracle може списувати
    function deductBalance(
        address account,
        uint256 amount,
        string calldata invoiceId
    ) external onlyBillingOracle {
        require(balances[account] >= amount, "Insufficient balance");
        balances[account] -= amount;
        emit Deduction(account, amount, invoiceId);
    }
}

Важливий паттерн: billingOracle — не EOA, а multisig або HSM сервіс. Компрометований ключ оракула — всі балансі під загрозою.

Автоматичне поповнення баланса

Low balance triggers — користувач настроює автопополнення при досяганні порога:

interface AutoRefillConfig {
  threshold: bigint;      // поповнювати коли баланс < threshold
  refillAmount: bigint;   // на скільки поповнювати
  sourceWallet: string;   // кошелек для автосписання (потрібен approve)
  maxMonthlySpend: bigint; // захист від billing runaway
}

maxMonthlySpend — обов'язковий захист. Без нього баглий клієнт робить мільйон запитів та пожирає весь баланс користувача за годину.

Алерти та rate limiting

Rate limiting на рівні API Gateway (не біллінгу): 1000 req/sec per API key — стандартний дефолт. Без rate limiting один користувач з багом може покласти ноду для всіх.

Billing alerts — нотифікації при:

  • Баланс упав нижче X% від звичайного місячного видатків
  • Різкий spike usage (>3x середнього за останню годину)
  • Нода недоступна (користувач платить за downtime — має компенсуватися SLA кредитами)

SLA credits — автоматичне нараховування кредитів при downtime. Вважається через uptime probe (зовнішній сервіс моніторингу, не ваш власний). Self-reported uptime 99.99% не викликає довіри у enterprise клієнтів.

Що ломається в production

Працюючи з NaaS біллінгом стикалися з кількома нетривіальними проблемами:

Clock skew між нодами — якщо billing proxy та нода мають розхідження годин >1 сек, timestamps в usage события некоректні. NTP обов'язковий, переважно chrony з Google NTP серверами.

Duplicate события при retry — Kafka at-least-once delivery означає дубліката при retry. Кожна година має idempotency key (request_id + node_id), rating engine дедуплицирует перед записом.

Timezone bugs в billing cycles — розрахунковий період «1-е число місяця» в UTC. Користувач з UTC-8 бачить що цикл закривається в 16:00 його часу. Потрібна явна документація та по бажанню — кастомні billing cycles.

Сроки розробки повнофункціональної NaaS billing системи: 3–5 місяців для команди з 2–3 backend інженерів. MVP з prepaid балансом та базовим rate limiting — 6–8 тижнів.