Реализация системы скачивания файлов после оплаты

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

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

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

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

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Реализация системы скачивания файлов после оплаты
Средняя
~2-3 рабочих дня
Часто задаваемые вопросы

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

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

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

  • 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

Реализация системы скачивания файлов после оплаты

Система скачивания после оплаты — критический путь в продаже цифровых товаров. Файл должен стать доступен в течение секунд после подтверждения платежа, ссылка должна быть одноразовой или ограниченной, а сам файл — никогда не отдаваться напрямую из публичной директории.

Поток данных

Платёж подтверждён (webhook от эквайера)
  ↓
PaymentController::webhook()
  ↓
PaymentConfirmedEvent → CreateDownloadLinksListener
  ↓
foreach (order.items as item where item.is_digital):
  CreateDigitalDownloadAction::execute(item)
    → DigitalOrderDownload (token, limits, expiry)
  ↓
DigitalDownloadReadyMail → покупатель получает email
  ↓
Покупатель кликает ссылку → /download/{token}
  ↓
DigitalDownloadController::download(token)
  → валидация токена
  → стриминг файла

Обработка webhook платёжной системы

class PaymentWebhookController
{
    public function handle(Request $request, string $provider): JsonResponse
    {
        $handler = PaymentHandlerFactory::make($provider);

        // Верифицируем подпись вебхука
        if (!$handler->verifySignature($request)) {
            Log::warning('Invalid payment webhook signature', ['provider' => $provider]);
            abort(400);
        }

        $paymentResult = $handler->parse($request);

        if ($paymentResult->isSuccessful()) {
            $order = Order::where('payment_id', $paymentResult->transactionId)->firstOrFail();

            DB::transaction(function () use ($order, $paymentResult) {
                $order->update([
                    'status'     => 'paid',
                    'paid_at'    => now(),
                    'payment_id' => $paymentResult->transactionId,
                ]);

                event(new PaymentConfirmedEvent($order));
            });
        }

        return response()->json(['ok' => true]);
    }
}

Немедленное vs. асинхронное создание ссылок

Синхронно (inline в Listener) — покупатель получает email через 1–2 секунды после оплаты. Подходит для малого числа позиций.

Асинхронно (через Queue) — надёжнее при высокой нагрузке. Email может задержаться на несколько секунд, но не задержит HTTP-ответ вебхуку.

class CreateDownloadLinksListener implements ShouldQueue
{
    public $queue = 'digital-downloads';
    public $tries = 5;
    public $backoff = [5, 15, 30, 60, 120];

    public function handle(PaymentConfirmedEvent $event): void
    {
        $order = $event->order;

        $digitalItems = $order->items->filter(
            fn($item) => $item->product->digitalProduct !== null
        );

        foreach ($digitalItems as $item) {
            app(CreateDigitalDownloadAction::class)->execute($item);
        }
    }
}

Стриминг крупных файлов

При отдаче файлов PHP важно не загружать весь файл в память. Laravel Storage::download() использует стриминг автоматически, но для очень крупных файлов (>500 МБ) лучше использовать X-Accel-Redirect (nginx) или presigned URL (S3):

// Вариант 1: X-Accel-Redirect (nginx обслуживает файл напрямую, PHP только авторизует)
public function downloadViaAccel(DigitalOrderDownload $download): Response
{
    $this->validateDownload($download);
    $this->recordDownload($download);

    $internalPath = '/private-files/' . $download->digitalProduct->storage_path;

    return response('', 200, [
        'X-Accel-Redirect'       => $internalPath,
        'Content-Type'           => $download->digitalProduct->mime_type,
        'Content-Disposition'    => 'attachment; filename="' . $download->digitalProduct->original_filename . '"',
        'X-Content-Type-Options' => 'nosniff',
    ]);
}
# nginx конфиг
location /private-files/ {
    internal;
    alias /var/www/storage/app/private/;
}
// Вариант 2: S3 Presigned URL (для больших файлов, CDN-выдача)
public function downloadViaS3(DigitalOrderDownload $download): RedirectResponse
{
    $this->validateDownload($download);
    $this->recordDownload($download);

    $url = Storage::disk('s3')->temporaryUrl(
        path: $download->digitalProduct->storage_path,
        expiration: now()->addMinutes(10),
        options: [
            'ResponseContentDisposition' => sprintf(
                'attachment; filename="%s"',
                $download->digitalProduct->original_filename
            ),
        ]
    );

    return redirect($url);
}

Email с ссылкой на скачивание

class DigitalDownloadReadyMail extends Mailable
{
    use Queueable, SerializesModels;

    public function __construct(
        private readonly DigitalOrderDownload $download,
    ) {}

    public function build(): self
    {
        $downloadUrl = route('digital.download', $this->download->token);

        return $this
            ->subject('Ваша покупка готова к скачиванию')
            ->markdown('emails.digital-download-ready', [
                'downloadUrl'    => $downloadUrl,
                'productName'    => $this->download->digitalProduct->product->name,
                'expiresAt'      => $this->download->expires_at?->format('d.m.Y'),
                'downloadsLimit' => $this->download->downloads_limit,
            ]);
    }
}

Страница скачивания

Опционально — промежуточная веб-страница вместо прямого редиректа на файл. Позволяет показать инструкцию по открытию файла, кнопку повторного скачивания, рекомендации сопутствующих товаров.

// Роут: GET /download/{token}     — страница
// Роут: GET /download/{token}/file — реальное скачивание

Route::get('/download/{token}', [DigitalDownloadController::class, 'show'])->name('digital.show');
Route::get('/download/{token}/file', [DigitalDownloadController::class, 'download'])->name('digital.download');

Повторная отправка ссылки

Покупатель может запросить повторную отправку письма через личный кабинет или форму на странице заказа (с проверкой email):

public function resend(DigitalOrderDownload $download): JsonResponse
{
    if ($download->expires_at?->isPast()) {
        return response()->json(['error' => 'Ссылка истекла'], 410);
    }

    // Rate limiting: не чаще 1 раза в 5 минут
    RateLimiter::attempt('resend-download:' . $download->token, 1, function () use ($download) {
        Mail::to($download->orderItem->order->email)
            ->send(new DigitalDownloadReadyMail($download));
    }, 300);

    return response()->json(['ok' => true]);
}

Сроки

Базовая система (webhook → создание ссылки → email → стриминг через PHP) — 3–4 рабочих дня. X-Accel-Redirect или S3 presigned URL + страница скачивания + повторная отправка — 5–6 рабочих дней.