Легкий старт

Алгоритм подключения к системе

Что нужно для старта?

1. В Вашем приложении Kaspi Pay создать новую точку продаж.

Подробнее
Для новой точки продаж необходимо использовать отдельное устройство, так как на устройстве будет постоянно запущено приложение SyncMe

2. Для новой точки продаж сформировать QR для приема оплаты.

Подробнее
Новый QR будет использоваться для приема и отслеживания оплаты

3. На устройство установить наше приложение SyncMe

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

Алгоритм работы

Схема и описание взаимодействия

схема взаимодействия

Алгоритм работы простыми словами

1 шаг. Ваш вендинговый аппарат (сайт и т. п.) формирует запрос на ожидаемую сумму и отправляет его на сервер обмена MQTT.

Подробнее

Простыми словами, Ваше устройство (сайт) публикует "объявление" о том, что оно сейчас ожидает оплату с определенной суммой.


Какие действия необходимы от покупателя?


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


При сканировании QR покупатель вводит сумму самостоятельно.


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


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

К примеру, на одном устройстве стоимость товара — 100 тг, а на другом — 102 тг.

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

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

2 шаг. Сервер обмена информирует сервер хранения о новом ожидаемом платеже.

Подробнее

На данном этапе сервер обмена узнаёт и добавляет в свою очередь задачу — оповестить ваш аппарат (сайт) с определённым ID о поступившей оплате.

3 шаг. Мобильный телефон, на котором установлено приложение SyncMe, ожидает уведомление от банка о поступлении платежа и пересылает его на сервер обработки.

Подробнее

На данном этапе сервер и вендинговый аппарат (сайт или приложение) находятся в режиме ожидания подтверждения оплаты.

4 шаг. Сервер хранения и обработки ожидает уведомлений от вашего устройства на котором установлено SyncMe и обрабатывает платеж с последующей отправкой на сервер обмена

Подробнее

Возможны 3 варианта обработки:

  • Успешная оплата при поступлении необходимой суммы.
    Сервер обработки публикует на сервер обмена сообщение об успешной оплате и сохраняет у себя подробную информацию.

  • Превышение лимита времени для оплаты. Для оплаты сервер обмена ожидает 3 минуты (можно изменить в личном кабинете).
    Сервер обработки публикует на сервер обмена сообщение об отмене ожидания по причине превышения тайм-аута.

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

    в данном случае сервер отправит уведомление в Telegram об ошибочном платеже
  • Почему MQTT?

    Преимущества данного протокола

    MQTT (Message Queuing Telemetry Transport) — это лёгкий протокол обмена сообщениями, специально созданный для устройств IoT и систем, где важно быстрое и надёжное взаимодействие между множеством клиентов.

    • Минимальные задержки. MQTT обеспечивает моментальную доставку сообщений даже при слабом интернет-соединении.
    • 🔁 Двусторонний обмен данными. Протокол поддерживает как публикацию, так и подписку — устройства могут отправлять и получать сообщения в реальном времени.
    • 🧩 Масштабируемость. Легко подключать сотни и тысячи клиентов без потери производительности.
    • 🔒 Надёжность и контроль качества доставки. MQTT поддерживает уровни QoS (Quality of Service), гарантирующие доставку сообщений.
    • 💡 Простая интеграция. Протокол поддерживается множеством языков и платформ: Python, Node.js, C++, Android, и др.

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

    Обзор системы

    Архитектура MQTT коммуникации

    Запросы

    Аппараты публикуют запросы на ожидание оплаты в топик:

    vending/requests

    Ответы

    Сервер отправляет ответы о результатах оплаты в топик:

    vending/response/CENTRAL/001

    Гарантия доставки

    Используется QoS=1 для гарантированной доставки сообщений
    Автоматическое переподключение при обрыве связи

    📤 Отправка запроса на оплату

    Формат и примеры запросов

    Формат JSON запроса

    
    {
        "machine_id": "CENTRAL_001",
        "user_id": "CENTRAL", 
        "amount": 10.00,
        "transaction_id": "jORfB2vGe",
        "callback_topic": "vending/response/CENTRAL/001"
    }
                                

    Поля запроса:

    machine_id Уникальный ID аппарата. Должен состоять из вашего ID и номера аппарата (уникальный для каждого терминала)
    user_id ID оператора (будет присвоен сервисом )
    amount Сумма оплаты
    transaction_id Набор случайных чисел является уникальным для каждого запроса и необходим для идентификации ответов.
    callback_topic Топик для ответов (уникальный)

    📥 Получение ответов от сервера

    Форматы успешных ответов и ошибок

    Успешная оплата

    {
      "status": "payment_confirmed",
      "message": "Dispense item now.",
      "datetime": "2025-11-10T01:42:50",
      "timestamp": "1765132235.4342535",
      "expires_at": "1765132238.4342535",
      "amount": "10.00",
      "transaction_id": "jORfB2vGe",
      "payer": "Имя плательщика"
    }

    Тайм-аут оплаты

    {
      "status": "payment_timeout", 
      "message": "Payment not received...",
      "datetime": "2025-11-10T01:42:50",
      "transaction_id": "jORfB2vGe",
      "timestamp": "1765132235.4342535",
      "expires_at": "1765132238.4342535",
    }

    Описание полей успешной оплаты

    status - Статус обработки

    message - Сообщение об успешной обработке

    datetime - Дата и время получения уведомления в текстовом формате

    timestamp - Дата и время в формате unixtimestamp

    expires_at - Время платежа + время истечения актуальности (тайм-аут) в формате unixtimestamp

    amount - Сумма платежа

    payer - Имя плательщика (покупателя)

    Описание полей тайм-аута

    status - Статус обработки

    message - Сообщение об отсутствии платежа

    datetime - Дата и время отправки уведомления в текстовом формате

    timestamp - Дата и время в формате unixtimestamp

    expires_at - Время сообщения + время истечения актуальности (тайм-аут) в формате unixtimestamp

    💻 Примеры кода

    Реализации для различных платформ

    Arduino
    
    // Пример кода Arduino
    
    // Подключение библиотек и настройка
    #include 
    #include 
    
    // Настройки WiFi и MQTT
    const char* ssid = "your_SSID";
    const char* password = "your_PASSWORD";
    const char* mqtt_server = "monet.uz";
    const int mqtt_port = 8883;
    const String user_id = "CENTRAL";
    const char* mqtt_user = user_id;
    const char* mqtt_pass = "test";
    const String machine_number = "001"; // Обязательно уникальный для разных устройств одного клиента
    
    // Корневой сертификат 
    const char* root_ca = \
    "-----BEGIN CERTIFICATE-----\n"
    "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\n"
    "-----END CERTIFICATE-----\n";
    
    // MQTT клиент
    WiFiClientSecure espClient;
    PubSubClient client(espClient);
    
    // Данные машины
    const String machine_id = user_id + "_" + machine_number;
    const String callback_topic = "vending/response/" + user_id + "/" + machine_number;
    
    // Основные функции
    // Функция подключения к WiFi
    void setupWiFi() {
      WiFi.begin(ssid, password);
      while (WiFi.status() != WL_CONNECTED) {
        delay(1000);
        Serial.println("Connecting to WiFi...");
      }
      Serial.println("Connected to WiFi");
    }
    
    // Функция подключения к MQTT
    void setupMQTT() {
      espClient.setCACert(root_ca);
      client.setServer(mqtt_server, mqtt_port);
      client.setCallback(mqttCallback);
    
      while (!client.connected()) {
        Serial.println("Connecting to MQTT...");
        String clientId = user_id + "/" + machine_number;
        if (client.connect(clientId.c_str(), mqtt_user, mqtt_pass)) {
          Serial.println("Connected to MQTT!");
          client.subscribe(callback_topic.c_str()); // подписка на callback топик
        } else {
          Serial.print("Failed, rc=");
          Serial.print(client.state());
          delay(2000);
        }
      }
    }
    
    // Функция отправки запроса на подтверждение транзакции
    void sendTransactionRequest(float amount, String transaction_id) {
      // Формируем JSON сообщение
      String message = "{";
      message += "\"machine_id\":\"" + machine_id + "\",";
      message += "\"user_id\":\"" + user_id + "\",";
      message += "\"amount\":" + String(amount, 2) + ",";
      message += "\"transaction_id\":\"" + transaction_id + "\",";
      message += "\"callback_topic\":\"" + callback_topic + "\"";
      message += "}";
      
      // Отправляем в топик запросов
      client.publish("vending/requests", message.c_str());
      
      Serial.println("Transaction request sent: " + message);
    }
    
    // Функция обработки входящих MQTT сообщений
    void mqttCallback(char* topic, byte* payload, unsigned int length) {
      String message = "";
      for (int i = 0; i < length; i++) {
        message += (char)payload[i];
      }
      
      Serial.println("Received message on topic: " + String(topic));
      Serial.println("Message: " + message);
      
      // Обрабатываем ответ от сервера
      processTransactionResponse(message);
    }
    
    // Функция обработки ответа о транзакции
    void processTransactionResponse(String response) {
      // Здесь парсим JSON ответ и обрабатываем результат
      // Пример положительного ответа: 
      //{
      //  "status": "payment_confirmed",
      //  "message": "Dispense item now.",
      //  "datetime": "2025-11-10T01:42:50",
      //  "timestamp": "1763585516.5818143",
      //  "expires_at": "1763585519.5818145",
      //  "amount": "10.00",
      //  "transaction_id": "KT5sEfjNk",  
      //  "payer": "Имя плательщика"
      //}
      // или тайм-аут оплаты
      //{
      // "status": "payment_timeout", 
      // "message": "Payment not received...",
      // "datetime": "2025-11-10T01:42:50",
      // transaction_id": "19nbXZ2tf",
      // "timestamp": "1763586039.5101495",
      // "expires_at": "1763586042.5101497",
      //}
    
      
      
      if (response.indexOf("\"status\":\"payment_confirmed\"") > 0) {
        Serial.println("Transaction APPROVED");
        // Запускаем выдачу товара
        dispenseProduct();
      } else if (response.indexOf("\"status\":\"payment_timeout\"") > 0) {
        Serial.println("Transaction TIMEOUT");
        // Показываем ошибку
        showError("Transaction timeout");
      }
    }
    
    // Интеграция в основной код
    void setup() {
      Serial.begin(115200);
      setupWiFi();
      setupMQTT();
    }
    
    void loop() {
      // Поддерживаем MQTT соединение
      if (!client.connected()) {
        setupMQTT();
      }
      client.loop();
      
      // Ваш основной код здесь...
    }
    
    // Пример использования в обработчике события
    void onProductSelected(float price, String productId) {
      // Генерируем уникальный ID транзакции
      String transaction_id = generateTransactionId();
      
      // Отправляем запрос на подтверждение
      sendTransactionRequest(price, transaction_id);
      
      // Ждем ответа (в это время можно показать "Ожидание подтверждения...")
      Serial.println("Waiting for transaction confirmation...");
    }
    
    // Генерация уникального ID транзакции
    String generateTransactionId() {
      return String(random(100000, 999999)) + String(millis());
    }
    
    // Дополнительные функции (заглушки)
    void dispenseProduct() {
      Serial.println("Dispensing product...");
      // Код выдачи товара
    }
    
    void showError(String error) {
      Serial.println("Error: " + error);
      // Код отображения ошибки
    }
                    

    📱 Мобильное приложение SyncMe

    Настройка приложения и разрешений

    Описание изображения 1

    Главное окно

    1. В поле "ID пользователя" - Укажите присвоенный Вам ID
      для тестирования используйте: CENTRAL
    2. В поле "Пароль пользователя" - Укажите присвоенный Вам пароль
      для тестирования используйте: test
    3. Сохраните ID и пароль
    4. В поле "URL сервера" - Укажите: https://monet.uz
    5. Переключатель для запуска "отслеживания"
      Включайте его только после настройки разрешений и внесения всех данных.
    Описание изображения 2

    Логи приложения

    Информационное поле, которое отображает уведомления, которые смогло "увидеть" и обработать приложение.

    Здесь вы можете самостоятельно проверить, что все входящие уведомления "видит" приложение SyncMe.

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

    Описание изображения 3

    Настройка разрешений

    Проверьте, что приложение SyncMe находится в списке "РАЗРЕШЕНО".

    Нажмите, чтобы перейти в "Окно настроек 2" и убедитесь, что все разрешения включены.

    Описание изображения 4

    Окно настроек 2

    Проверьте, что все переключатели включены

    🧪 Тестирование

    Пошаговая инструкция

    • Шаг №1. Установка приложения Mosquito - Для отправки запросов и прослушивание ответов сервера устанавливаем приложение Mosquito для Вашей операционной системы.
    • Шаг №2. Настройка мобильного приложения SyncMe - Выполните установку и настройку мобильного приложения SyncMe для пересылки уведомлений.
    • Шаг №3. Проверка настроек в ЛК
      • Войдите в личный кабинет (на этом сайте) используя тестовые данные:
        • User ID : CENTRAL
        • Пароль : test
      • На вкладке "Приложения для приема оплаты" проверьте, что у необходимого приложения активирован переключатель.
    • Шаг №4. Запуск терминала №1 (Подписчик) - Откройте терминал (CMD, PowerShell, bash), вставьте команду и нажмите "Enter". Этот терминал будет эмулировать функцию, ожидающую подтверждения оплаты.
    • Терминал 1 - Подписчик

      Слушаем ответы для аппарата с номером 001:

      🐧🪟💻 Вариант для Linux / macOS / Windows CMD / Windows PowerShell (bash, zsh и т.п.)

      mosquitto_sub -h monet.uz -p 8883 \
          --cafile /patch/to/downloaded/certificate/isrgrootx1.pem \ 
          -u CENTRAL -P test \ 
          -t "vending/response/CENTRAL/001" -q 1
    • Шаг №5. Запуск терминала 2 (Издатель) - Скопируйте команду ниже и вставьте её в терминал №2. Это действие эмулирует функцию отправки запроса на ожидание подтверждения оплаты.

      В данном запросе вы ожидаете платеж на сумму 100 тг. В течение 3 минут необходимо выполнить оплату.

    • Терминал 2 - Издатель

      Отправляем тестовый запрос:

      🐧💻 Вариант для Linux / macOS / Windows PowerShell (bash, zsh и т.п.)

      mosquitto_pub -h monet.uz -p 8883 \
          --cafile /patch/to/downloaded/certificate/isrgrootx1.pem \
          -u CENTRAL -P test \
          -t "vending/requests" \
          -m '{
              "machine_id": "CENTRAL_001",
              "user_id": "CENTRAL", 
              "amount": 100.00,
              "transaction_id": "jORfB2vGe",
              "callback_topic": "vending/response/CENTRAL/001"
          }' \
          -q 1                                    

      🪟 Вариант для Windows CMD

      mosquitto_pub -h monet.uz -p 8883 ^
          --cafile C:\patch\to\downloaded\certificate\isrgrootx1.pem ^
          -u CENTRAL -P test ^
          -t "vending/requests" ^
          -m "{\"machine_id\": \"CENTRAL_001\", \"user_id\": \"CENTRAL\", \"amount\": 100.00, \"transaction_id\": \"jORfB2vGe\", \"callback_topic\": \"vending/response/CENTRAL/001\"}" ^
          -q 1
      
    • Шаг №6. Тестовый платеж - Выполните тестовый платеж суммой 100 тг на устройство, где установлено приложение SyncMe, и на приложение, которое есть в списке активированных в личном кабинете (на данном сайте).

    Быстрая справка

    Топики

    • 📨 Запросы: vending/requests
    • 📩 Ответы: vending/response/{user ID}/{mashine number}

    Статусы

    • payment_confirmed
    • payment_timeout

    Настройки

    • 🌐 Хост: monet.uz
    • 🔢 Порт: 8883
    • ⚡ QoS: 1