Connecting Tinkoff payment to Telegram bot in pure php

Recently added payment to my Telegram bot. After some research, the choice fell on Tinkoff (now T-bank). The bot itself works on PHP without auxiliary libraries. Perhaps someone will find my experience and code useful.

The scheme is as follows:

  1. The user in the bot selects the amount he wants to top up his balance with.

  2. I am creating a tricky request to Tinkoff (code below).

  3. Tinkoff responds with a payment link.

  4. I send this link to the user.

  5. The user follows the link to the Tinkoff domain, selects a convenient method and pays.

  6. Tinkoff sends me not only a letter, but also a POST request.

  7. I take from this request the amount, status and, most importantly, the bot user ID.

  8. I am writing a thank you letter to a user on Telegram.

Brief payment scheme from the bot

Brief payment scheme from the bot

About choosing a payment method

There are two regular ones payment method in Telegram bot:

  1. Stars – the name suggests that this is ≠ money, so for now they won't force you Don't want.

  2. Through payment providers. This is when a window pops up right inside Telegram.

I tried the second method, started setting up ЮKassa. The scheme looks working, but there are some nuances:

  1. You need to enter the card number. It's not as cool as going to the bank's app in one click and paying there safely. As far as I understand, you can't do that in Tg.

  2. It's been a long time with them somehow…

I moved on to the next option: acquiring from Tinkoff. I thought that at least on the site I could easily integrate a payment module and accept payments there, and I already had an account. I used the Website Builder also from Tinkoff, added payment there, passed moderation, tested. Again, nuances:

  1. You need to somehow transfer the user ID from Telegram. Let's say you can embed it in the URL.

  2. Next, on the payment form, this id needs to be inserted into the form. Let's say you can write your own code in JS (the constructor allows it).

  3. Then there was a completely unexpected surprise: despite the obvious setting “notify about payment via http”, the bank does not want to send me such a notification. Support explained:

    If the payment is made from our site, then we always pass our NotificationURL for notification in init and it cannot be changed or deleted, since without it we will not know that the payment has taken place on the site, and we will not be able to pass the order to orders. Have a nice evening!

    That's all I have authority for. Of course, I can process a few payments manually, but I want to immediately notify the user that their money has arrived.

Luckily, I came across a support employee who understood my final plan. He explained that the site was an unnecessary link in my scheme, that I needed to connect via API, and gave me a link to the documentation. Well, the site was still useful for posting the offer.

How to create a payment link in Tinkoff

I won’t write about creating a payment terminal for Internet acquiring: this is done through the interface, there is reference. I'll focus on the part that is labeled as “programmer's help” in the help.

To get a payment link we need only one request Initit is described here.

Before sending you need to in an elegant way encrypt the password in the request body and get Token.

So, step by step:

  1. Form the request body – a JSON object with required fields:

    1. TerminalKey — taken here: Personal account → Internet acquiring → Stores → [Магазин] → Terminals → Working terminal → Configure, on the right under the word “Terminal”.

    2. Amount — amount in kopecks (integer).

    3. OrderId — must be unique. This is where I hide the user ID so that I can get it later in the payment notification, and add the unique order number through a hyphen.

    4. More Description semi-mandatory. I always add it, the user sees it.

  2. Collect an array of transmitted data in the form of key-value pairs. Only the parameters of the root object need to be added to the array. Nested objects and arrays do not participate in the token calculation.

  3. Add to array Password — taken from your personal account, in the same place as the Terminal.

  4. Sort array alphabetically by key.

  5. Concatenate only the pair values ​​into one string (without adding separators).

  6. Apply the SHA-256 hash function (with UTF-8 support) to the string.

  7. Place the resulting value into the parameter value Token into the request body (which was created in step 1).

  8. Remove from request body Password There is no need to transfer it.

  9. Send a POST request with a JSON body.

  10. Get the answer and extract the treasured link from it.

This is roughly how I got it in php:

<?php
function tinkoff_getLink($amount, $chatId, $orderNumber) {

    $link = false;

    // Amount нужно передать в копейках, целое число
    // OrderId должен быть уникальным
    $data = [
        "Amount"      => $amount*100,
        "Description" => 'Пополнение баланса бота "Мониторинг сайта"',
        "OrderId"     => "$chatId-n$orderNumber",
        "TerminalKey" => TINKOFF_TERMINAL_KEY,
        "Password"    => TINKOFF_TERMINAL_PASSWORD
    ];

    ksort($data);
    
    // Получаем все значения из массива
    $values = array_values($data);

    // Конкатенируем все значения в одну строку
    $concatenatedString = implode('', $values);

    // Хэшируем
    $hashedString = hash('sha256', $concatenatedString);

    $data['Token'] = $hashedString;
    unset($data['Password']);
    
    $postDataJson = json_encode($data);

    
    // Настройки cURL
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, TINKOFF_INIT_URL);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $postDataJson);

    // Добавляем заголовки для указания того, что тело запроса содержит JSON
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Content-Type: application/json',
        'Content-Length: ' . strlen($postDataJson)
    ]);

    // Выполнение запроса и получение ответа
    $output = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($output === false || $httpCode !== 200) {
        error_log('Не удалось выполнить запрос, HTTP код: ' . $httpCode);
        return false;
    }
    $outputArray = json_decode($output, true); // true означает декодирование в массив
    
    if (json_last_error() !== JSON_ERROR_NONE) {
        error_log('Ошибка при декодировании JSON: ' . json_last_error_msg());
        return false;
    }

    if (isset($outputArray['Success']) && $outputArray['Success'] === true 
        && isset($outputArray['PaymentURL'])) {

        return $outputArray['PaymentURL'];
    } else {
        error_log("Ссылка не пришла");
        return false;
    }
}

We send the resulting link to the user, who can follow it and pay.

How to know that payment has arrived

In the terminal settings, you need to enable notifications “By HTTP protocol” and specify your URL. Or you can send NotificationURL in the method Initit will take precedence over the terminal setting (not tested).

Notification Format Documentation Here. The IP addresses from which these notifications will come and the verification algorithm are also listed there. Token – similar to when sending Init.

It is important to respond to this notification correctly (code 200, “OK” in the body), otherwise the acquiring will stubbornly send the same notifications over and over again. To protect against duplicate receipts in the database, I recommend allowing only unique combinations Status + PaymentId. Or across the field Token.

To understand which user needs to be thanked for payment, I extract his id from the field OrderId. I previously composed this value from the user ID and unique order number, now it has returned in the payment notification. There is also an option to ask support to enable the transfer of the field DATAin which you can send arbitrary data.

Lyrical digression.

The ease and speed of the payment process – from scanning a QR code to receiving a message in Telegram – is fascinating. Especially when part of the code from this process was written by ChatGPT myself.

It is surprising that somewhere they still consider writing and sending paper bank checks by mail and recording them in a checkbook to be a normal payment process…

What kind of bot is this anyway?

Bot for monitoring website availability. Checks:

  • Code and response speed.

  • Where does the redirect lead?

  • <title> site.

  • Domain registration period.

  • Validity period of the SSL certificate.

And if something listed on the site changes, the bot sends a notification. And it also reminds you to renew the domain/update the certificate a few days before the expiration date.

I focused only on these functions and tried to make them well and understandably. I will be glad if you try and tell me if it worked. Monitoring one site there will remain free, but I will be no less glad if you try payment too 😉

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *