S2S ping: Difference between revisions

From Wiki Kaminari Click
(Created page with "= Общий алгоритм работы = # Пользователь приходит на ваш сайт. Вы генерируете уникальный <code>kmnrId</code> и отстукиваетесь с ним на сервер Kaminari на URL /ping, мы сохраняем данные в кэш. # Затем вы показываете пользователю страницу с нашим JS-скриптом, добавив в вызов скрипта...")
 
No edit summary
 
(26 intermediate revisions by the same user not shown)
Line 1: Line 1:
= Общий алгоритм работы =
<languages/>
# Пользователь приходит на ваш сайт. Вы генерируете уникальный <code>kmnrId</code> и отстукиваетесь с ним на сервер Kaminari на URL /ping, мы сохраняем данные в кэш.
<translate>
# Затем вы показываете пользователю страницу с нашим JS-скриптом, добавив в вызов скрипта тот же самый <code>kmnrId</code>.
<!--T:1-->
# Мы собираем данные о пользователе и шлём их на сервер Kaminari.
= General workflow =
# На сервере мы проверяем в кэше:
# The user arrives at your website. You generate a unique <code>kmnrId</code> and send it to Kaminari server via /ping URL, and we save the data in the cache.
## Если там есть запись с аналогичным <code>kmnrId</code> (пришедшая на /ping), мы склеиваем данные из кеша с результатами проверки и пишем их в статистику.
# Then you show the user a page with our JavaScript script, adding the same <code>kmnrId</code> to the script call.
## Если записи с таким <code>kmnrId</code> нет, мы ждём пять минут. Если вторая запись так и не приходит, мы пишем в статистику то, что есть.
# We collect user data and send it to the server.
## Так же мы периодически проверяем кэш и ищем просроченные клики, пришедшие на /ping, но так и оставшиеся без пары. Если нам не удалось никак их проверить, мы пишем их в стату со статусом «Технические потери».
# On the server, we check in the cache:
## If there is a record with a similar <code>kmnrId</code> (coming from /ping), we combine the data from the cache with the results of the check and write them to the statistics.
## If there is no record with such an <code>kmnrId</code>, we wait up to five minutes. If the second record still does not arrive, we write what we have in the statistics.
## We also periodically check the cache and look for expired clicks that came to /ping but remained unpaired. If we couldn't verify them in any way, we write them to statistics with the status "Technical Losses."


= Настройка на стороне клиента =
= Client-side configuration = <!--T:2-->


== Отправка данных на https://kaminari.systems/v2/ping ==
<!--T:3-->
Когда пользователь приходит на страницу необходимо с бэкенда (с помощью PHP, Python, NodeJS — в зависимости от того, на чём у вас написан сайт) послать на URL https://kaminari.systems/v2/ping инициализирующую информацию.
== Sending data to https://kaminari.systems/v2/ping ==
When a user visits the page, it is necessary to generate a unique display <code>kmnrId</code> on the client-side backend.


Данные должны слаться в формате JSON.
<!--T:4-->
Then, using PHP, Python, NodeJS, depending on the technology your website is built on, send initializing information to the https://kaminari.systems/v2/ping in the background.


Список параметров.
<!--T:5-->
The request may not be accepted on the first attempt (in which case, the server will respond with a 204 status),  so we recommend sending the request in a loop until you receive a response with a 200 status.


<!--T:6-->
The data should be sent using the POST method in JSON format.
<!--T:7-->
List of parameters:
<!--T:8-->
{| class="wikitable"
{| class="wikitable"
|-
|-
! Название !! Описание параметра !! Обязательный?
! Parameter Name !! Parameter Description !! Mandatory?
|-
| kmnrId || Random impression identifier. Generated on the client side. It can be anything - UUID, random number, random string of text. '''Must be sent as a string.''' || yes
|-
| kmnrKey || Integration identifier within which the impression is analyzed. || yes
|-
| sub1 || Filled sub-tags. If you do not use certain tags, there is no need to send them. || no
|-
| sub2 || ... || no
|-
|-
| kmnrId || Рандомный идентификатор показа. Генерируется на стороне клиента. Это может быть всё, что угодно — UUID, случайное число, случайная текстовая строка. '''Должна слаться в виде строки.''' || да
| sub3 || ... || no
|-
|-
| kmnrKey || Идентификатор интеграции, в рамках которой анализируется показ. || да
| sub4 || ... || no
|-
| sub5 || ... || no
|-
| sub6 || ... || no
|-
| sub7 || ... || no
|}
 
<!--T:9-->
In addition, the following headers obtained from the user should be added to this request:
{| class="wikitable"
|-
|-
| sub1 || Заполенные суб-метки. Если какие-то метки вы не используете, их слать не обязательно. || нет
! Parameter Name !! Parameter Description !! Mandatory?
|-
|-
| sub2 || ... || нет
| user-agent || User agent || yes
|-
|-
| sub3 || ... || нет
| referer || Page the user came from || no
|-
|-
| sub4 || ... || нет
| accept-language || Browser interface language || yes
|-
|-
| sub5 || ... || нет
| x-original-ip || Real user IP || yes
|-
|-
| sub6 || ... || нет
| x-forwarded-for || IP || yes
|-
|-
| sub7 || ... || нет
| x-real-ip || IP || no
|-
|-
| sub8 || ... || нет
| sec-ch-ua-arch || || no
|-
|-
| sub9 || ... || нет
| sec-ch-ua || || no
|-
|-
| sub10 || ... || нет
| sec-ch-ua-full-version || || no
|}
 
Также в этот запрос должны быть добавлены следующие заголовки, полученные от пользователя:
{| class="wikitable"
|-
|-
! Название !! Описание
| device-memory || || no
|-
|-
| user-agent || user agent пользователя
| dpr || || no
|-
|-
| referer || страница, с которой пришёл пользователь
| sec-ch-ua-mobile || || no
|-
|-
| accept-language || язык интерфейса браузера
| sec-ch-ua-model || || no
|-
|-
| x-original-ip || IP
| sec-ch-ua-platform || || no
|-
|-
| x-forwarded-for || IP
| sec-ch-ua-platform-version || || no
|-
|-
| x-real-ip || IP
| viewport-width || || no
|}
|}


Пример запроса:
<!--T:10-->
Example in CURL:
<pre>
<pre>
curl 'https://kaminari.systems/v2/ping' \
curl 'https://kaminari.systems/v2/ping' \
   -H 'accept: application/json' \  
   -H 'accept: application/json' \
   -H 'accept-language: en-US,en;q=0.9' \  
   -H 'accept-language: en-US,en;q=0.9,uk;q=0.8' \
   -H 'cache-control: no-cache' \  
   -H 'cache-control: no-cache' \
   -H 'content-type: text/plain;charset=UTF-8' \
   -H 'content-type: application/json;charset=UTF-8' \
   -H 'origin: https://test.com' \  
   -H 'origin: https://test.com' \
   -H 'pragma: no-cache' \  
   -H 'pragma: no-cache' \
   -H 'referer: https://test.com/' \  
   -H 'referer: https://test.com/' \
   -H 'user-agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36'  
   -H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36' \
   --data-raw '{"kmnrKey":"aaaaaaaa","kmnrId":"1125570260","sub1":"test","sub2":"1001","sub3":"10","sub4":"1385282124113622","sub5":"222","sub6":"555666","sub7":"8833705265931305","sub8":"94839150"}'  
  -H 'x-original-ip: 127.0.0.1' \
  -H 'x-forwarded-for: 192.168.0.1' \
  -H 'sec-ch-ua-arch: "x86"' \
  -H 'sec-ch-ua: "Google Chrome";v="129", "Not=A?Brand";v="8", "Chromium";v="129"' \
  -H 'sec-ch-ua-full-version: "129.0.6668.100"' \
  -H 'device-memory: 8' \
  -H 'dpr: 2' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'sec-ch-ua-model: ""' \
  -H 'sec-ch-ua-platform: "macOS"' \
  -H 'sec-ch-ua-platform-version: "14.5.0"' \
  -H 'viewport-width: 981' \
   --data-raw '{"kmnrKey":"XXXXXXXX","kmnrId":"1125570260","sub1":"test","sub2":"1001","sub3":"10","sub4":"1385282124113622","sub5":"222","sub6":"555666","sub7":"8833705265931305"}'  
   --compressed
   --compressed
</pre>
</pre>


== Добавление kmnrId в скрипт ==
<!--T:11-->
Сгенерированный kmnrId затем нужно добавить в вызов JS-скрипта на странице.
Example in PHP:
<pre>
$url = 'https://kaminari.systems/v2/ping';
$ch = curl_init( $url );
$payload = json_encode([
    'kmnrKey' => 'XXXXXXXX',
    'kmnrId' => '1125570260',
    'sub1' => 'test',
    'sub2' => '1001',
    'sub3' => '10',
    'sub4' => '1385282124113622',
    'sub5' => '222',
    'sub6' => '555666',
    'sub7' => '8833705265931305',
]);


<!--T:12-->
curl_setopt( $ch, CURLOPT_POSTFIELDS, $payload );
curl_setopt( $ch, CURLOPT_HTTPHEADER, [
    'Content-Type:application/json;charset=UTF-8',
    'Accept-Language:' . $_SERVER['HTTP_ACCEPT_LANGUAGE'],
    'User-Agent:' . $_SERVER['HTTP_USER_AGENT'],
    isset($_SERVER['HTTP_REFERER']) ? 'Referer:' . $_SERVER['HTTP_REFERER'] : '',
    'X-Original-Ip:' . $_SERVER['REMOTE_ADDR'],
    isset($_SERVER['X_FORWARDED_FOR']) ? 'X-Forwarded-For:' . $_SERVER['X_FORWARDED_FOR'] : '',
    isset($_SERVER['HTTP_SEC_CH_UA_ARCH']) ? 'Sec-CH-UA-Arch:' . $_SERVER['HTTP_SEC_CH_UA_ARCH'] : '',
    isset($_SERVER['HTTP_SEC_CH_UA']) ? 'Sec-CH-UA:' . $_SERVER['HTTP_SEC_CH_UA'] : '',
    isset($_SERVER['HTTP_SEC_CH_UA_FULL_VERSION']) ? 'Sec-CH-UA-Full-Version:' . $_SERVER['HTTP_SEC_CH_UA_FULL_VERSION'] : '',
    isset($_SERVER['HTTP_DEVICE_MEMORY']) ? 'Device-Memory:' . $_SERVER['HTTP_DEVICE_MEMORY'] : '',
    isset($_SERVER['HTTP_DPR']) ? 'DPR:' . $_SERVER['HTTP_DPR'] : '',
    isset($_SERVER['HTTP_SEC_CH_UA_MOBILE']) ? 'Sec-CH-UA-Mobile:' . $_SERVER['HTTP_SEC_CH_UA_MOBILE'] : '',
    isset($_SERVER['HTTP_SEC_CH_UA_MODEL']) ? 'Sec-CH-UA-Model:' . $_SERVER['HTTP_SEC_CH_UA_MODEL'] : '',
    isset($_SERVER['HTTP_SEC_CH_UA_PLATFORM']) ? 'Sec-CH-UA-Platform:' . $_SERVER['HTTP_SEC_CH_UA_PLATFORM'] : '',
    isset($_SERVER['HTTP_SEC_CH_UA_PLATFORM_VERSION']) ? 'Sec-CH-UA-Platform-Version:' . $_SERVER['HTTP_SEC_CH_UA_PLATFORM_VERSION'] : '',
    isset($_SERVER['HTTP_VIEWPORT_WIDTH']) ? 'Viewport-Width:' . $_SERVER['HTTP_VIEWPORT_WIDTH'] : '',
]);
curl_exec($ch);
curl_close($ch);
</pre>
<!--T:13-->
== Adding kmnrId to the script ==
The generated <code>kmnrId</code>, as well as the <code>kmnrKey</code> and sub-tags, should then be passed into the call of the JavaScript script on the page.
<!--T:14-->
<pre>
<pre>
<script>
<script>
     window.kmnr = {
     window.kmnr = {
         kmnrKey: 'aaaaaaaa',
         kmnrKey: 'XXXXXXXX',
         kmnrId: '1125570260',
         kmnrId: '1125570260',
         sub1: 'bbbbbbbb',
         sub1: 'test',
         sub2: 'cccccccc',
         sub2: '1001',
        sub3: '10',
        sub4: '1385282124113622',
        sub5: '222',
        sub6: '555666',
        sub7: '8833705265931305',
     };
     };


     var kmnrSc = document.createElement('script');
     <!--T:15-->
var kmnrSc = document.createElement('script');
     var kmnrPrnt = document.getElementsByTagName('head')[0] || document.body;
     var kmnrPrnt = document.getElementsByTagName('head')[0] || document.body;
     kmnrSc.setAttribute('async', true);
     kmnrSc.setAttribute('async', true);
Line 99: Line 189:
</script>
</script>
</pre>
</pre>
= Testing = <!--T:16-->
<!--T:17-->
For testing data transmission and ensuring their compatibility with the API, you can use the following URL: https://kaminari.systems/v1/pingtest
<!--T:18-->
=== Example in PHP: ===
<pre>
$url = 'https://kaminari.systems/v1/pingtest';
try {
    $ch = curl_init( $url );
    $payload = json_encode([
        'kmnrKey' => 'XXXXXXXX',
        'kmnrId' => (string)mt_rand(),
        'sub1' => 'test',
        'sub2' => '1001',
        'sub3' => '10',
        'sub4' => '40',
        'sub5' => '555',
        'sub6' => '6',
        'sub7' => '77777',
    ]);
    <!--T:19-->
curl_setopt( $ch, CURLOPT_POSTFIELDS, $payload );
    curl_setopt( $ch, CURLOPT_HTTPHEADER, [
        'Content-Type:application/json',
        'Accept-Language:' . $_SERVER['HTTP_ACCEPT_LANGUAGE'],
        'User-Agent:' . $_SERVER['HTTP_USER_AGENT'],
        'DPR:2',
        isset($_SERVER['HTTP_REFERER']) ? 'Referer:' . $_SERVER['HTTP_REFERER'] : 'Referer:""',
    ]);
    curl_setopt( $ch, CURLOPT_HEADER, true);
    curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
   
    $response = curl_exec($ch);
    $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
    $header = substr($response, 0, $header_size);
    $body = substr($response, $header_size);
    $body = str_replace('", "', '",<br />    "', $body);
    $body = str_replace('{ "', '{<br />    "', $body);
    echo "&lt;pre&gt;1. Response length: $header_size&lt;/pre&gt;";
    echo "&lt;pre&gt;2. Response body: $body&lt;/pre&gt;";
    <!--T:20-->
'curl error: ' . curl_error($ch);
    curl_close($ch);
} catch (Exception $e) {
    echo "Error register User: {$e->getMessage()}";
}
</pre>
<!--T:21-->
Response :
<pre>
1. Response length: 287
2. Response body: {"errors": {
    "X-Forwarded-For":"recommended to add in request headers",
    "X-Real-IP":"recommended to add in request headers",
    "sec-ch-ua-arch":"recommended to add in request headers",
    "sec-ch-ua":"recommended to add in request headers",
    "sec-ch-ua-full-version":"recommended to add in request headers",
    "device-memory":"recommended to add in request headers",
    "sec-ch-ua-mobile":"recommended to add in request headers",
    "sec-ch-ua-model":"recommended to add in request headers",
    "sec-ch-ua-platform":"recommended to add in request headers",
    "sec-ch-ua-platform-version":"recommended to add in request headers",
    "viewport-width":"recommended to add in request headers"}}
</pre>
<!--T:22-->
=== Example in Node.js: ===
<pre>
const https = require('https');
<!--T:23-->
const payload = JSON.stringify({
    'kmnrKey': 'XXXXXXXX',
    'kmnrId': (Math.random() + 1).toString(36).substring(9),
    'sub1': 'test',
    'sub2': '1001',
    'sub3': '10',
    'sub4': '40',
    'sub5': '555',
    'sub6': '6',
    'sub7': '77777',
});
<!--T:24-->
const options = {
    host: 'kaminari.click',
    port: 443,
    path: '/v1/pingtest',
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'Content-Length': Buffer.byteLength(payload),
        'Accept-Language': 'en-US,en;q=0.5',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0',
        'DPR': '2',
        'Referer': '""',
    }
};
<!--T:25-->
const req = https
    .request(options, (res) => {
        let data = '';
        <!--T:26-->
res.on('data', (chunk) => {
            data += chunk;
        });
       
        res.on('end', () => {
            try {
                data = JSON.parse(data);
                console.log(data);
            } catch {
                console.log(data);
            }
        });
    });
<!--T:27-->
req.on('error', (err) => {
    console.log("Error: " + err.message);
});
req.write(payload);
req.end();
</pre>
<!--T:28-->
Response:
<pre>
{
  errors: {
    'X-Forwarded-For': 'recommended to add in request headers',
    'X-Real-IP': 'recommended to add in request headers',
    'sec-ch-ua-arch': 'recommended to add in request headers',
    'sec-ch-ua': 'recommended to add in request headers',
    'sec-ch-ua-full-version': 'recommended to add in request headers',
    'device-memory': 'recommended to add in request headers',
    'sec-ch-ua-mobile': 'recommended to add in request headers',
    'sec-ch-ua-model': 'recommended to add in request headers',
    'sec-ch-ua-platform': 'recommended to add in request headers',
    'sec-ch-ua-platform-version': 'recommended to add in request headers',
    'viewport-width': 'recommended to add in request headers'
  }
}
</pre>
[[Category:Features]]
</translate>

Latest revision as of 16:53, 11 October 2024

Other languages:

General workflow

  1. The user arrives at your website. You generate a unique kmnrId and send it to Kaminari server via /ping URL, and we save the data in the cache.
  2. Then you show the user a page with our JavaScript script, adding the same kmnrId to the script call.
  3. We collect user data and send it to the server.
  4. On the server, we check in the cache:
    1. If there is a record with a similar kmnrId (coming from /ping), we combine the data from the cache with the results of the check and write them to the statistics.
    2. If there is no record with such an kmnrId, we wait up to five minutes. If the second record still does not arrive, we write what we have in the statistics.
    3. We also periodically check the cache and look for expired clicks that came to /ping but remained unpaired. If we couldn't verify them in any way, we write them to statistics with the status "Technical Losses."

Client-side configuration

Sending data to https://kaminari.systems/v2/ping

When a user visits the page, it is necessary to generate a unique display kmnrId on the client-side backend.

Then, using PHP, Python, NodeJS, depending on the technology your website is built on, send initializing information to the https://kaminari.systems/v2/ping in the background.

The request may not be accepted on the first attempt (in which case, the server will respond with a 204 status), so we recommend sending the request in a loop until you receive a response with a 200 status.

The data should be sent using the POST method in JSON format.

List of parameters:

Parameter Name Parameter Description Mandatory?
kmnrId Random impression identifier. Generated on the client side. It can be anything - UUID, random number, random string of text. Must be sent as a string. yes
kmnrKey Integration identifier within which the impression is analyzed. yes
sub1 Filled sub-tags. If you do not use certain tags, there is no need to send them. no
sub2 ... no
sub3 ... no
sub4 ... no
sub5 ... no
sub6 ... no
sub7 ... no

In addition, the following headers obtained from the user should be added to this request:

Parameter Name Parameter Description Mandatory?
user-agent User agent yes
referer Page the user came from no
accept-language Browser interface language yes
x-original-ip Real user IP yes
x-forwarded-for IP yes
x-real-ip IP no
sec-ch-ua-arch no
sec-ch-ua no
sec-ch-ua-full-version no
device-memory no
dpr no
sec-ch-ua-mobile no
sec-ch-ua-model no
sec-ch-ua-platform no
sec-ch-ua-platform-version no
viewport-width no

Example in CURL:

curl 'https://kaminari.systems/v2/ping' \
  -H 'accept: application/json' \
  -H 'accept-language: en-US,en;q=0.9,uk;q=0.8' \
  -H 'cache-control: no-cache' \
  -H 'content-type: application/json;charset=UTF-8' \
  -H 'origin: https://test.com' \
  -H 'pragma: no-cache' \
  -H 'referer: https://test.com/' \
  -H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36' \
  -H 'x-original-ip: 127.0.0.1' \
  -H 'x-forwarded-for: 192.168.0.1' \
  -H 'sec-ch-ua-arch: "x86"' \
  -H 'sec-ch-ua: "Google Chrome";v="129", "Not=A?Brand";v="8", "Chromium";v="129"' \
  -H 'sec-ch-ua-full-version: "129.0.6668.100"' \
  -H 'device-memory: 8' \
  -H 'dpr: 2' \
  -H 'sec-ch-ua-mobile: ?0' \
  -H 'sec-ch-ua-model: ""' \
  -H 'sec-ch-ua-platform: "macOS"' \
  -H 'sec-ch-ua-platform-version: "14.5.0"' \
  -H 'viewport-width: 981' \
  --data-raw '{"kmnrKey":"XXXXXXXX","kmnrId":"1125570260","sub1":"test","sub2":"1001","sub3":"10","sub4":"1385282124113622","sub5":"222","sub6":"555666","sub7":"8833705265931305"}' 
  --compressed

Example in PHP:

$url = 'https://kaminari.systems/v2/ping';
$ch = curl_init( $url );
$payload = json_encode([
    'kmnrKey' => 'XXXXXXXX',
    'kmnrId' => '1125570260',
    'sub1' => 'test',
    'sub2' => '1001',
    'sub3' => '10',
    'sub4' => '1385282124113622',
    'sub5' => '222',
    'sub6' => '555666',
    'sub7' => '8833705265931305',
]);

curl_setopt( $ch, CURLOPT_POSTFIELDS, $payload );
curl_setopt( $ch, CURLOPT_HTTPHEADER, [
    'Content-Type:application/json;charset=UTF-8',
    'Accept-Language:' . $_SERVER['HTTP_ACCEPT_LANGUAGE'],
    'User-Agent:' . $_SERVER['HTTP_USER_AGENT'],
    isset($_SERVER['HTTP_REFERER']) ? 'Referer:' . $_SERVER['HTTP_REFERER'] : '',
    'X-Original-Ip:' . $_SERVER['REMOTE_ADDR'],
    isset($_SERVER['X_FORWARDED_FOR']) ? 'X-Forwarded-For:' . $_SERVER['X_FORWARDED_FOR'] : '',
    isset($_SERVER['HTTP_SEC_CH_UA_ARCH']) ? 'Sec-CH-UA-Arch:' . $_SERVER['HTTP_SEC_CH_UA_ARCH'] : '',
    isset($_SERVER['HTTP_SEC_CH_UA']) ? 'Sec-CH-UA:' . $_SERVER['HTTP_SEC_CH_UA'] : '',
    isset($_SERVER['HTTP_SEC_CH_UA_FULL_VERSION']) ? 'Sec-CH-UA-Full-Version:' . $_SERVER['HTTP_SEC_CH_UA_FULL_VERSION'] : '',
    isset($_SERVER['HTTP_DEVICE_MEMORY']) ? 'Device-Memory:' . $_SERVER['HTTP_DEVICE_MEMORY'] : '',
    isset($_SERVER['HTTP_DPR']) ? 'DPR:' . $_SERVER['HTTP_DPR'] : '',
    isset($_SERVER['HTTP_SEC_CH_UA_MOBILE']) ? 'Sec-CH-UA-Mobile:' . $_SERVER['HTTP_SEC_CH_UA_MOBILE'] : '',
    isset($_SERVER['HTTP_SEC_CH_UA_MODEL']) ? 'Sec-CH-UA-Model:' . $_SERVER['HTTP_SEC_CH_UA_MODEL'] : '',
    isset($_SERVER['HTTP_SEC_CH_UA_PLATFORM']) ? 'Sec-CH-UA-Platform:' . $_SERVER['HTTP_SEC_CH_UA_PLATFORM'] : '',
    isset($_SERVER['HTTP_SEC_CH_UA_PLATFORM_VERSION']) ? 'Sec-CH-UA-Platform-Version:' . $_SERVER['HTTP_SEC_CH_UA_PLATFORM_VERSION'] : '',
    isset($_SERVER['HTTP_VIEWPORT_WIDTH']) ? 'Viewport-Width:' . $_SERVER['HTTP_VIEWPORT_WIDTH'] : '',
]);
curl_exec($ch);
curl_close($ch);

Adding kmnrId to the script

The generated kmnrId, as well as the kmnrKey and sub-tags, should then be passed into the call of the JavaScript script on the page.

<script>
    window.kmnr = {
        kmnrKey: 'XXXXXXXX',
        kmnrId: '1125570260',
        sub1: 'test',
        sub2: '1001',
        sub3: '10',
        sub4: '1385282124113622',
        sub5: '222',
        sub6: '555666',
        sub7: '8833705265931305',
    };

    var kmnrSc = document.createElement('script');
    var kmnrPrnt = document.getElementsByTagName('head')[0] || document.body;
    kmnrSc.setAttribute('async', true);
    kmnrSc.setAttribute('charset', 'utf-8');
    kmnrSc.src = '//kaminari.systems/v1/script.js?kmnrKey=' + window.kmnr.kmnrKey;
    kmnrPrnt && kmnrPrnt.appendChild(kmnrSc);
</script>

Testing

For testing data transmission and ensuring their compatibility with the API, you can use the following URL: https://kaminari.systems/v1/pingtest

Example in PHP:

$url = 'https://kaminari.systems/v1/pingtest';
try {
    $ch = curl_init( $url );
    $payload = json_encode([
        'kmnrKey' => 'XXXXXXXX',
        'kmnrId' => (string)mt_rand(),
        'sub1' => 'test',
        'sub2' => '1001',
        'sub3' => '10',
        'sub4' => '40',
        'sub5' => '555',
        'sub6' => '6',
        'sub7' => '77777',
    ]);

    curl_setopt( $ch, CURLOPT_POSTFIELDS, $payload );
    curl_setopt( $ch, CURLOPT_HTTPHEADER, [
        'Content-Type:application/json',
        'Accept-Language:' . $_SERVER['HTTP_ACCEPT_LANGUAGE'],
        'User-Agent:' . $_SERVER['HTTP_USER_AGENT'],
        'DPR:2',
        isset($_SERVER['HTTP_REFERER']) ? 'Referer:' . $_SERVER['HTTP_REFERER'] : 'Referer:""',
    ]);
    curl_setopt( $ch, CURLOPT_HEADER, true);
    curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
    
    $response = curl_exec($ch);
    $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
    $header = substr($response, 0, $header_size);
    $body = substr($response, $header_size);
    $body = str_replace('", "', '",<br />    "', $body);
    $body = str_replace('{ "', '{<br />    "', $body);
    echo "<pre>1. Response length: $header_size</pre>";
    echo "<pre>2. Response body: $body</pre>";

    'curl error: ' . curl_error($ch);
    curl_close($ch);
} catch (Exception $e) {
    echo "Error register User: {$e->getMessage()}";
}

Response :

1. Response length: 287
2. Response body: {"errors": {
    "X-Forwarded-For":"recommended to add in request headers",
    "X-Real-IP":"recommended to add in request headers",
    "sec-ch-ua-arch":"recommended to add in request headers",
    "sec-ch-ua":"recommended to add in request headers",
    "sec-ch-ua-full-version":"recommended to add in request headers",
    "device-memory":"recommended to add in request headers",
    "sec-ch-ua-mobile":"recommended to add in request headers",
    "sec-ch-ua-model":"recommended to add in request headers",
    "sec-ch-ua-platform":"recommended to add in request headers",
    "sec-ch-ua-platform-version":"recommended to add in request headers",
    "viewport-width":"recommended to add in request headers"}}

Example in Node.js:

const https = require('https');

const payload = JSON.stringify({
    'kmnrKey': 'XXXXXXXX',
    'kmnrId': (Math.random() + 1).toString(36).substring(9),
    'sub1': 'test',
    'sub2': '1001',
    'sub3': '10',
    'sub4': '40',
    'sub5': '555',
    'sub6': '6',
    'sub7': '77777',
});

const options = {
    host: 'kaminari.click',
    port: 443,
    path: '/v1/pingtest',
    method: 'POST',
    headers: { 
        'Content-Type': 'application/json',
        'Content-Length': Buffer.byteLength(payload),
        'Accept-Language': 'en-US,en;q=0.5',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/113.0',
        'DPR': '2',
        'Referer': '""',
    }
};

const req = https
    .request(options, (res) => {
        let data = '';

        res.on('data', (chunk) => {
            data += chunk;
        });
        
        res.on('end', () => {
            try {
                data = JSON.parse(data);
                console.log(data);
            } catch {
                console.log(data);
            }
        });
    });

req.on('error', (err) => {
    console.log("Error: " + err.message);
});
req.write(payload);
req.end();

Response:

{
  errors: {
    'X-Forwarded-For': 'recommended to add in request headers',
    'X-Real-IP': 'recommended to add in request headers',
    'sec-ch-ua-arch': 'recommended to add in request headers',
    'sec-ch-ua': 'recommended to add in request headers',
    'sec-ch-ua-full-version': 'recommended to add in request headers',
    'device-memory': 'recommended to add in request headers',
    'sec-ch-ua-mobile': 'recommended to add in request headers',
    'sec-ch-ua-model': 'recommended to add in request headers',
    'sec-ch-ua-platform': 'recommended to add in request headers',
    'sec-ch-ua-platform-version': 'recommended to add in request headers',
    'viewport-width': 'recommended to add in request headers'
  }
}