Business LogicPayment Providers

Payment Providers

Overview

Wafra implements a composite payment provider system that supports multiple fiat-to-crypto payment processors through a unified abstraction layer. This architecture enables optimal routing, quote comparison, and seamless provider switching while maintaining a consistent user experience.

Current Provider Ecosystem

Active Providers

ProviderStatusIntegration TypeKYC MethodOnrampOfframpGlobal Support
Onramp✅ LiveDirect APITraditional✅ Yes✅ Yes✅ Global
DTR✅ LiveWhitelabelSIWE + Sumsub✅ Yes🔄 Limited🔄 Limited

Provider Selection Logic

Current Implementation:

  • Quote-Driven: Best rates determine provider selection
  • Capability-Based: Provider features influence routing (onramp vs offramp)
  • Regional Availability: Geographic restrictions affect provider choice
  • KYC Status: User’s verification status impacts provider selection

Composite Payment Architecture

Service Abstraction

Provider Interface:

abstract class BasePaymentService {
  abstract getQuote(params: QuoteParams): Promise<Quote | null>;
  abstract createOrder(
    user: User,
    params: TransactionParams
  ): Promise<TransactionResult>;
  abstract getKycStatus(user: User): Promise<KycResponse>;
  abstract getUserLimit(user: User): Promise<UserLimit>;
  abstract handleWebhook(payload: any, headers: any): Promise<boolean>;
}
 
// Current implementations
class OnrampService extends BasePaymentService {
  /* ... */
}
class DtrService extends BasePaymentService {
  /* ... */
}

Composite Service:

export class PaymentService {
  private providers: Map<string, BasePaymentService> = new Map();
 
  constructor(providers: BasePaymentService[]) {
    providers.forEach((provider) => {
      this.providers.set(provider.id, provider);
    });
  }
 
  async getBestQuote(params: QuoteParams): Promise<Quote[]> {
    // Compare quotes from all available providers
    const quotes = await Promise.allSettled(
      Array.from(this.providers.values()).map((provider) =>
        provider.getQuote(params)
      )
    );
 
    return quotes
      .filter((result) => result.status === "fulfilled" && result.value)
      .map((result) => result.value)
      .sort((a, b) => b.cryptoAmount - a.cryptoAmount); // Best rate first
  }
 
  getProvider(providerId: string): BasePaymentService | undefined {
    return this.providers.get(providerId);
  }
}

Quote Comparison System

Multi-Provider Quotes:

interface Quote {
  provider: string;
  fiatAmount: number;
  cryptoAmount: number;
  rate: string;
  fees: {
    provider: number;
    network: number;
    total: number;
  };
  estimatedTime: string;
  paymentMethods: PaymentMethod[];
  quoteId?: string;
}
 
// Real-time quote comparison
const quotes = await paymentService.getBestQuote({
  fiatAmount: 1000,
  fiatCurrency: "USD",
  cryptoCurrency: "USDC",
  countryCode: "US",
  paymentMethod: "bank_transfer",
});

Onramp Provider Integration

Service Architecture

Onramp Service Structure:

export class OnrampService extends BasePaymentService {
  private apiClient: OnrampApiClient;
  private paymentMethods: OnrampPaymentMethods;
  private orderManager: OnrampOrderManager;
  private userManager: OnrampUserManager;
  private limitsService: OnrampLimitsService;
  private quoteService: OnrampQuoteService;
  private webhookService: OnrampWebhookService;
 
  // Service capabilities
  supportsOnramp: true;
  supportsOfframp: true;
  globalSupport: true;
}

Onramp Features

Key Capabilities:

  • Global Coverage: Supports 100+ countries
  • Multiple Payment Methods: Bank transfers, cards, e-wallets
  • Bidirectional: Both onramp (fiat→crypto) and offramp (crypto→fiat)
  • Traditional KYC: Standard identity verification process
  • Widget Integration: Embedded payment interface

Payment Method Support:

interface OnrampPaymentMethod {
  type: "instant" | "bank_transfer";
  currency: string;
  country: string;
  minAmount: number;
  maxAmount: number;
  feePercentage: number; // 1% for instant, 0.5% for bank
  estimatedTime: string;
}

Onramp Process

  • Step 1: Real-time exchange rates with fees

  • Step 2: Create or retrieve Onramp customer account

  • Step 3: Traditional identity verification

  • Step 4: Check user’s monthly transaction limits

  • Step 5: Submit transaction to Onramp API

  • Step 6: User completes payment via Onramp interface

  • Step 7: USDC delivered to user’s Safe wallet

Offramp Process

  • Step 1: Configure bank account or e-wallet details

  • Step 2: Crypto-to-fiat conversion rates

  • Step 3: Initiate withdrawal from user’s wallet

  • Step 4: Onramp handles fiat settlement

  • Step 5: USD delivered to user’s bank account

DTR Provider Integration

Service Architecture

DTR Service Structure:

export class DtrService extends BasePaymentService {
  private apiClient: DtrApiClient;
  private userManager: DtrUserManager;
  private quoteService: DtrQuoteService;
  private authService: DtrAuthService; // SIWE integration
  private webhookService: DTRWebhookService;
 
  // Service capabilities
  supportsOnramp: true;
  supportsOfframp: false; // Limited offramp support
  globalSupport: false; // Limited regional support
}

SIWE Authentication

Sign-In With Ethereum Integration:

// SIWE challenge generation
async generateSiweChallenge(user: User, userAddress: string): Promise<SiweChallenge> {
  const challenge = await this.authService.generateChallenge({
    address: userAddress,
    domain: 'wafra.app',
    statement: 'Sign in to Wafra for secure payment processing',
    uri: 'https://wafra.app',
    version: '1',
    chainId: 8453, // Base network
  });
 
  return challenge;
}
 
// Signature verification and authentication
async verifySiweAndAuthenticate(
  user: User,
  signatureData: SiweSignatureRequest
): Promise<AuthenticationResult> {
  const verification = await this.authService.verifySignature(signatureData);
 
  if (verification.success) {
    await this.userManager.createOrUpdateDtrUser(user, verification.address);
    return { authenticated: true, dtrUserId: verification.dtrUserId };
  }
 
  return { authenticated: false, error: verification.error };
}

Enhanced KYC with Sumsub

DTR KYC Process:

  • Step 1: User signs message with wallet

  • Step 2: Account created with verified wallet address

  • Step 3: Enhanced identity verification initiated

  • Step 4: Professional KYC/AML provider handles verification

  • Step 5: ID, selfie, and additional compliance documents

  • Step 6: AML, sanctions, and PEP screening

  • Step 7: Banking services activated upon approval

Sumsub Integration:

async getSumsubToken(user: User): Promise<SumsubTokenResponse> {
  const dtrUser = await this.userManager.getDtrUser(user);
 
  const tokenResponse = await this.apiClient.makeAuthenticatedRequest(
    'POST',
    '/kyc/sumsub/token',
    {
      userId: dtrUser.dtrUserId,
      applicantId: dtrUser.applicantId,
    }
  );
 
  return {
    token: tokenResponse.access_token,
    applicantId: tokenResponse.applicant_id,
    expiresAt: tokenResponse.expires_at,
  };
}

Payment Method Management

Dynamic Payment Methods

Payment Method Structure:

interface PaymentMethod {
  id: number;
  type: "bank_transfer" | "instant" | "card" | "ewallet";
  name: string;
  currency: string;
  country: string;
  provider: "onramp" | "dtr";
  minAmount: number;
  maxAmount: number;
  feeFixed: number;
  feePercentage: number;
  isPayInAllowed: boolean; // Onramp support
  isPayOutAllowed: boolean; // Offramp support
  isActive: boolean;
  details: {
    // Provider-specific metadata
    fiatType?: number; // Onramp fiat type ID
    methodName?: string; // Onramp method identifier
    bankCode?: string; // DTR bank code
    routingNumber?: string; // DTR routing number
  };
}

Provider-Specific Methods

Onramp Payment Methods:

  • Bank Transfers: ACH, SEPA, domestic transfers
  • Instant Transfers: Real-time payment networks
  • Cards: Credit and debit card support
  • E-Wallets: PayPal, digital wallet integration

DTR Payment Methods:

  • Virtual Bank Accounts: Dedicated account numbers
  • SWIFT Transfers: International wire transfers
  • Domestic Banking: Country-specific banking rails

Transaction Lifecycle Management

Order Management

Order Status Flow:

Order Tracking:

interface Order {
  id: string;
  userId: string;
  provider: "onramp" | "dtr";
  amount: number;
  usdcAmount: bigint;
  currency: string;
  status: OrderStatus;
  paymentMethod: string;
  depositAddress: string;
  foreignOrderId?: string; // Provider's transaction ID
  transactionType: "onramp" | "offramp";
  createdAt: Date;
  updatedAt: Date;
}

Real-Time Status Updates

Webhook Integration:

// Unified webhook handler
async function handleProviderWebhook(
  provider: string,
  payload: any,
  headers: any
): Promise<WebhookResponse> {
  const paymentProvider = paymentService.getProvider(provider);
 
  if (!paymentProvider) {
    throw new Error(`Unknown provider: ${provider}`);
  }
 
  // Provider handles webhook verification and processing
  const result = await paymentProvider.handleWebhook(payload, headers);
 
  if (result.success) {
    // Update order status in database
    await updateOrderStatus(result.orderId, result.status);
 
    // Notify user via Socket.IO
    await notifyUserOrderUpdate(result.userId, result);
  }
 
  return result;
}

Error Handling & Recovery

Provider Failure Handling

Automatic Fallback:

async function createOrderWithFallback(
  user: User,
  params: TransactionParams
): Promise<TransactionResult> {
  const providers = ["onramp", "dtr"]; // Priority order
 
  for (const providerId of providers) {
    try {
      const provider = paymentService.getProvider(providerId);
      if (!provider) continue;
 
      const result = await provider.createOrder(user, params);
 
      if (result.status !== "error") {
        return result;
      }
    } catch (error) {
      logger.warn(`Provider ${providerId} failed, trying next`, { error });
      continue;
    }
  }
 
  throw new Error("All payment providers failed");
}

Error Categories

Common Error Types:

enum PaymentError {
  INSUFFICIENT_FUNDS = "insufficient_funds",
  KYC_REQUIRED = "kyc_required",
  LIMIT_EXCEEDED = "limit_exceeded",
  UNSUPPORTED_REGION = "unsupported_region",
  PROVIDER_UNAVAILABLE = "provider_unavailable",
  INVALID_PAYMENT_METHOD = "invalid_payment_method",
  RATE_EXPIRED = "rate_expired",
}

Monitoring & Analytics

Provider Performance Metrics

Current Tracking:

  • Success Rates: Transaction completion by provider
  • Processing Times: Average time from creation to completion
  • Quote Accuracy: Rate comparison between quote and execution
  • User Preference: Provider selection patterns
  • Regional Performance: Success rates by geography

Cost Analysis

Fee Comparison:

interface ProviderCostAnalysis {
  provider: string;
  averageFeePercentage: number;
  averageProcessingTime: number;
  successRate: number;
  userSatisfactionScore: number;
  regionalAvailability: string[];
}

Future Enhancements

Additional Providers

Planned Integrations:

  • MoonPay: Additional global coverage
  • Ramp: European market focus
  • Banxa: APAC region specialization
  • Transak: Comprehensive global solution

Advanced Features

Roadmap Items:

  • Smart Routing: AI-driven provider selection
  • Dynamic Pricing: Real-time fee optimization
  • Batch Processing: Grouped transaction efficiency
  • Cross-Border Optimization: Multi-currency support
  • Regulatory Adaptation: Jurisdiction-specific compliance

This payment provider architecture ensures reliable, cost-effective fiat-to-crypto conversion while maintaining regulatory compliance and optimal user experience.