Інтеграція обмінника з платіжними системами
Крипто-обмінник без фіатних платіжних методів — це крипто-на-крипто платформа. Додавання фіатних платежів (банківський переказ, карти, електронні гаманці) розширює аудиторію і збільшує обсяги операцій. Головна складність — кожен платіжний провайдер має свої вимоги до KYC, свої ліміти та свої API.
Типи платіжних інтеграцій
Банківські карти (Visa/Mastercard)
Для прийому карт через PSP (Payment Service Provider): Stripe, Adyen, Checkout.com. Пряме підключення до Visa/Mastercard вимагає сертифікації PCI DSS — складно та дорого. PSP беруть на себе compliance.
import Stripe from 'stripe';
const stripe = new Stripe(STRIPE_SECRET_KEY, { apiVersion: '2023-10-16' });
class CardPaymentProvider {
async createPaymentIntent(amount: number, currency: string, metadata: object) {
// amount в мінімальних одиницях валюти (центи для USD)
const intent = await stripe.paymentIntents.create({
amount: Math.round(amount * 100),
currency: currency.toLowerCase(),
automatic_payment_methods: { enabled: true },
metadata: {
order_id: metadata.orderId,
user_id: metadata.userId,
},
});
return {
clientSecret: intent.client_secret,
intentId: intent.id,
};
}
async handleWebhook(rawBody: Buffer, signature: string) {
const event = stripe.webhooks.constructEvent(
rawBody,
signature,
STRIPE_WEBHOOK_SECRET
);
switch (event.type) {
case 'payment_intent.succeeded':
const intent = event.data.object;
await this.onPaymentSuccess(intent.metadata.order_id, intent.amount / 100);
break;
case 'payment_intent.payment_failed':
await this.onPaymentFailed(event.data.object.metadata.order_id);
break;
}
}
}
Банківські переводи (SEPA, SWIFT)
Для європейських користувачів — SEPA Credit Transfer. Для міжнародних — SWIFT. Інтеграція через Banking API провайдерів: Wise Business API, Modulr, Railsr.
class WiseProvider:
BASE_URL = 'https://api.wise.com'
def __init__(self, api_token: str, profile_id: str):
self.token = api_token
self.profile_id = profile_id
self.headers = {'Authorization': f'Bearer {api_token}'}
def create_quote(self, source_currency: str, target_currency: str, amount: float) -> dict:
"""Отримуємо котировку конверсії"""
response = requests.post(f"{self.BASE_URL}/v3/profiles/{self.profile_id}/quotes",
headers=self.headers,
json={
'sourceCurrency': source_currency,
'targetCurrency': target_currency,
'sourceAmount': amount,
}
)
return response.json()
def create_recipient(self, name: str, iban: str, country: str) -> dict:
"""Додаємо отримувача платежу"""
return requests.post(f"{self.BASE_URL}/v1/accounts",
headers=self.headers,
json={
'currency': 'EUR',
'type': 'iban',
'profile': self.profile_id,
'accountHolderName': name,
'details': {'iban': iban},
}
).json()
def create_transfer(self, quote_id: str, recipient_id: str, reference: str) -> dict:
"""Створюємо переказ"""
return requests.post(f"{self.BASE_URL}/v1/transfers",
headers=self.headers,
json={
'targetAccount': recipient_id,
'quoteUuid': quote_id,
'customerTransactionId': reference,
'details': {'reference': reference},
}
).json()
Електронні гаманці
QIWI, YooMoney (для RU ринку), PayPal, Skrill, Neteller:
class YooMoneyProvider:
def __init__(self, token: str, client_id: str):
self.token = token
self.client_id = client_id
def create_payment(self, amount: Decimal, order_id: str, return_url: str) -> str:
"""Створюємо платіжну форму, повертаємо redirect URL"""
response = requests.post('https://yoomoney.ru/quickpay/confirm', data={
'receiver': RECEIVER_WALLET,
'quickpay-form': 'button',
'targets': f'Order #{order_id}',
'paymentType': 'AC', # банківська карта
'sum': float(amount),
'label': order_id,
'successURL': return_url,
})
return response.url
def check_payment(self, label: str) -> Optional[PaymentInfo]:
"""Перевіряємо поступив ли платіж"""
response = requests.post('https://yoomoney.ru/api/operation-history',
headers={'Authorization': f'Bearer {self.token}'},
data={'label': label, 'type': 'deposition'}
)
operations = response.json().get('operations', [])
if operations and operations[0]['status'] == 'success':
return PaymentInfo(
amount=Decimal(str(operations[0]['amount'])),
datetime=operations[0]['datetime'],
operation_id=operations[0]['operation_id'],
)
return None
Абстракція провайдерів
Для підтримки кількох провайдерів потрібна єдина абстракція:
from abc import ABC, abstractmethod
class PaymentProvider(ABC):
@abstractmethod
async def create_payment(self, order: ExchangeOrder) -> PaymentLink:
"""Створює платіжне посилання/форму"""
pass
@abstractmethod
async def check_payment_status(self, payment_id: str) -> PaymentStatus:
"""Перевіряє статус платежу"""
pass
@abstractmethod
async def refund(self, payment_id: str, amount: Decimal) -> bool:
"""Повернення коштів"""
pass
class PaymentRouter:
def __init__(self, providers: dict[str, PaymentProvider]):
self.providers = providers
async def create_payment(self, order: ExchangeOrder, method: str) -> PaymentLink:
provider = self.providers.get(method)
if not provider:
raise ValueError(f"Payment method not supported: {method}")
# Перевіряємо ліміти методу
limits = PAYMENT_LIMITS[method]
if order.amount < limits.min or order.amount > limits.max:
raise ValueError(f"Amount out of range for {method}")
return await provider.create_payment(order)
Ліміти та пороги KYC
PAYMENT_LIMITS = {
'card': PaymentLimits(
min=Decimal('10'),
max_no_kyc=Decimal('500'), # до 500 EUR без KYC
max_basic_kyc=Decimal('2000'),
max_full_kyc=Decimal('50000'),
daily_no_kyc=Decimal('1000'),
),
'bank_transfer': PaymentLimits(
min=Decimal('50'),
max_no_kyc=Decimal('0'), # банківський переказ завжди вимагає KYC
max_basic_kyc=Decimal('5000'),
max_full_kyc=Decimal('100000'),
),
'yoomoney': PaymentLimits(
min=Decimal('100'),
max_no_kyc=Decimal('15000'), # у рублях
max_basic_kyc=Decimal('100000'),
),
}
def get_required_kyc_tier(method: str, amount: Decimal) -> KYCTier:
limits = PAYMENT_LIMITS[method]
if amount <= limits.max_no_kyc:
return KYCTier.NONE
elif amount <= limits.max_basic_kyc:
return KYCTier.BASIC
else:
return KYCTier.FULL
Моніторинг та звірка
-- Щоденна звірка: суми за методом платежу
SELECT
payment_method,
COUNT(*) AS transactions,
SUM(amount) AS total_volume,
SUM(CASE WHEN status = 'completed' THEN amount ELSE 0 END) AS completed_volume,
SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) AS failed_count,
SUM(CASE WHEN status = 'refunded' THEN amount ELSE 0 END) AS refunded_volume
FROM payments
WHERE created_at::date = CURRENT_DATE - INTERVAL '1 day'
GROUP BY payment_method;
Термін інтеграції
| Провайдер | Термін інтеграції |
|---|---|
| Stripe (карти) | 1–2 тижні |
| Wise (SEPA/SWIFT) | 2–3 тижні |
| QIWI/YooMoney | 1–2 тижні |
| PayPal | 1–2 тижні |
| Кожен додатковий | 1 тиждень |
Повна мультипровайдерна система з абстракцією, ліміти KYC та звіркою: 6–8 тижнів.







