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

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1 услугВсе 1306 услуг
Разработка системы подписок на контент за токены
Средняя
~3-5 рабочих дней
Часто задаваемые вопросы
Направления блокчейн-разработки
Этапы блокчейн-разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1221
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1163
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    855
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1056
  • image_logo-advance_0.png
    Разработка логотипа компании B2B Advance
    561
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    828

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

Токен-гейтинг контента — это не 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 в метаданных или в контракте. Продаваемый, transferable. Идеально для «пожизненных» подписок или ограниченных тиров.

ERC-1155 timed access: полуфунгибельные токены с привязкой к периоду. Например, tokenId = 202504 — доступ на апрель 2025. Экономит gas за счёт 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 (бывший Keepers) проверяет условие 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 недель.