Coupons and promotional codes generator for Laravel. This package requires Laravel 9.x and PHP 8.1.
You can install the package via composer:
composer require nagsamayam/laravel-promocodes
You can publish the config file with:
php artisan vendor:publish --tag="laravel-promocodes-config"
This is the contents of the published config file:
return [
'models' => [
'promocodes' => [
'model' => \NagSamayam\Promocodes\Models\Promocode::class,
'table_name' => 'promocodes',
'foreign_id' => 'promocode_id',
],
'users' => [
'model' => \App\Models\User::class,
'table_name' => 'users',
'foreign_id' => 'user_id',
],
'pivot' => [
'model' => \NagSamayam\Promocodes\Models\PromocodeUser::class,
'table_name' => 'promocode_user',
],
],
'code_mask' => '**-****-****',
'allowed_symbols' => 'ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789',
];
After you configure this file, publish and run the migrations with:
php artisan vendor:publish --tag="laravel-promocodes-migrations"
php artisan migrate
Now you will need to use AppliesPromocode on your user model.
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use NagSamayam\Promocodes\Traits\AppliesPromocode;
class User extends Authenticatable
{
use AppliesPromocode;
//
}
It's very easy to use. Methods are combined, so that you can configure promocodes easily.
- Reference
- Creating Promocodes
- Generating Promocodes
- Applying Promocode
- Expiring Promocode
- Applicable Discount
- Note
Name | Explanation |
---|---|
Mask | Astrisks will be replaced with random symbol |
Characters | Allowed symbols to use in mask replacement |
Type | Promocode type. flat or percent |
Multi use | Define if single code can be used multiple times, by the same user |
Unlimited | Generated code will have unlimited usages |
Bound to user | Define if promocode can be used only one user, if user is not assigned initially, first user will be bound to promocode |
User | Define user who will be initially bound to promocode |
Count | Amount of unique promocodes should be generated |
Usages | Define how many times can promocode be used |
Expiration | DateTime when promocode should be expired. Null means that promocode will never expire |
Details | Array of details which will be retrieved upon apply. discount key for flat type. percent_off key for percent type |
Min order value | Define order minimum value |
Max discount | Define maximum discount to be applied for an order. |
Status | Promocode status. active or inactive. default inactive |
Combine methods as you need. You can skip any method that you don't need, most of them already have default values. For custom code generation, create
method should have argument. Or you can use createWithCustomCode
with argument. Both the options works.
use NagSamayam\Promocodes\Facades\Promocodes;
use NagSamayam\Promocodes\Enums\PromocodeType;
use NagSamayam\Promocodes\Enums\PromocodeStatus;
Promocodes::mask('AA-***-BB') // default: config('promocodes.code_mask')
->characters('ABCDE12345') // default: config('promocodes.allowed_symbols')
->multiUse() // default: false
->unlimited() // default: false
->boundToUser() // default: false
->user(User::find(1)) // default: null
->count(5) // default: 1
->expiration(now()->addYear()) // default: null
->details(['percent_off' => 50]) // default: []
->minOrderValue(500) // default: null
->maxDiscount(5) // default: null
->createdByAdmin(User::find(1)) // default: null
->type(PromocodeType::PERCENT->value) // default: PromocodeType::FLAT->value
->status(PromocodeStatus::ACTIVE->value); // default: 'inactive'
->create(); // default: null
Promocodes::multiUse() // default: false
->boundToUser() // default: false
->user(User::find(1)) // default: null
->usages(5) // default: 1
->expiration(now()->addYear()) // default: null
->details(['discount' => 50]) // default: []
->minOrderValue(500) // default: null
->maxDiscount(5) // default: null
->createdByAdmin(User::find(1)) // default: null
->type(PromocodeType::FLAT->value) // default: PromocodeType::FLAT->value
->status(PromocodeStatus::ACTIVE->value); // default: 'inactive'
->create('AA-A4C-B1'); // default: null // Custom promo code can be passed // Equivalent to createWithCustomCode('MO-OT2P-897P') method
Promocodes::multiUse() // default: false
->boundToUser() // default: false
->user(User::find(1)) // default: null
->usages(5) // default: 1
->expiration(now()->addYear()) // default: null
->details([
'percent_off' => 50,
'message' => 'This one is for you. I hope you like it',
]) // default: []
->minOrderValue(500) // default: null
->maxDiscount(5) // default: null
->createdByAdmin(User::find(1)) // default: null
->type(PromocodeType::PERCENT->value) // default: PromocodeType::FLAT->value
->status(); // default: 'inactive'
->createWithCustomCode('MO-OT2P-897P');
There is a global helper function which will do the same as promocodes class. You can use named arguments magic from php 8.1.
use NagSamayam\Promocodes\Enums\PromocodeType;
use NagSamayam\Promocodes\Enums\PromocodeStatus;
create_promocodes(
type: PromocodeType::PERCENT->value, // default: PromocodeType::FLAT->value
mask: 'AA-***-BB', // default: config('promocodes.code_mask')
characters: 'ABCDE12345', // default: config('promocodes.allowed_symbols')
multiUse: true, // default: false
unlimited: true, // default: false
boundToUser: true, // default: false
user: User::find(1), // default: null
count: 5, // default: 1
expiration: now()->addYear(), // default: null
details: ['percent_off' => 50], // default: [],
createdByAdmin: User::find(98), // default: null
maxDiscount: 101, // default: null
minOrderValue: 1000, // default: null
status: PromocodeStatus::ACTIVE->value // default: PromocodeStatus::INACTIVE->value
);
create_custom_promocode(
code: '66-OMPY-ULZU',
type: PromocodeType::FLAT->value, // default: PromocodeType::FLAT->value
multiUse: true, // default: false
boundToUser: true, // default: false
user: User::find(1), // default: null
usages: 5, // default: 1
expiration: now()->addYear(), // default: null
details: ['discount' => 50], // default: [],
createdByAdmin: User::find(98), // default: null
maxDiscount: 101, // default: null
minOrderValue: 1000 // default: null
status: PromocodeStatus::ACTIVE->value // default: PromocodeStatus::INACTIVE->value
);
If you want to output promocodes and not save them to database, you can call generate method instead of create.
use NagSamayam\Promocodes\Facades\Promocodes;
use NagSamayam\Promocodes\Enums\PromocodeType;
use NagSamayam\Promocodes\Enums\PromocodeStatus;
Promocodes::mask('AA-***-BB') // default: config('promocodes.code_mask')
->characters('ABCDE12345') // default: config('promocodes.allowed_symbols')
->multiUse() // default: false
->unlimited() // default: false
->boundToUser() // default: false
->user(User::find(1)) // default: null
->count(5) // default: 1
->expiration(now()->addYear()) // default: null
->details(['percent_off' => 50]) // default: []
->minOrderValue(500)
->maxDiscount(5)
->createdByAdmin(User::find(1))
->status(PromocodeStatus::ACTIVE->value) // default: PromocodeStatus::INACTIVE->value
->type(PromocodeType::PERCENT->value) // default: PromocodeType::FLAT->value
->generate();
Combine methods as you need. You can skip any method that you don't need.
use NagSamayam\Promocodes\Facades\Promocodes;
Promocodes::code('ABC-DEF')
->user(User::find(1)) // default: null
->apply(['order_id' => '405-6828433-4214765']); // default: null // Good to send unique order ID // Preferbly string format
There is a global helper function which will do the same as promocodes class.
apply_promocode(
'ABC-DEF',
User::find(1), // default: null,
['order_id' => '405-6828433-4214765'] // default: null // Good to send unique order ID // Preferbly string format
);
While trying to apply promocode, you should be aware of exceptions. Most part of the code throws exceptions, when there is a problem:
// NagSamayam\Promocodes\Exceptions\*
PromocodeAlreadyUsedByUserException - "The given code `ABC-DEF` is already used by user with id 1."
PromocodeBoundToOtherUserException - "The given code `ABC-DEF` is bound to other user, not user with id 1."
PromocodeDoesNotExistException - "The given code `ABC-DEF` doesn't exist." | "The code was not event provided."
PromocodeExpiredException - "The given code `ABC-DEF` already expired."
PromocodeNoUsagesLeftException - "The given code `ABC-DEF` has no usages left."
UserHasNoAppliesPromocodeTrait - "The given user model doesn't have AppliesPromocode trait."
UserRequiredToAcceptPromocode - "The given code `ABC-DEF` requires to be used by user, not by guest."
PromocodeAlreadyExistedException - "The given promocode `10s%discount` is already existed. Please try with a new one."
PromocodeAlreadyUsedForOrderException - "The given code `ABC-DEF` is already applied for the order with id 405-6828433-4214765."
PromocodeNotAcitveException - "The given promocode `ABC-DEF` is not active. Please try with a new one."
There are two events which are fired upon applying.
// NagSamayam\Promocodes\Events\*
GuestAppliedPromocode // Fired when guest applies promocode
// It has public variable: promocode
UserAppliedPromocode // Fired when user applies promocode
// It has public variable: promocode
// It has public variable: user
There is a global helper function which will expire promocode.
expire_promocode('ABC-DEF');
use NagSamayam\Promocodes\Facades\Promocodes;
use NagSamayam\Promocodes\Enums\PromocodeStatus;
Promocodes::code('ABC-DEF')
->updatedByAdmin(User::find(6))
->updateStatus(PromocodeStatus::ACTIVE->value);
update_promocode_status('ABC-DEF',
PromocodeStatus::ACTIVE->value,
User::find(6)
);
If you added AppliesPromocode trait to your user model, you will have some additional methods on user.
$user = User::find(1);
$user->appliedPromocodes; // Returns promocodes applied by user
$user->boundPromocodes; // Returns promocodes bound to user
$user->applyPromocode('ABC-DEF', ['order_id' => '405-6828433-4214765']); // Applies promocode to user
There is a way to get the discount to be applied on total order value.
use NagSamayam\Promocodes\Facades\Promocodes;
Promocodes::code('ABC-DEF')
->orderTotal(1000) // default: 0
->getApplicableDiscount($forceCheck = true); // default: false // If false, it will not check for usagesLeft, status and expiry
Will be added very soon
Please see CHANGELOG for more information on what has changed recently.
Please see CONTRIBUTING for details.
If you discover any security related issues, please email nag.samayam@gmail.com instead of using the issue tracker.
This package is heavily based on the Laravel Promocodes package from Zura Gabievi. You can find the code on GitHub.
The MIT License (MIT). Please see License File for more information.
I have developed this package for my personal use. I made this public. Hope it helps you. Please do not expect the immediate bug fixes. Hope you understand.