DEV Community

Cover image for One Laravel Feature That Can Save You From Duplicate Payments and Race Conditions
Daniel Trix Smith
Daniel Trix Smith

Posted on

One Laravel Feature That Can Save You From Duplicate Payments and Race Conditions

Imagine this:

A user clicks the "Pay Now" button twice.

Or two requests hit your API at the exact same time.

Without proper protection, you might:

❌ Charge the customer twice.
❌ Create duplicate orders.
❌ Oversell inventory.
❌ Corrupt important data.

Many developers try to solve this with:

JavaScript button disabling
Validation rules
Unique checks

These improve the user experience—but they don't solve concurrency.

A better solution is to combine Laravel's database transactions with locking.

DB::transaction(function () use ($orderId) {
    $order = Order::where('id', $orderId)
        ->lockForUpdate()
        ->first();
    if ($order->status !== 'pending') {
        return;
    }
    $order->update([
        'status' => 'paid',
    ]);
});
Enter fullscreen mode Exit fullscreen mode

lockForUpdate() ensures that while one request is processing the order, every other concurrent request waits until the transaction completes.

For distributed systems, Laravel also provides cache locks:

Cache::lock("order:{$orderId}", 10)->block(5, function () {
    // Process payment safely
});
Enter fullscreen mode Exit fullscreen mode

The biggest lesson?

Validation protects your inputs.

Locks protect your data.

Knowing when to use each is what separates production-ready applications from applications that only work in development.

What's your preferred strategy for handling race conditions in Laravel?

Laravel #PHP #Backend #SoftwareEngineering #WebDevelopment #Programming #Concurrency #Database #Architecture

Top comments (0)