php 52 lines · 8 steps

Processing refunds safely in Laravel

An invokable Laravel controller validates a refund request, guards against duplicates, and wraps the payment call plus record creation in one transaction.

Explained by highlit
1<?php
2 
3namespace App\Http\Controllers;
4 
5use App\Models\Order;
6use App\Services\PaymentGateway;
7use Illuminate\Http\JsonResponse;
8use Illuminate\Http\Request;
9use Illuminate\Support\Facades\DB;
10 
11class RefundOrderController extends Controller
12{
13 public function __construct(
14 private readonly PaymentGateway $gateway,
15 ) {
16 }
17 
18 public function __invoke(Request $request, Order $order): JsonResponse
19 {
20 $validated = $request->validate([
21 'amount' => ['required', 'numeric', 'min:0.01', 'max:' . $order->paid_amount],
22 'reason' => ['required', 'string', 'max:500'],
23 ]);
24 
25 if ($order->isRefunded()) {
26 return response()->json([
27 'message' => 'This order has already been refunded.',
28 ], 409);
29 }
30 
31 $refund = DB::transaction(function () use ($order, $validated) {
32 $charge = $this->gateway->refund(
33 chargeId: $order->charge_id,
34 amount: $validated['amount'],
35 );
36 
37 return $order->refunds()->create([
38 'transaction_id' => $charge->id,
39 'amount' => $validated['amount'],
40 'reason' => $validated['reason'],
41 'processed_by' => $order->user_id,
42 ]);
43 });
44 
45 $order->markAsRefunded();
46 
47 return response()->json([
48 'message' => 'Refund processed successfully.',
49 'refund' => $refund->only(['id', 'amount', 'transaction_id']),
50 ], 201);
51 }
52}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Wrapping an external call and its database write in one transaction keeps your records consistent with what actually happened.
  2. 2Checking for an already-completed action before doing work prevents costly duplicate operations.
  3. 3Constructor injection lets a controller declare its dependencies explicitly rather than reaching for globals.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Processing refunds safely in Laravel — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code