Розробка системи підписок на контент за токени

Проєктуємо та розробляємо блокчейн-рішення повного циклу: від архітектури смарт-контрактів до запуску 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

Розробка системи підписок на контент за токени

Токен-гейтинг контенту — це не Patreon на блокчейні. Принципіальна відмінність: платежі через смарт-контракт без посередника, creator отримує дохід напрямик, підписка може бути NFT з вторинним ринком, доступ верифікується on-chain без централізованого сервера авторизації.

Реалізувати це коректно складніше, ніж кажется, — особливо у частині верифікації доступу до off-chain контенту (відео, статті, файли).

Моделі підписки: ERC-20 vs ERC-721 vs ERC-1155

ERC-20 staking: користувач стейкує визначену кількість токенів creator'а — отримує доступ. Проста модель, але стейк замораховує ліквідність.

ERC-721 subscription NFT: кожна активна підписка — окремий NFT з expiry timestamp у метаданих або контракті. Продаваний, трансфабельний. Ідеально для «пожизненних» підписок або обмежених тирів.

ERC-1155 timed access: полуфунгібельні токени з привязкою до періоду. Наприклад, tokenId = 202504 — доступ на квітень 2025. Економить газ з batch transfers, логично для щомісячних підписок.

contract ContentSubscription {
    struct SubscriptionTier {
        uint256 pricePerMonth;      // у wei або ERC-20 токенах
        address paymentToken;       // address(0) = native ETH
        uint256 maxSubscribers;     // 0 = unlimited
        string contentCID;          // IPFS CID для зашифрованого контенту
        bytes32 encryptionKeyHash;  // хеш ключа шифрування для цього тиру
    }
    
    struct Subscription {
        uint256 tierId;
        uint256 expiresAt;
        uint256 startedAt;
        bool autoRenew;
    }
    
    mapping(uint256 => SubscriptionTier) public tiers;
    mapping(address => mapping(uint256 => Subscription)) public subscriptions;
    
    address public creator;
    uint256 public platformFeeBps = 250; // 2.5%
    
    modifier onlyCreator() {
        require(msg.sender == creator, "Not creator");
        _;
    }
    
    function subscribe(uint256 tierId, uint256 months, bool autoRenew) external payable {
        SubscriptionTier memory tier = tiers[tierId];
        uint256 totalCost = tier.pricePerMonth * months;
        
        if (tier.paymentToken == address(0)) {
            require(msg.value >= totalCost, "Insufficient ETH");
        } else {
            IERC20(tier.paymentToken).transferFrom(msg.sender, address(this), totalCost);
        }
        
        Subscription storage sub = subscriptions[msg.sender][tierId];
        
        // Продовження існуючої або нова підписка
        uint256 currentExpiry = sub.expiresAt > block.timestamp 
            ? sub.expiresAt 
            : block.timestamp;
        
        sub.expiresAt = currentExpiry + months * 30 days;
        sub.tierId = tierId;
        sub.autoRenew = autoRenew;
        if (sub.startedAt == 0) sub.startedAt = block.timestamp;
        
        _distributeRevenue(tier, totalCost);
        
        emit Subscribed(msg.sender, tierId, sub.expiresAt);
    }
    
    function isSubscribed(address user, uint256 tierId) external view returns (bool) {
        return subscriptions[user][tierId].expiresAt > block.timestamp;
    }
    
    function _distributeRevenue(SubscriptionTier memory tier, uint256 amount) internal {
        uint256 platformFee = amount * platformFeeBps / 10000;
        uint256 creatorAmount = amount - platformFee;
        
        if (tier.paymentToken == address(0)) {
            payable(creator).transfer(creatorAmount);
            payable(platform).transfer(platformFee);
        } else {
            IERC20(tier.paymentToken).transfer(creator, creatorAmount);
            IERC20(tier.paymentToken).transfer(platform, platformFee);
        }
    }
}

Верифікація доступу до зашифрованого контенту

Зберігати контент on-chain неможливо та безсмисленно. Стандартна схема: контент шифрується та завантажується в IPFS, ключ шифрування выдається тільки верифікованим підписникам.

Проблема: хто выдає ключ? Якщо централізований сервер — немає сенсу у блокчейні. Рішення — Lit Protocol або Threshold Network: децентралізована мережа нод зберігає ключові шарди та выдає доступ тільки при on-chain умові.

import { LitNodeClient, checkAndSignAuthMessage } from '@lit-protocol/lit-node-client';

// Умова доступу: активна підписка на тир 1
const accessControlConditions = [
    {
        contractAddress: SUBSCRIPTION_CONTRACT_ADDRESS,
        standardContractType: '',
        chain: 'ethereum',
        method: 'isSubscribed',
        parameters: [':userAddress', '1'],  // tierId = 1
        returnValueTest: {
            comparator: '=',
            value: 'true'
        }
    }
];

// Шифрування контенту (creator при завантаженні)
async function encryptContent(content) {
    const client = new LitNodeClient();
    await client.connect();
    
    const authSig = await checkAndSignAuthMessage({ chain: 'ethereum' });
    
    const { encryptedString, symmetricKey } = await LitJsSdk.encryptString(content);
    
    const encryptedSymmetricKey = await client.saveEncryptionKey({
        accessControlConditions,
        symmetricKey,
        authSig,
        chain: 'ethereum'
    });
    
    return {
        encryptedContent: encryptedString,
        encryptedKey: encryptedSymmetricKey
    };
}

// Розшифровка (користувач при читанні)
async function decryptContent(encryptedContent, encryptedKey) {
    const client = new LitNodeClient();
    await client.connect();
    
    const authSig = await checkAndSignAuthMessage({ chain: 'ethereum' });
    
    const symmetricKey = await client.getEncryptionKey({
        accessControlConditions,
        toDecrypt: encryptedKey,
        chain: 'ethereum',
        authSig
    });
    
    return await LitJsSdk.decryptString(encryptedContent, symmetricKey);
}

Моделі монетизації для creator'а

Модель Описання Плюси Мінуси
Flat subscription Фіксована ціна в місяць Предбачуваний дохід Немає премиум тирів
Tiered access Базовий / Pro / VIP Гнучко, максимізує виручку Складніше управляти
Pay-per-content Кожна частина окремо Монетизація hit-контенту Трення для користувача
NFT membership Пожизненна підписка як NFT Вторинний ринок = доп доход Одноразовий платіж
Hybrid Базовий тір + розові покупки Оптимальний баланс Найбільша складність

NFT membership заслуговує уваги: creator отримує royalty від вторинних продаж (ERC-2981). Якщо ранній holder продає своє місце — creator отримує 5–10% від ціни перепродажі автоматично.

Auto-renewal через Chainlink Automation

Автопродовження підписки потребує зовнішнього триггера — смарт-контракт не може сам себе вызвати по розписанню.

Chainlink Automation перевіряє умову checkUpkeep() та вызиває performUpkeep() при необхідності:

contract AutoRenewSubscription is AutomationCompatibleInterface {
    function checkUpkeep(bytes calldata) external view override 
        returns (bool upkeepNeeded, bytes memory performData) 
    {
        // Знайти істекаючі підписки з autoRenew=true
        address[] memory toRenew = _findExpiringSubscriptions();
        upkeepNeeded = toRenew.length > 0;
        performData = abi.encode(toRenew);
    }
    
    function performUpkeep(bytes calldata performData) external override {
        address[] memory toRenew = abi.decode(performData, (address[]));
        
        for (uint256 i = 0; i < toRenew.length; i++) {
            _attemptRenewal(toRenew[i]);
        }
    }
    
    function _attemptRenewal(address subscriber) internal {
        // Спроба списати, якщо баланс достатній — продовжити
        // При неудачі — подія, уведомлення користувача
    }
}

Терміни розробки

Базова система підписок з одним тиром, Lit Protocol інтеграцією та простим frontend — 4–6 тижнів. Повнофункціональна платформа з tiered access, NFT membership, auto-renewal та аналітикою для creator'а — 10–14 тижнів.