@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
Markdown
[](https://www.npmjs.com/package/@koin_payments/react-native-payments-plugin)
[](https://opensource.org/licenses/MIT)
[](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.
- 💳 **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"
}
```
```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!
```
A API Key da Koin é sensível e **nunca** deve ser commitada no controle de versão ou hardcoded no código-fonte.
**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.
```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',
});
```
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.
- ✅ 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)
};
```
---
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;
}
```
---
Helper para criar requisições de pagamento de forma simplificada.
Cria requisição para pagamento PIX em tela cheia.
```typescript
const pixRequest = PaymentDefaults.PIX_PAYMENT(
99.90,
'order-123',
'cliente@email.com'
);
```
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'
);
```
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'
);
```
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'
);
```
```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);
}
}
```
```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');
}
}
```
```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
}
```
```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
}
```
```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);
```
- ✅ 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.
- ✅ 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;
}
```
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 ..`
**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 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.