This document describes the protocol of Mono Corp API Proxy (MCAP). This protocol is created for Mono PWA project.
-
Root: describes the publicly available URL path, which contains methods as subfolders;
-
Method: Named public command, the way to interact with MCAP for different actions. A simulated subfolder of Root;
-
Secret Proof: A password, that elevates access rights for the request;
-
Public Method: Generally available method, that is intended to be called by the client app;
-
Service Method: A method, that must not be called by the client app, requires Secret Proof;
-
Response: JSON marshaled output of Method with
application/json
Content-Type and200 OK
response code; -
Error: A Response with an
error
key instead of the expected output. The key contains error description ->{"error": "description"}
; -
Long-Polling: Keeping an HTTP request alive without outputting the response body until a trigger;
-
%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; -
Roll-In Token: A random string, which is paired with a Monobank Token Request ID;
-
Request Token: A random string, which is paired with a Monobank token and a Roll-In token;
-
Private Key: An OpenSSL generated key, which was passed to Monobank team while Corporative API set up;
-
Public Key: A public key, which was extracted from the Private key;
-
Key-ID: Marshaled Public key representation, hashed with sha1 (in hex). Received from Monobank team;
-
<MCAP %name%>: Mono Corp API Proxy described data structure
-
Webhook Endpoint: Service method, that accepts update requests from Monobank;
-
Webhook Request: A request with
application/json
input, that is being accepted by registered Webhook Endpoint; -
Push Server: An endpoint, that accepts Request Token, has a Webhook Endpoint, can list subscriptions and send the data using Web Push API;
-
Push API Private Key: Secret self-generated VAPID key;
-
Push API Public Key: A public pair to Push API Private Key;
-
Push API Subscription: Endpoint, auth and secret which are being used to send a push with Web Push API;
-
Notification Channel: A unit, that describes a category of acceptable pushes via id and type.
The methods are placed in the auth lifecycle order:
Method | Server action |
---|---|
check-proto | Describes itself by telling name, author, protocol version, homepage, optionally UI message, Push Server |
roll-in | Creates a roll-in token |
exchange-token | Checks if there are available Monobank user tokens for current roll-in token, and if so, creates a pair request token and gives it |
request | Sends requests to Monobank |
Every method must always return Access-Control-Allow-Origin: *
header
These methods belong to Push Server (usually at /push/ location):
Method | Server action |
---|---|
list | List available subscriptions |
subscribe | Activate subscription and update Push API data |
unsubscribe | Deactivate subscription |
Every method must always return Access-Control-Allow-Origin: *
header
These methods must not be called from the app itself and require an additional secret proof:
Method | Server action |
---|---|
broadcast | Broadcast user notifications for specified channel |
- In this documentation, a Push Server method has a name, that starts with
push/
- In reality, every request URL looks like PushServer
/%ingredient%/%method-name%
. For example, if your Push Server ishttps://example.com/push
, the%ingredient%
ispasswd
and the%method-name%
islist
, the Request URL will look likehttps://example.com/push/passwd/list
- Plain text:
{
"mode": "text",
// The text that will be displayed
"value": "My text"
}
- Value from localization files:
{
"mode": "local"
// The ID of localization string
"value": "@push/i/news/sign"
}
- Account description, e.g.
**2817 USD Black
{
"mode": "statement",
// Mono account ID
"value": "XcG_Ynrm5eUbDrPDt2MhhQ"
}
- Open a URL:
{
// Defines the URL action
"act": "url",
// The url to open
"url": "https://example.com/",
// Allows to open new tabs. Default - false
"openNew": false,
// Allows to refocus the client tab to open the url. Default - false
"refocus": false
}
- Broadcast a JavaScript message:
{
// Defines the message broadcast action
"act": "msg",
// JSON marshaled data to broadcast
"data": "{...}",
// Allow to broadcast the message to all available windows. Default - false
"all": false,
// Denies to open new tabs. Default - false
"denyNewWindow": false,
}
- Custom multilang push:
{
"act": "custom-push",
"isMultilang": "true",
"push": {
"ru": {
<MCAP Notification>
},
"uk": {
<MCAP Notification>
},
...
}
}
- Custom single language push:
{
"act": "custom-push",
"push": {
<MCAP Notification>
}
}
- Transaction push
{
"act": "statement-item",
"item": {<StatementItem>},
"account": {<Account>}
}
- Check Notification constructor options
{
// Notification title
"title": "String",
...<MCAP Notification Options>,
// Use included into client badge
"libraryBadge": "m" || "news" || "payIn" || "payOut" || "card",
// Describes action for different onclick payloads.
"actionDescriptor": {
// Empty payload serves simple notification click
"": <MCAP Action>
}
}
Monobank API is sensible to HTTP method
The Root for Monobank API is https://api.monobank.ua
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 |
Not to be confused with encryption
The signed string contains 3 ingredients:
- Timestamp from X-Time
- Monobank token usually, can be something else. Referred as 2nd Sign Ingredient
- 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
The string contains all the ingredients concatenated together ($ing1.$ing2.$ing3
for PHP, ${ing1}${ing2}${ing3}
or ing1+ing2+ing3
for JavaScript)
- Make a sign of the string, that corresponds to the OpenSSL signing
Property | Value |
---|---|
Algorithm | SHA256 |
Key | Private Key |
- Encode the sign to Base64
$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);
import base64
import ecdsa
...
data = (timestamp + permissions + url).encode('utf-8')
sign = PrivateSigningKey.sign(data, hashfunc=hashlib.sha256)
signB64 = base64.b64encode(sign)
See https://github.com/shal/mono
Make sure to keep the Endpoint path secret
- Input data format:
{
"type": "StatementItem",
"data": {
"account": Account.id,
"statementItem": <StatementItem>
}
}
- 2nd Sign Ingredient is empty
- Request body is
{"webHookUrl": "%webhook%"}
, where%webhook%
is your Webhook Endpoint URL - Response is empty
- 2nd Sign Ingredient is empty
[public method]
Describes itself by telling name, author, protocol version, homepage and optionally setting a UI message
All fields are required unless otherwise specified
{
// Describes used MCAP protocol
"proto": {
"version": 1,
"patch": 3
},
// Describes the implementation of protocol
"implementation": {
"name": "PHP Mono Corp API Proxy",
"author": "Sominemo",
"homepage": "https://github.com/Sominemo/Mono-Corp-API-Proxy-PHP"
},
// Describes the state of the running instance of this implementation
"server": {
// Tells the client to display a message to the user, optional
"message": {
// The text to display
"text": "content",
// Adds clickable URL at the end of text, optional
"link": "https://example.com"
},
// Describes a Push Server if available, optional
"push": {
// Push Server endpoint
"api": "https://example.com/push",
// Push API public key
"cert": "QktvWDBndm44VWFCdjNnQVhDM0x4Q0lfblJQ...",
// Push Server display name
"name": "Sominemo Push Server (example.com)",
}
}
}
[public method] Creates an auth request in Monobank, generates a paired roll-in token and returns both
User interacts with Monobank interface and confirms the auth request, then Monobank sends a request to a Webhook. More details in Getting the Monobank token.
- 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)
- Get Monobank Token Request ID %requestId% from the response
{"tokenRequestId": "***"}
and Auth URL %url%{"acceptUrl": "***"}
- Error, if the request went wrong
- Generate Roll-In token %token% and a random %proof% string for it, pair the token with Monobank Token Request ID
- Generate QR code from Auth URL (250px, e.g. using Google Charts API)
- Encode the QR to Base64 %qr%
{
"token": %token%,
"requestId": %requestId%,
"url": %url%,
"qr": %qr%
}
- 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%
) - Receive the Monobank User Token in
X-Request-Id
(can be alsox-request-id
) incoming header - Check if %proof% and %token% from GET correspond each other, else Error
- Generate a Request Token, pair Roll-In and the Monobank User tokens with it
- If you are not going to support multiple devices Sign-In, skip to 7
- Make GET /client-info Monobank request and retrieve
clientId
from it. - Update Monobank Token for every Request Token with such
clientId
. Link theclientId
with current Request Token. - Response
200 OK
[public method] Long-polling until received roll-in token won't get a corresponding request token or the request will timeout. Checks if there are available Monobank user tokens for current roll-in token, and if so, creates a pair request token and gives it
- Receive an existing roll-in token from user
- Error, if the token does not exist
- Check if Request Token that corresponds to the Roll-In Token already exists
- If so, skip to authed
- Start Long-Polling until token receive. On trigger skip to authed
- If current Long-Polling session is out of time, %return_token% = false & Response (In this case client will send a request to this method again)
- Error, if current Roll-In Token is out of time
- authed
- Discontinue & Unlink Roll-In token
- %return_token% = Request Token & Response
{
"token": %return_token%
}
[public method] This method is a synthetic path, which signs and redirects requests to Monobank.
e.g.: POST /request/personal/auth/request
turns to a Monobank request POST /personal/auth/request
- Get Monobank token paired with Request token from
X-Token
incoming header, else Error - Clone incoming headers %headers%
- Clone the raw HTTP request body (e.g.
php://input
) - Remove
Host
header from the request (there also can be more inappropriate headers added by your HTTP server, such asSSL
orGeoIp-Country-Code
). Replace the originalX-Token
with paired Monobank token - Send the request to Monobank with the same HTTM method as incoming request, get the answer
- Clone response headers and answer to output
[public method] This method lists available Push subscriptions
Name | Value |
---|---|
endpoint | Push Subscription endpoint |
The ingredient for this Push Server method is Request Token
- Get available notification channels:
- List your own channels
- If you want to support transaction updates, get accounts from GET /client-info Monobank Request
- Get subscriptions with such endpoint and Request Token
- Mark activated notification channels in list as turned on
[
{
// Notification Channel Type
"type": "sominemo",
// Notification Channel ID
"id": "app_updates",
// Material Display Icon
"icon": "new_releases",
// Display name
"sign": {
<MCAP Text Token>
},
// Channel description
"description": {
<MCAP Text Token>
},
// Notification channel state (turned on/off)
"state": false
},
...
]
Subscribes current Push subscription on a Notification Channel and updates Push Subscriptions for other subscriptions on this Request Token
The ingredient for this Push Server method is Request Token
Name | Value |
---|---|
type | Notification Channel ID |
id | Notification Channel Type |
endpoint | Push Subscription Endpoint |
key | Push Subscription Key |
auth | Push Subscription Auth |
expires | Push Subscription Expiration time |
encoding | Push Subscription Encoding |
cert | Push Server Public Key |
- Check if
cert
is the same as Push Server Public Key, else Error - Check if such Notification Channel already exists for this Request Token. If so, skip to 6
- Check if it's possible to subscribe on this Notification Channel
- Create new subscription on this Notification Channel for this Request Token
- Send a success notification on current Push Subscription. If fails, Error
- Update Push Subscription for all subscriptions on this Request Token
{ "result": true }
[public method] Deletes subscriptions from current Request Token
The ingredient for this Push Server method is Request Token
Name | Value |
---|---|
endpoint | Push Subscription Endpoint |
channels | JSON marshaled array of Notification Channels, e.g. [{ "type": "string", "id": "string"}] |
- Delete subscriptions of all specified Notification Channels for this Request Token
{ "result": true }
[service method] Broadcasts a custom message on a specified Notification Channel
Name | Value |
---|---|
id | Notification Channel ID |
type | Notification Channel Type |
JSON marshaled <MCAP Push>
The ingredient for this method is Secret Proof
- Check if Secret Proof is correct, else Error
- Check if Notification Channel is correct, else Error
- Check if Request Body is a correct JSON, else Error
- Send the push to each Push Subscription on this Notification Channel. Remove failed ones.
unspecified
[public method] Deletes all personal data related to the Request Token
The Request ID for this Push Server method is Request Token
- Find Mono clientId for the token
- Find all tokens for the clientId
- Delete all push subscriptions related to the found tokens
- Delete all related tokens
{ "status": true }