Платежи в Laravel: три подхода к приёму денег и их цена
Прямая интеграция с эквайрингом, готовый пакет вроде Laravel Cashier или сторонний биллинг — у каждого варианта своя цена в коде и в поддержке. Разбираем, что выбрать под разные задачи.

Приём платежей выглядит просто ровно до первого рефанда, первого дубля вебхука и первой рекуррентной подписки, которую надо продлить в 3 часа ночи по расписанию. В Laravel есть как минимум три способа принять деньги: писать интеграцию с провайдером руками, взять готовый пакет для подписок или отдать биллинг внешнему сервису. Каждый вариант решает разный класс задач и берёт за это разную цену — в строках кода, в контроле над логикой и в объёме того, что вам придётся сопровождать самим.
Ниже — сравнение для Laravel 11.x и 12.x. Речь про архитектуру приёма платежей, а не про то, какой эквайринг дешевле по комиссии: это отдельный разговор с бухгалтерией.
Задача и почему «просто дёрнуть API» мало
Минимальный сценарий приёма платежа — это не один запрос. Это цепочка: создать заказ, инициировать оплату у провайдера, увести пользователя на его форму или собрать данные через виджет, получить асинхронное уведомление (вебхук) о результате, проверить его подпись, обновить статус заказа, обработать повтор того же вебхука без двойного зачисления. Дальше начинаются рефанды, частичные возвраты и — если у вас подписки — рекуррентные списания и обработка неуспешной оплаты продления.
Наивная реализация «контроллер дёрнул HTTP-клиент и обновил заказ» ломается на асинхронности. Провайдер не гарантирует, что вебхук придёт один раз и в правильном порядке. Пользователь может закрыть вкладку до редиректа обратно. Поэтому источником истины должен быть вебхук, а не то, что вернулось в браузер, — и это верно для любого из трёх подходов.
Статус платежа определяется вебхуком, а не редиректом пользователя. Всё остальное — детали реализации. Если это правило нарушено, вы рано или поздно зачислите деньги, которых нет, или потеряете деньги, которые есть.
Подход 1. Прямая интеграция с провайдером
Вы работаете с API эквайринга напрямую через HTTP-клиент Laravel. Провайдер отдаёт свой SDK для PHP или голое REST API — вы оборачиваете его в сервис-класс.
Что выигрываете: полный контроль над каждым полем запроса, никаких лишних абстракций, любые нестандартные сценарии провайдера доступны. Чем платите: подпись вебхука, идемпотентность, повторные попытки, маппинг статусов провайдера на ваши — всё это вы пишете и тестируете сами.
<?php
// app/Services/Payments/AcquiringClient.php (Laravel 11.x)
namespace App\Services\Payments;
use Illuminate\Support\Facades\Http;
class AcquiringClient
{
public function __construct(
private string $baseUrl,
private string $secretKey,
) {}
// Создаём платёж, возвращаем ссылку на форму оплаты
public function createPayment(int $orderId, int $amountKopecks): array
{
$response = Http::withToken($this->secretKey)
->acceptJson()
// idempotency-ключ защищает от дубля при повторе запроса
->withHeaders(['Idempotence-Key' => (string) $orderId])
->post($this->baseUrl . '/payments', [
'amount' => ['value' => $amountKopecks, 'currency' => 'RUB'],
'metadata' => ['order_id' => $orderId],
]);
$response->throw(); // бросит исключение на 4xx/5xx
return $response->json();
}
}
Проверку подписи вебхука пишите строго по документации вашего провайдера — алгоритм и заголовок у каждого свои. Не изобретайте схему подписи сами и не доверяйте телу запроса без проверки.
Подход 2. Laravel Cashier
Официальный пакет Laravel для подписок и разовых платежей. Существует в двух вариантах: laravel/cashier для Stripe и laravel/cashier-paddle для Paddle. Cashier даёт трейты для моделей (Billable), готовую обработку вебхуков, управление подписками, пробными периодами и купонами из коробки.
Что выигрываете: подписки, продления, обработка неуспешной оплаты и вебхуки уже написаны и покрыты тестами создателями фреймворка. Чем платите: вы привязаны к Stripe или Paddle. Российский эквайринг Cashier из коробки не поддерживает — это ключевое ограничение для проектов с оплатой в рублях через локальные банки.
<?php
// Модель пользователя с трейтом Billable (Cashier для Stripe)
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Laravel\Cashier\Billable;
class User extends Authenticatable
{
use Billable;
}
<?php
// Оформление подписки — Cashier сам создаст запись и обработает вебхуки
$user->newSubscription('default', 'price_id_из_stripe')
->trialDays(14)
->create($paymentMethodId);
Точные имена методов и сигнатуры сверяйте с документацией версии Cashier, которую вы ставите: между мажорными релизами API менялось, и то, что работало в Cashier для старой версии Stripe SDK, могло измениться.
Подход 3. Внешний биллинг-сервис
Приём денег, подписки, счета и налоговую отчётность отдаёт внешний сервис (merchant of record или биллинг-платформа). Ваше приложение получает от него вебхуки о состоянии подписки и просто открывает/закрывает доступ к функциям.
Что выигрываете: не храните платёжные данные, часто снимаете с себя часть налоговой и юридической нагрузки, не пишете логику продлений. Чем платите: самая высокая комиссия, наименьший контроль, привязка к чужому продукту и его тарифам. Логика биллинга живёт вне вашего кода — отлаживать её тяжелее.
Сравнение
| Критерий | Прямая интеграция | Cashier | Внешний биллинг |
|---|---|---|---|
| Контроль над логикой | Полный | Средний | Низкий |
| Код, который пишете сами | Много | Мало | Минимум |
| Подписки из коробки | Нет | Да | Да |
| Российский эквайринг | Да | Нет | Зависит от сервиса |
| Что сопровождаете сами | Всю интеграцию | Настройку и обновления пакета | Только приём вебхуков |
| Стоимость | Комиссия эквайринга | Комиссия Stripe/Paddle | Самая высокая |
Когда что выбирать
- Разовые платежи, оплата в рублях через российский банк — прямая интеграция. Cashier тут не поможет, а внешний биллинг избыточен. Пишете сервис-класс, вебхук и идемпотентность.
- SaaS с подписками, платежи через Stripe или Paddle — Cashier. Экономит недели на логике продлений и обработке отказов оплаты.
- Продажа софта на международный рынок, не хотите заниматься налогами разных стран — внешний биллинг как merchant of record. Платите комиссией за то, что не держите юридический и налоговый узел у себя.
- Нестандартный сценарий: сплит-платежи, маркетплейс, эскроу — прямая интеграция, потому что готовые пакеты такие случаи закрывают редко.
Что ломается независимо от подхода
- Дубль вебхука. Провайдер повторяет уведомление, если не получил 200. Без проверки «этот платёж уже обработан» вы зачислите деньги дважды. Храните ID транзакции провайдера и проверяйте его перед зачислением.
- Гонка между редиректом и вебхуком. Пользователь вернулся на страницу «спасибо» раньше, чем пришёл вебхук. Не рисуйте «оплачено» по факту редиректа — показывайте «проверяем оплату».
- Долгая обработка внутри обработчика вебхука. Провайдер ждёт быстрый ответ. Тяжёлую логику (письма, генерация документов) уводите в очередь через
dispatch, а вебхуку сразу отвечайте 200. - Непроверенная подпись. Без проверки подписи любой может отправить вам поддельный «успешный платёж». Проверка подписи — не опция.
Что делать дальше
Определитесь с одним критерием first: нужен ли вам российский эквайринг. Ответ «да» вычёркивает Cashier и сужает выбор до прямой интеграции или внешнего сервиса с поддержкой рублёвых платежей. После выбора подхода первым делом настройте идемпотентность вебхуков и очередь для тяжёлой обработки — это две вещи, которые ломаются в проде раньше всего. Логику зачисления покрывайте тестами с эмуляцией повторного вебхука: именно этот сценарий чаще всего не проверяют до инцидента.
Задачи: математика, физика, химия: Разбор задачи по шагам с проверкой и похожей задачей
Спросить за 10 ₽Источники: Laravel Cashier (Stripe) — официальная документация, Laravel HTTP Client — официальная документация
Частые вопросы
Работает ли Laravel Cashier с российскими банками и ЮKassa?
У меня другая версия Laravel — примеры сработают?
Что делать, если вебхук пришёл дважды?
Можно ли определять статус оплаты по редиректу пользователя обратно на сайт?
Почему обработка внутри вебхука должна быть быстрой?
Нужно ли мне хранить данные банковских карт у себя?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.