Интеграция

Генерация ключей

Выполните эти две команды в терминале:

terminal
openssl genrsa -out finik_private.pem 2048
openssl rsa -in finik_private.pem -pubout > finik_public.pem
ФайлНазначение
finik_private.pemПодписывайте каждый запрос этим ключом. Храните в секрете — любой, кто его получит, сможет отправлять запросы от вашего имени.
finik_public.pemОтправьте в Finik по защищённому каналу (email Finik), чтобы сервис мог проверять ваши запросы.

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

Каждый запрос требует заголовок signature — RSA-SHA256, Base64-кодированный, сгенерированный с вашим приватным ключом. Finik предоставляет готовые пакеты для Node.js и Python.

Установите @mancho.devs/authorizer и node-fetch, затем:

create-payment.ts
import { Signer, RequestData } from '@mancho.devs/authorizer';
import fetch from 'node-fetch'; // or axios

// Choose the environment you call:
const baseUrl = 'https://api.acquiring.averspay.kg'; // prod
// const baseUrl = 'https://beta.api.acquiring.averspay.kg'; // beta

// IMPORTANT: Host header must match the URL host exactly.
const host = new URL(baseUrl).host;

// Your credentials
const apiKey = process.env.FINIK_API_KEY!; // from Finik
const privateKey = process.env.FINIK_PRIVATE_PEM!; // contents of finik_private.pem
const timestamp = Date.now().toString(); // UNIX ms

// Create Payment body per spec
const body = {
Amount: 100,
CardType: 'FINIK_QR',
PaymentId: '00000000-0000-0000-0000-000000000000', // use a real UUID
RedirectUrl: 'https://example.com/success',
Data: {
accountId: 'your-account-id',
name_en: 'your-qr-name',
},
};

// Build the canonical input for signing
const requestData: RequestData = {
httpMethod: 'POST',
path: '/v1/payment', // absolute path only (no query)
headers: {
Host: host, // must match baseUrl host
'x-api-key': apiKey, // included in signature
'x-api-timestamp': timestamp, // UNIX ms; same value used in signature
// You may send other headers, but only `host` and `x-api-*` are included in the signature.
},
queryStringParameters: undefined, // or { ... } if you have query; lib sorts & encodes
body, // plain JS object; lib will canonicalize/JSON-stringify
};

// Produce Base64 RSA-SHA256 signature
const signature = await new Signer(requestData).sign(privateKey);

// Send the actual HTTP request
const url = `${baseUrl}${requestData.path}`;
const res = await fetch(url, {
method: requestData.httpMethod,
headers: {
'content-type': 'application/json',
'x-api-key': apiKey,
'x-api-timestamp': timestamp,
signature, // <- attach signature header
},
body: JSON.stringify(body),
// If your API currently returns 302 (HTML) on S2S calls, prevent auto-follow:
redirect: 'manual', // remove once API returns 201 JSON by default
});

if (res.status === 302) {
// Read Location when you opt into redirects
console.log('Redirect to:', res.headers.get('location'));
} else {
console.error(res.status, await res.text());
}

Только Host и заголовки, начинающиеся с x-api-, включаются в подпись. Одно и то же значение временно́й метки должно использоваться и в подписи, и в заголовке запроса.

Создание платежа

POST подписанный запрос на:

API endpoints
POST https://api.acquiring.averspay.kg/v1/payment — Прод
POST https://beta.api.acquiring.averspay.kg/v1/payment — Бета

Заголовки

HeaderОписание
signatureПодпись, которую вы сгенерировали.
x-api-keyВаш ключ API от Finik.
x-api-timestampТекущее время в миллисекундах UNIX — то же значение, что использовалось при подписи.

Тело запроса

FieldТипОбязательноОписание
AmountNumberОпциональноФиксированная сумма платежа. Если не указана, клиент может оплатить любую сумму.
CardTypeStringОбязательноВсегда FINIK_QR.
PaymentIdStringОбязательноУникальный ID платежа — предотвращает дублирование платежей.
RedirectUrlStringОбязательноКуда Finik отправляет клиента после успешной оплаты.
DataObjectОбязательноДетали платежа — см. ниже.

Data object

FieldТипОбязательноОписание
accountIdStringОбязательноID вашего счёта Finik — куда зачисляются средства.
name_enStringОбязательноИмя QR-кода, отображаемое клиенту.
webhookUrlStringОбязательноВаш серверный endpoint для уведомлений о статусе платежа.
descriptionStringОпциональноОписание на платёжной странице.
startDateNumberОпциональноНачало срока действия QR (UNIX мс).
endDateNumberОпциональноКонец срока действия QR (UNIX мс).
additionalDataArrayОпциональноЕсли поле value не задано, клиент заполняет форму перед показом платежных методов. Максимальное количество полей — 20.

additionalData items

FieldТипОбязательноОписание
fieldIdStringОбязательноКлюч этого поля в payload webhook.
nameStringОбязательноМетка поля на платёжной странице.
isHiddenBooleanОпциональноСкрыть поле на платёжной странице.
valueStringОпциональноПредзаполненное значение.

Пример тела запроса

POST /v1/payment — body
{
  "Amount": 100,
  "CardType": "FINIK_QR",
  "PaymentId": "a3f1c2e4-7b9d-4e2a-8c1f-3d0e9b2a5f6c",
  "RedirectUrl": "https://example.com/success",
  "Data": {
    "accountId": "your-account-id",
    "name_en": "your-qr-name",
    "webhookUrl": "https://merchant.example.com/webhooks/finik",
    "description": "your-qr-description",
    "startDate": 1737369000000,
    "endDate": 1737455400000
  }
}

Обработка ответа

Успешный Create Payment возвращает 302 редирект с URL платёжной страницы в заголовке Location:

response
HTTP/1.1 302 Found
Location: https://qr.finik/<payment-path>
  1. Вызовите POST /v1/payment с бэкенда с отключёнными редиректами.
  2. Прочитайте Location — это URL вашей платёжной страницы.
  3. Отправьте этот URL клиенту — перенаправьте браузер на него или верните в ваше SPA / мобильное приложение для открытия в webview.
  4. Клиент завершает оплату и перенаправляется на ваш RedirectUrl.
  5. Finik отправляет webhook с итоговым статусом — считайте его источником истины.

Отключите авто-переход и читайте заголовок Location

fetch.js
const res = await fetch("https://api.acquiring.averspay.kg/v1/payment", {
  method: "POST",
  headers: { "content-type": "application/json", "x-api-key": apiKey, "x-api-timestamp": ts, signature },
  body: JSON.stringify(body),
  redirect: "manual", // don't auto-follow
});

if (res.status === 302) {
const paymentUrl = res.headers.get("location"); // send this to the browser
}

Бэкенд может перенаправить браузер (302 Location: <paymentUrl>) или вернуть JSON в ваше SPA и установить window.location = paymentUrl.