P2P Cryptocurrency Exchanger Development

We design and develop full-cycle blockchain solutions: from smart contract architecture to launching DeFi protocols, NFT marketplaces and crypto exchanges. Security audits, tokenomics, integration with existing infrastructure.
Showing 1 of 1 servicesAll 1306 services
P2P Cryptocurrency Exchanger Development
Complex
~1-2 weeks
FAQ
Blockchain Development Services
Blockchain Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1214
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    823

Integration of Exchanger with Payment Systems

A crypto-exchanger without fiat payment methods is a crypto-to-crypto platform. Adding fiat payments (bank transfer, cards, e-wallets) expands the audience and increases transaction volumes. The main challenge is that each payment provider has its own KYC requirements, limits, and API.

Types of Payment Integrations

Bank Cards (Visa/Mastercard)

For accepting cards through a PSP (Payment Service Provider): Stripe, Adyen, Checkout.com. Direct connection to Visa/Mastercard requires PCI DSS certification—complex and expensive. PSPs handle compliance for you.

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 in minimal currency units (cents for 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;
    }
  }
}

Bank Transfers (SEPA, SWIFT)

For European users—SEPA Credit Transfer. For international—SWIFT. Integration through Banking API providers: 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:
        """Get a conversion quote"""
        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:
        """Add a payment recipient"""
        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:
        """Create a transfer"""
        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()

E-wallets

QIWI, YooMoney (for RU market), 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:
        """Create a payment form, return redirect URL"""
        response = requests.post('https://yoomoney.ru/quickpay/confirm', data={
            'receiver': RECEIVER_WALLET,
            'quickpay-form': 'button',
            'targets': f'Order #{order_id}',
            'paymentType': 'AC',  # bank card
            'sum': float(amount),
            'label': order_id,
            'successURL': return_url,
        })
        return response.url
    
    def check_payment(self, label: str) -> Optional[PaymentInfo]:
        """Check if payment has been received"""
        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

Provider Abstraction

To support multiple providers, you need a unified abstraction:

from abc import ABC, abstractmethod

class PaymentProvider(ABC):
    @abstractmethod
    async def create_payment(self, order: ExchangeOrder) -> PaymentLink:
        """Create a payment link/form"""
        pass
    
    @abstractmethod
    async def check_payment_status(self, payment_id: str) -> PaymentStatus:
        """Check payment status"""
        pass
    
    @abstractmethod
    async def refund(self, payment_id: str, amount: Decimal) -> bool:
        """Refund funds"""
        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}")
        
        # Check method limits
        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)

Limits and KYC Thresholds

PAYMENT_LIMITS = {
    'card': PaymentLimits(
        min=Decimal('10'),
        max_no_kyc=Decimal('500'),  # up to 500 EUR without 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'),    # bank transfer always requires KYC
        max_basic_kyc=Decimal('5000'),
        max_full_kyc=Decimal('100000'),
    ),
    'yoomoney': PaymentLimits(
        min=Decimal('100'),
        max_no_kyc=Decimal('15000'),  # in rubles
        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

Monitoring and Reconciliation

-- Daily reconciliation: volumes by payment method
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;

Integration Timeline

Provider Integration Time
Stripe (cards) 1–2 weeks
Wise (SEPA/SWIFT) 2–3 weeks
QIWI/YooMoney 1–2 weeks
PayPal 1–2 weeks
Each additional 1 week

Full multi-provider system with abstraction, KYC limits, and reconciliation: 6–8 weeks.