Skip to content

Instantly share code, notes, and snippets.

@Sominemo
Last active November 6, 2023 22:20
Show Gist options
  • Save Sominemo/8714a82e26a268c30e4a332b0b2fd943 to your computer and use it in GitHub Desktop.
Save Sominemo/8714a82e26a268c30e4a332b0b2fd943 to your computer and use it in GitHub Desktop.
Monobank Corp API Interaction

Mono Corp API Interaction

Also available: English version

Обозначения

  • Secret Proof: Секретный пароль, который должен знать только сервер monobank;

  • Ответ: JSON сериализированный вывод с Content-Type: application/json и кодом ответа 200 OK;

  • %METHOD% /%resource% Monobank Request: HTTP %METHOD% (GET|POST|...) запрос к https://api.monobank.ua/%resource% -> POST /personal/auth/request Monobank Request. См. Отправка запросов в Monobank;

  • Закрытый ключ: Сгенерированный OpenSSL ключ;

  • Публичный ключ: Открытый ключ, который был извлечен из Закрытого ключа, был передан команде Monobank при настройке корпоративного API;

  • Key-ID: Сериализированное представление Публичного ключа, хэшированное с помощью sha1 (в hex формате). Получается от команды Monobank;

  • Webhook Endpoint: Обработчик вашего сервера, который принимает запросы с событиями от сервера Monobank;

  • Webhook Request: Запрос с входящими данными типа application/json, который принимается зарегистрированным Webhook Endpoint;

Отправка запросов в Monobank

Примечание

API Monobank чувствителен к HTTP методу

Корень

Корнем для Monobank API является https://api.monobank.ua.

Заголовки

Header Содержимое Описание
X-Time Unix timestamp php time()
X-Sign Подпись см. Подпись запроса
X-Key-Id Key-ID См. Обозначения
X-Token Monobank token Если таковой есть

Подпись запроса

Не путать подпись с шифрованием

1. Ингредиенты

Строка подписи состоит из трёх ингредиентов:

  1. Отметка времени (unix timestamp), которая используется в заголовке запроса X-Time
  2. X-Request-Id, если не указано иное. В дальнейшем упоминается как Второй ингредиент подписи
  3. Название метода - /%resource% (например /personal/client-info)

Исключения для Второго ингредиента подписи

  • /personal/auth/request: разрешения токена (например, sp)
  • /personal/corp/webhook, /personal/corp/webhook: пустая строка

2. Составление строки подписи

Строка подписи представляет собой конкатенацию всех ингредиентов ($ing1.$ing2.$ing3 для PHP, ${ing1}${ing2}${ing3} или ing1+ing2+ing3 для JavaScript)

3. Подпись

  1. Сделайте подпись с помощью OpenSSL или его аналогов, используя следующие параметры:
Свойство Значение
Алгоритм SHA256
Ключ Ваш закрытый ключ
  1. Перекодируйте подпись с помощью Base64

  2. Передайте полученную строку в заголовке X-Sign

Примеры

PHP

$key = openssl_get_privatekey("file://private.key", "");
    $str = $time.$t.$url;
    openssl_sign($str, $sig, $key, OPENSSL_ALGO_SHA256);
    openssl_free_key($key);
    return base64_encode($sig);

Python

import base64
import ecdsa
...
data = (timestamp + permissions + url).encode('utf-8')
sign = PrivateSigningKey.sign(data, hashfunc=hashlib.sha256)
signB64 = base64.b64encode(sign)

Go

См. https://github.com/shal/mono

Настройте свой Webhook

Убедитесь, что путь к webhook-у остаётся в тайне
  • Формат входных данных:
{
  "type": "StatementItem",
  "data": {
    "account": Account.id,
    "statementItem": <StatementItem>
  }
}

Зарегистрируйте свой вебхук

POST /personal/corp/webhook Monobank Request

  • 2-й ингредиент подписи пуст.
  • Тело запроса {"webHookUrl": "%webhook%"}, где %webhook% - URL вашего Webhook Endpoint.
  • Ответ пустой

GET /personal/corp/settings Monobank Request.

  • 2-й ингредиент подписи пуст

Авторизация пользователя

Действия сервера

  1. Сервер генерирует уникальный %token% для пользователя и секретный %proof%, который должен знать только monobank.
  2. Создайте обработчик, который будет получать %proof% и %token% в качестве GET параметров или в uri (например, /webhook/%token%/%proof% или /webhook?token=%token%&proof=%proof%). Это будет ваш %webhook%.
  3. POST /personal/auth/request Monobank Request: Headers(X-Callback: %webhook%; X-Permissions: %permissions%)
  • %webhook% - ваш webhook обработчик для получения токена от monobank
  • %permissions%:
    Список прав, которые сервис хочет получить (1 буква на 1 разрешение). Список возможных прав:

s - выписка (включает баланс и саму выписку)
p - личная информация (имя и фамилия).

например, X-Permissions: sp

  1. Получите %requestId% из ответа {"tokenRequestId": "***"} и Auth URL %url% {"acceptUrl": "***"}
  2. Верните ошибку, если запрос прошел неправильно
  3. Сгенерируйте QR-код из Auth URL (Например, используя Google Charts API).
  4. Покажите QR-код/откройте ссылку на стороне клиента, чтобы пользователь открыл приложение monobank и смог подтвердить запрос на вход.
  5. Как только пользователь одобрит запрос на вход, вы получите входящий запрос на webhook с токеном пользователя Monobank в заголовке X-Request-Id (может быть также x-request-id).
  6. Проверьте, соответствуют ли %proof% и %token% друг другу, иначе запрос невалиден
  7. Создайте ассоциацию токена пользователя Monobank с вашим пользователем через ваш собственный %token%, но не используйте оригинальный токен пользователя Monobank как идентификатор пользователя.
  8. Предыдущие токены перестают работать, когда вы создаете новый, поэтому вам следует либо создать собственную систему учетных записей пользователей, либо обновить токен monobank во всех старых сессиях, запросив и сохранив clientId из /client-info на этапе 10.

Mono Corp API Interaction

Читать на русском

Vocabulary

  • Secret Proof: A password, that elevates access rights for the request;

  • Response: JSON marshaled server response with application/json Content-Type and 200 OK response code;

  • %METHOD% /%resource% Monobank Request: An HTTP %METHOD% (GET|POST|...) request to https://api.monobank.ua/%resource% -> POST /personal/auth/request Monobank Request. See Sending requests to Monobank;

  • Private Key: An OpenSSL generated key;

  • Public Key: A public key, which was extracted from the Private key, which was passed to Monobank team while Corporative API set up;

  • Key-ID: Marshaled Public key representation, hashed with sha1 (in hex). Received from Monobank team;

  • Webhook Endpoint: Your server method, that accepts update requests from Monobank;

  • Webhook Request: A request with application/json input, that is being accepted by registered Webhook Endpoint;

Sending requests to Monobank

Note

Monobank API is sensitive to HTTP method

Root

The Root for Monobank API is https://api.monobank.ua

Headers

Header Content Description
X-Time Unix timestamp php time()
X-Sign Sign See Signing the request
X-Key-Id Key-ID See Entities
X-Token Monobank token If present

Signing the request

Not to be confused with encryption

1. The ingredients

The signed string contains 3 ingredients:

  1. Timestamp from X-Time
  2. Monobank token usually, can be something else. Referred as 2nd Sign Ingredient
  3. Requested Monobank /%resource%

If it's an auth request to /personal/auth/request, token permissions from X-Permissions are being used instead of 2nd ingredient

2. Make the string to sign

The string contains all the ingredients concatenated together ($ing1.$ing2.$ing3 for PHP, ${ing1}${ing2}${ing3} or ing1+ing2+ing3 for JavaScript)

3. Create the sign

  1. Make a sign of the string, that corresponds to the OpenSSL signing
Property Value
Algorithm SHA256
Key Private Key
  1. Encode the sign to Base64

Examples

PHP
$key = openssl_get_privatekey("file://private.key", "");
    $str = $time.$t.$url;
    openssl_sign($str, $sig, $key, OPENSSL_ALGO_SHA256);
    openssl_free_key($key);
    return base64_encode($sig);
Python
import base64
import ecdsa
...
data = (timestamp + permissions + url).encode('utf-8')
sign = PrivateSigningKey.sign(data, hashfunc=hashlib.sha256)
signB64 = base64.b64encode(sign)
Go
See https://github.com/shal/mono

Set up a Webhook Endpoint

Make sure to keep the Endpoint path secret
  • Input data format:
{
  "type": "StatementItem",
  "data": {
    "account": Account.id,
    "statementItem": <StatementItem>
  }
}

Register your webhook

POST /personal/corp/webhook Monobank Request

  • 2nd Sign Ingredient is empty
  • Request body is {"webHookUrl": "%webhook%"}, where %webhook% is your Webhook Endpoint URL
  • Response is empty

GET /personal/corp/settings Monobank Request

  • 2nd Sign Ingredient is empty

Signing user in

Server actions

  1. Generate a unique %token% for user, and a secret %proof% that should be known by monobank only
  2. Set up an endpoint, that will receive a %proof% and %token% as GET parameters or in-uri (e.g. /webhook/%token%/%proof% or /webhook?token=%token%&proof=%proof%). That will be your %webhook%
  3. POST /personal/auth/request Monobank request Headers(X-Callback: %webhook%; X-Permissions: %permissions%)
  • %webhook% is a public http resource to receive the resulting request from Monobank
  • %permissions%:
List of rights that the service wants to receive from the client (1 letter per 1 permission). List of possible rights:

s - statement (includes balance and statement itself)
p - Personal information (name and surname).

e.g. X-Permissions: sp

  1. Get %requestId% from the response {"tokenRequestId": "***"} and Auth URL %url% {"acceptUrl": "***"}
  2. Error, if the request went wrong
  3. Generate QR code from Auth URL (e.g. using Google Charts API)
  4. Show the QR/Open the link at the client side, so the user opens monobank app and can approve sign-in request
  5. As soon as user approves the sign in request, you will receive an incoming request on the webhook with the Monobank User Token in X-Request-Id (can be also x-request-id) incoming header
  6. Check if %proof% and %token% from GET correspond each other, else Error
  7. Associate the Monobank User Token with your user through your own %token%, but don't use the original Monobank User Token for user identification
  8. Previous tokens stop working when you create a new one, so you should either create your own user account system, or update the associated request token in all old sessions by requesting and storing clientId from /client-info on stage 8
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment