Перейти к содержимому

Как принимать пополнения через P2P-переводы и делать выплаты

Вам нужно принимать пополнения через P2P-переводы (пользователь сам переводит деньги по реквизитам) и делать выплаты на карты или счета. Вы реализуете P2P-поток: Gateway создаёт заказ у провайдера, возвращает реквизиты для перевода, а затем отслеживает поступление средств.

Отличие от H2H: данные карты плательщика не передаются — Gateway только создаёт заказ и возвращает реквизиты, куда пользователь должен перевести деньги. Что вам понадобится: API-ключ провайдера, спецификация его эндпоинтов (pay, status, callback), тестовый счёт/карта для выплат.


1. Реализуйте пополнение (Payin) через P2P — получение реквизитов

Заголовок раздела «1. Реализуйте пополнение (Payin) через P2P — получение реквизитов»

Входящий запрос не содержит данных карты — только информация о пользователе:

{
"method_name": "pay",
"params": {
"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"
}
}

Полная структура запроса описана в Базовые Контракты.

Отправьте запрос провайдеру с суммой, валютой и reference (токеном платежа). Провайдер создаст заказ и вернёт реквизиты для перевода.

Ответ должен содержать status: "pending" и поле requisites:

{
"result": true,
"status": "pending",
"gateway_token": "order_12345",
"amount": 10000,
"currency": "USD",
"requisites": {
"card": "461761****3933",
"holder": "IVAN I.",
"bank_name": "BankOfBaku",
"phone": "+77001234567"
},
"logs": [...]
}

Поле requisites может содержать:

  • card — номер карты получателя (маскированный)
  • holder — имя получателя
  • bank_name — название банка
  • phone — номер телефона (для переводов на телефон)
  • account — номер счёта
  • link — URL для перехода (SberPay и т.п.)
  • deeplink — true, если реквизиты содержат ссылку для перехода
  • qr_data — QR-код для перевода

После того как пользователь перевёл деньги, провайдер присылает вебхук на POST /callback или статус проверяется через POST /status.

Реализация коллбеков описана в Callbacks & JWT. Проверка статуса — в Status и Payout.


{
"params": {
"amount": 50000,
"currency": "EUR",
"customer": { "email": "receiver@example.com" },
"card": { "pan": "4111111111111111" }
},
"payment": {
"token": "payout_tok_xxxx"
},
"settings": {
"api_key": "provider_api_token"
}
}

Важно: В P2P Payout сумма и валюта могут быть в params, а не в payment — проверьте спецификацию вашего провайдера.

Шаг 2. Конвертируйте сумму и отправьте выплату

Заголовок раздела «Шаг 2. Конвертируйте сумму и отправьте выплату»

amount передаётся в копейках. Поделите на 100 перед отправкой провайдеру.

{
"result": true,
"status": "approved",
"gateway_token": "payout_12345",
"amount": 50000,
"currency": "EUR",
"details": "Payout successful",
"logs": [...]
}

Если выплата не мгновенная — верните status: "pending" и ждите коллбек.


  • Создать endpoint POST /pay
  • Создать заказ у провайдера
  • Получить реквизиты для перевода
  • Вернуть status: "pending" + requisites + gateway_token + logs
  • Реализовать POST /callback или POST /status для финализации статуса
  • Создать endpoint POST /payout
  • Извлечь params.card.pan или params.bank_account
  • Сконвертировать params.amount (в копейках) в основные единицы для провайдера
  • Отправить выплату провайдеру
  • Вернуть logs с деталями запроса/ответа
  • Если выплата не мгновенная — вернуть status: "pending" и ожидать коллбек

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,
reference: body.payment.token,
};
const providerRes = await fetch("https://api.provider.com/v1/orders", {
method: "POST",
headers: { Authorization: `Bearer ${apiKey}` },
body: JSON.stringify(providerPayload),
});
const data = await providerRes.json();
res.status(200).json({
result: true,
status: "pending",
gateway_token: data.id,
amount: body.payment.gateway_amount,
currency: body.payment.gateway_currency,
requisites: {
card: data.card_number,
holder: data.beneficiary,
bank_name: data.bank_name,
},
logs: [
{
request: { body: providerPayload },
status: providerRes.status,
response: data,
kind: "pay",
duration: 0.5,
},
],
});
});
app.post("/payout", async (req, res) => {
const body = req.body;
const amount = body.params.amount / 100;
const apiKey = body.settings.api_key;
const providerPayload = {
amount,
currency: body.params.currency,
card: body.params.card?.pan,
bank_account: body.params.bank_account,
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.params.amount,
currency: body.params.currency,
logs: [
{
request: { body: providerPayload },
status: providerRes.status,
response: data,
kind: "payout",
duration: 0.5,
},
],
});
});