atelierdisko/coupon_code
Atelierdisko's coupon_code package helps Laravel applications generate, validate, and redeem coupon codes. It supports configurable code formats, usage limits, expiry dates, and redemption tracking, offering a clean, extensible API for adding promotional codes to your Eloquent models.
The package is intentionally small: it generates and validates coupon code strings. Treat it as a string utility, not a full coupon system — it has no concept of redemption, expiry, or persistence. Look first at the CouponCode class (the generator) and CouponCodeValidator (the rules), then check the README for the exact method names in the version you're installing.
Minimal steps:
composer require atelierdisko/coupon_codecomposer show atelierdisko/coupon_code and glance at vendor/atelierdisko/coupon_code/src/.use Atelierdisko\CouponCode\CouponCode;
$code = new CouponCode();
echo $code->generate(); // e.g. "A7K3-9MXP"
$validator = new CouponCodeValidator();
$validator->isValid($_POST['coupon']);
First realistic use case: generating a batch of codes at checkout-promo creation time, and validating a user-submitted string before you hand it to your own domain logic. Since the library is stateless, wire it into a Laravel service or action class rather than a controller inline — you'll want to swap the generator for tests.
Bind the generator behind an interface. Wrap it in your own CouponGenerator contract so you can stub it in tests and change the underlying library later:
// app/Services/CouponCodeGenerator.php
interface CouponCodeGenerator
{
public function generate(): string;
public function validate(string $code): bool;
}
// app/Services/LibraryCouponCodeGenerator.php
class LibraryCouponCodeGenerator implements CouponCodeGenerator
{
public function __construct(
private CouponCode $generator,
private CouponCodeValidator $validator,
) {}
public function generate(): string
{
return $this->generator->generate();
}
public function validate(string $code): bool
{
return $this->validator->isValid($code);
}
}
Register the binding in a CouponServiceProvider (or AppServiceProvider).
Batch generation at creation time. Generate codes once, persist them, and validate against your own database afterward. Validation via the library is a cheap format check, not a uniqueness guarantee:
$codes = collect(range(1, 500))->map(fn () => $this->generator->generate());
// Persist with unique constraint, retry on collision
DB::transaction(function () use ($codes) {
Coupon::insert($codes->map(fn ($code) => [
'code' => $code,
'created_at' => now(),
'updated_at' => now(),
])->all());
});
Two-stage validation. Use the library first (format/shape), then your own DB check (existence, single-use, expiry, amount). This short-circuits garbage input before touching the database:
public function redeem(string $rawCode, User $user): CouponRedemption
{
$code = strtoupper(trim($rawCode));
if (! $this->codes->validate($code)) {
throw ValidationException::withMessages(['coupon' => 'Invalid coupon format.']);
}
return DB::transaction(function () use ($code, $user) {
$coupon = Coupon::where('code', $code)->lockForUpdate()->first();
if (! $coupon || $coupon->redeemed_at) {
throw ValidationException::withMessages(['coupon' => 'Coupon not found or already used.']);
}
// mark redeemed, apply discount, etc.
});
}
Form request validation. Add a custom rule that delegates to the generator's validator:
class CouponRule implements ValidationRule
{
public function __construct(private CouponCodeGenerator $codes) {}
public function validate(string $attribute, mixed $value, Closure $fail): void
{
if (! is_string($value) || ! $this->codes->validate($value)) {
$fail('The :attribute is not a valid coupon code.');
}
}
}
Config quirks. The library typically exposes configuration (length, allowed character set, grouping/separator) via its constructor or setters. Pin these values in config/coupons.php and resolve them through your service provider, so the format is controlled in one place — not scattered through controllers.
UNIQUE constraint on the code column and handle the exception (retry with a fresh generation), rather than trusting the library.0/O and 1/I for OCR/typing safety. Check whether this package does — if not, consider a custom generator or post-processing.AppServiceProvider. Any config lives in your own config/coupons.php.$this->mock(CouponCodeGenerator::class) rather than trying to predict the random output.composer.json and add a regex-based integration test asserting your expected format, so an upgrade that changes character set or separator fails loudly.How can I help you explore Laravel packages today?