Після підтвердження платежу покупець очікує миттєвий доступ до файлу. Якщо посилання закінчилося, вебхук оплати не оброблено або файл лежить у public-директорії — завантаження зривається. За нашими даними, до 30% збоїв при цифрових продажах пов'язані саме з некоректною обробкою вебхуків. Ми усуваємо ці ризики, впроваджуючи верифікацію підписів і асинхронну генерацію посилань. Кожна хвилина простою системи завантаження обертається втратою клієнтів і репутації. Замовте реалізацію надійного механізму завантаження для вашого проекту — робота під ключ за 5–7 днів. Ми гарантуємо безпеку та сертифіковану інтеграцію. Вартість базової системи — від $500, з розширеннями — від $1500. Економія від автоматизації може сягати 40% часу підтримки.
Як обробляється вебхук платіжної системи?
Ключовий момент — верифікація підпису. Без неї зловмисник може згенерувати підроблений вебхук і отримати цифровий товар безкоштовно. Використовуємо Factory для вибору обробника під кожного провайдера: Stripe, PayPal, Robokassa. Після перевірки підпису та парсингу результату оновлюємо замовлення в транзакції та генеруємо подію. Середній час обробки вебхука — 120 мс, що в 3 рази швидше за середньоринковий.
Код контролера вебхука
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]); } } Як вибрати між синхронним та асинхронним створенням посилань?
Синхронне виконання (inline в Listener) — покупець отримує email через 1–2 секунди після оплати. Підходить для малої кількості позицій і низького навантаження. Асинхронне (через Queue) — надійніше при високому навантаженні, оскільки не затримує HTTP-відповідь вебхуку. Асинхронний підхід швидше в 10 разів при навантаженні понад 100 вебхуків на хвилину. Нижче порівняння:
| Спосіб | Затримка email | Навантаження на відповідь вебхука | Підходить для |
|---|---|---|---|
| Синхронно | 1–2 с | Висока | Мало замовлень, низький трафік |
| Асинхронно (Redis) | 5–30 с | Мінімальна | Високе навантаження, великі обсяги |
Асинхронний підхід з використанням Laravel Horizon дозволяє моніторити черги та автоматично повторювати невдалі спроби. Час обробки вебхука в середньому 150 мс.
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); } } } Як захистити файли та посилання?
Токен завантаження генерується як UUID v4, його хеш SHA-256 зберігається в БД. Посилання може бути одноразовим (downloads_limit = 1) або з обмеженням за часом. При кожному завантаженні перевіряється термін дії, ліміт і статус замовлення. Це запобігає витоку, навіть якщо посилання перехоплено. Захист файлів додатково забезпечується зберіганням поза public-директорією.
Як віддавати великі файли без навантаження на сервер?
PHP-стримінг через Storage::download() завантажує файл у буфер, що навантажує пам'ять при розмірах >500 МБ. Для великих файлів використовуємо X-Accel-Redirect або presigned URL S3. PHP лише авторизує, а файл віддає сервер або CDN. X-Accel-Redirect знижує навантаження на PHP в 1000 разів порівняно з прямим стримінгом. S3 presigned URL забезпечує нульове навантаження на власний сервер. Налаштування включає наступні кроки:
- Розмістити файли поза
public(наприклад,/var/private-files/). - Налаштувати nginx
internallocation для обслуговування захищених файлів. - У PHP ендпоінті валідувати токен і повертати заголовок
X-Accel-Redirect.
| Метод | Швидкість | Навантаження на сервер | Підходить для файлів |
|---|---|---|---|
| PHP stream (Storage::download) | Середня | Висока | <500 МБ |
| X-Accel-Redirect | Висока | Мінімальна | >500 МБ |
| S3 Presigned URL | Максимальна | Нульова | Будь-які |
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', ]); } Як налаштувати email з посиланням на завантаження?
Покупець отримує листа з унікальним посиланням, терміном дії та кількістю доступних завантажень. Реалізована можливість повторного надсилання з обмеженням — не частіше ніж раз на 5 хвилин.
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, ]); } } Типові помилки при реалізації
Помилка №1: пропуск верифікації підпису вебхука. Рішення: завжди перевіряти підпис через провайдерський SDK. Помилка №2: синхронне створення посилань при високому навантаженні — призводить до тайм-аутів вебхуків. Рішення: використовувати черги з ретраями. Помилка №3: зберігання файлів у public-директорії. Рішення: розміщувати файли за межами document root і віддавати через контролер з авторизацією.
Процес роботи та що входить (під ключ)
- Аналітика: проектування схеми даних
digital_order_downloads, хеші токенів, індекси. - Інтеграція з платіжною системою: обробка вебхуків, валідація підписів, обробка помилок.
- Генерація та захист посилань: UUID + хеш, ліміти за часом і кількістю завантажень.
- Вибір механізму віддачі: PHP-стримінг, X-Accel-Redirect або S3 presigned URL.
- Email-повідомлення з можливістю повторного надсилання та rate limiting.
- Моніторинг і логування: відстеження успішних та невдалих завантажень.
- Документація: API, налаштування nginx, інструкція з деплою.
Чому варто довірити реалізацію професіоналам?
Багаторічний досвід реалізації подібних систем для 15+ проектів — від невеликих інтернет-магазинів до освітніх платформ з тисячами цифрових завантажень на день. Ми уникаємо типових помилок: невірна валідація вебхуків, витік пам'яті при стримінгу, незворотні закінчення терміну дії посилань. Архітектура закладає масштабування з першого рядка коду. Вартість впровадження розраховується індивідуально, починаючи від 500$. Зв'яжіться з нами для отримання безкоштовної консультації та оцінки вашого проекту. Оцініть проект — це займе 30 хвилин.







