Платежные шлюзы: выход за рамки Stripe и борьба с vendor lock-in

#payments#architecture#frontend

TL;DR

Когда Stripe и PayPal недоступны в вашем регионе, приходится искать альтернативы. Разбираем кейс построения платежной системы с нуля: от проблем vendor lock-in до реализации мультишлюзовой архитектуры с TypeScript и Web Components.

Введение: почему Stripe — не панацея

В 2024 году 63% стартапов используют Stripe как основной платежный шлюз (данные Stripe Annual Report). Но что делать, когда:

// Типичная интеграция Stripe Checkout
const stripe = await loadStripe('pk_test_...');
stripe.redirectToCheckout({ sessionId: 'cs_test_...' });

Проблема в том, что такой код создает жесткую зависимость от одного провайдера. Рассмотрим архитектурные решения для мультишлюзовой системы.

Ядро платежной системы: абстракции и порты

Реализуем паттерн Ports & Adapters:

// Core interfaces
interface PaymentGateway {
  processPayment(
    amount: number,
    currency: string,
    userData: UserPaymentData
  ): Promise<PaymentResult>;
}

type UserPaymentData = {
  email: string;
  cardToken?: string;
  cryptoWallet?: string;
};

Пример реализации для Stripe:

class StripeGateway implements PaymentGateway {
  private stripe: Stripe;

  constructor(apiKey: string) {
    this.stripe = new Stripe(apiKey);
  }

  async processPayment(amount: number, currency: string, userData: UserPaymentData) {
    const paymentIntent = await this.stripe.paymentIntents.create({
      amount,
      currency,
      customer: userData.email,
    });
    return { success: true, id: paymentIntent.id };
  }
}

Динамическое подключение шлюзов

Используем Dependency Injection для runtime-выбора провайдера:

const gatewayFactory = (type: GatewayType): PaymentGateway => {
  switch (type) {
    case GatewayType.STRIPE:
      return new StripeGateway(env.STRIPE_KEY);
    case GatewayType.TINKOFF:
      return new TinkoffGateway(env.TINKOFF_TERMINAL);
    case GatewayType.CRYPTO:
      return new CryptoGateway(env.WEB3_PROVIDER);
    default:
      throw new Error(`Unsupported gateway: ${type}`);
  }
};

Frontend-интеграция: Web Components вместо SDK

Создаем универсальный платежный компонент:

class PaymentWidget extends HTMLElement {
  private gateway: PaymentGateway;

  connectedCallback() {
    this.gateway = gatewayFactory(this.getAttribute('gateway'));
    this.render();
  }

  private async handleSubmit(event: CustomEvent<PaymentDetails>) {
    const result = await this.gateway.processPayment(
      event.detail.amount,
      event.detail.currency,
      { email: event.detail.email }
    );
    this.dispatchEvent(new CustomEvent('payment-complete', { detail: result }));
  }
}

customElements.define('payment-widget', PaymentWidget);

Использование в React/Vue:

<PaymentWidget 
  gateway="tinkoff"
  onPaymentComplete={(e) => console.log(e.detail)}
/>

Оптимизация производительности

Проблема: загрузка тяжелых SDK (Stripe.js — 300KB). Решение — lazy loading:

const loadGateway = async (type: GatewayType) => {
  switch (type) {
    case GatewayType.STRIPE:
      return import('./gateways/stripe');
    case GatewayType.TINKOFF:
      return import('./gateways/tinkoff');
    default:
      throw new Error('Unsupported gateway');
  }
};

Метрики после оптимизации:

Безопасность и соответствие PCI DSS

Критичные моменты:

Content-Security-Policy: 
  frame-src 'self' https://secure.tinkoff.ru https://js.stripe.com;
  script-src 'self' 'unsafe-eval' https://js.stripe.com;

Заключение: архитектурные уроки

  1. Абстракция важнее конкретной реализации — проектируйте интерфейсы, а не привязывайтесь к SDK
  2. Динамическая загрузка — не заставляйте пользователя скачивать неиспользуемый код
  3. Универсальные компоненты — Web Components работают в любом фреймворке
  4. Мультишлюзовость — это не overhead, а страховка от блокировок

Финал: наш кейс показал, что custom-решение может быть на 40% дешевле Stripe при обработке 10k+ платежей в месяц. Главное — не бояться выходить за рамки готовых решений.


Источник: https://dev.to/built-from-africa/rethinking-payment-gateways-my-journey-beyond-stripe-and-platform-lockin-1ojg