UNPKG

@koin_payments/react-native-payments-plugin

Version:

Plugin React Native oficial da Koin para integração com o SDK de Payments, permitindo adicionar funcionalidades de pagamento de forma simples e segura

627 lines (475 loc) 19 kB
# @koin_payments/react-native-payments-plugin [![npm version](https://badge.fury.io/js/@koin_payments%2Freact-native-payments-plugin.svg)](https://www.npmjs.com/package/@koin_payments/react-native-payments-plugin) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Platform](https://img.shields.io/badge/platform-iOS%20%7C%20Android-lightgrey.svg)](https://github.com/mauricio-bittencourt-koin/react-native-payments-react-native-plugin) Plugin React Native oficial da Koin para integração com o SDK de Payments, permitindo adicionar funcionalidades de pagamento de forma simples e segura em seus aplicativos iOS e Android. ## ✨ Features - 💳 **Pagamentos com Cartão** - Suporte completo para pagamentos com cartão de crédito/débito - 🏦 **PIX** - Integração nativa com PIX para pagamentos instantâneos - 📅 **Buy Now Pay Later (BNPL)** - Parcelamento sem cartão - 💰 **PayPal** - Integração com PayPal (apenas iOS) - 📱 **Bottom Sheet** - Apresentação em modal parcial para pagamentos com cartão (iOS e Android) - 🔒 **Seguro** - Integração com SDK nativo homologado - 🚀 **Turbo Module** - Arquitetura moderna do React Native para máximo desempenho - 📱 **Multiplataforma** - Suporte completo para iOS e Android - 🎨 **UI Nativa** - Experiência de checkout nativa em cada plataforma ## 📱 Métodos de Pagamento por Plataforma | Método | iOS | Android | Bottom Sheet | Observações | |--------|-----|---------|--------------|-------------| | PIX | ✅ | ✅ | ❌ | Suporte completo em ambas plataformas | | Cartão | ✅ | ✅ | ✅ | Crédito e débito com suporte a bottom sheet | | BNPL | ✅ | ✅ | ❌ | Parcelamento sem cartão | | PayPal | ✅ | ❌ | ❌ | **Disponível apenas no iOS** | > **Nota**: PayPal está disponível apenas no iOS devido a limitações do SDK nativo Android. Tentativas de usar PayPal no Android resultarão em erro com mensagem clara. ## 🎨 Modos de Apresentação da UI (UI Modes) O plugin suporta dois modos de apresentação para a interface de checkout: ### 🖼️ Fullscreen (Padrão) - Apresentação em tela cheia - Experiência imersiva completa - **Disponível para**: PIX, Cartão, BNPL e PayPal - **Comportamento padrão** quando `uiMode` não é especificado ### 📱 Bottom Sheet - Apresentação como sheet/modal parcial - Mantém contexto da tela anterior visível - **Disponível apenas para**: Pagamentos com Cartão - **Plataformas**: iOS e Android - Melhor experiência para fluxos rápidos de pagamento **Importante**: O modo bottom sheet é **exclusivo para pagamentos com cartão**. Para outros métodos (PIX, BNPL, PayPal), o parâmetro `uiMode` será ignorado e a apresentação será sempre fullscreen. ## 📋 Requisitos - React Native >= 0.71.0 - iOS >= 13.0 - Android >= API 24 (Android 7.0) - Swift 5.0+ - Kotlin 1.8+ - New Architecture habilitada (`RCT_NEW_ARCH_ENABLED=1`) ## 📦 Instalação ```bash # npm npm install @koin_payments/react-native-payments-plugin # yarn yarn add @koin_payments/react-native-payments-plugin ``` ### Configuração iOS 1. Instale as dependências CocoaPods: ```bash cd ios && pod install && cd .. ``` 2. Configure o `Info.plist` (opcional, se necessário permissões): ```xml <key>NSCameraUsageDescription</key> <string>Necessário para escanear cartões</string> ``` ### Configuração Android 1. Certifique-se de que o `minSdkVersion` está configurado: ```gradle // android/build.gradle buildscript { ext { minSdkVersion = 24 compileSdkVersion = 34 targetSdkVersion = 34 } } ``` 2. Verifique que o Kotlin está configurado: ```gradle // android/build.gradle dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.0" } ``` ## 🚀 Quick Start ```typescript import { initializeSDK, initiatePayment, PaymentDefaults, type PaymentResult, } from '@koin_payments/react-native-payments-plugin'; // 1. Inicializar o SDK (faça isso uma vez no início do app) await initializeSDK('sua-api-key', { isHomolog: true, // false para produção enableLogs: true, currencyCode: 'BRL', primaryColorHex: '#008f21', secondaryColorHex: '#004E1D', }); // 2. Processar pagamento PIX (fullscreen) const pixRequest = PaymentDefaults.PIX_PAYMENT( 99.90, 'order-123', 'cliente@email.com' ); const result: PaymentResult = await initiatePayment(pixRequest); if (result.status === 'success') { console.log('Pagamento aprovado!', result.transactionId); } else { console.log('Pagamento falhou:', result.error); } // 3. Processar pagamento com Cartão (bottom sheet) const cardRequest = PaymentDefaults.CARD_PAYMENT_BOTTOM_SHEET( 150.00, 'order-456', 'cliente@email.com' ); const cardResult = await initiatePayment(cardRequest); // Apresenta checkout como bottom sheet - mantém contexto da tela anterior! ``` ## 🔐 Configuração de API Key e Segurança ### ⚠️ NUNCA exponha sua API Key no código! A API Key da Koin é sensível e **nunca** deve ser commitada no controle de versão ou hardcoded no código-fonte. ### ✅ Método Recomendado: Configuração Nativa (Build-Time) **IMPORTANTE:** Este plugin **NÃO requer** bibliotecas adicionais como `react-native-config`. A API key deve ser passada como parâmetro na inicialização do SDK. O plugin segue o padrão dos SDKs nativos iOS e Android, onde a responsabilidade de gerenciar API keys fica com o desenvolvedor do aplicativo. #### Para usar o plugin em seu app: ```typescript import { initializeSDK } from '@koin_payments/react-native-payments-plugin'; // ❌ ERRADO - Hardcoded (NUNCA FAÇA ISSO!) const apiKey = 'sk_uFSocsl9YRxToVjnCw0UMAuGmRKKFPfT'; // ✅ CORRETO - Gerenciar a chave de forma segura // Opção 1: Usar seu próprio sistema de configuração nativa // Opção 2: Usar react-native-config (se preferir) // Opção 3: Usar variáveis de ambiente do seu sistema de CI/CD // Exemplo de uso (a forma de obter a chave fica a seu critério): await initializeSDK(suaApiKey, { isHomolog: true, enableLogs: true, currencyCode: 'BRL', primaryColorHex: '#008f21', secondaryColorHex: '#004E1D', }); ``` #### Exemplo de Configuração Nativa (exemplo no repositório) O app de exemplo deste repositório (`/example`) usa configuração nativa seguindo o padrão dos SDKs nativos: **iOS** (`Config.xcconfig`): ```xcconfig API_KEY = sk_your_api_key_here PAYPAL_CLIENT_ID = your_paypal_client_id_here ``` **Android** (`local.properties`): ```properties api.key=sk_your_api_key_here paypal.client.id=your_paypal_client_id_here ``` Veja o [README do exemplo](./example/README.md) para detalhes completos de como configurar o app de exemplo. ### 📋 Checklist de Segurança - ✅ API Key passada como parâmetro (não hardcoded no código) - ✅ Usar sistema de configuração seguro (nativo, .env, ou CI/CD) - ✅ Arquivos de configuração adicionados ao `.gitignore` - ✅ Templates com placeholders commitados (.sample files) - ✅ Validação de API Key implementada no código - ✅ Diferentes chaves para ambientes (dev/homolog/prod) - ⚠️ NUNCA commitar arquivos de configuração com chaves reais - ⚠️ NUNCA compartilhar API keys em issues, PRs ou documentação ### 🔑 Como obter uma API Key Para obter uma API Key da Koin, entre em contato com: 📧 **mauricio.bittencourt@koin.com.br** ## 📖 API Reference ### `initializeSDK(apiKey: string, config?: SDKConfig): Promise<string>` Inicializa o SDK de pagamentos. **Parâmetros:** - `apiKey` (required): Sua chave de API da Koin - `config` (optional): Configurações do SDK **Exemplo de Configuração:** ```typescript const config = { isHomolog: true, // Ambiente (true = homolog, false = prod) enableLogs: true, // Habilitar logs de debug currencyCode: 'BRL', // Código da moeda primaryColorHex: '#008f21', // Cor primária do SDK secondaryColorHex: '#004E1D', // Cor secundária do SDK maxRetries: 3, // Tentativas máximas de retry pollingInterval: 3.0, // Intervalo de polling (segundos) pollingTimeout: 360.0, // Timeout de polling (segundos) }; ``` --- ### `initiatePayment(request: PaymentRequest): Promise<PaymentResult>` Processa um pagamento. **Parâmetros:** - `request`: Objeto de requisição de pagamento **Interface PaymentRequest:** ```typescript interface PaymentRequest { amount: number; // Valor do pagamento referenceId: string; // ID único da transação paymentType?: 'pix' | 'card' | 'paypal' | 'bnpl'; // Tipo de pagamento email?: string; // Email do cliente account?: string; // Conta da transação businessId?: string; // ID do negócio (para BNPL) storeCode?: string; // Código da loja expirationMinutes?: number; // Tempo de expiração (padrão: 30) // Modo de apresentação da UI uiMode?: 'fullscreen' | 'bottomsheet'; // - 'fullscreen': Tela cheia (padrão) // - 'bottomsheet': Modal parcial (apenas para 'card') } ``` **Retorno:** ```typescript interface PaymentResult { status: 'success' | 'failed' | 'cancelled'; message?: string; transactionId?: string; data?: { referenceId: string; amount: number; paymentType: string; currency: string; timestamp: string; }; error?: string; } ``` --- ### `PaymentDefaults` Helper para criar requisições de pagamento de forma simplificada. #### `PIX_PAYMENT(amount, referenceId, email?)` Cria requisição para pagamento PIX em tela cheia. ```typescript const pixRequest = PaymentDefaults.PIX_PAYMENT( 99.90, 'order-123', 'cliente@email.com' ); ``` #### `CARD_PAYMENT(amount, referenceId, email?)` Cria requisição para pagamento com cartão em tela cheia (fullscreen). ```typescript const cardRequest = PaymentDefaults.CARD_PAYMENT( 150.00, 'order-456', 'cliente@email.com' ); ``` #### `CARD_PAYMENT_BOTTOM_SHEET(amount, referenceId, email?)` Cria requisição para pagamento com cartão em modo bottom sheet. ```typescript const cardBottomSheetRequest = PaymentDefaults.CARD_PAYMENT_BOTTOM_SHEET( 150.00, 'order-456', 'cliente@email.com' ); ``` #### `BNPL_PAYMENT(amount, referenceId, email?, businessId?)` Cria requisição para Buy Now Pay Later (parcelamento sem cartão). ```typescript const bnplRequest = PaymentDefaults.BNPL_PAYMENT( 500.00, 'order-789', 'cliente@email.com', 'business-id-123' ); ``` ## 💳 Exemplos de Uso ### Pagamento com PIX ```typescript import { initiatePayment, PaymentDefaults } from '@koin_payments/react-native-payments-plugin'; async function processarPagamentoPIX() { try { const request = PaymentDefaults.PIX_PAYMENT( 99.90, `PIX-${Date.now()}`, 'cliente@example.com' ); const result = await initiatePayment(request); if (result.status === 'success') { Alert.alert('Sucesso', `PIX aprovado! ID: ${result.transactionId}`); } else if (result.status === 'cancelled') { Alert.alert('Cancelado', 'Pagamento cancelado pelo usuário'); } else { Alert.alert('Erro', result.error || 'Erro desconhecido'); } } catch (error) { Alert.alert('Erro', error.message); } } ``` ### Pagamento com Cartão (Fullscreen) ```typescript async function processarPagamentoCartao() { const request = PaymentDefaults.CARD_PAYMENT( 150.00, `CARD-${Date.now()}`, 'cliente@example.com' ); const result = await initiatePayment(request); if (result.status === 'success') { Alert.alert('Sucesso', `Pagamento aprovado! ID: ${result.transactionId}`); } else if (result.status === 'cancelled') { Alert.alert('Cancelado', 'Pagamento cancelado pelo usuário'); } else { Alert.alert('Erro', result.error || 'Erro desconhecido'); } } ``` ### Pagamento com Cartão (Bottom Sheet) ```typescript import { initiatePayment, PaymentDefaults } from '@koin_payments/react-native-payments-plugin'; async function processarPagamentoBottomSheet() { try { // Usando o helper const request = PaymentDefaults.CARD_PAYMENT_BOTTOM_SHEET( 150.00, `CARD-${Date.now()}`, 'cliente@example.com' ); const result = await initiatePayment(request); if (result.status === 'success') { Alert.alert('Sucesso', `Cartão processado! ID: ${result.transactionId}`); } else if (result.status === 'cancelled') { Alert.alert('Cancelado', 'Usuário fechou o bottom sheet'); } else { Alert.alert('Erro', result.error || 'Erro ao processar pagamento'); } } catch (error) { Alert.alert('Erro', error.message); } } ``` **Ou usando configuração manual:** ```typescript async function processarPagamentoBottomSheetCustom() { const request = { amount: 150.00, referenceId: `CARD-${Date.now()}`, paymentType: 'card', email: 'cliente@example.com', uiMode: 'bottomsheet', // Define apresentação como bottom sheet expirationMinutes: 30, }; const result = await initiatePayment(request); // ... tratar resultado } ``` ### Buy Now Pay Later (BNPL) ```typescript async function processarPagamentoBNPL() { const request = PaymentDefaults.BNPL_PAYMENT( 500.00, `BNPL-${Date.now()}`, 'cliente@example.com', 'my-business-id' ); const result = await initiatePayment(request); // ... tratar resultado } ``` ### Pagamento Customizado ```typescript import { initiatePayment, type PaymentRequest } from '@koin_payments/react-native-payments-plugin'; const customRequest: PaymentRequest = { amount: 250.00, referenceId: `CUSTOM-${Date.now()}`, paymentType: 'pix', email: 'cliente@example.com', expirationMinutes: 30, // Campos opcionais: account: 'my-account', businessId: 'my-business', storeCode: 'store-001', }; const result = await initiatePayment(customRequest); ``` ## ✅ Best Practices ### Quando usar Bottom Sheet vs Fullscreen #### Use **Bottom Sheet** quando: - ✅ O pagamento é uma ação rápida dentro de um fluxo maior - ✅ Você quer manter o contexto visual da tela anterior - ✅ O usuário precisa ver informações da tela de origem - ✅ A experiência deve parecer mais leve e menos intrusiva - ✅ Está processando pagamento com **cartão** **Exemplo:** Checkout em um carrinho de compras onde o usuário quer ver os itens enquanto paga. #### Use **Fullscreen** quando: - ✅ O checkout é o foco principal da experiência - ✅ Você quer uma experiência imersiva e sem distrações - ✅ Está usando PIX, BNPL ou PayPal (obrigatório) - ✅ O fluxo de pagamento tem múltiplas etapas **Exemplo:** Página dedicada de checkout onde o foco total é completar o pagamento. ### Tratamento de Erros Sempre trate os três estados possíveis do resultado: ```typescript const result = await initiatePayment(request); switch (result.status) { case 'success': // Pagamento aprovado - atualizar UI, navegar para tela de sucesso console.log('Transaction ID:', result.transactionId); break; case 'cancelled': // Usuário cancelou - voltar ao estado anterior console.log('Usuário cancelou o pagamento'); break; case 'failed': // Erro no pagamento - mostrar mensagem, permitir retry console.error('Erro:', result.error); Alert.alert('Erro', result.message || 'Falha ao processar pagamento'); break; } ``` ### Performance e UX 1. **Inicialize o SDK uma única vez** no início do app (AppDelegate/Application) 2. **Não adicione timeouts customizados** - o SDK gerencia automaticamente 3. **Use referenceId únicos** para cada transação 4. **Valide dados antes** de chamar `initiatePayment` 5. **Forneça feedback visual** enquanto aguarda o resultado ### Segurança - ⚠️ **Nunca** exponha sua API key no código (veja [Configuração de API Key e Segurança](#-configuração-de-api-key-e-segurança)) - ✅ Use configuração nativa build-time ou sistema de gerenciamento de secrets - ✅ Sempre use HTTPS em ambiente de produção - ✅ Valide valores de pagamento no backend antes de confirmar - ✅ Adicione arquivos de configuração ao `.gitignore` para prevenir commits acidentais - ✅ Use diferentes API keys para desenvolvimento, homologação e produção ## 🔧 Troubleshooting ### iOS **Erro: "SDK not initialized"** - Certifique-se de chamar `initializeSDK()` antes de `initiatePayment()` - Aguarde a Promise do `initializeSDK()` ser resolvida antes de processar pagamentos **Erro: "Module not found"** - Execute `cd ios && pod install && cd ..` - Limpe o build: `cd ios && xcodebuild clean && cd ..` - Reabra o Xcode **Erro: "KoinPaymentCheckout version not found"** - Atualize o CocoaPods: `pod update KoinPaymentCheckout` - Verifique a conexão com internet ### Android **Erro: "SDK initialization failed"** - Verifique se o `minSdkVersion >= 24` - Confirme que o Kotlin está configurado no projeto - Limpe e reconstrua: `cd android && ./gradlew clean && cd ..` **Erro: "KoinPaymentCheckout has already been initialized"** - Isso é normal em hot reload/desenvolvimento - O plugin gerencia automaticamente re-inicializações **Build falha com erro de Kotlin** - Verifique a versão do Kotlin no `android/build.gradle` - Sincronize o projeto: `cd android && ./gradlew sync && cd ..` ### Geral **Metro bundler não conecta** - Verifique se o Metro está rodando: `yarn start` - iOS: use a porta padrão 8081 - Android: verifique o emulador/device está acessível **Erro de timeout** - O SDK gerencia timeouts internamente - Não adicione timeouts customizados nos pagamentos - Verifique a conexão de internet do dispositivo ### Bottom Sheet **Bottom sheet não aparece (aparece fullscreen)** - ✅ Verifique que `uiMode: 'bottomsheet'` está definido corretamente - ✅ Confirme que `paymentType` é `'card'` (bottom sheet só funciona com cartão) - ✅ Certifique-se de usar SDK versão 1.0.0 ou superior - ✅ iOS: verifique que KoinPaymentCheckout >= 1.0.0-build.10 - ✅ Android: verifique que payment-checkout >= 0.1.0 **Bottom sheet não funciona com PIX/BNPL/PayPal** - ⚠️ **Esperado**: Bottom sheet é **exclusivo para pagamentos com cartão** - Para outros métodos, use `uiMode: 'fullscreen'` ou omita o parâmetro **Bottom sheet fecha inesperadamente** - Verifique se não há conflitos com outros modals na aplicação - Certifique-se de que não há overlays ou views bloqueando a interação - iOS: verifique que não há gesture recognizers conflitantes **Problemas de UI em diferentes tamanhos de tela** - O SDK adapta automaticamente o bottom sheet para diferentes dispositivos - Teste em múltiplos tamanhos de tela (phones e tablets) - Reporte issues específicas com modelo do device ## 📄 License MIT © [Koin](https://www.koin.com.br) ## 🤝 Contributing Contribuições são bem-vindas! Por favor, leia o [CONTRIBUTING.md](CONTRIBUTING.md) para detalhes.