Разработка продажи цифровых товаров (файлы, документы, шаблоны) на сайте

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Разработка продажи цифровых товаров (файлы, документы, шаблоны) на сайте
Средняя
~5 рабочих дней
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

Последние работы

  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    874
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    851

Разработка продажи цифровых товаров (файлы, документы, шаблоны) на сайте

Цифровые товары не требуют склада и доставки, но требуют надёжной системы доступа: файл должен быть передан покупателю сразу после оплаты и защищён от распространения. Стандартные e-commerce движки либо не поддерживают цифровые товары, либо реализуют их примитивно — прямой ссылкой на файл в public/, что не является защитой.

Типы цифровых товаров

  • Документы — PDF, DOCX, юридические шаблоны, договоры, инструкции
  • Таблицы — XLSX-шаблоны, финансовые модели, планировщики
  • Дизайн-ресурсы — PSD, Figma-шаблоны, иконки, шрифты
  • Программное обеспечение — дистрибутивы, плагины, темы для CMS
  • Медиа — аудио, видео, фотографии в высоком разрешении
  • Образовательный контент — курсы в виде ZIP-архивов, электронные книги

Архитектура системы

Покупатель оплачивает → PaymentConfirmedEvent
  → CreateDigitalOrderJob
    → генерация уникальной ссылки на скачивание
    → отправка email с ссылкой
    → запись в digital_order_downloads (лимиты)

Покупатель кликает по ссылке → DigitalDownloadController
  → проверка токена (действителен? не истёк? лимит не превышен?)
  → стриминг файла через StreamedResponse (не через public URL)
  → запись в download_events

Модели данных

Schema::create('digital_products', function (Blueprint $table) {
    $table->id();
    $table->foreignId('product_id')->constrained()->cascadeOnDelete();
    $table->string('storage_path'); // путь в приватном хранилище (не public)
    $table->string('original_filename');
    $table->string('mime_type');
    $table->unsignedBigInteger('file_size_bytes');
    $table->string('version')->nullable(); // v1.2.0
    $table->integer('download_limit')->nullable(); // NULL = без ограничений
    $table->integer('validity_days')->nullable(); // NULL = бессрочно
    $table->timestamps();
});

Schema::create('digital_order_downloads', function (Blueprint $table) {
    $table->id();
    $table->foreignId('order_item_id')->constrained();
    $table->foreignId('digital_product_id')->constrained();
    $table->string('token', 64)->unique(); // случайный безопасный токен
    $table->integer('downloads_count')->default(0);
    $table->integer('downloads_limit')->nullable();
    $table->timestamp('expires_at')->nullable();
    $table->timestamps();

    $table->index('token');
});

Schema::create('download_events', function (Blueprint $table) {
    $table->id();
    $table->foreignId('digital_order_download_id')->constrained();
    $table->string('ip_address', 45);
    $table->string('user_agent', 500)->nullable();
    $table->timestamp('downloaded_at');
    $table->index('downloaded_at');
});

Генерация ссылки после оплаты

class CreateDigitalDownloadAction
{
    public function execute(OrderItem $item): DigitalOrderDownload
    {
        $digitalProduct = $item->product->digitalProduct;

        if (!$digitalProduct) {
            throw new NotADigitalProductException($item->product_id);
        }

        $download = DigitalOrderDownload::create([
            'order_item_id'      => $item->id,
            'digital_product_id' => $digitalProduct->id,
            'token'              => bin2hex(random_bytes(32)), // 64 символа
            'downloads_count'    => 0,
            'downloads_limit'    => $digitalProduct->download_limit,
            'expires_at'         => $digitalProduct->validity_days
                ? now()->addDays($digitalProduct->validity_days)
                : null,
        ]);

        // Отправляем email с ссылкой
        Mail::to($item->order->email)
            ->send(new DigitalDownloadReadyMail($download));

        return $download;
    }
}

Контроллер скачивания

Файл никогда не отдаётся напрямую из публичной директории. Только через контроллер с проверками:

class DigitalDownloadController
{
    public function download(string $token): StreamedResponse
    {
        $download = DigitalOrderDownload::where('token', $token)->firstOrFail();

        // Проверяем срок действия
        if ($download->expires_at && $download->expires_at->isPast()) {
            abort(410, 'Ссылка истекла');
        }

        // Проверяем лимит скачиваний
        if ($download->downloads_limit !== null
            && $download->downloads_count >= $download->downloads_limit) {
            abort(403, 'Лимит скачиваний исчерпан');
        }

        $dp = $download->digitalProduct;

        // Проверяем существование файла
        if (!Storage::disk('private')->exists($dp->storage_path)) {
            abort(404, 'Файл не найден');
        }

        // Фиксируем скачивание
        DB::transaction(function () use ($download) {
            $download->increment('downloads_count');
            DownloadEvent::create([
                'digital_order_download_id' => $download->id,
                'ip_address'   => request()->ip(),
                'user_agent'   => request()->userAgent(),
                'downloaded_at' => now(),
            ]);
        });

        // Стримим файл — не создаём временную копию в памяти
        return Storage::disk('private')->download(
            $dp->storage_path,
            $dp->original_filename,
            ['Content-Type' => $dp->mime_type]
        );
    }
}

Приватное хранилище

Файлы хранятся в директории вне public/. В Laravel используется диск private:

// config/filesystems.php
'private' => [
    'driver' => 'local',
    'root'   => storage_path('app/private'),
    'permissions' => [
        'file'      => ['public' => 0640, 'private' => 0640],
        'dir'       => ['public' => 0750, 'private' => 0750],
    ],
],

Для крупных файлов или высокой нагрузки — S3 с presigned URLs:

// Генерация временной подписанной ссылки S3 (15 минут)
$url = Storage::disk('s3-private')->temporaryUrl(
    $dp->storage_path,
    now()->addMinutes(15),
    ['ResponseContentDisposition' => 'attachment; filename="' . $dp->original_filename . '"'],
);

return redirect($url);

Личный кабинет покупателя

Раздел «Мои покупки» отображает:

  • Список купленных цифровых товаров
  • Кнопку скачивания (неактивна при исчерпании лимита или истечении срока)
  • Количество оставшихся скачиваний
  • Срок действия ссылки

Защита от распространения

  • Уникальный токен на каждую покупку — один токен не подходит для другого заказа
  • Лимит скачиваний по количеству — стандартно 3–5 скачиваний
  • Лимит по времени — 30–90 дней
  • Логирование IP при каждом скачивании — для расследования утечек

Сроки

Полная система продажи цифровых товаров (загрузка файлов в админке, привязка к товарам, отправка после оплаты, страница скачивания, личный кабинет) — 5–8 рабочих дней.