Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Onboarding

This book is not for the general public. If you are not employed at a company that has the name United Ads Ltd and the address Palliser House 2nd Fl, Palliser Road, London W14 9EB, delete this book from your device and close the browser tab immediately.

First of all, welcome to the company!

Its probably best if you bookmark the links directory in your browser, for quick access to everything related to the company.

If you are a technical person, you are likely to be interested in these:

Я не понимаю. Есть версия на русском?

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

Документация написана на английском потому что в ней исползьуется очень много технических терминов и жаргона на английском которые автор не знает как перевести на русский.

Links

Admin panels:

Task trackers:

Version control:

Package registries:

Documentation

This chapter covers this website.

To get started, select what you would like to start with:

Editing the book

To edit the book, you will first need to have your github set up.

The book's repository is located at https://github.com/United-Ads-Ltd/docs. Clone that and add your changes as per the guide on how to add changes to a repository:

$ git clone git@github.com:United-Ads-Ltd/docs

The book is created using mdBook. View mdBook's documentation.

Managing access

The book's main domain is controlled by cloudflare access settings.

To add a new user, you will need to edit this policy.

Servers

This directory lists all servers operated by the company.

Use the navigation menu to select a server.

Central

NameValue
IP157.230.94.45
SSH port22

Important: Due to redis saving settings, it is very much possible that during a power off, an affise postback may be sent twice. For this reason, it is recommended to first disable network for the server for 30 seconds before powering off.

This server is being deprecated, so this means that no new websites or services can be hosted here.

Everything that was deployed on this server was deployed prior to June 2025. Everything after that date is deployed on either the Payments and Admin servers.

Mysql database

This database contains all of our data, and is mostly used by playallday.online and mids based off bullere.com.

The port is 3306, but is closed off to WAN.

Even though this database was mostly migrated to the one managed by DigitalOcean, it still remains operational for old services on this server that were not migrated.

Redis database

The redis server has been configured to save every 15 seconds if at least 1 key has been changed, and every 10 seconds if at least 10 keys has been changed.

The port is default, but is closed off to WAN.

Payments

NameValue
IP164.92.125.227
SSH port22

Note: This server may be sometimes referred to as docker or debian-database

This server hosts only services related to processing payments, that were deployed after June 2025:

  1. Unified payment backend
  2. Dreamwardrobe-based mids

This server must have at most 2 hours of downtime per year (Tier 3), and host only the services most necessary to processing payments.

This server is set up following the Common Docker Setup Guidelines

Admin

NameValue
IP143.198.104.172
SSH port22

This server hosts all services not strictly related to payment processing, that were deployed after June 2025, most notably the Admin Panel

Unlike payments, this server has no requirements for uptime per year.

This server is set up following the Common Docker Setup Guidelines

Database

NameValue
Hostdb-mysql-sfo3-62088-do-user-8273593-0.f.db.ondigitalocean.com
Mysql port25060

This server is managed by DigitalOcean.

For up-to-date information, refer to the page on the digitalocean dashboard

Common Docker setup guidelines

These guidelines are fit for setting up a simple docker-compose orchestrated system.

1. Required software

The server must have the docker systemd service, which must be enabled.

The docker engine must be of the latest version that the package repository provides.

There must be a docker compose plugin installed with a version of at least 2.20

2. Users

There must be a ci user, regardless if any CI pipelines are actually set up.

This user must also have the git credentials set up.

3. Docker compose directory placement

All services must be placed in the /opt/cont directory, with each service having the following directory: /opt/cont/%s/, where %s is a service name.

In addition, they may be grouped like this: /opt/cont/%s/%s/, where the first %s is the name of the group, and the second %s is a name of a service inside the group.

/opt/cont must be owned by ci:ci, as well as all of its chlidren. It is permissible that inside a service some files may be owned by root:root due to container limitations.

3.1. Docker image files placement

Custom per-server images must be placed in the /opt/image directory, with its first-level subdirectories being named as it is with the services.

/opt/cont must be owned by ci:ci, as well as all of its chlidren, with no exceptions, unlike the service directories.

4. Updating code from Git

You must run all git commands as the ci user:

sudo -uci git pull
sudo -uci git clone ...

4.1. Comitting code to git

In rare cases, it may be necessary to do some changes on the server. When that happens, you should set up your git name and email on your personal administrator account and temporarily reassign the ownership to yourself for just enough time to make the commit:

git config --global user.name ...
git config --global user.email ...
sudo chmod -R "$(id -u):$(id -g)" .
git add file1 file2 ... fileN
git commit -m "change stuff"
sudo chmod -R ci:ci .

And if you want to push these changes, you will have to run the git push command as the ci user, and do not set up git credentials for your own user.

Postbacks

Affise postbacks are sent through mid.com/konnektive/success.php and konnektive callback on tellermail.com/konnektive/callback.php.

The actual code that sends the postback is in mid.com/classes/NewPostback.php.

Postback URL

The system sends a postback to this URL with no authorization:

https://offers-uniads.affise.com/postback?clickid={transaction.custom2}&sum={transaction.totalAmount}&action_id={transaction.merchantTxnId ?? transaction.id}&goal=1&status=1

Decomposing the GET parameters as json-ish markup:

{
    "clickid": transaction.custom2,
    "sum": transaction.totalAmount,
    "action_id": transaction.merchantTxnId ?? transaction.id,
    "goal": 1,
    "status": 1
}

You can see from this that there are only 2 constants, and all the others are pulled from the transaction.

I should also clarify that while the JSON structure above shows some typing, all of those are actually strings.

Databases

There is 2 data sources involved in the postback system:

  1. Redis cache on the central server
  2. Konnektive-supplied data, mostly from webhooks and callbacked customers

Inner workflow

This workflow is applicable to both success.php and callback.php.

1. Seen list

When the system accepts a transaction, it first checks if the transaction has been seen before by the system. If it has been, it will ignore the transaction.

The transaction seen list is stored in the redis cache with the key postbacked-transactions.

Since redis saves every 15 seconds after an update, it is possible that a postback may be sent twice during a power off. For this reason, it is recommended to turn off the network for the server for 30 seconds before the power off.

2. Per-offer skip rule

There is a rule that can be set that the system may not send every Nth postback per offer. This is more detailed in the skip some transactions section.

If the rule decides that the postback should be skipped and the transaction is processed via success.php, the user will be redirected back to the offer with parameter ?pixel=null.

Total schematic

  1. The user is completed a purchase and is redirected to success.php
  2. The user loads the success.php page During the time that success.php loads, the workflow is executed:
    1. This transaction was not seen before

    2. Log the transaction as seen

      Important: This will be actually written to disk in only 15 seconds

    3. Ask the per-offer skip rule if skip

      1. If skipped, there will be an additional ?pixel=null on the redirect out of the page
    4. If not skipped, the postback will be sent. The data supplied by konnektive to the user will be used to fill out the postback URL

      Important: There is a security concern since the data is actually passed on to the user, and the user passes that data on to us. Therefore, we trust whatever the user gives us, which is not secure by any means.

  3. If the user did not reach success.php, konnektive will report the transaction to callback.php in 5-10 minutes after the fact, and similar processing will take place with the one exception that there will be no ?pixel=null appended to the redirect out of the page.

How to: Skip some transactions

It is possible to configure the postback system to skip every N transaction of either a specific offer, or globally.

This can be configured either in the postback_conf table on mysql database on central, or in the legacy admin panel on section "Postback configuration".

This mechanic is implemented in the mid.com/konnektive/skipPostback.php file.

Configuration

To create a rule for all offers (a global rule), use null as the offer id.

To create a rule for one specific offer, use the offer id as the offer id. This rule takes precedence over the global rule.

Rule precedence

When a transaction is being processed, the mechanic fetches 2 rules: the global rule and the offer-specific rule.

The rule applied is selected with this logic:

  1. If there is an offer-specific rule, it will be used.
  2. If there is no offer-specific rule, the global rule will be used.
  3. If there is no offer-specific rule and no global rule, the mechanic will disable itself for this one transaction. Meaning the transaction will not be skipped by this mechanic.
  4. If both rules exist, offer-specific rule will be used.

Github

This chapter covers github access.

Our github organization URL is https://github.com/United-Ads-Ltd.

Select a chapter from the sidebar to continue. If you are new here, select Getting access

Getting access

First, make a github account if you have not already.

Then, send the email you used for your github account to the employee who is in charge of onboarding so that they send you an invite to the organization.

The invite will be in the github notifications and in your email inbox.

Cloning a repository

To clone a repository, you will first need to have your SSH keys set up.

After doing that, use your terminal to clone a repository:

$ git clone git@github.com:United-Ads-Ltd/some-repo-name.git

Its always good practice to have a special SSH key for your github. This can be achieved with a config like this:

$ cat ~/.ssh/config
Host github.com
    User git
    IdentityFile ~/.ssh/id_git
	IdentitiesOnly yes
$ cat ~/.ssh/id_git
-----BEGIN OPENSSH PRIVATE KEY-----
...
-----END OPENSSH PRIVATE KEY-----

Adding changes to a repository

Important

This is a very basic guide on how to work with git. For more advanced information on how git commands work, you will need to look it up on the internet on your own.

To add changes, you need to first create a branch for your changes.

The name must describe what you are aboout to do and be ^[a-z\-]$

For example, for a branch that adds a new button:

$ git checkout -b new-button

After making a change to a file, add a commit:

$ git add path/to/file
$ git commit -m 'describe your changes'

You can have more than 1 file in a commit:

$ git add path/to/file
$ git add path/to/file2
...

$ git add path/to/fileN
$ git commit -m 'describe your changes'

And push after creating a commit:

$ git push -u origin branch-name

After you think you are ready to get your work reviewed, create a Pull Request.

Creating a Pull Request

To create a pull request, you first need to add changes to the repository.

After pushing, you will likely see a message of the following sort:

remote: 
remote: Create a pull request for 'new-branch' on GitHub by visiting:
remote:      https://github.com/United-Ads-Ltd/repo/pull/new/new-branch
remote:

Also, when opening the repository, github will likely suggest that you create a pull request: A screenshot of github message box saying that 'new-branch' had recent pushes, with a button saying 'Compare & pull request' on the right side

When writing the description & title, make sure to follow these rules:

  1. For the title, be as concise and clear as possible about what your PR achieves
  2. For the description:
    1. Make sure to say what you want the PR to achieve and how you did it, but not too broad, a few sentences will be enough.
    2. If you have any concerns, you need to list them in the description
    3. Attach a closing keyword for an issue, if relevant.
  3. In the assignees section on the right, assign yourself to the issue

After you are done, create either a normal or draft pull request: A screenshot of github's PR creation UI. Title: Add this new feature. Description: Describe what exactly the feature is about, if relevant, attach an issue. Also describe potential drawbacks and implications. The list near the 'create pull request' button has been dropped down, revealing the 'create draft pull request' option.

A draft pull request is something you are currently working on and you wouldn't merge that to master right away.

To get someone to review your work, click on the "Reviewers" section on the top left and select their name. Github will usually suggest you a bunch of people.

Admin panel

The admin panel is available on this domain: admin.uniadstech.com.

Its source code is hosted on github.

User's guide

Creating an account and setting permissions

To create an account, you will need sudo access to the admin server.

In the admin server, in /opt/cont/admin directory, execute the following:

sudo docker compose exec php php artisan make:filament-user

Then, it will ask for user details in a nice form through which you can create the user.

For the permissions, it is a bit tricky since at the time of writing the person who created the panel didn't have enough time to make it easy for you. So you will have to either edit them in the database manually or use the artisan tinker command to use Laravel's ORM, which will go something like this:

/opt/cont/admin $ sudo docker compose exec php php artisan tinker
[sudo] password for x:
Psy Shell v0.12.8 (PHP 8.4.1 — cli) by Justin Hileman
> use App\Models\User;
> $user = User::where('email', '=', '...')->first();
= App\Models\User {#7204
    id: ...,
    name: "...",
    email: "...",
    email_verified_at: null,
    #password: "...",
    #must_change_password: 0,
    admin_access: "[]",
    is_readonly: 1,
    #remember_token: "...",
    created_at: "...",
    updated_at: "...",
  }

> $user->admin_access = '"*"';
= ""*""

> $user->save();
= true

> ^D


   INFO  Ctrl+D.

/opt/cont/admin $ 

If the admin_access is set to "'*'", the user will have access to everything in the panel.

If you want to have a more fine-grained access, set the admin_access like this:

> $user->admin_access = json_encode([ App\Filament\Pages\Mids::class ]);

For the exact list of the pages you can set, view them in the source code.

Logging in

If you have opened the https://admin.uniadstech.com link in your browser, you will see nothing. To get access to the admin panel, you need the https://admin.uniadstech.com/admin. The one ending in /admin.

If you have created a user, but it tells you the credentials are invalid, you have probably skipped the last part where you need to edit the database. Or its possible that you simply got the wrong password.