Wendell Adriel випустив Laravel Idempotency — пакет, що додає підтримку ідемпотентності для POST, PUT та PATCH маршрутів у Laravel. Якщо запит повторюється з тим самим ключем і даними, пакет повертає кешовану відповідь замість повторного виконання логіки. Це стандартне рішення для платіжних шлюзів, створення замовлень та будь-яких API, де клієнти можуть дублювати запити через збої мережі.
# Налаштування Middleware
Пакет пропонує два способи впровадження ідемпотентності. Перший — через стандартний middleware для маршрутів:
use WendellAdriel\Idempotency\Http\Middleware\Idempotent;
Route::post('/orders', StoreOrderController::class)->middleware(Idempotent::class);
Middleware очікує заголовок Idempotency-Key. Якщо надіслати той самий ключ із ідентичними даними, пакет поверне оригінальну відповідь із доданим заголовком Idempotency-Replayed: true.
Налаштувати поведінку для конкретного маршруту можна за допомогою Idempotent::using():
Route::post('/payments', ChargePaymentController::class)->middleware(
Idempotent::using(
ttl: 600,
lockTimeout: 30,
required: false,
scope: \WendellAdriel\Idempotency\Enums\IdempotencyScope::Ip,
header: 'X-Idempotency-Key',
)
);
Другий варіант — використання PHP-атрибутів на рівні класу або методу з тими ж параметрами:
use WendellAdriel\Idempotency\Attributes\Idempotent;
use WendellAdriel\Idempotency\Enums\IdempotencyScope;
#[Idempotent]
class PaymentController
{
#[Idempotent(ttl: 600, lockTimeout: 30, scope: IdempotencyScope::Ip)]
public function store()
{
// ...
}
}
Оскільки атрибут розширює вбудований у Laravel механізм middleware для контролерів, методи only та except працюють за стандартною логікою.
# Область дії ключів (Scoping)
Механізм ізоляції ключів можна налаштувати глобально у config/idempotency.php або перевизначити для кожного маршруту окремо:
| Scope | Поведінка |
|---|---|
user |
Ключі прив'язані до автентифікованого користувача (для гостей використовується IP) |
ip |
Ключі розділяються за IP-адресою клієнта |
global |
Ключ є унікальним для всієї системи незалежно від користувача чи IP |
# Вирішення конфліктів
Пакет обробляє два типи конфліктів. Якщо запит має той самий ключ, але інші дані, повернеться помилка 422 Unprocessable Entity. Якщо другий запит надходить, поки перший ще обробляється (одночасний дублікат), повернеться 409 Conflict із заголовком Retry-After: 1. Для коректної роботи необхідний драйвер кешу з підтримкою атомарних замків (atomic locks), наприклад Redis або Memcached.
# Команди Artisan
Для моніторингу та керування кешованими записами доступні дві команди.
idempotency:list виводить таблицю активних записів із детальною інформацією: область дії, ідентифікатор, ключ, маршрут, статус-код та термін дії:
php artisan idempotency:list --scope=user --id=5
idempotency:forget дозволяє видаляти записи за вказаними параметрами. Для безповоротного видалення без підтвердження використовуйте прапор --force:
# видалити всі записи для конкретного користувача
php artisan idempotency:forget --scope=user --id=5 --force
# видалити запис за конкретним ключем
php artisan idempotency:forget --key=checkout-1 --force
Вихідний код Laravel Idempotency доступний на GitHub.