PHPackages                             bvtterfly/replay - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Authentication &amp; Authorization](/categories/authentication)
4. /
5. bvtterfly/replay

AbandonedArchivedLibrary[Authentication &amp; Authorization](/categories/authentication)

bvtterfly/replay
================

Laravel middleware for idempotency

1.3.0(3y ago)453946[5 PRs](https://github.com/bvtterfly/replay/pulls)MITPHPPHP ^8.0

Since Jun 23Pushed 2y ago1 watchersCompare

[ Source](https://github.com/bvtterfly/replay)[ Packagist](https://packagist.org/packages/bvtterfly/replay)[ Docs](https://github.com/bvtterfly/replay)[ RSS](/packages/bvtterfly-replay/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (9)Dependencies (13)Versions (14)Used By (0)

🚨 THIS PACKAGE HAS BEEN ABANDONED 🚨

I no longer use Laravel and cannot justify the time needed to maintain this package. That's why I have chosen to abandon it. Feel free to fork my code and maintain your own copy.

🔄 Replay - Idempotency Middleware
=================================

[](#-replay---idempotency-middleware)

[![Latest Version on Packagist](https://camo.githubusercontent.com/388ea36d648ac6ceff425e9e9a6dd6866f03d30e3e93e2789af088b3d7498480/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f627674746572666c792f7265706c61792e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/bvtterfly/replay)[![GitHub Tests Action Status](https://camo.githubusercontent.com/09cf062ab90dd7b8955165a5cc03d4f2b52b9dfd3530be3fb082ae508002448f/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f776f726b666c6f772f7374617475732f627674746572666c792f7265706c61792f72756e2d74657374733f6c6162656c3d7465737473)](https://github.com/bvtterfly/replay/actions?query=workflow%3Arun-tests+branch%3Amain)[![GitHub Code Style Action Status](https://camo.githubusercontent.com/49679caa16ac4f3997f4213a5173dfd07a910cf5d4d7f3d46a586d27c97d0c53/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f776f726b666c6f772f7374617475732f627674746572666c792f7265706c61792f436865636b253230262532306669782532307374796c696e673f6c6162656c3d636f64652532307374796c65)](https://github.com/bvtterfly/replay/actions?query=workflow%3A%22Check+%26+fix+styling%22+branch%3Amain)[![Total Downloads](https://camo.githubusercontent.com/61a80db35e2e8fab012bb679dada83ef63af71f0001cbeac23e9a6261097c587/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f627674746572666c792f7265706c61792e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/bvtterfly/replay)

This package makes your endpoints idempotent easily.

Check out this [Stripe Blog Post](https://stripe.com/blog/idempotency) about Idempotency.

Implementation inspired by [Stripe API](https://stripe.com/docs/api/idempotent_requests).

💡 Features
----------

[](#-features)

- Adding support idempotency requests to your APIs easily by adding a middleware.
- Works only for `POST` requests. Other endpoints are ignored.
- Record and replay only successful(2xx) and server-side errors(5xx) responses, without touching your controller again.
- it's safe to retry, it doesn't record the response with client-side errors (4xx).
- To prevent accidental misuse of the cached responses, the request's signature is validated to ensure that the cached response is returned using the same combination of Idempotency-Key and Request.
- Concurrency protection using Laravel's atomic locks to prevent race conditions.

Installation
------------

[](#installation)

You can install the package via composer:

```
composer require bvtterfly/replay
```

You can publish the config file with:

```
php artisan vendor:publish --tag="replay-config"
```

This is the contents of the published config file:

```
use Bvtterfly\Replay\StripePolicy;

return [

    /*
    |--------------------------------------------------------------------------
    | Cache Store
    |--------------------------------------------------------------------------
    |
    | This option controls the cache store that gets used while Replay will store the
    | information required for it to function.
    | By default, Replay will use the default cache store.
    |
    | Please see config/cache.php for the list of all available Cache Stores.
    |
     */

    'use' => env('REPLAY_CACHE_STORE', config('cache.default')),

    /*
    |--------------------------------------------------------------------------
    | Replay Master Switch
    |--------------------------------------------------------------------------
    |
    | Replay is enabled by default,
    | Use this setting to enable/disable the Replay.
    |
    */

    'enabled' => env('REPLAY_ENABLED', true),

    /*
    |--------------------------------------------------------------------------
    | Expiration Seconds
    |--------------------------------------------------------------------------
    |
    | This value controls the number of seconds until an idempotency response
    | is considered expired.
    |
    | The default is set to 1 day.
    |
    */

    'expiration' => 60 * 60 * 24,

    /*
    |--------------------------------------------------------------------------
    | Request Header Name
    |--------------------------------------------------------------------------
    |
    | Replay will check this header name to determine
    | if a request is an Idempotency request.
    |
    */

    'header_name' => 'Idempotency-Key',

    /*
    |--------------------------------------------------------------------------
    | Response Header Name
    |--------------------------------------------------------------------------
    |
    | Replay will add this header to previously executed responses
    | that's being replayed from the server.
    |
    | Use null or empty, if you don't need to identify these responses.
    |
    */
    'replied_header_name' => 'Idempotent-Replayed',

    /*
    |--------------------------------------------------------------------------
    | Policy
    |--------------------------------------------------------------------------
    |
    | The policy determines whether a request is idempotent and whether the response should
    |  be recorded.
    |
    */

    'policy' => StripePolicy::class,

];
```

> **Note:** Replay needs a cache driver that supports [Cache Tags](https://laravel.com/docs/9.x/cache#cache-tags) &amp; [Atomic Locks](https://laravel.com/docs/9.x/cache#atomic-locks) features. Refer to [Laravel's documentation](https://laravel.com/docs/9.x/cache) to see if your driver supports these features.

Optionally, you can publish the translations using

```
php artisan vendor:publish --tag="replay-translations"
```

✨ Server Usage
--------------

[](#-server-usage)

The `Bvtterfly\Replay\Replay`-middleware must be registered in the kernel:

```
//app/Http/Kernel.php

protected $routeMiddleware = [
  ...
  'replay' => \Bvtterfly\Replay\Replay,
];
```

Next, For idempotent an endpoint, apply `replay` middleware to it:

```
Route::post('/payments', function () {
    //
})->middleware('replay');
```

By default, Replay stores the idempotent key as a cache key in the cache store, So all routes with replay middleware share the same cache key with an idempotent key. It's Okay to store it this way in most cases, but in some scenarios, we just need to separate them. In these scenarios, we can add a prefix to cache keys using middleware parameters:

```
Route::post('/payments', function () {
//
})->middleware('replay:payments');
```

### Custom Policy

[](#custom-policy)

Replay use Policy to determine whether a request is idempotent and whether the response should be recorded. By default, Replay includes and uses `StripePolicy` Policy. To create your custom policy, you first need to implement the `\Bvtterfly\Replay\Contracts\Policy` contract:

```
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

interface Policy
{
    public function isIdempotentRequest(Request $request): bool;

    public function isRecordableResponse(Response $response): bool;
}
```

If you want to view an example implementation take a look at the `StripePolicy` class.

For using this policy, We can change the `policy` in the config file.

✨ Client Usage
--------------

[](#-client-usage)

To perform an idempotent request, Client must provide an additional `Idempotency-Key : ` header with a unique key to the request.

it is recommended to:

- Use "V4 UUIDs" for the creation of the idempotency unique keys (e.g. `07cd2d27-e0dc-466f-8193-28453e9c3023`).
- Derive the key from a user-attached object, like the ID of a shopping cart. This provides a relatively straightforward way to protect against double submissions.

Once Replay detects a key, it'll look it up in cache store. If found, it will serve the same response without hitting your controller action again.

To identify a previously executed response that’s being replayed from the server, look for the header `Idempotent-Replayed: true`.

If Replay can't find the key, it attempts to acquire a cache lock and caches successful or server error responses. Still, if it can't acquire the lock, another request with the same key is already in progress, then it will respond with the HTTP Conflict response status code.

Resetting the Cache
-------------------

[](#resetting-the-cache)

If you need to manually reset the cache for this package, you may use the following artisan command:

```
php artisan replay:cache-reset
```

🧪 Testing
---------

[](#-testing)

```
composer test
```

Changelog
---------

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

Security Vulnerabilities
------------------------

[](#security-vulnerabilities)

Please review [our security policy](../../security/policy) on how to report security vulnerabilities.

Credits
-------

[](#credits)

- [Ari](https://github.com/bvtterfly)
- [All Contributors](../../contributors)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

33

—

LowBetter than 75% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity26

Limited adoption so far

Community13

Small or concentrated contributor base

Maturity60

Established project with proven stability

 Bus Factor1

Top contributor holds 66% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~10 days

Recently: every ~0 days

Total

9

Last Release

1344d ago

Major Versions

0.1.0 → 1.0.02022-07-01

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/99682351?v=4)[Λгi](/maintainers/bvtterfly)[@bvtterfly](https://github.com/bvtterfly)

---

Top Contributors

[![bvtterfly](https://avatars.githubusercontent.com/u/99682351?v=4)](https://github.com/bvtterfly "bvtterfly (31 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (8 commits)")[![github-actions[bot]](https://avatars.githubusercontent.com/in/15368?v=4)](https://github.com/github-actions[bot] "github-actions[bot] (8 commits)")

---

Tags

idempotencylaravellaravel-packagelaravelreplaybvtterfly

###  Code Quality

TestsPest

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/bvtterfly-replay/health.svg)

```
[![Health](https://phpackages.com/badges/bvtterfly-replay/health.svg)](https://phpackages.com/packages/bvtterfly-replay)
```

###  Alternatives

[spatie/laravel-permission

Permission handling for Laravel 12 and up

12.9k89.8M1.0k](/packages/spatie-laravel-permission)[bezhansalleh/filament-shield

Filament support for `spatie/laravel-permission`.

2.8k2.9M88](/packages/bezhansalleh-filament-shield)[jeffgreco13/filament-breezy

A custom package for Filament with login flow, profile and teams support.

1.0k1.7M41](/packages/jeffgreco13-filament-breezy)[spatie/laravel-login-link

Quickly login to your local environment

4381.2M1](/packages/spatie-laravel-login-link)[ryangjchandler/laravel-cloudflare-turnstile

A simple package to help integrate Cloudflare Turnstile.

438896.6k2](/packages/ryangjchandler-laravel-cloudflare-turnstile)[spatie/laravel-passkeys

Use passkeys in your Laravel app

444494.4k16](/packages/spatie-laravel-passkeys)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
