pdazcom/laravel-referrals

A referrals system for a laravel projects.

Maintainers

Package info

github.com/pdazcom/laravel-referrals

pkg:composer/pdazcom/laravel-referrals

Statistics

Installs: 1 485

Dependents: 0

Suggesters: 0

Stars: 28

Open Issues: 0


README

Latest Version on Packagist Software License Tests Total Downloads

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.php in Laravel 11 or 12. Middleware registration moved to bootstrap/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 _referrals property:

[
   'ref_id_1' => 'expires_timestamp',
   'ref_id_2' => 'expires_timestamp',
   ...
   'ref_id_n' => 'expires_timestamp'
]

ref_id_n is the referral link ID, and expires_timestamp is 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 name value stored in referral_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_name to referral, 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

  1. Create a referral link and note both $link->referral_link and $link->referral_code.
  2. Visit the share URL and confirm the middleware redirects to a clean URL and stores the referral cookie.
  3. Complete signup and confirm a referral_relationships row exists for the new user.
  4. 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:

  • StoreReferralCode must be active on your registration route.
  • Your registration flow must fire Illuminate\Auth\Events\Registered (Laravel's built-in RegisteredController and 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 ReferralCase every time the configured event fires. If you want to reward only on the first purchase, add a guard inside your program's reward() 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.