Uplatnenie kupónu
Každé uplatnenie ide cez CouponManager, ktorý spúšťa RedeemCouponAction — či už voláte Coupons::code()->redeem(), priame Coupons::redeem(), alebo skratky $coupon->redeemBy() a $user->redeemCoupon(). Akcia overí oprávnenosť, atomicky navýši počet použití, pri zapnutom sledovaní zapíše riadok zákazníka a spustí CouponRedeemed. S kupónom SAVE20 (zľava 20 %) na košík v hodnote 50.00 EUR:
use RoundlyConsulting\Coupons\Facades\Coupons;
use RoundlyConsulting\Money\Money;
$result = Coupons::code('SAVE20')->redeem(Money::ofMinor(5000, 'EUR'), $user);
$result->coupon; // the refreshed Coupon, usage already incremented
$result->discount; // Money 10.00 EUR — never more than the price
$result->total; // Money 40.00 EUR — the new total after the discount
$result->redeemer; // the redeemer you passed, or null
$result->freeShipping; // true for a free-shipping coupon — zero your own shipping line
// The flat form of the same call:
$result = Coupons::redeem('SAVE20', Money::ofMinor(5000, 'EUR'), redeemer: $user);
// Model shorthand — sugar over the same manager (redeemer may be null for guest checkout):
$result = $coupon->redeemBy($user, Money::ofMinor(5000, 'EUR'));Čo sa pri uplatnení deje
- Nájde kupón podľa modelu alebo kódu bez ohľadu na veľkosť písmen. Neznámy alebo prázdny kód spustí CouponRedemptionFailed a vyhodí CouponNotFound.
- Otvorí transakciu na vlastnom spojení modelu kupónu a riadok kupónu znovu načíta cez lockForUpdate(), takže súbežné uplatnenia nikdy neprekročia max_usage — ani keď coupons.model používa iné ako predvolené spojenie. Soft-deleted Coupon sa tu nenájde a vyhodí CouponNotFound.
- Spustí kontroly oprávnenosti v pevnom poradí a pri prvom zlyhaní vyhodí výnimku.
- Vypočíta zľavu cez discountFor() a celkovú sumu ako cenu mínus zľavu.
- Navýši usage relatívnym zápisom a obnoví model.
- Zapíše riadok coupon_redemptions — len ak je zadaný zákazník a coupons.redeemer.track je zapnuté. Uplatnenia bez zákazníka sa rátajú len do celkového limitu.
- Spustí CouponRedeemed a navyše CouponExhausted presne raz — pri uplatnení, ktoré dotiahne usage na kladný max_usage —, obe až po potvrdení vonkajšej transakcie.
Poradie kontrol
Kontroly bežia vždy v rovnakom poradí, takže zákazník dostane vždy rovnakú odpoveď:
| # | RedemptionFailureReason | Výnimka | Význam |
|---|---|---|---|
| 1 | NotFound | CouponNotFound | Neznámy alebo prázdny kód, prípadne soft-deleted inštancia Coupon. |
| 2 | CurrencyMismatch | CurrencyMismatch | Kupón je uzamknutý na inú menu, než je mena ceny. |
| 3 | MinimumSpendNotMet | MinimumSpendNotMet | Cena je pod minimálnou hodnotou nákupu kupónu. |
| 4 | Expired | CouponExpired | Ešte nie je aktívny alebo mu uplynula platnosť. |
| 5 | AtMaxUsage | CouponAtMaxUsage | Celkový limit použití je vyčerpaný. |
| 6 | AlreadyRedeemed | CouponAlreadyRedeemed | Limit na zákazníka je vyčerpaný — kontroluje sa len pri zadanom zákazníkovi. |
redeem() pri zlyhaní vždy vyhodí výnimku — uplatnenie bez výnimky neexistuje. Ak sa chcete najprv opýtať, Coupons::check() vráti prvý neúspešný RedemptionFailureReason alebo null bez výnimky, zamykania či zápisu; isRedeemableBy() a pravidlo Redeemable spúšťajú rovnaké kontroly.
Spracovanie zlyhaní
Každé zlyhanie je presná výnimka, ktorú môžete zachytiť. Všetky okrem CouponNotFound dedia z CouponNotRedeemable; obe dedia z CouponException:
use RoundlyConsulting\Coupons\Exceptions\{CouponNotFound, CouponExpired, CouponAtMaxUsage,
CouponAlreadyRedeemed, MinimumSpendNotMet, CurrencyMismatch};
try {
$result = Coupons::redeem($code, $price, redeemer: $user);
} catch (CouponNotFound) { // unknown code
} catch (CouponExpired) { // not active / past expiry
} catch (CouponAtMaxUsage) { // global cap reached
} catch (CouponAlreadyRedeemed) { // per-redeemer cap reached
} catch (MinimumSpendNotMet) { // price below the coupon's minimum
} catch (CurrencyMismatch) { // coupon locked to another currency
}Alebo zachyťte naraz oba základné typy:
use RoundlyConsulting\Coupons\Exceptions\CouponNotFound;
use RoundlyConsulting\Coupons\Exceptions\CouponNotRedeemable;
try {
$result = Coupons::redeem((string) $request->input('code'), $cartTotal, $request->user());
} catch (CouponNotFound|CouponNotRedeemable $e) {
return back()->withErrors(['code' => $e->getMessage()]);
}Doprava zadarmo
Balík nespravuje košík ani riadok dopravy, takže kupón na dopravu zadarmo z ceny tovaru nič neodpočíta — discount je nula a total sa rovná cene. Vlastnú dopravu vynulujte, keď je $result->freeShipping (alebo $coupon->appliesToShipping()) true:
use RoundlyConsulting\Money\Money;
$result = Coupons::redeem('SHIPFREE', $cartTotal, redeemer: $user);
$result->discount; // zero — the goods price is untouched
$result->total; // equals $cartTotal
if ($result->freeShipping) {
$shipping = Money::zero($shipping->currency()); // zero your own shipping line
}Priame volanie akcie
To isté uplatnenie bez fasády aj manažéra — pozrite si DI a akcie:
use RoundlyConsulting\Coupons\Actions\RedeemCouponAction;
use RoundlyConsulting\Coupons\DataTransferObjects\RedeemCouponData;
$result = app(RedeemCouponAction::class)->execute(
new RedeemCouponData(coupon: 'SAVE20', price: $cartTotal, redeemer: $user),
);Prejavte lásku k open source
Tento balík je zadarmo pod licenciou MIT. Ak vám šetrí čas, jednorazový príspevok alebo členstvo na Patreone nám pomôže ho ďalej udržiavať, testovať a dokumentovať.
Ďalšie spôsoby podpory vrátane kryptomienOdoslaním daru súhlasíte s našimi podmienkami prijímania darov.
Chcete to zabudovať do svojho produktu?
Naše balíky integrujeme do zákazkových Laravel a AI riešení. Napíšte nám, na čom pracujete, a ozveme sa do 48 hodín.