pdazcom / laravel-referrals
A referrals system for a laravel projects.
Requires
- php: ^8.2
- ext-json: *
- laravel/framework: ^9.52.18|^10|^11|^12
Requires (Dev)
- orchestra/testbench: ^7.48|^8.0|^9.0|^10.0
- dev-master
- 2.4.0
- 2.3.0
- 2.2.0
- 2.1.0
- 2.0.0
- 1.1.1
- 1.1.0
- 1.0.1
- 1.0.0
- 0.2.1
- 0.2.0
- 0.1.0
- 0.0.1
- dev-feature/m4-research-design
- dev-feature/v2.4-optional-rewards-integrations
- dev-feature/milestone-v2.3
- dev-docs/issue-26-contributor-local-dev-docs
- dev-docs/issue-25-config-reference
- dev-docs/issue-23-laravel-11-12-install
- dev-docs/issue-24-quickstart-guide
- dev-feature/referrals-install-command
- dev-dev
- dev-pdazcom-patch-1
This package is auto-updated.
Last update: 2026-03-21 16:33:46 UTC
README
A simple system of referrals with the ability to assign different programs for different users.
This package was created based on the lesson author is Damir Miladinov, with some minor changes, for which I express my gratitude to him.
Installation
These steps are verified against a fresh Laravel 11 and Laravel 12 application.
1. Install the package
composer require pdazcom/laravel-referrals
Laravel registers the service provider automatically through package discovery.
2. Publish the config file
php artisan vendor:publish --tag=referrals-config
This creates config/referrals.php, where you register your referral program classes.
If you prefer the package command, you can run:
php artisan referrals:install --config
3. Run the migrations
php artisan migrate
The package loads its migrations automatically for the default setup, so you do not need to publish them unless you want to customize the migration files before running them.
If you need to customize the migrations, publish them first:
php artisan vendor:publish --tag=referrals-migrations
Or use the package command:
php artisan referrals:install --migrations
4. Register the middleware
In Laravel 11 and 12, append the middleware to the web stack in bootstrap/app.php:
use Illuminate\Foundation\Configuration\Middleware; ->withMiddleware(function (Middleware $middleware): void { $middleware->web(append: [ \Pdazcom\Referrals\Http\Middleware\StoreReferralCode::class, ]); })
This middleware stores referral links in cookies so they can be attached when the user signs up.
It accepts both legacy UUID links and human-friendly referral codes in the same ?ref= query parameter. For the recommended sharing patterns, see Sharing and Entry Flows.
5. Add the trait to your user model
Add Pdazcom\Referrals\Traits\ReferralsMember to app/Models/User.php:
use Pdazcom\Referrals\Traits\ReferralsMember; class User extends Authenticatable { use HasFactory, Notifiable, ReferralsMember; }
Upgrade notes for older docs
- If you are upgrading from older README instructions, do not edit
app/Http/Kernel.phpin Laravel 11 or 12. Middleware registration moved tobootstrap/app.php. - You only need to publish the package migrations if you want to edit them before running
php artisan migrate.
Note
Starting from v2.0, several referral programs can be applied to the same user. They are stored in cookies as a JSON object, and the request instance exposes them in the
_referralsproperty:[ 'ref_id_1' => 'expires_timestamp', 'ref_id_2' => 'expires_timestamp', ... 'ref_id_n' => 'expires_timestamp' ]
ref_id_nis the referral link ID, andexpires_timestampis the cookie expiration timestamp. Expired links are deleted automatically.
Next: continue with the quickstart to create your first referral program and verify the reward flow.
Configuration Reference
The package configuration file is config/referrals.php:
return [ 'programs' => [ 'example' => \Pdazcom\Referrals\Programs\ExampleProgram::class, ], 'cookie_name' => 'ref', 'hooks' => [ 'signup' => false, 'first_purchase' => [ 'enabled' => false, 'event' => null, 'programs' => [], 'user_accessor' => 'user', 'reward_accessor' => null, ], ], ];
Keys
| Key | Default | Required | Behavior |
|---|---|---|---|
programs |
['example' => \Pdazcom\Referrals\Programs\ExampleProgram::class] |
Yes (for reward execution) | Maps referral_programs.name to a reward handler class. RewardUser resolves handler classes via config('referrals.programs.<program_name>'). Missing mappings are skipped with a warning log. |
cookie_name |
'ref' |
No | Controls the query parameter read by StoreReferralCode and the cookie name used to persist active referral link IDs and expiry timestamps. |
hooks.signup |
false |
No | When true, automatically dispatches UserReferred on Illuminate\Auth\Events\Registered. Requires StoreReferralCode on the registration route. |
hooks.first_purchase |
(see above) | No | When enabled is true and event is set, automatically dispatches ReferralCase when the configured event fires. See Reward Hooks for full options. |
programs
Use programs to register each referral program name with the class that should run when ReferralCase is dispatched.
- The array key must match the
namevalue stored inreferral_programs. - The class should implement package program behavior (typically by extending
Pdazcom\Referrals\Programs\AbstractProgram). - If no mapping exists for a program name, no reward class is executed for that event.
Example (single program):
'programs' => [ 'welcome-bonus' => \App\ReferralPrograms\WelcomeBonusProgram::class, ],
Example (multiple programs):
'programs' => [ 'welcome-bonus' => \App\ReferralPrograms\WelcomeBonusProgram::class, 'first-purchase' => \App\ReferralPrograms\FirstPurchaseProgram::class, ],
cookie_name
cookie_name defines the referral parameter your links use and the cookie key the middleware writes to.
- With the default value (
ref), links look like:/register?ref=ABC123. - If you set
cookie_nametoreferral, links look like:/register?referral=ABC123. - Existing links must use the same query parameter name as your configured
cookie_name.
Quickstart
This quickstart gives you a verified path from install to the first successful referral relationship and reward dispatch in a fresh Laravel 11 or 12 app.
1. Install the package
composer require pdazcom/laravel-referrals php artisan vendor:publish --tag=referrals-config php artisan migrate
You do not need to publish the package migrations for the default setup. The package loads them automatically.
Checkpoint: php artisan about should show a Laravel Referrals section.
2. Register the middleware and trait
In Laravel 11 and 12, append the middleware in bootstrap/app.php:
use Illuminate\Foundation\Configuration\Middleware; ->withMiddleware(function (Middleware $middleware): void { $middleware->web(append: [ \Pdazcom\Referrals\Http\Middleware\StoreReferralCode::class, ]); })
Then add the trait to your app/Models/User.php model:
use Pdazcom\Referrals\Traits\ReferralsMember; class User extends Authenticatable { use HasFactory, Notifiable, ReferralsMember; }
3. Add a reward handler
Create app/ReferralPrograms/QuickstartProgram.php:
<?php namespace App\ReferralPrograms; use Illuminate\Support\Facades\Log; use Pdazcom\Referrals\Programs\AbstractProgram; class QuickstartProgram extends AbstractProgram { public function reward(mixed $rewardObject): void { Log::info('Quickstart reward triggered', [ 'program' => $this->program->name, 'recruit_user_id' => $this->recruitUser->id, 'referral_user_id' => $this->referralUser->id, 'reward' => $rewardObject, ]); } }
Register it in config/referrals.php:
'programs' => [ 'quickstart' => \App\ReferralPrograms\QuickstartProgram::class, ],
4. Create a referrer, program, and referral link
php artisan tinker --execute='use App\Models\User; use Pdazcom\Referrals\Models\ReferralLink; use Pdazcom\Referrals\Models\ReferralProgram; $referrer = User::firstOrCreate(["email" => "referrer@example.com"], ["name" => "Referrer", "password" => "secret123"]); $program = ReferralProgram::firstOrCreate(["name" => "quickstart"], ["title" => "Quickstart Program", "description" => "Quickstart verification", "uri" => "/register", "lifetime_minutes" => 60]); $link = ReferralLink::firstOrCreate(["user_id" => $referrer->id, "referral_program_id" => $program->id]); echo json_encode(["referrer_id" => $referrer->id, "program_id" => $program->id, "link_id" => $link->id, "code" => $link->code, "url" => $link->link], JSON_PRETTY_PRINT);'
Checkpoint: the output includes a link_id, code, and url.
5. Dispatch the referral event for a new user
php artisan tinker --execute='use App\Models\User; use Pdazcom\Referrals\Events\UserReferred; use Pdazcom\Referrals\Models\ReferralLink; use Pdazcom\Referrals\Models\ReferralRelationship; $link = ReferralLink::firstOrFail(); $email = "referred+" . now()->timestamp . "@example.com"; $user = User::create(["name" => "Referred User", "email" => $email, "password" => "secret123"]); UserReferred::dispatch([$link->id => now()->addHour()->timestamp], $user); $relationship = ReferralRelationship::where("user_id", $user->id)->first(); echo json_encode(["referred_user_id" => $user->id, "relationship_exists" => (bool) $relationship, "relationship_link_id" => $relationship?->referral_link_id], JSON_PRETTY_PRINT);'
Checkpoint: relationship_exists is true.
6. Dispatch the reward event
php artisan tinker --execute='use App\Models\User; use Pdazcom\Referrals\Events\ReferralCase; $user = User::latest("id")->firstOrFail(); ReferralCase::dispatch("quickstart", $user, ["order_total" => 1500]); echo json_encode(["rewarded_user_id" => $user->id], JSON_PRETTY_PRINT);'
tail -n 5 storage/logs/laravel.log
Checkpoint: the log contains Quickstart reward triggered.
At this point the package is installed, the referral relationship is stored, and the reward handler is running. To wire this into your real registration flow, dispatch UserReferred::dispatch($request->input(StoreReferralCode::REFERRALS), $user) after signup as shown below.
If you want to support code sharing in chat, SMS, or native mobile flows, continue with Sharing and Entry Flows.
Sharing and Entry Flows
Use this section to choose the referral flow that matches your product surface. The package now supports both shareable links and code-only attribution without breaking existing link-based integrations.
For a deeper guide with examples and verification steps, see docs/sharing-and-entry-flows.md.
Choose the right flow
| Flow | Best for | What you share | What the user does |
|---|---|---|---|
referral_link |
Chat, email, SMS, landing pages | Human-friendly link such as /register?ref=INVITE2024 |
Opens the link and signs up normally |
referral_code |
Support flows, native mobile apps, offline campaigns | Short code such as INVITE2024 |
Types or pastes the code into your app |
link |
Backward-compatible integrations | UUID link such as /register?ref=550e8400-e29b-41d4-a716-446655440000 |
Opens the legacy link |
Share a human-friendly link
Use referral_link when you want a readable URL for public sharing:
$link = ReferralLink::create([ 'user_id' => $user->id, 'referral_program_id' => $program->id, ]); $shareUrl = $link->referral_link; $shareCode = $link->referral_code;
This is the recommended default for web, email, SMS, and messaging apps because the same code can also be shown separately for manual entry.
Accept manual code entry
Use registerWithCode() when the referred user enters a code directly instead of visiting a link. The method accepts either the human-friendly referral_code or the legacy UUID code.
use Illuminate\Http\Request; public function store(Request $request) { $data = $request->validate([ 'name' => ['required', 'string'], 'email' => ['required', 'email'], 'password' => ['required', 'string'], 'referral_code' => ['nullable', 'string'], ]); $user = User::create($data); if (!empty($data['referral_code'])) { $user->registerWithCode($data['referral_code']); } return redirect('/dashboard'); }
registerWithCode() returns true when the code resolves to a referral link and false when the code is unknown, so you can decide whether to show validation feedback or continue without attribution.
Keep legacy links if you already use them
The original link attribute still returns a URL with the UUID-based code:
$legacyUrl = $link->link;
This keeps older integrations working. New user-facing sharing surfaces should prefer referral_link and referral_code.
Verify attribution
- Create a referral link and note both
$link->referral_linkand$link->referral_code. - Visit the share URL and confirm the middleware redirects to a clean URL and stores the referral cookie.
- Complete signup and confirm a
referral_relationshipsrow exists for the new user. - Repeat the same attribution using
$user->registerWithCode($link->referral_code)and confirm you get the same relationship result.
Reward Hooks
Reward hooks let you trigger referral events automatically in response to standard application events, without adding manual event dispatch to every controller or service. All hooks are opt-in and disabled by default.
Signup hook
The signup hook listens to Illuminate\Auth\Events\Registered and automatically dispatches UserReferred for any referral link stored in the current request by the StoreReferralCode middleware.
Requirements:
StoreReferralCodemust be active on your registration route.- Your registration flow must fire
Illuminate\Auth\Events\Registered(Laravel's built-inRegisteredControllerand Fortify/Breeze do this automatically).
Enable in config/referrals.php:
'hooks' => [ 'signup' => true, ],
When enabled, you no longer need to manually dispatch UserReferred in your registration controller. The hook handles it automatically as long as the referral cookie is present on the request.
First-purchase hook
The first-purchase hook listens to a configurable application event and dispatches ReferralCase for the configured programs. This is useful when you want to reward the referrer when a referred user makes their first purchase.
Enable and configure in config/referrals.php:
'hooks' => [ 'first_purchase' => [ 'enabled' => true, 'event' => \App\Events\OrderCreated::class, 'programs' => ['welcome-bonus', 'first-purchase'], 'user_accessor' => 'user', 'reward_accessor' => 'order', ], ],
Options:
| Key | Default | Description |
|---|---|---|
enabled |
false |
Set to true to activate the hook. |
event |
null |
Fully-qualified class name of the event to listen for. Must be set when enabled is true. |
programs |
[] |
Array of referral program names to reward. Must match name values in referral_programs table. |
user_accessor |
'user' |
Property or zero-argument method name on the event that returns the referred Eloquent user model. |
reward_accessor |
null |
Property or zero-argument method name on the event to use as the $rewardObject passed to ReferralCase. When null, the event object itself is passed. |
Example event:
namespace App\Events; class OrderCreated { public function __construct( public \App\Models\User $user, public \App\Models\Order $order, ) {} }
With the config above, ReferralCase::dispatch(['welcome-bonus', 'first-purchase'], $event->user, $event->order) is dispatched automatically whenever OrderCreated fires.
Note: The hook dispatches
ReferralCaseevery time the configured event fires. If you want to reward only on the first purchase, add a guard inside your program'sreward()method (for example, check whether a reward has already been recorded for this user).
Backward compatibility
Enabling hooks does not change any existing behavior. Existing manual dispatches of UserReferred and ReferralCase continue to work. You can keep manual dispatches alongside hooks without double-rewarding as long as you are not dispatching the same event twice for the same user action.
Usage
Add new referrer event
Then in Http/Controllers/Auth/RegisterController.php add event dispatcher:
...
use Pdazcom\Referrals\Events\UserReferred;
use Pdazcom\Referrals\Http\Middleware\StoreReferralCode;
...
// overwrite registered function
public function registered(Request $request, $user)
{
// dispatch user referred event here
UserReferred::dispatch($request->input(StoreReferralCode::REFERRALS), $user);
}
From this point all referral links would be attached new users as referrals to users owners of these links.
Create referral program
And then you need to create a referral program in database and attach it to users by referral_program_id field:
php artisan tinker
Pdazcom\Referrals\Models\ReferralProgram::create(['name'=>'example', 'title' => 'Example Program', 'description' => 'Laravel Referrals made easy thanks to laravel-referrals package based on an article by Damir Miladinov,', 'uri' => 'register']);
add association to config referrals.programs:
...
'example' => \App\ReferralPrograms\ExampleProgram::class,
and create the reward class App\ReferralPrograms\ExampleProgram.php for referral program:
<?php
namespace App\ReferralPrograms;
use Pdazcom\Referrals\Programs\AbstractProgram;
class ExampleProgram extends AbstractProgram {
const ROYALTY_PERCENT = 30;
/**
* It can be anything that will allow you to calculate the reward.
*
* @param $rewardObject
*/
public function reward(mixed $rewardObject): void
{
$this->recruitUser->balance = $this->recruitUser->balance + $rewardObject * (self::ROYALTY_PERCENT/100);
$this->recruitUser->save();
}
}
create referral link:
php artisan tinker
Pdazcom\Referrals\Models\ReferralLink::create(['user_id' => 1, 'referral_program_id' => 1]);
and finally dispatch reward event in any place of your code:
use Pdazcom\Referrals\Events\ReferralCase;
...
ReferralCase::dispatch('example', $referralUser, $rewardObject);
From this point all referrals action you need would be reward recruit users by code logic in your reward classes.
Create many programs and their reward classes. Enjoy!
Bonus Content
If you want to list all the users for a given Referral Link, simply use
$referralLink->referredUsers()
Contributing
See CONTRIBUTING.md for local setup, how to run tests, and the pull request workflow.
Security
If you discover any security related issues, please email kostya.dn@gmail.com instead of using the issue tracker.
Credits
License
The MIT License (MIT). Please see License File for more information.