php 49 lines · 7 steps

Validating coupons before applying a discount in Laravel

A Laravel service runs a coupon through every eligibility check before computing the discount it earns.

Explained by highlit
1<?php
2 
3namespace App\Services;
4 
5use App\Models\Coupon;
6use App\Models\Order;
7use App\Exceptions\CouponException;
8use Carbon\Carbon;
9 
10class CouponValidator
11{
12 public function apply(string $code, Order $order): float
13 {
14 $coupon = Coupon::where('code', strtoupper(trim($code)))->first();
15 
16 if (! $coupon || ! $coupon->is_active) {
17 throw new CouponException('This coupon code is not valid.');
18 }
19 
20 if ($coupon->expires_at !== null && $coupon->expires_at->isPast()) {
21 throw new CouponException('This coupon has expired.');
22 }
23 
24 if ($coupon->starts_at !== null && $coupon->starts_at->isFuture()) {
25 throw new CouponException('This coupon is not active yet.');
26 }
27 
28 if ($coupon->max_uses !== null && $coupon->times_used >= $coupon->max_uses) {
29 throw new CouponException('This coupon has reached its usage limit.');
30 }
31 
32 if ($coupon->onlyOncePerCustomer() && $order->customer->hasUsedCoupon($coupon)) {
33 throw new CouponException('You have already used this coupon.');
34 }
35 
36 if ($order->subtotal < $coupon->min_order_total) {
37 throw new CouponException(sprintf(
38 'A minimum order of %s is required to use this coupon.',
39 money_format($coupon->min_order_total)
40 ));
41 }
42 
43 $discount = $coupon->type === 'percent'
44 ? round($order->subtotal * ($coupon->value / 100), 2)
45 : min($coupon->value, $order->subtotal);
46 
47 return $discount;
48 }
49}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Stacking guard clauses keeps each business rule isolated and the happy path unindented at the bottom.
  2. 2Normalizing user input like the coupon code before lookup avoids false negatives from casing or whitespace.
  3. 3Separating validation from calculation means the discount math only runs once the coupon is fully proven valid.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Validating coupons before applying a discount in Laravel — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code