1. Go to this page and download the library: Download devaction-labs/idempotency library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
// Allow the route to be called without a key (e.g. public webhook probe)
Route::post('/webhooks/stripe', $handler)
->middleware('idempotent:optional');
// Short TTL for ephemeral actions
Route::post('/votes', $handler)
->middleware('idempotent:ttl=60');
// Stricter scope for tenant-partitioned operations
Route::post('/charges', $handler)
->middleware('idempotent:ttl=900,scope=user');
use DevactionLabs\Idempotency\Events\IdempotencyAlertFired;
use DevactionLabs\Idempotency\Logging\EventType;
Event::listen(IdempotencyAlertFired::class, function (IdempotencyAlertFired $event): void {
match ($event->eventType) {
EventType::PAYLOAD_MISMATCH => logger()->warning('idempotency.mismatch', $event->context),
EventType::CONCURRENT_CONFLICT => logger()->info('idempotency.concurrent', $event->context),
EventType::SIZE_WARNING => logger()->notice('idempotency.large', $event->context),
default => logger()->debug('idempotency.'.$event->eventType->value, $event->context),
};
});
use DevactionLabs\Idempotency\Contracts\{
KeyValidator,
PayloadHasher,
ScopeResolver,
ResponseSerializer,
TelemetryDriver,
};
// In your AppServiceProvider
public function register(): void
{
$this->app->bind(ScopeResolver::class, TenantScopeResolver::class);
$this->app->bind(PayloadHasher::class, WebhookAwareHasher::class);
}
final class TenantScopeResolver implements ScopeResolver
{
public function resolve(Illuminate\Http\Request $request): string
{
$tenant = $request->header('X-Tenant-ID') ?? 'public';
$user = $request->user()?->getAuthIdentifier() ?? 'guest';
return "t:{$tenant}:u:{$user}";
}
}
use DevactionLabs\Idempotency\Facades\Idempotency;
Idempotency::flush('123e4567-e89b-12d3-a456-426614174000', scope: 'u42');
Idempotency::has('123e4567-e89b-12d3-a456-426614174000');
// config/cache.php
'stores' => [
'idempotency' => [
'driver' => 'redis',
'connection' => 'idempotency', // a distinct Redis DB or instance
],
],
// B) Shared store but prefixed — good enough
'cache' => [
'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME'), '_').'_cache'),
],
// bootstrap/app.php — Laravel 11+
->withMiddleware(function (Middleware $middleware) {
$middleware->trustProxies(at: '*'); // or specific subnets
})