Интеграция обменника с платёжными системами
Крипто-обменник без фиатных платёжных методов — это крипто-на-крипто платформа. Добавление фиатных платежей (банковский перевод, карты, электронные кошельки) расширяет аудиторию и увеличивает объёмы. Главная сложность — каждый платёжный провайдер имеет свои требования к 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)
Для European пользователей — SEPA Credit Transfer. Для international — 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 неделя |
Полная multi-provider система с абстракцией, KYC лимитами и сверкой: 6–8 недель.







