How to Accept P2P Payments and Make Payouts
You need to accept deposits via P2P transfers (user manually transfers money using provided details) and make payouts to cards or accounts. You are implementing a P2P flow: Gateway creates an order with the provider, returns transfer details, and then tracks the receipt of funds.
Difference from H2H: Payer’s card data is not transmitted — Gateway only creates an order and returns transfer details where the user should send money. What you’ll need: Provider’s API key, specification of their endpoints (pay, status, callback), test account/card for payouts.
1. Implement P2P Deposit (Payin) — Receiving Transfer Details
Section titled “1. Implement P2P Deposit (Payin) — Receiving Transfer Details”Step 1. Accept Order Creation Request
Section titled “Step 1. Accept Order Creation Request”Incoming request does not contain card data — only user information:
{ "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" }}Full request structure is described in Basic Contracts.
Step 2. Create Order with Provider
Section titled “Step 2. Create Order with Provider”Send request to provider with amount, currency, and reference (payment token). Provider will create an order and return transfer details.
Step 3. Return Transfer Details
Section titled “Step 3. Return Transfer Details”Response must contain status: "pending" and requisites field:
{ "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": [...]}Field requisites may contain:
card— recipient’s card number (masked)holder— recipient’s namebank_name— bank namephone— phone number (for phone transfers)account— account numberlink— URL for redirection (SberPay, etc.)deeplink— true if details contain a redirect linkqr_data— QR code for transfer
Step 4. Implement Status Finalization
Section titled “Step 4. Implement Status Finalization”After user transfers money, provider sends webhook to POST /callback or status is checked via POST /status.
Callback implementation is described in Callbacks & JWT. Status checking — in Status and Payout.
2. Implement P2P Payout
Section titled “2. Implement P2P Payout”Step 1. Extract Recipient’s Data
Section titled “Step 1. Extract Recipient’s Data”{ "params": { "amount": 50000, "currency": "EUR", "customer": { "email": "receiver@example.com" }, "card": { "pan": "4111111111111111" } }, "payment": { "token": "payout_tok_xxxx" }, "settings": { "api_key": "provider_api_token" }}Important: In P2P Payout, amount and currency may be in
params, not inpayment— check your provider’s specification.
Step 2. Convert Amount and Send Payout
Section titled “Step 2. Convert Amount and Send Payout”amount is passed in kopecks. Divide by 100 before sending to provider.
Step 3. Return Result
Section titled “Step 3. Return Result”{ "result": true, "status": "approved", "gateway_token": "payout_12345", "amount": 50000, "currency": "EUR", "details": "Payout successful", "logs": [...]}If payout is not instant — return status: "pending" and wait for callback.
Implementation Checklist
Section titled “Implementation Checklist”For Deposit (Payin)
Section titled “For Deposit (Payin)”- Create endpoint
POST /pay - Create order with provider
- Get transfer details
- Return
status: "pending"+requisites+gateway_token+logs - Implement
POST /callbackorPOST /statusfor status finalization
For Payout
Section titled “For Payout”- Create endpoint
POST /payout - Extract
params.card.panorparams.bank_account - Convert
params.amount(in kopecks) to base units for provider - Send payout to provider
- Return
logswith request/response details - If payout is not instant — return
status: "pending"and wait for callback
Node.js Implementation Example
Section titled “Node.js Implementation Example”Deposit (Payin)
Section titled “Deposit (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, 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, }, ], });});Payout
Section titled “Payout”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, }, ], });});