Після підтвердження платежу покупець очікує миттєвий доступ до файлу. Якщо посилання закінчилося, вебхук оплати не оброблено або файл лежить у 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 хвилин.







