Как принимать платежи с карты и делать выплаты (H2H)
Вам нужно принимать платежи с банковских карт (Payin) и делать выплаты на карты (Payout), при этом данные карты передаются через ваш Gateway. Вы реализуете H2H flow (Host-to-Host) — прямой обмен данными карты между вашим микросервисом и провайдером, без участия пользователя.
1. Реализуйте обработку Payin (POST /pay)
Заголовок раздела «1. Реализуйте обработку Payin (POST /pay)»Шаг 1. Извлеките данные карты плательщика
Заголовок раздела «Шаг 1. Извлеките данные карты плательщика»Входящий запрос содержит данные карты в params:
{ "params": { "pan": "4111111111111111", "cvv": "123", "expires": "12/25", "customer": { "email": "user@example.com", "ip": "192.168.1.1" } }, "payment": { "token": "tok_xxxx", "gateway_amount": 10000, "gateway_currency": "USD" }, "settings": { "api_key": "provider_api_token" }}Полная структура запроса описана в Базовые Контракты.
Шаг 2. Конвертируйте сумму
Заголовок раздела «Шаг 2. Конвертируйте сумму»Параметр gateway_amount всегда передаётся в копейках (10000 = 100.00 USD).
Шаг 3. Отправьте запрос провайдеру
Заголовок раздела «Шаг 3. Отправьте запрос провайдеру»Используйте settings.api_key для аутентификации. Сформируйте payload под спецификацию вашего провайдера.
Работа с параметрами settings описаны в Настройки и Статусы.
Шаг 4. Обработайте три возможных сценария ответа
Заголовок раздела «Шаг 4. Обработайте три возможных сценария ответа»| Сценарий | Статус в ответе | Действие |
|---|---|---|
| Платёж успешен | "approved" |
Вернуть result: true, status: "approved" |
| Требуется 3DS | "pending" |
Вернуть redirect_request с URL ACS-сервера |
| Платёж отклонён | "declined" |
Вернуть result: true, status: "declined" + details с причиной |
Пример ответа с 3DS:
{ "result": true, "status": "pending", "gateway_token": "txn_12345", "redirect_request": { "url": "https://acs-bank.com/challenge", "type": "post", "params": { "creq": "eyJhbGciOiJIUzI1NiIs..." } }, "logs": [...]}Подробнее про формат
redirect_request— в Базовые Контракты (секция 3).
Шаг 5. Верните logs
Заголовок раздела «Шаг 5. Верните logs»Каждый HTTP-запрос к провайдеру зафиксируйте в массиве logs:
{ "request": { "url": "...", "body": "..." }, "status": 200, "response": "{...}", "kind": "pay", "duration": 0.35}Поле kind принимает значения: "pay", "payout", "status", "callback".
2. Реализуйте обработку Payout (POST /payout)
Заголовок раздела «2. Реализуйте обработку Payout (POST /payout)»Шаг 1. Извлеките данные получателя
Заголовок раздела «Шаг 1. Извлеките данные получателя»В отличие от Payin, здесь в params передаётся карта получателя (только PAN) или bank_account:
{ "params": { "card": { "pan": "4111111111111111" }, "customer": { "email": "receiver@example.com" } }, "payment": { "token": "payout_tok_xxxx", "gateway_amount": 5000, "gateway_currency": "EUR" }}Шаг 2. Отправьте выплату провайдеру
Заголовок раздела «Шаг 2. Отправьте выплату провайдеру»Сконвертируйте gateway_amount из копеек и отправьте запрос согласно спецификации провайдера.
Шаг 3. Верните результат
Заголовок раздела «Шаг 3. Верните результат»Если выплата мгновенная:
{ "result": true, "status": "approved", "gateway_token": "payout_12345", "logs": [...]}Если выплата требует подтверждения — верните status: "pending" и дождитесь коллбека от провайдера.
Подробнее про формат Payout-запроса — в Status и Payout.
3. Реализуйте проверку статуса (POST /status)
Заголовок раздела «3. Реализуйте проверку статуса (POST /status)»Для асинхронных платежей (3DS, pending) реализуйте эндпоинт, который запрашивает у провайдера текущий статус транзакции и возвращает его в нормализованном виде:
{ "result": true, "status": "approved", "amount": 10000, "currency": "USD", "logs": [...]}Детальная спецификация — в Status и Payout.
4. Реализуйте обработку коллбеков (POST /callback)
Заголовок раздела «4. Реализуйте обработку коллбеков (POST /callback)»Когда провайдер присылает вебхук:
- Валидируйте подпись провайдера (механизм зависит от провайдера — JWT, RSA, HMAC).
- Нормализуйте статус (
approved/declined). - Отправьте результат в Business, подписав JWT ключом
settings.sign_key.
Полная инструкция по коллбекам и JWT — в Callbacks & JWT. Обработка ошибок описана в Ошибки и Таймауты.
Пример реализации на Node.js
Заголовок раздела «Пример реализации на Node.js»Пополнение (H2H Payin)
Заголовок раздела «Пополнение (H2H Payin)»app.post("/pay", async (req, res) => { const body = req.body; const amount = body.payment.gateway_amount / 100; const apiKey = body.settings.api_key;
const providerPayload = { amount, currency: body.payment.gateway_currency, card: { number: body.params.pan, cvv: body.params.cvv, expiry: body.params.expires, }, customer: body.params.customer, reference: body.payment.token, };
const start = Date.now(); const providerRes = await fetch("https://api.provider.com/v1/charge", { method: "POST", headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json", }, body: JSON.stringify(providerPayload), }); const providerData = await providerRes.json(); const duration = (Date.now() - start) / 1000;
let response = { result: true, gateway_token: providerData.id, amount: body.payment.gateway_amount, currency: body.payment.gateway_currency, logs: [ { request: { body: providerPayload }, status: providerRes.status, response: providerData, kind: "pay", duration, }, ], };
if (providerData.status === "success") { response.status = "approved"; response.details = "Payment successful"; } else if (providerData.status === "3ds_required") { response.status = "pending"; response.redirect_request = { url: providerData.acs_url, type: "post", params: { creq: providerData.creq }, }; } else { response.status = "declined"; response.details = providerData.message || "Payment declined"; }
res.status(200).json(response);});Выплата (H2H Payout)
Заголовок раздела «Выплата (H2H Payout)»app.post("/payout", async (req, res) => { const body = req.body; const amount = body.payment.gateway_amount / 100; const apiKey = body.settings.api_key;
const providerPayload = { amount, currency: body.payment.gateway_currency, card: body.params.card?.pan, bank_account: body.params.bank_account, customer: body.params.customer, reference: body.payment.token, };
const providerRes = await fetch("https://api.provider.com/v1/payouts", { method: "POST", headers: { Authorization: `Bearer ${apiKey}` }, body: JSON.stringify(providerPayload), }); const data = await providerRes.json();
res.status(200).json({ result: true, status: data.status === "success" ? "approved" : "pending", gateway_token: data.id, amount: body.payment.gateway_amount, currency: body.payment.gateway_currency, logs: [ { request: { body: providerPayload }, status: providerRes.status, response: data, kind: "payout", duration: 0.5, }, ], });});