Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
34.62% |
9 / 26 |
|
0.00% |
0 / 2 |
CRAP | |
0.00% |
0 / 1 |
| CouponService | |
34.62% |
9 / 26 |
|
0.00% |
0 / 2 |
44.82 | |
0.00% |
0 / 1 |
| resolve | |
50.00% |
9 / 18 |
|
0.00% |
0 / 1 |
22.50 | |||
| redeem | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
2 | |||
| 1 | <?php |
| 2 | |
| 3 | namespace App\Services\Cart; |
| 4 | |
| 5 | use App\Models\Coupon; |
| 6 | use App\Models\CouponRedemption; |
| 7 | |
| 8 | /** |
| 9 | * Validates coupon codes against a cart subtotal and records redemptions. |
| 10 | */ |
| 11 | class CouponService |
| 12 | { |
| 13 | /** |
| 14 | * Resolve a usable coupon for this code + subtotal + customer, or return |
| 15 | * a [null, reason] pair explaining why it can't be applied. |
| 16 | * |
| 17 | * @return array{0: ?Coupon, 1: ?string} |
| 18 | */ |
| 19 | public function resolve(string $code, float $subtotal, ?int $customerId = null, ?string $email = null): array |
| 20 | { |
| 21 | if (! feature('coupons')) { |
| 22 | return [null, 'Coupons are turned off.']; |
| 23 | } |
| 24 | |
| 25 | $coupon = Coupon::query()->where('code', strtoupper(trim($code)))->first(); |
| 26 | |
| 27 | if (! $coupon) { |
| 28 | return [null, 'That coupon code is not valid.']; |
| 29 | } |
| 30 | |
| 31 | if (! $coupon->isLive()) { |
| 32 | return [null, 'This coupon has expired or is no longer available.']; |
| 33 | } |
| 34 | |
| 35 | if ($subtotal < (float) $coupon->min_order) { |
| 36 | return [null, 'Add ' . money($coupon->min_order - $subtotal) . ' more to use this coupon.']; |
| 37 | } |
| 38 | |
| 39 | if ($coupon->per_customer_limit !== null && ($customerId || $email)) { |
| 40 | $used = CouponRedemption::query() |
| 41 | ->where('coupon_id', $coupon->id) |
| 42 | ->when($customerId, fn ($q) => $q->where('customer_id', $customerId)) |
| 43 | ->when(! $customerId && $email, fn ($q) => $q->where('email', $email)) |
| 44 | ->count(); |
| 45 | |
| 46 | if ($used >= $coupon->per_customer_limit) { |
| 47 | return [null, 'You have already used this coupon.']; |
| 48 | } |
| 49 | } |
| 50 | |
| 51 | return [$coupon, null]; |
| 52 | } |
| 53 | |
| 54 | public function redeem(Coupon $coupon, float $amount, ?int $orderId = null, ?int $customerId = null, ?string $email = null): void |
| 55 | { |
| 56 | CouponRedemption::query()->create([ |
| 57 | 'coupon_id' => $coupon->id, |
| 58 | 'order_id' => $orderId, |
| 59 | 'customer_id' => $customerId, |
| 60 | 'email' => $email, |
| 61 | 'amount' => $amount, |
| 62 | ]); |
| 63 | |
| 64 | $coupon->increment('used_count'); |
| 65 | } |
| 66 | } |