Рубрики
Uncategorized

Создание партнерской программы с Laravel

Недавно я создал простую партнерскую программу для сайта sauce. Вот короткая статья с подробным описанием того, как я имп… С пометкой “Показать разработчика”, “учебник”, “php”, “laravel”.

Недавно я создал простую партнерскую программу для сайта sauce . Вот короткая статья с подробным описанием того, как я это реализовал.

Примечание: Я создал это с нуля, чтобы иметь больше контроля над партнерским опытом, но вы можете использовать такую платформу, как Похвально реализовать что-то подобное с небольшой работой с вашей стороны.

Основы нашей программы будут заключаться в следующем: пользователи могут зарегистрироваться в нашей реферальной программе и получить специальную ссылку для обмена. Когда кто-то регистрируется по своей ссылке, он будет отмечен как привлеченный нашим пользователем, и он будет получать комиссию за все будущие покупки, которые он совершит.

Давайте сначала выясним, каким мы хотим видеть наш партнерский опыт. Изначально я планировал использовать подход Fathom , когда привлеченные пользователи связаны со специальной реферальной страницей , которая помечает их как привлеченных, но в итоге решил использовать подход, аналогичный тому, что Вознаграждающий делает, позволяя нашим партнерам добавлять ? через= на любой URL-адрес нашего маркетингового сайта, чтобы считать их рефералами.

Итак, давайте начнем с создания скрипта, который позволяет нам регистрировать привлеченных пользователей, когда они посещают страницу с параметром запроса через . Код довольно прост, если параметр существует, мы храним его в файле cookie в течение 30 дней. Поскольку наш маркетинговый сайт и приложение Laravel находятся на разных поддоменах, мы добавляем . перед доменом, что делает его доступным на всех поддоменах.

import Cookies from 'js-cookie'

const via = new URL(location.href).searchParams.get('via')

if (via) {
    Cookies.set('sitesauce_affiliate', via, {
        expires: 30,
        domain: '.sitesauce.app',
        secure: true,
        sameSite: 'lax'
    })
}

Это делает трюк с точки зрения функциональности, но я хотел сделать еще один шаг, показав специальный призыв к действию и имя партнера, что должно сделать его более привлекательным и дать понять, что вы только что перешли по реферальной ссылке. Вот макет того, как я хотел, чтобы мой баннер выглядел при посещении реферальной ссылки.

Однако, чтобы это сработало, нам нужно хранить больше, чем партнерский тег. Давайте создадим прототип воображаемого API, который возвращает некоторые данные из тега, и заставим наш пример работать.

import axios from 'axios'
import Cookies from 'js-cookie'

const via = new URL(location.href).searchParams.get('via')

if (via) {
    axios.post(`https://app.sitesauce.app/api/affiliate/${encodeURIcomponent(this.via)}`).then(response => {
        Cookies.set('sitesauce_affiliate', response.data, { expires: 30, domain: '.sitesauce.app', secure: true, sameSite: 'lax' })
    }).catch(error => {
        if (!error.response || error.response.status !== 404) return console.log('Something went wrong')

        console.log('Affiliate does not exist. Register for our referral program here: https://app.sitesauce.app/affiliate')
    })
}

Видишь это encodeURIComponent вызов при построении URL-адреса? Это делается для того, чтобы защитить нас от уязвимости Обхода пути . Мы отправляем запрос в /api/referral/:через , так что , если кто-то создал ссылку , содержащую ? через=../../выход из системы , пользователи будут выходить из системы при переходе по ссылке. В этом сценарии особого вреда не будет, но вы, вероятно, можете представить себе множество других случаев, когда это было бы нехорошо.

Поскольку сайт saucer landing уже использует Alpine в нескольких местах, давайте создадим наш баннер в качестве компонента Alpine. Спасибо моему другу Райану за то, что помог понять, почему мои переходы не работали.

Давайте теперь воплотим этот воображаемый API в жизнь, чтобы мы могли увидеть, как все это работает. Вместо создания новой партнерской модели я добавлю несколько полей в нашу существующую пользовательскую модель. Пока не обращайте внимания на последние два, мы вернемся к ним позже.

class AddAffiliateColumnsToUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('affiliate_tag')->nullable();
            $table->string('referred_by')->nullable();

            $table->string('paypal_email')->nullable();
            $table->timestamp('cashed_out_at')->nullable();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn('affiliate_tag', 'referred_by', 'paypal_email', 'cashed_out_at');
        });
    }
}

Для нашего маршрута API мы будем использовать новую функцию маршрутизации полей привязки (доступна в Laravel 7.X), чтобы все работало с одной строкой кода.

Route::post('api/affiliate/{user:affiliate_tag}', function (User $user) {
    return $user->only('id', 'name', 'avatar', 'affiliate_tag');
})->middleware('throttle:30,1');

Прежде чем мы начнем работу по пометке пользователей как указанных при регистрации, нам необходимо убедиться, что Laravel может считывать наши файлы cookie. Поскольку он не зашифрован, нам нужно будет добавить его в список исключений нашего промежуточного программного обеспечения EncryptCookies .

// app/Http/Middleware/EncryptCookies.php

use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;

class EncryptCookies extends Middleware
{
    /**
    * The names of the cookies that should not be encrypted
    *
    * @var array
    */
    protected $except = [
        'sitesauce_referral',
    ];
}

С этим покончено, давайте перейдем к маркировке пользователей как рекомендованных. Мы можем использовать метод аутентифицированный на нашем Контроллере регистрации для выполнения логики после создания нашего пользователя. Мы убедимся, что и файл cookie, и партнер существуют, и пометим пользователя как привлеченного, если он это сделает.

/**
 * The user has been registered.
 *
 * @param \Illuminate\Http\Request $request
 * @param \App\User $user
 */
protected function registered(Request $request, User $user)
{
    if (! $request->hasCookie('sitesauce.referral')) return;

    $referral = json_decode($request->cookie('sitesauce_referral'), true)['affiliate_tag'];

    if (! User::where('affiliate_tag', $referral)->exists()) return;

    $user->update([
        'referred_by' => $referral,
    ]);
}

Нам также понадобится способ получить список пользователей, которых пригласил наш партнер. Мы можем достичь этого, создав связь hasMany с пользовательской моделью из нашей пользовательской модели.

class User extends Model
{
    public function referred()
    {
        return $this->hasMany(self::class, 'referred_by', 'affiliate_tag');
    }
}

Давайте теперь отвлечемся от бэкэнда, чтобы создать красивую страницу, на которой наши пользователи смогут зарегистрироваться в программе. Мы позволим им выбрать свой тег и запросить у них адрес электронной почты PayPal, чтобы они могли обналичить свои доходы. Вот как получился мой дизайн:

Как только наш партнер зарегистрируется, мы также должны разрешить ему обновлять как свою электронную почту, так и свой тег. Если они обновят свой тег, нам нужно будет обновить его для всех привлеченных пользователей, чтобы убедиться, что отчетность не нарушается. Мы сделаем это в транзакции базы данных, чтобы гарантировать, что в случае сбоя одной из двух операций (обновление тега для привлеченных пользователей и обновление тега для нашего аффилированного пользователя) обе они будут отменены.

public function update(Request $request)
    {
        $request->validate([
            'affiliate_tag' => ['required', 'string', 'min:3', 'max:255', Rule::unique('users')->ignoreModel($request->user())],
            'paypal_email' => ['required', 'string', 'max:255', 'email'],
        ]);

        DB::transaction(function () use ($request) {
            if ($request->input('affiliate_tag') != $request->user()->affiliate_tag) {
                User::where('referred_by', $request->user()->affiliate_tag)
                    ->update(['referred_by' => $request->input('affiliate_tag')]);
            }

            $request->user()->update([
                'affiliate_tag' => $request->input('affiliate_tag'),
                'paypal_email' => $request->input('paypal_email'),
            ]);
        });

        return redirect()->route('affiliate');
    }

Наконец, нам понадобится способ подсчитать, сколько заработал наш партнер. Мы можем сделать это, сложив все счета от привлеченных пользователей и рассчитав процент. Для расчета текущего баланса мы будем учитывать только счета, выставленные с даты последней выплаты. Мы будем использовать Пакет процентов Маттиаса , чтобы сделать расчет более понятным.

use Mattiasgeniar\Percentage\Percentage;

const COMMISSION_PERCENTAGE = 20;

public function getReferralBalance() : int
{
    return Percentage::of(static::COMISSION_PERCENTAGE,
        $this->referred
            ->map(fn (User $user) => $user->invoices(false, ['created' => ['gte' => optional($this->cashed_out_at)->timestamp]]))
            ->flatten()
            ->map(fn (\Stripe\Invoice $invoice) => $invoice->subtotal)
            ->sum()
    );
}

Единственное, что осталось сделать, это вручную отправлять партнерам их доходы в конце каждого месяца и обновлять временную метку cashed_out_at . Вот как выглядит наш результат:

Надеюсь, это было полезно! Эта статья началась как ветка в Твиттере , возможно, вы захотите подписаться на меня там вы найдете больше полу-живого кодирования и советов. И если вы хотите увидеть, как я делаю что-то подобное в видеоформате, вы можете подписаться на мой канал YouTube , так как этим летом я планирую выпускать больше видеоконтента. Хорошего вам дня!

Оригинал: “https://dev.to/m1guelpf/building-an-affiliate-program-with-laravel-568m”