diff --git a/README.md b/README.md
index c963ff3..292fd07 100644
--- a/README.md
+++ b/README.md
@@ -179,6 +179,148 @@ enum InstallmentType {
}
```
+## 🎨 UI Customization & Theming
+
+Customize the appearance of PagBank SDK modal dialogs to match your app's design. The library provides complete control over colors, themes, and UI elements displayed during payment transactions.
+
+
+
+
Payment modal with custom dark theme matching your app's design
+
+
+### ⚡ Quick Theme Setup
+
+```typescript
+import { setStyleTheme } from 'react-native-plugpag-nitro';
+
+// Create and apply a custom theme
+await setStyleTheme({
+ headBackgroundColor: '#1A1A1D', // Header background
+ headTextColor: '#FFFFFF', // Header text
+ positiveButtonBackground: '#22C55E', // "Confirm" button color
+ negativeButtonBackground: '#EF4444', // "Cancel" button color
+ contentTextColor: '#FFFFFF', // Body text color
+ lineColor: '#71717A' // Borders and lines
+});
+```
+
+### 🛠️ Custom Theme Creation
+
+Create themes that match your app's design system:
+
+```typescript
+// Create a custom brand-based theme
+const customTheme = {
+ headBackgroundColor: '#00D4FF', // Your primary brand color
+ headTextColor: '#FFFFFF', // White text on colored header
+ contentTextColor: '#1A1A1D', // Dark text for body content
+ positiveButtonBackground: '#00D4FF', // Consistent brand color
+ negativeButtonBackground: '#EF4444', // Standard red for cancel
+ lineColor: '#E5E7EB' // Light gray for borders
+};
+
+await setStyleTheme(customTheme);
+```
+
+### 🎨 Complete Style Properties
+
+
+📋 All available style properties
+
+```typescript
+interface PlugpagStyleData {
+ // Header styling
+ headTextColor?: string; // Header text color
+ headBackgroundColor?: string; // Header background color
+
+ // Content styling
+ contentTextColor?: string; // General content text
+ contentTextValue1Color?: string; // Primary monetary values
+ contentTextValue2Color?: string; // Secondary monetary values
+
+ // Button styling
+ positiveButtonTextColor?: string; // Confirm button text
+ positiveButtonBackground?: string; // Confirm button background
+ negativeButtonTextColor?: string; // Cancel button text
+ negativeButtonBackground?: string; // Cancel button background
+ genericButtonBackground?: string; // Generic button background
+ genericButtonTextColor?: string; // Generic button text
+
+ // Input field styling
+ genericSmsEditTextBackground?: string; // SMS input background
+ genericSmsEditTextTextColor?: string; // SMS input text
+
+ // Interface elements
+ lineColor?: string; // Lines, borders, dividers
+}
+```
+
+
+
+### 🔧 Theme Utilities
+
+```typescript
+import { ThemeUtils } from 'react-native-plugpag-nitro';
+
+// Validate theme colors
+const errors = ThemeUtils.validateTheme(myTheme);
+if (errors.length === 0) {
+ await setStyleTheme(myTheme);
+}
+
+// Merge themes
+const customizedTheme = ThemeUtils.mergeThemes(
+ baseTheme, // Your base theme
+ { positiveButtonBackground: '#FF6B6B' }
+);
+
+// Preview theme colors (for debugging)
+const preview = ThemeUtils.createThemePreview(myTheme);
+console.log(preview); // { headBackgroundColor: '#1A1A1D', ... }
+```
+
+### 💡 Best Practices
+
+- **Apply early**: Set themes during app initialization, before payment operations
+- **Match your design**: Use colors that complement your existing UI
+- **Accessibility**: Ensure sufficient contrast between text and backgrounds
+- **Validation**: Always validate themes before applying them
+- **Error handling**: Wrap theme operations in try-catch blocks
+
+### 🏗️ Integration Example
+
+```typescript
+import React, { useEffect } from 'react';
+import { setStyleTheme } from 'react-native-plugpag-nitro';
+
+export default function App() {
+ useEffect(() => {
+ // Apply theme matching your app's design
+ const initializeTheme = async () => {
+ try {
+ const customTheme = {
+ headBackgroundColor: '#1A1A1D',
+ headTextColor: '#FFFFFF',
+ positiveButtonBackground: '#22C55E',
+ negativeButtonBackground: '#EF4444'
+ };
+
+ await setStyleTheme(customTheme);
+ console.log('Theme applied successfully');
+ } catch (error) {
+ console.error('Failed to apply theme:', error);
+ }
+ };
+
+ initializeTheme();
+ }, []);
+
+ // ... rest of your app
+}
+```
+
+> 📖 **Complete styling guide**: See [STYLING_GUIDE.md](STYLING_GUIDE.md) for detailed documentation and examples.
+
## 💡 Usage Examples
### Complete Payment Flow
diff --git a/STYLING_GUIDE.md b/STYLING_GUIDE.md
new file mode 100644
index 0000000..7e3635e
--- /dev/null
+++ b/STYLING_GUIDE.md
@@ -0,0 +1,340 @@
+````markdown
+# PagBank SDK UI Customization
+
+This document explains how to customize the appearance of PagBank SDK modal dialogs, buttons, and text using the new styling API.
+
+## Overview
+
+The PagBank SDK displays modal dialogs during payment transactions. You can customize the colors and appearance of these dialogs to match your app's design by creating custom themes within your application.
+
+## Quick Start
+
+```typescript
+import { setStyleTheme } from 'react-native-plugpag-nitro';
+
+// Create and apply a custom theme
+await setStyleTheme({
+ headBackgroundColor: '#1A1A1D',
+ headTextColor: '#FFFFFF',
+ positiveButtonBackground: '#22C55E',
+ positiveButtonTextColor: '#FFFFFF',
+ negativeButtonBackground: '#EF4444',
+ negativeButtonTextColor: '#FFFFFF',
+});
+```
+
+## Available Style Properties
+
+### Header Styling
+- `headTextColor` - Color for header text
+- `headBackgroundColor` - Background color for header area
+
+### Content Styling
+- `contentTextColor` - General content text color
+- `contentTextValue1Color` - Primary monetary values color
+- `contentTextValue2Color` - Secondary monetary values color
+
+### Button Styling
+- `positiveButtonTextColor` - Text color for confirmation buttons (e.g., "Imprimir")
+- `positiveButtonBackground` - Background color for confirmation buttons
+- `negativeButtonTextColor` - Text color for cancel buttons (e.g., "Cancelar")
+- `negativeButtonBackground` - Background color for cancel buttons
+- `genericButtonBackground` - Background for generic buttons (e.g., "Enviar SMS")
+- `genericButtonTextColor` - Text color for generic buttons
+
+### Input Fields
+- `genericSmsEditTextBackground` - Background for SMS input fields
+- `genericSmsEditTextTextColor` - Text color for SMS input fields
+
+### Interface Elements
+- `lineColor` - Color for lines, borders, and dividers
+
+## Creating Custom Themes
+
+All themes are created within your application. The library provides utilities to help validate and manage themes, but no predefined themes are included.
+
+### Example: Dark Theme
+```typescript
+const darkTheme = {
+ headTextColor: '#FFFFFF',
+ headBackgroundColor: '#0A0A0B',
+ contentTextColor: '#F3F4F6',
+ contentTextValue1Color: '#00D4FF',
+ contentTextValue2Color: '#9CA3AF',
+ positiveButtonBackground: '#10B981',
+ positiveButtonTextColor: '#FFFFFF',
+ negativeButtonBackground: '#EF4444',
+ negativeButtonTextColor: '#FFFFFF',
+ genericButtonBackground: '#1F2937',
+ genericButtonTextColor: '#F3F4F6',
+ genericSmsEditTextBackground: '#1F2937',
+ genericSmsEditTextTextColor: '#F3F4F6',
+ lineColor: '#374151',
+};
+
+await setStyleTheme(darkTheme);
+```
+
+### Example: Light Theme
+```typescript
+const lightTheme = {
+ headTextColor: '#1F2937',
+ headBackgroundColor: '#FFFFFF',
+ contentTextColor: '#1F2937',
+ contentTextValue1Color: '#0EA5E9',
+ contentTextValue2Color: '#6B7280',
+ positiveButtonTextColor: '#FFFFFF',
+ positiveButtonBackground: '#10B981',
+ negativeButtonTextColor: '#FFFFFF',
+ negativeButtonBackground: '#EF4444',
+ genericButtonBackground: '#F3F4F6',
+ genericButtonTextColor: '#1F2937',
+ genericSmsEditTextBackground: '#F9FAFB',
+ genericSmsEditTextTextColor: '#1F2937',
+ lineColor: '#E5E7EB',
+};
+
+await setStyleTheme(lightTheme);
+```
+
+### Example: Brand-Based Theme
+```typescript
+const brandTheme = {
+ headTextColor: '#FFFFFF',
+ headBackgroundColor: '#7C3AED', // Your brand purple
+ contentTextColor: '#1F2937',
+ contentTextValue1Color: '#7C3AED',
+ contentTextValue2Color: '#6B7280',
+ positiveButtonTextColor: '#FFFFFF',
+ positiveButtonBackground: '#7C3AED',
+ negativeButtonTextColor: '#FFFFFF',
+ negativeButtonBackground: '#EF4444',
+ genericButtonBackground: '#F3F4F6',
+ genericButtonTextColor: '#1F2937',
+ genericSmsEditTextBackground: '#F9FAFB',
+ genericSmsEditTextTextColor: '#1F2937',
+ lineColor: '#E5E7EB',
+};
+
+await setStyleTheme(brandTheme);
+```
+
+## Usage in Your App
+
+### Apply Theme on App Initialization
+```typescript
+import { useEffect } from 'react';
+import { setStyleTheme } from 'react-native-plugpag-nitro';
+
+export default function App() {
+ useEffect(() => {
+ const initializeTheme = async () => {
+ try {
+ const customTheme = {
+ headBackgroundColor: '#1A1A1D',
+ headTextColor: '#FFFFFF',
+ positiveButtonBackground: '#22C55E',
+ // ... other properties
+ };
+
+ await setStyleTheme(customTheme);
+ console.log('Theme applied successfully');
+ } catch (error) {
+ console.error('Failed to apply theme:', error);
+ }
+ };
+
+ initializeTheme();
+ }, []);
+
+ // ... rest of your app
+}
+```
+
+### Dynamic Theme Switching
+```typescript
+import { useState } from 'react';
+import { setStyleTheme } from 'react-native-plugpag-nitro';
+
+function ThemeSelector() {
+ const [currentTheme, setCurrentTheme] = useState('dark');
+
+ const createDarkTheme = () => ({
+ headBackgroundColor: '#0A0A0B',
+ headTextColor: '#FFFFFF',
+ positiveButtonBackground: '#10B981',
+ // ... other properties
+ });
+
+ const createLightTheme = () => ({
+ headBackgroundColor: '#FFFFFF',
+ headTextColor: '#1F2937',
+ positiveButtonBackground: '#10B981',
+ // ... other properties
+ });
+
+ const switchTheme = async (themeName: string) => {
+ try {
+ let theme;
+ switch (themeName) {
+ case 'dark':
+ theme = createDarkTheme();
+ break;
+ case 'light':
+ theme = createLightTheme();
+ break;
+ default:
+ return;
+ }
+
+ await setStyleTheme(theme);
+ setCurrentTheme(themeName);
+ } catch (error) {
+ console.error('Failed to switch theme:', error);
+ }
+ };
+
+ return (
+
+ switchTheme('dark')} />
+ switchTheme('light')} />
+
+ );
+}
+```
+
+## Theme Validation
+
+Validate your theme before applying it:
+
+```typescript
+import { ThemeUtils } from 'react-native-plugpag-nitro';
+
+const myTheme = {
+ headBackgroundColor: '#1A1A1D',
+ positiveButtonBackground: '#22C55E',
+ // ... other properties
+};
+
+const errors = ThemeUtils.validateTheme(myTheme);
+if (errors.length > 0) {
+ console.error('Theme validation errors:', errors);
+} else {
+ await setStyleTheme(myTheme);
+}
+```
+
+## Merging Themes
+
+Extend base themes with custom overrides:
+
+```typescript
+import { ThemeUtils } from 'react-native-plugpag-nitro';
+
+const baseTheme = {
+ headBackgroundColor: '#0A0A0B',
+ headTextColor: '#FFFFFF',
+ positiveButtonBackground: '#10B981',
+ // ... other properties
+};
+
+const customizedTheme = ThemeUtils.mergeThemes(baseTheme, {
+ positiveButtonBackground: '#FF6B6B', // Custom red for confirm buttons
+ negativeButtonBackground: '#4ECDC4', // Custom teal for cancel buttons
+});
+
+await setStyleTheme(customizedTheme);
+```
+
+## Theme Utility Functions
+
+The library provides utility functions to help with theme management:
+
+```typescript
+import { ThemeUtils } from 'react-native-plugpag-nitro';
+
+// Validate theme colors
+const errors = ThemeUtils.validateTheme(myTheme);
+
+// Merge themes safely
+const mergedTheme = ThemeUtils.mergeThemes(baseTheme, overrides);
+
+// Create theme preview (for debugging)
+const preview = ThemeUtils.createThemePreview(myTheme);
+console.log(preview); // Shows all color values
+```
+
+## Color Format
+
+All colors should be provided as hex strings:
+- 6-digit hex: `#FFFFFF` (white)
+- 3-digit hex: `#FFF` (white, shorthand)
+
+Invalid formats will be ignored and default colors will be used.
+
+## Best Practices
+
+1. **Apply themes early**: Set your theme during app initialization, before any payment operations.
+
+2. **Match your app's design**: Use colors that complement your existing UI for a cohesive experience.
+
+3. **Consider accessibility**: Ensure sufficient contrast between text and background colors.
+
+4. **Test on real devices**: Colors may appear differently on various screens.
+
+5. **Handle errors gracefully**: Always wrap theme application in try-catch blocks.
+
+6. **Create theme factories**: Use functions to generate consistent themes across your app.
+
+## Example: App-Matching Theme Factory
+
+Create reusable theme generators in your app:
+
+```typescript
+// themes.ts
+export const createAppTheme = (isDark: boolean = true) => {
+ if (isDark) {
+ return {
+ headBackgroundColor: '#0A0A0B',
+ headTextColor: '#FFFFFF',
+ contentTextColor: '#F3F4F6',
+ contentTextValue1Color: '#00D4FF',
+ contentTextValue2Color: '#9CA3AF',
+ positiveButtonBackground: '#10B981',
+ positiveButtonTextColor: '#FFFFFF',
+ negativeButtonBackground: '#EF4444',
+ negativeButtonTextColor: '#FFFFFF',
+ genericButtonBackground: '#1F2937',
+ genericButtonTextColor: '#F3F4F6',
+ genericSmsEditTextBackground: '#1F2937',
+ genericSmsEditTextTextColor: '#F3F4F6',
+ lineColor: '#374151',
+ };
+ }
+
+ return {
+ headBackgroundColor: '#FFFFFF',
+ headTextColor: '#1F2937',
+ contentTextColor: '#1F2937',
+ contentTextValue1Color: '#0EA5E9',
+ contentTextValue2Color: '#6B7280',
+ positiveButtonBackground: '#10B981',
+ positiveButtonTextColor: '#FFFFFF',
+ negativeButtonBackground: '#EF4444',
+ negativeButtonTextColor: '#FFFFFF',
+ genericButtonBackground: '#F3F4F6',
+ genericButtonTextColor: '#1F2937',
+ genericSmsEditTextBackground: '#F9FAFB',
+ genericSmsEditTextTextColor: '#1F2937',
+ lineColor: '#E5E7EB',
+ };
+};
+
+// Usage
+await setStyleTheme(createAppTheme(true)); // Dark theme
+await setStyleTheme(createAppTheme(false)); // Light theme
+```
+
+This approach gives you complete control over your app's payment modal styling while maintaining consistency with your app's design system.
+
+````
diff --git a/android/src/main/java/com/margelo/nitro/plugpagnitro/ErrorCodeMapper.kt b/android/src/main/java/com/margelo/nitro/plugpagnitro/ErrorCodeMapper.kt
new file mode 100644
index 0000000..4a138ee
--- /dev/null
+++ b/android/src/main/java/com/margelo/nitro/plugpagnitro/ErrorCodeMapper.kt
@@ -0,0 +1,44 @@
+package com.margelo.nitro.plugpagnitro
+
+import br.com.uol.pagseguro.plugpagservice.wrapper.PlugPag
+
+/**
+ * Utility class for mapping PlugPag SDK error codes to Nitro error codes
+ * Eliminates duplication across multiple methods
+ */
+object ErrorCodeMapper {
+
+ /**
+ * Maps PlugPag SDK result codes to Nitro ErrorCode enum
+ * @param plugPagResult The result code from PlugPag SDK
+ * @return Corresponding ErrorCode enum value
+ */
+ fun mapToErrorCode(plugPagResult: Int): ErrorCode {
+ return when (plugPagResult) {
+ PlugPag.RET_OK -> ErrorCode.OK
+ PlugPag.OPERATION_ABORTED -> ErrorCode.OPERATION_ABORTED
+ PlugPag.AUTHENTICATION_FAILED -> ErrorCode.AUTHENTICATION_FAILED
+ PlugPag.COMMUNICATION_ERROR -> ErrorCode.COMMUNICATION_ERROR
+ PlugPag.NO_PRINTER_DEVICE -> ErrorCode.NO_PRINTER_DEVICE
+ PlugPag.NO_TRANSACTION_DATA -> ErrorCode.NO_TRANSACTION_DATA
+ else -> ErrorCode.COMMUNICATION_ERROR
+ }
+ }
+
+ /**
+ * Maps PlugPag SDK result codes to string representation
+ * @param plugPagResult The result code from PlugPag SDK
+ * @return String representation of the error code
+ */
+ fun mapToErrorCodeString(plugPagResult: Int): String {
+ return when (plugPagResult) {
+ PlugPag.RET_OK -> "RET_OK"
+ PlugPag.OPERATION_ABORTED -> "OPERATION_ABORTED"
+ PlugPag.AUTHENTICATION_FAILED -> "AUTHENTICATION_FAILED"
+ PlugPag.COMMUNICATION_ERROR -> "COMMUNICATION_ERROR"
+ PlugPag.NO_PRINTER_DEVICE -> "NO_PRINTER_DEVICE"
+ PlugPag.NO_TRANSACTION_DATA -> "NO_TRANSACTION_DATA"
+ else -> "COMMUNICATION_ERROR"
+ }
+ }
+}
diff --git a/android/src/main/java/com/margelo/nitro/plugpagnitro/OperationHandler.kt b/android/src/main/java/com/margelo/nitro/plugpagnitro/OperationHandler.kt
new file mode 100644
index 0000000..45c31ad
--- /dev/null
+++ b/android/src/main/java/com/margelo/nitro/plugpagnitro/OperationHandler.kt
@@ -0,0 +1,55 @@
+package com.margelo.nitro.plugpagnitro
+
+import android.util.Log
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
+
+/**
+ * Generic exception handler for consistent error handling across all operations
+ * Eliminates repetitive try/catch blocks
+ */
+object OperationHandler {
+
+ private const val TAG = "OperationHandler"
+
+ /**
+ * Executes an operation with consistent error handling and context switching
+ * @param operationName Name of the operation for logging
+ * @param operation The suspend function to execute
+ * @return Result of the operation
+ * @throws Exception with formatted error message
+ */
+ suspend fun executeOperation(
+ operationName: String,
+ operation: suspend () -> T
+ ): T {
+ return withContext(Dispatchers.IO) {
+ try {
+ Log.d(TAG, "Starting operation: $operationName")
+ val result = operation()
+ Log.d(TAG, "Operation completed successfully: $operationName")
+ result
+ } catch (e: Exception) {
+ val errorMessage = "${operationName.uppercase()}_ERROR: ${e.message ?: "Unknown error"}"
+ Log.e(TAG, "Error in operation $operationName", e)
+ throw Exception(errorMessage)
+ }
+ }
+ }
+
+ /**
+ * Simplified version for operations that don't need context switching
+ */
+ fun executeSimpleOperation(
+ operationName: String,
+ operation: () -> T
+ ): T {
+ return try {
+ operation()
+ } catch (e: Exception) {
+ val errorMessage = "${operationName.uppercase()}_ERROR: ${e.message ?: "Unknown error"}"
+ Log.e(TAG, "Error in operation $operationName", e)
+ throw Exception(errorMessage)
+ }
+ }
+}
diff --git a/android/src/main/java/com/margelo/nitro/plugpagnitro/PaymentEventHandler.kt b/android/src/main/java/com/margelo/nitro/plugpagnitro/PaymentEventHandler.kt
new file mode 100644
index 0000000..a2bf306
--- /dev/null
+++ b/android/src/main/java/com/margelo/nitro/plugpagnitro/PaymentEventHandler.kt
@@ -0,0 +1,130 @@
+package com.margelo.nitro.plugpagnitro
+
+import br.com.uol.pagseguro.plugpagservice.wrapper.PlugPagEventData
+import android.util.Log
+
+/**
+ * Configuration-driven event handler for payment events
+ * Reduces complex switch statements to simple configuration
+ */
+object PaymentEventHandler {
+
+ private const val TAG = "PaymentEventHandler"
+
+ // Event configuration mapping
+ private val eventConfig = mapOf(
+ // Password events
+ PlugPagEventData.EVENT_CODE_DIGIT_PASSWORD to EventConfig(
+ baseCode = 1010.0,
+ messageProvider = { data, context ->
+ val count = context["passwordCount"] as? Int ?: 0
+ "Senha: ${"*".repeat(minOf(count, 6))}"
+ }
+ ),
+ PlugPagEventData.EVENT_CODE_NO_PASSWORD to EventConfig(
+ baseCode = 1011.0,
+ message = "Digite sua senha"
+ )
+ )
+
+ // Pattern-based event mapping for dynamic messages
+ private val patternConfig = listOf(
+ PatternConfig(
+ patterns = listOf("cartão", "card"),
+ subPatterns = mapOf(
+ listOf("inserir", "insert") to EventConfig(1004.0, "Aguardando cartão..."),
+ listOf("remov", "retire") to EventConfig(1030.0, "Retire o cartão")
+ ),
+ defaultConfig = EventConfig(1001.0, "Cartão detectado")
+ ),
+ PatternConfig(
+ patterns = listOf("processa", "process"),
+ defaultConfig = EventConfig(1020.0, "Processando transação...")
+ ),
+ PatternConfig(
+ patterns = listOf("conecta", "connect"),
+ defaultConfig = EventConfig(1021.0, "Conectando à rede...")
+ ),
+ PatternConfig(
+ patterns = listOf("envian", "send"),
+ defaultConfig = EventConfig(1022.0, "Enviando dados...")
+ ),
+ PatternConfig(
+ patterns = listOf("aguard", "wait"),
+ defaultConfig = EventConfig(1023.0, "Aguardando resposta...")
+ ),
+ PatternConfig(
+ patterns = listOf("aprovad", "approved"),
+ defaultConfig = EventConfig(1031.0, "Transação aprovada")
+ ),
+ PatternConfig(
+ patterns = listOf("negad", "denied", "recusad"),
+ defaultConfig = EventConfig(1032.0, "Transação negada")
+ )
+ )
+
+ /**
+ * Handles payment events using configuration-driven approach
+ * @param eventData The event data from PlugPag SDK
+ * @param context Additional context (like password count)
+ */
+ fun handleEvent(eventData: PlugPagEventData, context: MutableMap = mutableMapOf()) {
+ val eventCode = eventData.eventCode
+ val message = eventData.customMessage ?: ""
+
+ // Handle specific event codes first
+ eventConfig[eventCode]?.let { config ->
+ val finalMessage = config.messageProvider?.invoke(eventData, context)
+ ?: config.message
+ ?: message
+ emitEvent(config.baseCode, finalMessage)
+ return
+ }
+
+ // Handle pattern-based events
+ if (message.isNotEmpty()) {
+ patternConfig.forEach { patternConfig ->
+ if (patternConfig.patterns.any { pattern ->
+ message.contains(pattern, ignoreCase = true)
+ }) {
+ // Check sub-patterns first
+ patternConfig.subPatterns?.forEach { (subPatterns, config) ->
+ if (subPatterns.any { subPattern ->
+ message.contains(subPattern, ignoreCase = true)
+ }) {
+ emitEvent(config.baseCode, config.message ?: message)
+ return
+ }
+ }
+ // Use default pattern config
+ emitEvent(
+ patternConfig.defaultConfig.baseCode,
+ patternConfig.defaultConfig.message ?: message
+ )
+ return
+ }
+ }
+
+ // Default case for unknown patterns
+ emitEvent(1020.0, message)
+ }
+ }
+
+ private fun emitEvent(code: Double, message: String) {
+ Log.d(TAG, "Emitting event - Code: $code, Message: $message")
+ PlugpagEventEmitter.emitPaymentEvent(code, message)
+ }
+
+ // Configuration data classes
+ data class EventConfig(
+ val baseCode: Double,
+ val message: String? = null,
+ val messageProvider: ((PlugPagEventData, Map) -> String)? = null
+ )
+
+ data class PatternConfig(
+ val patterns: List,
+ val subPatterns: Map, EventConfig>? = null,
+ val defaultConfig: EventConfig
+ )
+}
diff --git a/android/src/main/java/com/margelo/nitro/plugpagnitro/PlugpagNitro.kt b/android/src/main/java/com/margelo/nitro/plugpagnitro/PlugpagNitro.kt
index ac54334..eaf5518 100644
--- a/android/src/main/java/com/margelo/nitro/plugpagnitro/PlugpagNitro.kt
+++ b/android/src/main/java/com/margelo/nitro/plugpagnitro/PlugpagNitro.kt
@@ -17,12 +17,12 @@ import br.com.uol.pagseguro.plugpagservice.wrapper.PlugPagPrinterListener
import br.com.uol.pagseguro.plugpagservice.wrapper.PlugPagPrintResult
import br.com.uol.pagseguro.plugpagservice.wrapper.PlugPagEventListener
import br.com.uol.pagseguro.plugpagservice.wrapper.PlugPagEventData
-import com.margelo.nitro.plugpagnitro.PlugpagEventEmitter
@DoNotStrip
class PlugpagNitro : HybridPlugpagNitroSpec() {
private lateinit var plugPag: PlugPag
+ private val eventContext = mutableMapOf()
companion object {
private const val TAG = "PlugpagNitro"
@@ -57,55 +57,41 @@ class PlugpagNitro : HybridPlugpagNitroSpec() {
}
override fun getTerminalSerialNumber(): String {
- return try {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- Build.getSerial()
- } else {
- @Suppress("DEPRECATION")
- Build.SERIAL ?: "UNKNOWN"
+ return OperationHandler.executeSimpleOperation("getTerminalSerialNumber") {
+ when {
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> {
+ try {
+ Build.getSerial()
+ } catch (e: SecurityException) {
+ Log.w(TAG, "Permission required to access serial number", e)
+ "PERMISSION_REQUIRED"
+ }
+ }
+ else -> {
+ @Suppress("DEPRECATION")
+ Build.SERIAL ?: "UNKNOWN"
+ }
}
- } catch (e: SecurityException) {
- Log.w(TAG, "Permission required to access serial number", e)
- "PERMISSION_REQUIRED"
- } catch (e: Exception) {
- Log.e(TAG, "Error getting terminal serial number", e)
- "UNKNOWN"
}
}
override fun initializeAndActivatePinPad(activationCode: String): Promise {
return Promise.async {
- withContext(Dispatchers.IO) {
- try {
- initializePlugPag()
-
- val activationData = PlugPagActivationData(activationCode)
- val result = plugPag.initializeAndActivatePinpad(activationData)
-
- val errorCode = when (result.result) {
- PlugPag.RET_OK -> ErrorCode.OK
- PlugPag.OPERATION_ABORTED -> ErrorCode.OPERATION_ABORTED
- PlugPag.AUTHENTICATION_FAILED -> ErrorCode.AUTHENTICATION_FAILED
- PlugPag.COMMUNICATION_ERROR -> ErrorCode.COMMUNICATION_ERROR
- PlugPag.NO_PRINTER_DEVICE -> ErrorCode.NO_PRINTER_DEVICE
- PlugPag.NO_TRANSACTION_DATA -> ErrorCode.NO_TRANSACTION_DATA
- else -> ErrorCode.COMMUNICATION_ERROR
- }
-
- PlugpagInitializationResult(
- result = errorCode,
- errorCode = result.errorCode ?: "",
- errorMessage = result.errorMessage ?: ""
- )
- } catch (e: Exception) {
- Log.e(TAG, "Error initializing pin pad", e)
- throw Exception("INITIALIZATION_ERROR: ${e.message ?: "Unknown error"}")
- }
+ OperationHandler.executeOperation("initializeAndActivatePinPad") {
+ initializePlugPag()
+
+ val activationData = PlugPagActivationData(activationCode)
+ val result = plugPag.initializeAndActivatePinpad(activationData)
+
+ PlugpagInitializationResult(
+ result = ErrorCodeMapper.mapToErrorCode(result.result),
+ errorCode = result.errorCode ?: "",
+ errorMessage = result.errorMessage ?: ""
+ )
}
}
}
-
override fun doPayment(
amount: Double,
type: PaymentType,
@@ -115,169 +101,29 @@ class PlugpagNitro : HybridPlugpagNitroSpec() {
userReference: String
): Promise {
return Promise.async {
- withContext(Dispatchers.IO) {
- try {
- initializePlugPag()
-
- // Convert enum to PlugPag SDK constants
- val paymentType = when (type) {
- PaymentType.CREDIT -> PlugPag.TYPE_CREDITO
- PaymentType.DEBIT -> PlugPag.TYPE_DEBITO
- PaymentType.VOUCHER -> PlugPag.TYPE_VOUCHER
- PaymentType.PIX -> PlugPag.TYPE_PIX
- }
-
- val installmentTypeInt = when (installmentType) {
- InstallmentType.NO_INSTALLMENT -> PlugPag.INSTALLMENT_TYPE_A_VISTA
- InstallmentType.SELLER_INSTALLMENT -> PlugPag.INSTALLMENT_TYPE_PARC_VENDEDOR
- InstallmentType.BUYER_INSTALLMENT -> PlugPag.INSTALLMENT_TYPE_PARC_COMPRADOR
- }
-
- val plugPagPaymentData = PlugPagPaymentData(
- paymentType,
- amount.toInt(),
- installmentTypeInt,
- installments.toInt(),
- userReference,
- printReceipt
- )
-
- // Set up event listener for real-time payment events
- var passwordCount = 0
- plugPag.setEventListener(object : PlugPagEventListener {
- override fun onEvent(plugPagEventData: PlugPagEventData) {
- val eventCode = plugPagEventData.eventCode
- var message = plugPagEventData.customMessage ?: ""
-
- // Handle specific events and enhance messages
- when (eventCode) {
- PlugPagEventData.EVENT_CODE_DIGIT_PASSWORD -> {
- passwordCount++
- message = when (passwordCount) {
- 1 -> "Senha: *"
- 2 -> "Senha: **"
- 3 -> "Senha: ***"
- 4 -> "Senha: ****"
- 5 -> "Senha: *****"
- 6 -> "Senha: ******"
- else -> "Senha: ****"
- }
- PlugpagEventEmitter.emitPaymentEvent(1010.0, message)
- }
- PlugPagEventData.EVENT_CODE_NO_PASSWORD -> {
- passwordCount = 0
- message = "Digite sua senha"
- PlugpagEventEmitter.emitPaymentEvent(1011.0, message)
- }
- else -> {
- // Handle other events with generic messages
- when {
- message.contains("cartão", ignoreCase = true) ||
- message.contains("card", ignoreCase = true) -> {
- if (message.contains("inserir", ignoreCase = true) ||
- message.contains("insert", ignoreCase = true)) {
- PlugpagEventEmitter.emitPaymentEvent(1004.0, "Aguardando cartão...")
- } else if (message.contains("remov", ignoreCase = true) ||
- message.contains("retire", ignoreCase = true)) {
- PlugpagEventEmitter.emitPaymentEvent(1030.0, "Retire o cartão")
- } else {
- PlugpagEventEmitter.emitPaymentEvent(1001.0, message.ifEmpty { "Cartão detectado" })
- }
- }
- message.contains("processa", ignoreCase = true) ||
- message.contains("process", ignoreCase = true) -> {
- PlugpagEventEmitter.emitPaymentEvent(1020.0, message.ifEmpty { "Processando transação..." })
- }
- message.contains("conecta", ignoreCase = true) ||
- message.contains("connect", ignoreCase = true) -> {
- PlugpagEventEmitter.emitPaymentEvent(1021.0, message.ifEmpty { "Conectando à rede..." })
- }
- message.contains("envian", ignoreCase = true) ||
- message.contains("send", ignoreCase = true) -> {
- PlugpagEventEmitter.emitPaymentEvent(1022.0, message.ifEmpty { "Enviando dados..." })
- }
- message.contains("aguard", ignoreCase = true) ||
- message.contains("wait", ignoreCase = true) -> {
- PlugpagEventEmitter.emitPaymentEvent(1023.0, message.ifEmpty { "Aguardando resposta..." })
- }
- message.contains("aprovad", ignoreCase = true) ||
- message.contains("aprovad", ignoreCase = true) -> {
- emitPaymentEvent(1031.0, "Transação aprovada")
- }
- message.contains("negad", ignoreCase = true) ||
- message.contains("denied", ignoreCase = true) ||
- message.contains("recusad", ignoreCase = true) -> {
- emitPaymentEvent(1032.0, "Transação negada")
- }
- else -> {
- if (message.isNotEmpty()) {
- emitPaymentEvent(1020.0, message)
- }
- }
- }
- }
- }
- }
- })
-
- // Emit initial event
- PlugpagEventEmitter.emitPaymentEvent(1004.0, "Aguardando cartão...")
-
- val result = plugPag.doPayment(plugPagPaymentData)
-
- // Clear event listener after payment with a no-op listener
- try {
- plugPag.setEventListener(object : PlugPagEventListener {
- override fun onEvent(plugPagEventData: PlugPagEventData) {
- // No-op listener to clear events
- }
- })
- } catch (e: Exception) {
- Log.w(TAG, "Could not clear event listener", e)
- }
-
- val errorCode = when (result.result) {
- PlugPag.RET_OK -> ErrorCode.OK
- PlugPag.OPERATION_ABORTED -> ErrorCode.OPERATION_ABORTED
- PlugPag.AUTHENTICATION_FAILED -> ErrorCode.AUTHENTICATION_FAILED
- PlugPag.COMMUNICATION_ERROR -> ErrorCode.COMMUNICATION_ERROR
- PlugPag.NO_PRINTER_DEVICE -> ErrorCode.NO_PRINTER_DEVICE
- PlugPag.NO_TRANSACTION_DATA -> ErrorCode.NO_TRANSACTION_DATA
- else -> ErrorCode.COMMUNICATION_ERROR
- }
-
- // Emit final event based on result
- if (errorCode == ErrorCode.OK) {
- PlugpagEventEmitter.emitPaymentEvent(1031.0, "Transação aprovada")
- } else {
- PlugpagEventEmitter.emitPaymentEvent(1032.0, "Transação negada")
- }
-
- PlugpagTransactionResult(
- result = errorCode,
- errorCode = result.errorCode ?: "",
- message = result.message ?: "",
- transactionCode = result.transactionCode ?: "",
- transactionId = result.transactionId ?: "",
- hostNsu = result.hostNsu ?: "",
- date = result.date ?: "",
- time = result.time ?: "",
- cardBrand = result.cardBrand ?: "",
- bin = result.bin ?: "",
- holder = result.holder ?: "",
- userReference = result.userReference ?: "",
- terminalSerialNumber = result.terminalSerialNumber ?: "",
- amount = result.amount ?: "",
- availableBalance = result.availableBalance ?: "",
- cardApplication = result.cardApplication ?: "",
- label = result.label ?: "",
- holderName = result.holderName ?: "",
- extendedHolderName = result.extendedHolderName ?: ""
- )
- } catch (e: Exception) {
- Log.e(TAG, "Error processing payment with events", e)
- throw Exception("PAYMENT_WITH_EVENTS_ERROR: ${e.message ?: "Unknown error"}")
- }
+ OperationHandler.executeOperation("doPayment") {
+ initializePlugPag()
+
+ val plugPagPaymentData = PlugPagPaymentData(
+ mapPaymentType(type),
+ amount.toInt(),
+ mapInstallmentType(installmentType),
+ installments.toInt(),
+ userReference,
+ printReceipt
+ )
+
+ setupPaymentEventListener()
+
+ // Emit initial event
+ PlugpagEventEmitter.emitPaymentEvent(1004.0, "Aguardando cartão...")
+
+ val result = plugPag.doPayment(plugPagPaymentData)
+
+ clearEventListener()
+ emitFinalEvent(result.result ?: -1)
+
+ TransactionResultBuilder.buildFrom(result)
}
}
}
@@ -288,117 +134,77 @@ class PlugpagNitro : HybridPlugpagNitroSpec() {
printReceipt: Boolean
): Promise {
return Promise.async {
- withContext(Dispatchers.IO) {
- try {
- initializePlugPag()
-
- val plugPagVoidData = PlugPagVoidData(
- transactionCode,
- transactionId,
- printReceipt
- )
-
- val result = plugPag.voidPayment(plugPagVoidData)
-
- val errorCode = when (result.result) {
- PlugPag.RET_OK -> ErrorCode.OK
- PlugPag.OPERATION_ABORTED -> ErrorCode.OPERATION_ABORTED
- PlugPag.AUTHENTICATION_FAILED -> ErrorCode.AUTHENTICATION_FAILED
- PlugPag.COMMUNICATION_ERROR -> ErrorCode.COMMUNICATION_ERROR
- PlugPag.NO_PRINTER_DEVICE -> ErrorCode.NO_PRINTER_DEVICE
- PlugPag.NO_TRANSACTION_DATA -> ErrorCode.NO_TRANSACTION_DATA
- else -> ErrorCode.COMMUNICATION_ERROR
- }
-
- PlugpagTransactionResult(
- result = errorCode,
- errorCode = result.errorCode ?: "",
- message = result.message ?: "",
- transactionCode = result.transactionCode ?: "",
- transactionId = result.transactionId ?: "",
- hostNsu = result.hostNsu ?: "",
- date = result.date ?: "",
- time = result.time ?: "",
- cardBrand = result.cardBrand ?: "",
- bin = result.bin ?: "",
- holder = result.holder ?: "",
- userReference = result.userReference ?: "",
- terminalSerialNumber = result.terminalSerialNumber ?: "",
- amount = result.amount ?: "",
- availableBalance = result.availableBalance ?: "",
- cardApplication = result.cardApplication ?: "",
- label = result.label ?: "",
- holderName = result.holderName ?: "",
- extendedHolderName = result.extendedHolderName ?: ""
- )
- } catch (e: Exception) {
- Log.e(TAG, "Error processing refund payment", e)
- throw Exception("REFUND_PAYMENT_ERROR: ${e.message ?: "Unknown error"}")
- }
+ OperationHandler.executeOperation("refundPayment") {
+ initializePlugPag()
+
+ val plugPagVoidData = PlugPagVoidData(
+ transactionCode,
+ transactionId,
+ printReceipt
+ )
+
+ val result = plugPag.voidPayment(plugPagVoidData)
+ TransactionResultBuilder.buildFrom(result)
}
}
}
- override fun doAbort(): Promise {
+ override fun doAbort(): Promise {
return Promise.async {
- withContext(Dispatchers.IO) {
+ OperationHandler.executeOperation("doAbort") {
+ val result = plugPag.abort()
+ ErrorCodeMapper.mapToErrorCode(result.result ?: -1)
+ }
+ }
+ }
+
+ override fun setStyleTheme(styleData: PlugpagStyleData): Promise {
+ return Promise.async {
+ OperationHandler.executeOperation("setStyleTheme") {
+ initializePlugPag()
+
try {
- initializePlugPag()
- val result = plugPag.abort()
-
- PlugpagAbortResult(
- result = result.result == PlugPag.RET_OK
- )
+ val plugPagStyleData = StyleConfigurationManager.createStyleDataFromNitro(styleData)
+ val result = plugPag.setStyleData(plugPagStyleData)
+ result
} catch (e: Exception) {
- Log.e(TAG, "Error aborting operation", e)
- throw Exception("ABORT_ERROR: ${e.message ?: "Unknown error"}")
+ Log.e(TAG, "Error in setStyleTheme", e)
+ when (e) {
+ is NoSuchMethodError -> {
+ Log.w(TAG, "setStyleData method is not available in this PlugPag SDK version")
+ true // Return true to avoid breaking the app
+ }
+ else -> throw e
+ }
}
}
}
}
- override fun print(filePath: String): Promise {
+ override fun print(filePath: String, textSize: Double): Promise {
return Promise.async {
- withContext(Dispatchers.IO) {
- try {
- initializePlugPag()
- val printerData = PlugPagPrinterData(filePath, 4, 0)
-
- plugPag.setPrinterListener(object : PlugPagPrinterListener {
- override fun onError(result: PlugPagPrintResult) {
- Log.e(TAG, "Print error: ${result.message}")
- }
-
- override fun onSuccess(result: PlugPagPrintResult) {
- Log.d(TAG, "Print success")
- }
- })
-
- plugPag.printFromFile(printerData)
- Unit
- } catch (e: Exception) {
- Log.e(TAG, "Error printing", e)
- throw Exception("PRINT_ERROR: ${e.message ?: "Unknown error"}")
- }
+ OperationHandler.executeOperation("print") {
+ initializePlugPag()
+ val printerData = PlugPagPrinterData(filePath, 4, 0)
+
+ plugPag.setPrinterListener(createPrinterListener())
+ val result = plugPag.printFromFile(printerData)
+ ErrorCodeMapper.mapToErrorCode(result.result ?: -1)
}
}
}
override fun reprintCustomerReceipt(): Promise {
return Promise.async {
- withContext(Dispatchers.IO) {
- try {
- initializePlugPag()
- plugPag.reprintCustomerReceipt()
- Unit
- } catch (e: Exception) {
- Log.e(TAG, "Error reprinting customer receipt", e)
- throw Exception("REPRINT_ERROR: ${e.message ?: "Unknown error"}")
- }
+ OperationHandler.executeOperation("reprintCustomerReceipt") {
+ initializePlugPag()
+ plugPag.reprintCustomerReceipt()
+ Unit
}
}
}
+ // Private helper methods
private fun initializePlugPag() {
if (!::plugPag.isInitialized) {
val context = NitroModules.applicationContext as Context
@@ -406,14 +212,70 @@ class PlugpagNitro : HybridPlugpagNitroSpec() {
}
}
- private fun emitPaymentEvent(code: Double, message: String) {
+ private fun mapPaymentType(type: PaymentType): Int {
+ return when (type) {
+ PaymentType.CREDIT -> PlugPag.TYPE_CREDITO
+ PaymentType.DEBIT -> PlugPag.TYPE_DEBITO
+ PaymentType.VOUCHER -> PlugPag.TYPE_VOUCHER
+ PaymentType.PIX -> PlugPag.TYPE_PIX
+ }
+ }
+
+ private fun mapInstallmentType(installmentType: InstallmentType): Int {
+ return when (installmentType) {
+ InstallmentType.NO_INSTALLMENT -> PlugPag.INSTALLMENT_TYPE_A_VISTA
+ InstallmentType.SELLER_INSTALLMENT -> PlugPag.INSTALLMENT_TYPE_PARC_VENDEDOR
+ InstallmentType.BUYER_INSTALLMENT -> PlugPag.INSTALLMENT_TYPE_PARC_COMPRADOR
+ }
+ }
+
+ private fun setupPaymentEventListener() {
+ eventContext["passwordCount"] = 0
+
+ plugPag.setEventListener(object : PlugPagEventListener {
+ override fun onEvent(plugPagEventData: PlugPagEventData) {
+ // Handle password count tracking
+ if (plugPagEventData.eventCode == PlugPagEventData.EVENT_CODE_DIGIT_PASSWORD) {
+ val currentCount = eventContext["passwordCount"] as Int
+ eventContext["passwordCount"] = currentCount + 1
+ } else if (plugPagEventData.eventCode == PlugPagEventData.EVENT_CODE_NO_PASSWORD) {
+ eventContext["passwordCount"] = 0
+ }
+
+ PaymentEventHandler.handleEvent(plugPagEventData, eventContext)
+ }
+ })
+ }
+
+ private fun clearEventListener() {
try {
- Log.d(TAG, "Payment Event - Code: $code, Message: $message")
-
- // Emit event using the dedicated event emitter
- PlugpagEventEmitter.emitPaymentEvent(code, message)
+ plugPag.setEventListener(object : PlugPagEventListener {
+ override fun onEvent(plugPagEventData: PlugPagEventData) {
+ // No-op listener to clear events
+ }
+ })
} catch (e: Exception) {
- Log.e(TAG, "Error emitting payment event", e)
+ Log.w(TAG, "Could not clear event listener", e)
+ }
+ }
+
+ private fun emitFinalEvent(result: Int) {
+ val (code, message) = when (result) {
+ PlugPag.RET_OK -> 1031.0 to "Transação aprovada"
+ else -> 1032.0 to "Transação negada"
+ }
+ PlugpagEventEmitter.emitPaymentEvent(code, message)
+ }
+
+ private fun createPrinterListener(): PlugPagPrinterListener {
+ return object : PlugPagPrinterListener {
+ override fun onError(result: PlugPagPrintResult) {
+ Log.e(TAG, "Print error: ${result.message}")
+ }
+
+ override fun onSuccess(result: PlugPagPrintResult) {
+ Log.d(TAG, "Print success")
+ }
}
}
}
diff --git a/android/src/main/java/com/margelo/nitro/plugpagnitro/StyleConfigurationManager.kt b/android/src/main/java/com/margelo/nitro/plugpagnitro/StyleConfigurationManager.kt
new file mode 100644
index 0000000..1000690
--- /dev/null
+++ b/android/src/main/java/com/margelo/nitro/plugpagnitro/StyleConfigurationManager.kt
@@ -0,0 +1,142 @@
+package com.margelo.nitro.plugpagnitro
+
+import android.graphics.Color
+import br.com.uol.pagseguro.plugpagservice.wrapper.PlugPagStyleData
+
+/**
+ * Utility class for managing PagBank SDK style customization
+ * Handles conversion between Nitro style data and PlugPagStyleData
+ */
+object StyleConfigurationManager {
+
+ /**
+ * Converts a hex color string to Android Color integer
+ * Supports formats: #RGB, #ARGB, #RRGGBB, #AARRGGBB
+ * @param hexColor Hex color string
+ * @return Android Color integer or null if invalid
+ */
+ private fun parseHexColor(hexColor: String?): Int? {
+ if (hexColor.isNullOrBlank()) return null
+
+ return try {
+ val colorString = if (hexColor.startsWith("#")) {
+ hexColor
+ } else {
+ "#$hexColor"
+ }
+ Color.parseColor(colorString)
+ } catch (e: IllegalArgumentException) {
+ null
+ }
+ }
+
+ /**
+ * Creates a PlugPagStyleData from Nitro style configuration
+ * @param styleData Map containing style properties from JavaScript
+ * @return PlugPagStyleData configured with the provided colors
+ */
+ fun createStyleData(styleData: Map): PlugPagStyleData {
+ return PlugPagStyleData(
+ headTextColor = parseHexColor(styleData["headTextColor"] as? String),
+ headBackgroundColor = parseHexColor(styleData["headBackgroundColor"] as? String),
+ contentTextColor = parseHexColor(styleData["contentTextColor"] as? String),
+ contentTextValue1Color = parseHexColor(styleData["contentTextValue1Color"] as? String),
+ contentTextValue2Color = parseHexColor(styleData["contentTextValue2Color"] as? String),
+ positiveButtonTextColor = parseHexColor(styleData["positiveButtonTextColor"] as? String),
+ positiveButtonBackground = parseHexColor(styleData["positiveButtonBackground"] as? String),
+ negativeButtonTextColor = parseHexColor(styleData["negativeButtonTextColor"] as? String),
+ negativeButtonBackground = parseHexColor(styleData["negativeButtonBackground"] as? String),
+ genericButtonBackground = parseHexColor(styleData["genericButtonBackground"] as? String),
+ genericButtonTextColor = parseHexColor(styleData["genericButtonTextColor"] as? String),
+ genericSmsEditTextBackground = parseHexColor(styleData["genericSmsEditTextBackground"] as? String),
+ genericSmsEditTextTextColor = parseHexColor(styleData["genericSmsEditTextTextColor"] as? String),
+ lineColor = parseHexColor(styleData["lineColor"] as? String)
+ )
+ }
+
+ /**
+ * Creates a PlugPagStyleData from Nitro generated PlugpagStyleData
+ * @param nitroStyleData Nitro generated style data
+ * @return PlugPagStyleData configured with the provided colors
+ */
+ fun createStyleDataFromNitro(nitroStyleData: com.margelo.nitro.plugpagnitro.PlugpagStyleData): PlugPagStyleData {
+ return try {
+ // Use reflection to call setStyleData method
+ val plugPagClass = Class.forName("br.com.uol.pagseguro.plugpagservice.wrapper.PlugPag")
+ val methods = plugPagClass.methods
+
+ // Check if setStyleData method exists
+ val hasSetStyleData = methods.any { it.name == "setStyleData" }
+
+ if (hasSetStyleData) {
+ // Method exists, create the style data
+ PlugPagStyleData(
+ headTextColor = parseHexColor(nitroStyleData.headTextColor),
+ headBackgroundColor = parseHexColor(nitroStyleData.headBackgroundColor),
+ contentTextColor = parseHexColor(nitroStyleData.contentTextColor),
+ contentTextValue1Color = parseHexColor(nitroStyleData.contentTextValue1Color),
+ contentTextValue2Color = parseHexColor(nitroStyleData.contentTextValue2Color),
+ positiveButtonTextColor = parseHexColor(nitroStyleData.positiveButtonTextColor),
+ positiveButtonBackground = parseHexColor(nitroStyleData.positiveButtonBackground),
+ negativeButtonTextColor = parseHexColor(nitroStyleData.negativeButtonTextColor),
+ negativeButtonBackground = parseHexColor(nitroStyleData.negativeButtonBackground),
+ genericButtonBackground = parseHexColor(nitroStyleData.genericButtonBackground),
+ genericButtonTextColor = parseHexColor(nitroStyleData.genericButtonTextColor),
+ genericSmsEditTextBackground = parseHexColor(nitroStyleData.genericSmsEditTextBackground),
+ genericSmsEditTextTextColor = parseHexColor(nitroStyleData.genericSmsEditTextTextColor),
+ lineColor = parseHexColor(nitroStyleData.lineColor)
+ )
+ } else {
+ throw UnsupportedOperationException("setStyleData method not available in this PlugPag SDK version")
+ }
+ } catch (e: Exception) {
+ throw RuntimeException("Theme configuration failed: ${e.message}")
+ }
+ }
+
+ /**
+ * Creates a dark theme style configuration
+ * @return PlugPagStyleData with dark theme colors
+ */
+ fun createDarkTheme(): PlugPagStyleData {
+ return PlugPagStyleData(
+ headTextColor = Color.WHITE,
+ headBackgroundColor = Color.parseColor("#1A1A1D"),
+ contentTextColor = Color.WHITE,
+ contentTextValue1Color = Color.parseColor("#00D4FF"),
+ contentTextValue2Color = Color.parseColor("#A1A1AA"),
+ positiveButtonTextColor = Color.WHITE,
+ positiveButtonBackground = Color.parseColor("#22C55E"),
+ negativeButtonTextColor = Color.WHITE,
+ negativeButtonBackground = Color.parseColor("#EF4444"),
+ genericButtonBackground = Color.parseColor("#2A2A2F"),
+ genericButtonTextColor = Color.WHITE,
+ genericSmsEditTextBackground = Color.parseColor("#2A2A2F"),
+ genericSmsEditTextTextColor = Color.WHITE,
+ lineColor = Color.parseColor("#71717A")
+ )
+ }
+
+ /**
+ * Creates a light theme style configuration
+ * @return PlugPagStyleData with light theme colors
+ */
+ fun createLightTheme(): PlugPagStyleData {
+ return PlugPagStyleData(
+ headTextColor = Color.parseColor("#1F2937"),
+ headBackgroundColor = Color.WHITE,
+ contentTextColor = Color.parseColor("#1F2937"),
+ contentTextValue1Color = Color.parseColor("#0EA5E9"),
+ contentTextValue2Color = Color.parseColor("#6B7280"),
+ positiveButtonTextColor = Color.WHITE,
+ positiveButtonBackground = Color.parseColor("#10B981"),
+ negativeButtonTextColor = Color.WHITE,
+ negativeButtonBackground = Color.parseColor("#EF4444"),
+ genericButtonBackground = Color.parseColor("#F3F4F6"),
+ genericButtonTextColor = Color.parseColor("#1F2937"),
+ genericSmsEditTextBackground = Color.parseColor("#F9FAFB"),
+ genericSmsEditTextTextColor = Color.parseColor("#1F2937"),
+ lineColor = Color.parseColor("#E5E7EB")
+ )
+ }
+}
diff --git a/android/src/main/java/com/margelo/nitro/plugpagnitro/TransactionResultBuilder.kt b/android/src/main/java/com/margelo/nitro/plugpagnitro/TransactionResultBuilder.kt
new file mode 100644
index 0000000..b70551e
--- /dev/null
+++ b/android/src/main/java/com/margelo/nitro/plugpagnitro/TransactionResultBuilder.kt
@@ -0,0 +1,41 @@
+package com.margelo.nitro.plugpagnitro
+
+import br.com.uol.pagseguro.plugpagservice.wrapper.PlugPagTransactionResult as PlugPagSDKResult
+
+/**
+ * Builder pattern for creating PlugpagTransactionResult objects
+ * Eliminates duplication in result object creation
+ */
+object TransactionResultBuilder {
+
+ /**
+ * Creates a PlugpagTransactionResult from PlugPag SDK result
+ * @param sdkResult The result from PlugPag SDK
+ * @return Formatted PlugpagTransactionResult for Nitro
+ */
+ fun buildFrom(sdkResult: PlugPagSDKResult): PlugpagTransactionResult {
+ val errorCode = ErrorCodeMapper.mapToErrorCode(sdkResult.result ?: -1)
+
+ return PlugpagTransactionResult(
+ result = errorCode,
+ errorCode = sdkResult.errorCode ?: "",
+ message = sdkResult.message ?: "",
+ transactionCode = sdkResult.transactionCode ?: "",
+ transactionId = sdkResult.transactionId ?: "",
+ hostNsu = sdkResult.hostNsu ?: "",
+ date = sdkResult.date ?: "",
+ time = sdkResult.time ?: "",
+ cardBrand = sdkResult.cardBrand ?: "",
+ bin = sdkResult.bin ?: "",
+ holder = sdkResult.holder ?: "",
+ userReference = sdkResult.userReference ?: "",
+ terminalSerialNumber = sdkResult.terminalSerialNumber ?: "",
+ amount = sdkResult.amount ?: "",
+ availableBalance = sdkResult.availableBalance ?: "",
+ cardApplication = sdkResult.cardApplication ?: "",
+ label = sdkResult.label ?: "",
+ holderName = sdkResult.holderName ?: "",
+ extendedHolderName = sdkResult.extendedHolderName ?: ""
+ )
+ }
+}
diff --git a/example/src/App.tsx b/example/src/App.tsx
index 6723782..ec6f88b 100644
--- a/example/src/App.tsx
+++ b/example/src/App.tsx
@@ -1,418 +1,130 @@
-import React, { useEffect, useState } from 'react';
+import { useEffect, useRef } from 'react';
+import { ScrollView, StyleSheet, StatusBar } from 'react-native';
+import { useTransactionEvent, setStyleTheme } from 'react-native-plugpag-nitro';
+import { theme } from './constants/theme';
+import { usePaymentOperations } from './hooks/usePaymentOperations';
import {
- ActivityIndicator,
- Alert,
- ScrollView,
- StyleSheet,
- Text,
- TouchableOpacity,
- View,
-} from 'react-native';
-import {
- type PlugpagTransactionResult,
- initializeAndActivatePinPad,
- doPayment,
- refundPayment,
- useTransactionEvent,
- getTerminalSerialNumber,
- PaymentType,
- ErrorCode,
- InstallmentType,
-} from 'react-native-plugpag-nitro';
+ Header,
+ PaymentEvent,
+ LoadingIndicator,
+ PaymentButtons,
+ TransactionInfo,
+ StyleExample,
+} from './components';
export default function App() {
- const [isProcessing, setIsProcessing] = useState(false);
-
- const [terminalSerial, setTerminalSerial] = useState('N/A');
-
- const [lastPayment, setLastPayment] =
- useState(null);
-
- const [isInitialized, setIsInitialized] = useState(false);
+ const {
+ isProcessing,
+ isInitialized,
+ terminalSerial,
+ lastPayment,
+ handleInitialize,
+ handlePayment,
+ handleRefund,
+ } = usePaymentOperations();
// Real-time payment events
const paymentEvent = useTransactionEvent();
- // Get terminal serial on mount
- useEffect(() => {
- try {
- setTerminalSerial(getTerminalSerialNumber());
- } catch (e) {
- console.warn(e);
- }
- }, []);
-
- // Initialize terminal
- const handleInitialize = async () => {
- setIsProcessing(true);
- try {
- const result = await initializeAndActivatePinPad('403938');
- if (result.result === ErrorCode.OK) {
- setIsInitialized(true);
- Alert.alert('✅ Sucesso', 'Terminal inicializado com sucesso');
- } else {
- Alert.alert('❌ Erro', result.errorMessage || 'Falha ao inicializar');
- }
- } catch (e: any) {
- Alert.alert('❌ Error', e.message);
- }
- setIsProcessing(false);
- };
-
- // Credit payment
- const handleCreditPayment = async () => {
- if (!isInitialized) {
- Alert.alert('⚠️ Aviso', 'Por favor, inicialize o terminal primeiro');
- return;
- }
+ // Track if initialization has been attempted to prevent loops
+ const initializationAttempted = useRef(false);
- setIsProcessing(true);
- try {
- const result = await doPayment({
- amount: 2500, // R$ 25.00
- type: PaymentType.CREDIT,
- });
- setLastPayment(result);
-
- if (result.result === ErrorCode.OK) {
- Alert.alert(
- '✅ Pagamento Aprovado',
- `Transação realizada com sucesso!\nCódigo: ${result.transactionCode}\nValor: R$ 25,00`
- );
- } else {
- Alert.alert(
- '❌ Pagamento Negado',
- result.message || 'Transação falhou'
- );
- }
- } catch (e: any) {
- Alert.alert('❌ Erro', e.message);
- }
- setIsProcessing(false);
- };
-
- // Debit payment
- const handleDebitPayment = async () => {
- if (!isInitialized) {
- Alert.alert('⚠️ Aviso', 'Por favor, inicialize o terminal primeiro');
- return;
- }
-
- setIsProcessing(true);
- try {
- const result = await doPayment({
- amount: 1500, // R$ 15.00
- type: PaymentType.DEBIT,
- });
- setLastPayment(result);
-
- if (result.result === ErrorCode.OK) {
- Alert.alert(
- '✅ Pagamento Aprovado',
- `Transação de débito realizada com sucesso!\nCódigo: ${result.transactionCode}\nValor: R$ 15,00`
- );
- } else {
- Alert.alert(
- '❌ Pagamento Negado',
- result.message || 'Transação falhou'
- );
- }
- } catch (e: any) {
- Alert.alert('❌ Erro', e.message);
- }
- setIsProcessing(false);
- };
-
- // Credit payment
- const handlePIXPayment = async () => {
- if (!isInitialized) {
- Alert.alert('⚠️ Aviso', 'Por favor, inicialize o terminal primeiro');
- return;
- }
-
- setIsProcessing(true);
- try {
- const result = await doPayment({
- amount: 1500, // R$ 15.00
- type: PaymentType.PIX,
- });
- setLastPayment(result);
-
- if (result.result === ErrorCode.OK) {
- Alert.alert(
- '✅ Pagamento Aprovado',
- `Transação realizada com sucesso!\nCódigo: ${result.transactionCode}\nValor: R$ 25,00`
- );
- } else {
- Alert.alert(
- '❌ Pagamento Negado',
- result.message || 'Transação falhou'
- );
- }
- } catch (e: any) {
- Alert.alert('❌ Erro', e.message);
- }
- setIsProcessing(false);
- };
-
- // Installment payment
- const handleInstallmentPayment = async () => {
- if (!isInitialized) {
- Alert.alert('⚠️ Aviso', 'Por favor, inicialize o terminal primeiro');
- return;
- }
-
- setIsProcessing(true);
- try {
- const result = await doPayment({
- amount: 10000, // R$ 100.00
- type: PaymentType.CREDIT,
- installmentType: InstallmentType.BUYER_INSTALLMENT,
- installments: 3,
- });
- setLastPayment(result);
-
- if (result.result === ErrorCode.OK) {
- Alert.alert(
- '✅ Pagamento Aprovado',
- `Pagamento parcelado realizado com sucesso!\nCódigo: ${result.transactionCode}\nValor: R$ 100,00 (3x)`
- );
- } else {
- Alert.alert(
- '❌ Pagamento Negado',
- result.message || 'Transação falhou'
- );
- }
- } catch (e: any) {
- Alert.alert('❌ Erro', e.message);
- }
- setIsProcessing(false);
- };
-
- // Refund last payment
- const handleRefund = async () => {
- if (!lastPayment?.transactionCode || !lastPayment.transactionId) {
- Alert.alert('⚠️ Aviso', 'Nenhuma transação para estornar');
+ // Auto-initialize terminal and apply theme on startup
+ useEffect(() => {
+ // Only initialize once and if not already initialized
+ if (initializationAttempted.current || isInitialized) {
return;
}
- setIsProcessing(true);
- try {
- const result = await refundPayment({
- transactionCode: lastPayment.transactionCode,
- transactionId: lastPayment.transactionId,
- });
-
- if (result.result === ErrorCode.OK) {
- Alert.alert(
- '✅ Estorno Realizado',
- `Código: ${result.transactionCode}`
- );
- setLastPayment(null);
- } else {
- Alert.alert('❌ Estorno Falhou', result.message || 'Estorno falhou');
+ initializationAttempted.current = true;
+
+ const initializeApp = async () => {
+ console.log('🚀 Initializing PlugPag Example App...');
+
+ try {
+ // First, initialize the terminal
+ console.log('📱 Activating terminal...');
+ await handleInitialize();
+
+ // After successful activation, apply the custom theme
+ console.log('🎨 Applying custom theme...');
+ const appDarkTheme = {
+ headBackgroundColor: '#0A0A0B',
+ headTextColor: '#FFFFFF',
+ contentTextColor: '#F3F4F6',
+ contentTextValue1Color: '#00D4FF',
+ contentTextValue2Color: '#9CA3AF',
+ positiveButtonBackground: '#10B981',
+ positiveButtonTextColor: '#FFFFFF',
+ negativeButtonBackground: '#EF4444',
+ negativeButtonTextColor: '#FFFFFF',
+ genericButtonBackground: '#1F2937',
+ genericButtonTextColor: '#F3F4F6',
+ genericSmsEditTextBackground: '#1F2937',
+ genericSmsEditTextTextColor: '#F3F4F6',
+ lineColor: '#374151',
+ };
+
+ await setStyleTheme(appDarkTheme);
+ console.log('✅ App initialization complete!');
+ } catch (error) {
+ console.warn('⚠️ App initialization failed:', error);
+ // Reset the flag so user can try manual initialization if needed
+ initializationAttempted.current = false;
}
- } catch (e: any) {
- Alert.alert('❌ Erro', e.message);
- }
- setIsProcessing(false);
- };
+ };
- const getEventColor = (code: number) => {
- if (code >= 1001 && code <= 1004) return '#007AFF'; // Card events - Blue
- if (code >= 1010 && code <= 1012) return '#FF9500'; // Password events - Orange
- if (code >= 1020 && code <= 1023) return '#FFCC02'; // Processing - Yellow
- if (code >= 1030 && code <= 1032) return '#34C759'; // Terminal response - Green
- if (code >= 1040 && code <= 1043) return '#FF3B30'; // Errors - Red
- return '#8E8E93'; // Default - Gray
- };
+ initializeApp();
+ }, [handleInitialize, isInitialized]); // Include dependencies
return (
-
-
- Demo PlugPag Nitro
- Terminal: {terminalSerial}
-
- Status: {isInitialized ? '✅ Pronto' : '❌ Não Inicializado'}
-
-
-
- {paymentEvent.code > 0 && (
-
- {paymentEvent.message}
- Code: {paymentEvent.code}
- {paymentEvent.customMessage && (
- {paymentEvent.customMessage}
- )}
-
- )}
-
- {/* Processing Indicator */}
- {isProcessing && (
-
-
- Processando...
-
- )}
-
-
- Inicializar Terminal
-
-
-
- Pagamento Crédito - R$ 25,00
-
-
-
- Pagamento Débito - R$ 15,00
-
-
-
- PIX - R$ 15,00
-
-
-
-
- Pagamento Parcelado - R$ 100,00 (3x)
-
-
-
-
+
+
- Estornar Última Transação
-
-
- {lastPayment && (
-
- Última Transação
- Código: {lastPayment.transactionCode}
- ID: {lastPayment.transactionId}
-
- Status:{' '}
- {lastPayment.result === ErrorCode.OK ? '✅ Aprovado' : '❌ Negado'}
-
- {lastPayment.hostNsu && NSU: {lastPayment.hostNsu} }
- {lastPayment.cardBrand && (
- Bandeira: {lastPayment.cardBrand}
- )}
-
- )}
-
+
+
+
+
+
+
+
+
+
+
+
+ console.log(`Applied theme: ${themeName}`)
+ }
+ />
+
+ >
);
}
const styles = StyleSheet.create({
container: {
- padding: 20,
- backgroundColor: '#f5f5f5',
- },
- header: {
- alignItems: 'center',
- marginBottom: 20,
- },
- title: {
- fontSize: 24,
- fontWeight: 'bold',
- color: '#333',
- marginBottom: 5,
- },
- subtitle: {
- fontSize: 16,
- color: '#666',
- marginBottom: 5,
- },
- status: {
- fontSize: 14,
- fontWeight: '600',
- },
- eventContainer: {
- padding: 15,
- borderRadius: 8,
- marginBottom: 15,
- },
- eventText: {
- color: 'white',
- fontWeight: 'bold',
- fontSize: 16,
- },
- eventCode: {
- color: 'white',
- fontSize: 12,
- marginTop: 5,
- },
- eventCustom: {
- color: 'white',
- fontSize: 14,
- marginTop: 5,
- fontStyle: 'italic',
- },
- processingContainer: {
- alignItems: 'center',
- padding: 20,
- backgroundColor: 'white',
- borderRadius: 8,
- marginBottom: 15,
- },
- processingText: {
- marginTop: 10,
- fontSize: 16,
- color: '#666',
- },
- button: {
- backgroundColor: '#007AFF',
- padding: 15,
- borderRadius: 8,
- marginBottom: 10,
- alignItems: 'center',
- },
- primaryButton: {
- backgroundColor: '#34C759',
- },
- refundButton: {
- backgroundColor: '#FF9500',
- },
- buttonText: {
- color: 'white',
- fontSize: 16,
- fontWeight: '600',
- },
- transactionInfo: {
- backgroundColor: 'white',
- padding: 15,
- borderRadius: 8,
- marginTop: 10,
+ flex: 1,
+ backgroundColor: theme.colors.background,
},
- transactionTitle: {
- fontSize: 16,
- fontWeight: 'bold',
- marginBottom: 5,
+ contentContainer: {
+ padding: theme.spacing.md,
+ paddingBottom: theme.spacing.xl,
},
});
diff --git a/example/src/components/Button.tsx b/example/src/components/Button.tsx
new file mode 100644
index 0000000..d9bd094
--- /dev/null
+++ b/example/src/components/Button.tsx
@@ -0,0 +1,96 @@
+import React from 'react';
+import {
+ TouchableOpacity,
+ Text,
+ StyleSheet,
+ type TouchableOpacityProps,
+} from 'react-native';
+import { theme } from '../constants/theme';
+
+export type ButtonVariant =
+ | 'primary'
+ | 'secondary'
+ | 'success'
+ | 'warning'
+ | 'error';
+
+interface ButtonProps extends TouchableOpacityProps {
+ title: string;
+ variant?: ButtonVariant;
+ fullWidth?: boolean;
+ loading?: boolean;
+}
+
+export const Button: React.FC = ({
+ title,
+ variant = 'primary',
+ fullWidth = true,
+ loading = false,
+ disabled,
+ style,
+ ...props
+}) => {
+ const buttonStyle = [
+ styles.button,
+ styles[variant],
+ fullWidth && styles.fullWidth,
+ (disabled || loading) && styles.disabled,
+ style,
+ ];
+
+ const textStyle = [styles.text, (disabled || loading) && styles.disabledText];
+
+ return (
+
+ {loading ? 'Processando...' : title}
+
+ );
+};
+
+const styles = StyleSheet.create({
+ button: {
+ paddingVertical: theme.spacing.md,
+ paddingHorizontal: theme.spacing.lg,
+ borderRadius: theme.borderRadius.md,
+ alignItems: 'center',
+ justifyContent: 'center',
+ marginBottom: theme.spacing.sm,
+ ...theme.shadows.small,
+ },
+ fullWidth: {
+ width: '100%',
+ },
+ text: {
+ ...theme.typography.body,
+ fontWeight: '600',
+ color: theme.colors.textPrimary,
+ },
+ disabled: {
+ opacity: 0.6,
+ },
+ disabledText: {
+ color: theme.colors.textSecondary,
+ },
+
+ // Variants
+ primary: {
+ backgroundColor: theme.colors.primary,
+ },
+ secondary: {
+ backgroundColor: theme.colors.secondary,
+ },
+ success: {
+ backgroundColor: theme.colors.success,
+ },
+ warning: {
+ backgroundColor: theme.colors.warning,
+ },
+ error: {
+ backgroundColor: theme.colors.error,
+ },
+});
diff --git a/example/src/components/Card.tsx b/example/src/components/Card.tsx
new file mode 100644
index 0000000..739ff2e
--- /dev/null
+++ b/example/src/components/Card.tsx
@@ -0,0 +1,36 @@
+import React from 'react';
+import { View, StyleSheet, type ViewProps } from 'react-native';
+import { theme } from '../constants/theme';
+
+interface CardProps extends ViewProps {
+ elevated?: boolean;
+ children: React.ReactNode;
+}
+
+export const Card: React.FC = ({
+ elevated = false,
+ children,
+ style,
+ ...props
+}) => {
+ const cardStyle = [styles.card, elevated && styles.elevated, style];
+
+ return (
+
+ {children}
+
+ );
+};
+
+const styles = StyleSheet.create({
+ card: {
+ backgroundColor: theme.colors.surface,
+ borderRadius: theme.borderRadius.md,
+ padding: theme.spacing.md,
+ marginBottom: theme.spacing.sm,
+ },
+ elevated: {
+ backgroundColor: theme.colors.surfaceElevated,
+ ...theme.shadows.medium,
+ },
+});
diff --git a/example/src/components/Header.tsx b/example/src/components/Header.tsx
new file mode 100644
index 0000000..e1cb68a
--- /dev/null
+++ b/example/src/components/Header.tsx
@@ -0,0 +1,50 @@
+import React from 'react';
+import { View, Text, StyleSheet } from 'react-native';
+import { theme } from '../constants/theme';
+import { getStatusColor, getStatusText } from '../utils/payment';
+import { Card } from './Card';
+
+interface HeaderProps {
+ terminalSerial: string;
+ isInitialized: boolean;
+}
+
+export const Header: React.FC = ({
+ terminalSerial,
+ isInitialized,
+}) => {
+ const statusColor = getStatusColor(isInitialized);
+ const statusText = getStatusText(isInitialized);
+
+ return (
+
+
+ Demo PlugPag Nitro
+ Terminal: {terminalSerial}
+
+ Status: {statusText}
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ alignItems: 'center',
+ },
+ title: {
+ ...theme.typography.h1,
+ color: theme.colors.textPrimary,
+ marginBottom: theme.spacing.xs,
+ },
+ subtitle: {
+ ...theme.typography.body,
+ color: theme.colors.textSecondary,
+ marginBottom: theme.spacing.xs,
+ },
+ status: {
+ ...theme.typography.caption,
+ fontWeight: '600',
+ },
+});
diff --git a/example/src/components/LoadingIndicator.tsx b/example/src/components/LoadingIndicator.tsx
new file mode 100644
index 0000000..42e84d0
--- /dev/null
+++ b/example/src/components/LoadingIndicator.tsx
@@ -0,0 +1,35 @@
+import React from 'react';
+import { Text, ActivityIndicator, StyleSheet } from 'react-native';
+import { theme } from '../constants/theme';
+import { Card } from './Card';
+
+interface LoadingIndicatorProps {
+ visible: boolean;
+ message?: string;
+}
+
+export const LoadingIndicator: React.FC = ({
+ visible,
+ message = 'Processando...',
+}) => {
+ if (!visible) return null;
+
+ return (
+
+
+ {message}
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ alignItems: 'center',
+ paddingVertical: theme.spacing.lg,
+ },
+ text: {
+ ...theme.typography.body,
+ color: theme.colors.textSecondary,
+ marginTop: theme.spacing.sm,
+ },
+});
diff --git a/example/src/components/PaymentButtons.tsx b/example/src/components/PaymentButtons.tsx
new file mode 100644
index 0000000..5060921
--- /dev/null
+++ b/example/src/components/PaymentButtons.tsx
@@ -0,0 +1,67 @@
+import React from 'react';
+import { View, StyleSheet } from 'react-native';
+import { Button } from './Button';
+import { PAYMENT_BUTTONS } from '../utils/payment';
+import { InstallmentType } from 'react-native-plugpag-nitro';
+
+interface PaymentButtonsProps {
+ isInitialized: boolean;
+ isProcessing: boolean;
+ hasLastPayment: boolean;
+ onPayment: (options: {
+ amount: number;
+ type: any;
+ installmentType?: InstallmentType;
+ installments?: number;
+ }) => void;
+ onRefund: () => void;
+}
+
+export const PaymentButtons: React.FC = ({
+ isInitialized,
+ isProcessing,
+ hasLastPayment,
+ onPayment,
+ onRefund,
+}) => {
+ const handlePaymentClick = (
+ buttonConfig: (typeof PAYMENT_BUTTONS)[number]
+ ) => {
+ const paymentOptions = {
+ amount: buttonConfig.amount,
+ type: buttonConfig.paymentType,
+ ...('installments' in buttonConfig && {
+ installmentType: InstallmentType.BUYER_INSTALLMENT,
+ installments: buttonConfig.installments,
+ }),
+ };
+ onPayment(paymentOptions);
+ };
+
+ return (
+
+ {PAYMENT_BUTTONS.map((buttonConfig) => (
+ handlePaymentClick(buttonConfig)}
+ disabled={isProcessing || !isInitialized}
+ />
+ ))}
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ },
+});
diff --git a/example/src/components/PaymentEvent.tsx b/example/src/components/PaymentEvent.tsx
new file mode 100644
index 0000000..7c2d038
--- /dev/null
+++ b/example/src/components/PaymentEvent.tsx
@@ -0,0 +1,54 @@
+import React from 'react';
+import { Text, StyleSheet } from 'react-native';
+import { theme } from '../constants/theme';
+import { getEventColor } from '../utils/payment';
+import { Card } from './Card';
+
+interface PaymentEventProps {
+ code: number;
+ message: string;
+ customMessage?: string;
+}
+
+export const PaymentEvent: React.FC = ({
+ code,
+ message,
+ customMessage,
+}) => {
+ if (code <= 0) return null;
+
+ const eventColor = getEventColor(code);
+
+ return (
+
+ {message}
+ Código: {code}
+ {customMessage && (
+ {customMessage}
+ )}
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ backgroundColor: theme.colors.primary,
+ },
+ message: {
+ ...theme.typography.body,
+ color: theme.colors.textPrimary,
+ fontWeight: '600',
+ marginBottom: theme.spacing.xs,
+ },
+ code: {
+ ...theme.typography.small,
+ color: theme.colors.textPrimary,
+ opacity: 0.9,
+ },
+ customMessage: {
+ ...theme.typography.caption,
+ color: theme.colors.textPrimary,
+ fontStyle: 'italic',
+ marginTop: theme.spacing.xs,
+ },
+});
diff --git a/example/src/components/StyleExample.tsx b/example/src/components/StyleExample.tsx
new file mode 100644
index 0000000..659baf0
--- /dev/null
+++ b/example/src/components/StyleExample.tsx
@@ -0,0 +1,429 @@
+import { useState, useEffect } from 'react';
+import {
+ View,
+ Text,
+ StyleSheet,
+ TouchableOpacity,
+ Alert,
+ ScrollView,
+} from 'react-native';
+import { setStyleTheme, ThemeUtils } from 'react-native-plugpag-nitro';
+import type { PlugpagStyleData } from 'react-native-plugpag-nitro';
+
+interface StyleExampleProps {
+ onThemeApplied?: (themeName: string) => void;
+}
+
+interface ThemeButtonProps {
+ theme: PlugpagStyleData;
+ name: string;
+ themeKey: string;
+ currentTheme: string;
+ onPress: (theme: PlugpagStyleData, name: string) => void;
+ disabled: boolean;
+}
+
+const ThemeButton: React.FC = ({
+ theme,
+ name,
+ themeKey,
+ currentTheme,
+ onPress,
+ disabled,
+}) => (
+ onPress(theme, name)}
+ disabled={disabled}
+ >
+
+
+
+
+
+ {name}
+ {currentTheme === themeKey && (
+ ✓ Active
+ )}
+
+);
+
+export const StyleExample: React.FC = ({
+ onThemeApplied,
+}) => {
+ const [currentTheme, setCurrentTheme] = useState('dark');
+ const [isApplying, setIsApplying] = useState(false);
+
+ const createAppMatchingTheme = (): PlugpagStyleData => {
+ return {
+ headBackgroundColor: '#0A0A0B', // Match app's darker background
+ headTextColor: '#FFFFFF',
+ contentTextColor: '#F3F4F6',
+ contentTextValue1Color: '#00D4FF', // App's primary blue
+ contentTextValue2Color: '#9CA3AF', // Secondary text
+ positiveButtonBackground: '#10B981', // Improved green
+ positiveButtonTextColor: '#FFFFFF',
+ negativeButtonBackground: '#EF4444', // Clean red
+ negativeButtonTextColor: '#FFFFFF',
+ genericButtonBackground: '#1F2937', // Better contrast
+ genericButtonTextColor: '#F3F4F6',
+ genericSmsEditTextBackground: '#1F2937',
+ genericSmsEditTextTextColor: '#F3F4F6',
+ lineColor: '#374151', // Visible but subtle lines
+ };
+ };
+
+ const createLightTheme = (): PlugpagStyleData => {
+ return {
+ headTextColor: '#1F2937',
+ headBackgroundColor: '#FFFFFF',
+ contentTextColor: '#1F2937',
+ contentTextValue1Color: '#0EA5E9',
+ contentTextValue2Color: '#6B7280',
+ positiveButtonTextColor: '#FFFFFF',
+ positiveButtonBackground: '#10B981',
+ negativeButtonTextColor: '#FFFFFF',
+ negativeButtonBackground: '#EF4444',
+ genericButtonBackground: '#F3F4F6',
+ genericButtonTextColor: '#1F2937',
+ genericSmsEditTextBackground: '#F9FAFB',
+ genericSmsEditTextTextColor: '#1F2937',
+ lineColor: '#E5E7EB',
+ };
+ };
+
+ const createPagBankTheme = (): PlugpagStyleData => {
+ return {
+ headTextColor: '#FFFFFF',
+ headBackgroundColor: '#00A859',
+ contentTextColor: '#1F2937',
+ contentTextValue1Color: '#00A859',
+ contentTextValue2Color: '#6B7280',
+ positiveButtonTextColor: '#FFFFFF',
+ positiveButtonBackground: '#00A859',
+ negativeButtonTextColor: '#6B7280',
+ negativeButtonBackground: '#F3F4F6',
+ genericButtonBackground: '#E5E7EB',
+ genericButtonTextColor: '#1F2937',
+ genericSmsEditTextBackground: '#F9FAFB',
+ genericSmsEditTextTextColor: '#1F2937',
+ lineColor: '#D1D5DB',
+ };
+ };
+
+ const createHighContrastTheme = (): PlugpagStyleData => {
+ return {
+ headTextColor: '#FFFFFF',
+ headBackgroundColor: '#000000',
+ contentTextColor: '#000000',
+ contentTextValue1Color: '#0066CC',
+ contentTextValue2Color: '#333333',
+ positiveButtonTextColor: '#FFFFFF',
+ positiveButtonBackground: '#006600',
+ negativeButtonTextColor: '#FFFFFF',
+ negativeButtonBackground: '#CC0000',
+ genericButtonBackground: '#F0F0F0',
+ genericButtonTextColor: '#000000',
+ genericSmsEditTextBackground: '#FFFFFF',
+ genericSmsEditTextTextColor: '#000000',
+ lineColor: '#000000',
+ };
+ };
+
+ const createPurpleTheme = (): PlugpagStyleData => {
+ return {
+ headTextColor: '#FFFFFF',
+ headBackgroundColor: '#8B5CF6',
+ contentTextColor: '#1F2937',
+ contentTextValue1Color: '#8B5CF6',
+ contentTextValue2Color: '#6B7280',
+ positiveButtonTextColor: '#FFFFFF',
+ positiveButtonBackground: '#8B5CF6',
+ negativeButtonTextColor: '#FFFFFF',
+ negativeButtonBackground: '#EF4444',
+ genericButtonBackground: '#F3F4F6',
+ genericButtonTextColor: '#1F2937',
+ genericSmsEditTextBackground: '#F9FAFB',
+ genericSmsEditTextTextColor: '#1F2937',
+ lineColor: '#E5E7EB',
+ };
+ };
+
+ const createPremiumTheme = (): PlugpagStyleData => {
+ return {
+ headBackgroundColor: '#111827', // Rich dark background
+ headTextColor: '#F9FAFB',
+ contentTextColor: '#F3F4F6',
+ contentTextValue1Color: '#3B82F6', // Premium blue
+ contentTextValue2Color: '#9CA3AF',
+ positiveButtonBackground: '#059669', // Premium emerald
+ positiveButtonTextColor: '#FFFFFF',
+ negativeButtonBackground: '#DC2626', // Premium red
+ negativeButtonTextColor: '#FFFFFF',
+ genericButtonBackground: '#374151', // Elevated surface
+ genericButtonTextColor: '#F9FAFB',
+ genericSmsEditTextBackground: '#374151',
+ genericSmsEditTextTextColor: '#F9FAFB',
+ lineColor: '#4B5563', // Subtle borders
+ };
+ };
+
+ const predefinedThemes = [
+ { key: 'dark', name: 'Dark Theme', theme: createAppMatchingTheme() },
+ { key: 'light', name: 'Light Theme', theme: createLightTheme() },
+ {
+ key: 'pagbank',
+ name: 'PagBank Official',
+ theme: createPagBankTheme(),
+ },
+ {
+ key: 'high-contrast',
+ name: 'High Contrast',
+ theme: createHighContrastTheme(),
+ },
+ ];
+
+ const customThemes = [
+ {
+ key: 'purple',
+ name: 'Purple Modern',
+ theme: createPurpleTheme(),
+ },
+ {
+ key: 'app-matching',
+ name: 'App Matching',
+ theme: createAppMatchingTheme(),
+ },
+ {
+ key: 'premium',
+ name: 'Premium Dark',
+ theme: createPremiumTheme(),
+ },
+ ];
+
+ const applyTheme = async (theme: PlugpagStyleData, themeName: string) => {
+ if (isApplying) return;
+
+ setIsApplying(true);
+ try {
+ const errors = ThemeUtils.validateTheme(theme);
+ if (errors.length > 0) {
+ Alert.alert(
+ 'Invalid Theme',
+ `Theme validation failed: ${errors.join(', ')}`
+ );
+ return;
+ }
+
+ await setStyleTheme(theme);
+ setCurrentTheme(themeName);
+ Alert.alert('Theme Applied', `${themeName} theme applied successfully!`);
+ onThemeApplied?.(themeName);
+ } catch (error) {
+ Alert.alert('Theme Error', `Failed to apply theme: ${error}`);
+ console.error('Theme application failed:', error);
+ } finally {
+ setIsApplying(false);
+ }
+ };
+
+ useEffect(() => {
+ // Note: Theme is now applied automatically by App.tsx on startup
+ // This component only handles manual theme switching
+ }, []);
+
+ return (
+
+
+ 🎨 Theme Customization
+
+ Customize the appearance of PagBank SDK payment modals
+
+
+
+
+ Popular Themes
+
+ Common theme configurations for different app styles
+
+
+
+ {predefinedThemes.map((item) => (
+
+ ))}
+
+
+
+
+ Creative Themes
+
+ Advanced theme configurations showcasing customization possibilities
+
+
+
+ {customThemes.map((item) => (
+
+ ))}
+
+
+
+
+ 💡 How it Works
+
+ • All themes are created within this app, not from the library{'\n'}•
+ Themes are applied globally to all PagBank SDK modals{'\n'}• Colors
+ automatically adapt to modal components{'\n'}• Validation ensures
+ theme compatibility{'\n'}• Changes take effect immediately for new
+ modals
+
+
+
+
+
+ {isApplying ? 'Applying theme...' : `Current theme: ${currentTheme}`}
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: '#0A0A0B',
+ },
+ header: {
+ padding: 20,
+ alignItems: 'center',
+ },
+ title: {
+ fontSize: 28,
+ fontWeight: '700',
+ color: '#FFFFFF',
+ marginBottom: 8,
+ },
+ subtitle: {
+ fontSize: 16,
+ color: '#9CA3AF',
+ textAlign: 'center',
+ lineHeight: 22,
+ },
+ section: {
+ marginVertical: 16,
+ paddingHorizontal: 20,
+ },
+ sectionTitle: {
+ fontSize: 20,
+ fontWeight: '600',
+ color: '#FFFFFF',
+ marginBottom: 8,
+ },
+ sectionDescription: {
+ fontSize: 14,
+ color: '#9CA3AF',
+ marginBottom: 16,
+ lineHeight: 20,
+ },
+ themeGrid: {
+ flexDirection: 'row',
+ flexWrap: 'wrap',
+ gap: 12,
+ },
+ button: {
+ backgroundColor: '#1F2937',
+ borderRadius: 12,
+ padding: 16,
+ flex: 1,
+ minWidth: 150,
+ alignItems: 'center',
+ borderWidth: 2,
+ borderColor: 'transparent',
+ },
+ activeTheme: {
+ borderColor: '#3B82F6',
+ backgroundColor: '#1E3A8A',
+ },
+ themePreview: {
+ flexDirection: 'row',
+ marginBottom: 12,
+ gap: 4,
+ },
+ previewColor: {
+ width: 16,
+ height: 16,
+ borderRadius: 8,
+ borderWidth: 1,
+ borderColor: '#374151',
+ },
+ buttonText: {
+ fontSize: 14,
+ fontWeight: '600',
+ color: '#FFFFFF',
+ textAlign: 'center',
+ marginBottom: 4,
+ },
+ activeIndicator: {
+ fontSize: 12,
+ color: '#3B82F6',
+ fontWeight: '500',
+ },
+ infoSection: {
+ margin: 20,
+ padding: 16,
+ backgroundColor: '#1F2937',
+ borderRadius: 12,
+ borderLeftWidth: 4,
+ borderLeftColor: '#3B82F6',
+ },
+ infoTitle: {
+ fontSize: 16,
+ fontWeight: '600',
+ color: '#FFFFFF',
+ marginBottom: 8,
+ },
+ infoText: {
+ fontSize: 14,
+ color: '#9CA3AF',
+ lineHeight: 20,
+ },
+ footer: {
+ padding: 20,
+ alignItems: 'center',
+ },
+ footerText: {
+ fontSize: 14,
+ color: '#6B7280',
+ fontStyle: 'italic',
+ },
+});
diff --git a/example/src/components/TransactionInfo.tsx b/example/src/components/TransactionInfo.tsx
new file mode 100644
index 0000000..95dcb4a
--- /dev/null
+++ b/example/src/components/TransactionInfo.tsx
@@ -0,0 +1,76 @@
+import React from 'react';
+import { View, Text, StyleSheet } from 'react-native';
+import { theme } from '../constants/theme';
+import { Card } from './Card';
+import {
+ ErrorCode,
+ type PlugpagTransactionResult,
+} from 'react-native-plugpag-nitro';
+
+interface TransactionInfoProps {
+ transaction: PlugpagTransactionResult | null;
+}
+
+export const TransactionInfo: React.FC = ({
+ transaction,
+}) => {
+ if (!transaction) return null;
+
+ const isApproved = transaction.result === ErrorCode.OK;
+ const statusColor = isApproved ? theme.colors.success : theme.colors.error;
+ const statusText = isApproved ? '✅ Aprovado' : '❌ Negado';
+
+ return (
+
+ Última Transação
+
+ Código:
+ {transaction.transactionCode}
+
+
+ ID:
+ {transaction.transactionId}
+
+
+ Status:
+ {statusText}
+
+ {transaction.hostNsu && (
+
+ NSU:
+ {transaction.hostNsu}
+
+ )}
+ {transaction.cardBrand && (
+
+ Bandeira:
+ {transaction.cardBrand}
+
+ )}
+
+ );
+};
+
+const styles = StyleSheet.create({
+ title: {
+ ...theme.typography.h3,
+ color: theme.colors.textPrimary,
+ marginBottom: theme.spacing.sm,
+ },
+ row: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ marginBottom: theme.spacing.xs,
+ },
+ label: {
+ ...theme.typography.body,
+ color: theme.colors.textSecondary,
+ fontWeight: '500',
+ },
+ value: {
+ ...theme.typography.body,
+ color: theme.colors.textPrimary,
+ fontWeight: '600',
+ },
+});
diff --git a/example/src/components/index.ts b/example/src/components/index.ts
new file mode 100644
index 0000000..1eae9f4
--- /dev/null
+++ b/example/src/components/index.ts
@@ -0,0 +1,8 @@
+export { Button, type ButtonVariant } from './Button';
+export { Card } from './Card';
+export { Header } from './Header';
+export { LoadingIndicator } from './LoadingIndicator';
+export { PaymentButtons } from './PaymentButtons';
+export { PaymentEvent } from './PaymentEvent';
+export { StyleExample } from './StyleExample';
+export { TransactionInfo } from './TransactionInfo';
diff --git a/example/src/constants/index.ts b/example/src/constants/index.ts
new file mode 100644
index 0000000..7b1f54e
--- /dev/null
+++ b/example/src/constants/index.ts
@@ -0,0 +1 @@
+export * from './theme';
diff --git a/example/src/constants/theme.ts b/example/src/constants/theme.ts
new file mode 100644
index 0000000..9682515
--- /dev/null
+++ b/example/src/constants/theme.ts
@@ -0,0 +1,104 @@
+// Dark theme constants and colors
+export const theme = {
+ colors: {
+ primary: '#00D4FF',
+ secondary: '#7C3AED',
+ success: '#22C55E',
+ warning: '#F59E0B',
+ error: '#EF4444',
+
+ // Background colors
+ background: '#0A0A0B',
+ surface: '#1A1A1D',
+ surfaceElevated: '#2A2A2F',
+
+ // Text colors
+ textPrimary: '#FFFFFF',
+ textSecondary: '#A1A1AA',
+ textTertiary: '#71717A',
+
+ // Event colors
+ cardEvent: '#0EA5E9',
+ passwordEvent: '#F97316',
+ processing: '#EAB308',
+ terminalResponse: '#22C55E',
+ errorEvent: '#EF4444',
+ default: '#6B7280',
+
+ // Status colors
+ ready: '#22C55E',
+ notInitialized: '#EF4444',
+ processingStatus: '#F59E0B',
+ },
+
+ spacing: {
+ xs: 4,
+ sm: 8,
+ md: 16,
+ lg: 24,
+ xl: 32,
+ },
+
+ borderRadius: {
+ sm: 8,
+ md: 12,
+ lg: 16,
+ },
+
+ typography: {
+ h1: {
+ fontSize: 28,
+ fontWeight: '700' as const,
+ lineHeight: 36,
+ },
+ h2: {
+ fontSize: 22,
+ fontWeight: '600' as const,
+ lineHeight: 28,
+ },
+ h3: {
+ fontSize: 18,
+ fontWeight: '600' as const,
+ lineHeight: 24,
+ },
+ body: {
+ fontSize: 16,
+ fontWeight: '400' as const,
+ lineHeight: 24,
+ },
+ caption: {
+ fontSize: 14,
+ fontWeight: '400' as const,
+ lineHeight: 20,
+ },
+ small: {
+ fontSize: 12,
+ fontWeight: '400' as const,
+ lineHeight: 16,
+ },
+ },
+
+ shadows: {
+ small: {
+ shadowColor: '#000000',
+ shadowOffset: { width: 0, height: 2 },
+ shadowOpacity: 0.1,
+ shadowRadius: 4,
+ elevation: 2,
+ },
+ medium: {
+ shadowColor: '#000000',
+ shadowOffset: { width: 0, height: 4 },
+ shadowOpacity: 0.15,
+ shadowRadius: 8,
+ elevation: 4,
+ },
+ large: {
+ shadowColor: '#000000',
+ shadowOffset: { width: 0, height: 8 },
+ shadowOpacity: 0.2,
+ shadowRadius: 16,
+ elevation: 8,
+ },
+ },
+} as const;
diff --git a/example/src/hooks/index.ts b/example/src/hooks/index.ts
new file mode 100644
index 0000000..5089b83
--- /dev/null
+++ b/example/src/hooks/index.ts
@@ -0,0 +1 @@
+export * from './usePaymentOperations';
diff --git a/example/src/hooks/usePaymentOperations.ts b/example/src/hooks/usePaymentOperations.ts
new file mode 100644
index 0000000..e8d8307
--- /dev/null
+++ b/example/src/hooks/usePaymentOperations.ts
@@ -0,0 +1,176 @@
+import { useState, useEffect, useCallback } from 'react';
+import {
+ type PlugpagTransactionResult,
+ initializeAndActivatePinPad,
+ doPayment,
+ refundPayment,
+ getTerminalSerialNumber,
+ PaymentType,
+ ErrorCode,
+ InstallmentType,
+} from 'react-native-plugpag-nitro';
+import {
+ TERMINAL_ACTIVATION_CODE,
+ PAYMENT_AMOUNTS,
+ formatCurrency,
+ getPaymentTypeText,
+ showAlert,
+ withAsyncOperation,
+} from '../utils';
+
+export interface PaymentOptions {
+ amount: number;
+ type: PaymentType;
+ installmentType?: InstallmentType;
+ installments?: number;
+}
+
+export const usePaymentOperations = () => {
+ const [isProcessing, setIsProcessing] = useState(false);
+ const [isInitialized, setIsInitialized] = useState(false);
+ const [terminalSerial, setTerminalSerial] = useState('N/A');
+ const [lastPayment, setLastPayment] =
+ useState(null);
+
+ // Get terminal serial on mount
+ useEffect(() => {
+ try {
+ setTerminalSerial(getTerminalSerialNumber());
+ } catch (e) {
+ console.warn('Failed to get terminal serial:', e);
+ }
+ }, []);
+
+ const handleInitialize = useCallback(async (): Promise => {
+ await withAsyncOperation(
+ async () => {
+ const result = await initializeAndActivatePinPad(
+ TERMINAL_ACTIVATION_CODE
+ );
+ if (result.result === ErrorCode.OK) {
+ setIsInitialized(true);
+ showAlert.success('Sucesso', 'Terminal inicializado com sucesso');
+ } else {
+ showAlert.error(
+ 'Erro',
+ result.errorMessage || 'Falha ao inicializar'
+ );
+ }
+ return result;
+ },
+ setIsProcessing,
+ (error) => showAlert.error('Error', error.message)
+ );
+ }, []);
+
+ const handlePayment = async (options: PaymentOptions): Promise => {
+ if (!isInitialized) {
+ showAlert.warning('Aviso', 'Por favor, inicialize o terminal primeiro');
+ return;
+ }
+
+ await withAsyncOperation(
+ async () => {
+ const result = await doPayment(options);
+ setLastPayment(result);
+
+ if (result.result === ErrorCode.OK) {
+ const paymentTypeText = getPaymentTypeText(options.type);
+ const amountText = formatCurrency(options.amount);
+ const installmentText = options.installments
+ ? ` (${options.installments}x)`
+ : '';
+
+ showAlert.success(
+ 'Pagamento Aprovado',
+ `${paymentTypeText} realizado com sucesso!\nCódigo: ${result.transactionCode}\nValor: ${amountText}${installmentText}`
+ );
+ } else {
+ showAlert.error(
+ 'Pagamento Negado',
+ result.message || 'Transação falhou'
+ );
+ }
+ return result;
+ },
+ setIsProcessing,
+ (error) => showAlert.error('Erro', error.message)
+ );
+ };
+
+ const handleCreditPayment = (): Promise => {
+ return handlePayment({
+ amount: PAYMENT_AMOUNTS.credit,
+ type: PaymentType.CREDIT,
+ });
+ };
+
+ const handleDebitPayment = (): Promise => {
+ return handlePayment({
+ amount: PAYMENT_AMOUNTS.debit,
+ type: PaymentType.DEBIT,
+ });
+ };
+
+ const handlePIXPayment = (): Promise => {
+ return handlePayment({
+ amount: PAYMENT_AMOUNTS.pix,
+ type: PaymentType.PIX,
+ });
+ };
+
+ const handleInstallmentPayment = (): Promise => {
+ return handlePayment({
+ amount: PAYMENT_AMOUNTS.installment,
+ type: PaymentType.CREDIT,
+ installmentType: InstallmentType.BUYER_INSTALLMENT,
+ installments: 3,
+ });
+ };
+
+ const handleRefund = async (): Promise => {
+ if (!lastPayment?.transactionCode || !lastPayment.transactionId) {
+ showAlert.warning('Aviso', 'Nenhuma transação para estornar');
+ return;
+ }
+
+ await withAsyncOperation(
+ async () => {
+ const result = await refundPayment({
+ transactionCode: lastPayment.transactionCode!,
+ transactionId: lastPayment.transactionId!,
+ });
+
+ if (result.result === ErrorCode.OK) {
+ showAlert.success(
+ 'Estorno Realizado',
+ `Código: ${result.transactionCode}`
+ );
+ setLastPayment(null);
+ } else {
+ showAlert.error('Estorno Falhou', result.message || 'Estorno falhou');
+ }
+ return result;
+ },
+ setIsProcessing,
+ (error) => showAlert.error('Erro', error.message)
+ );
+ };
+
+ return {
+ // State
+ isProcessing,
+ isInitialized,
+ terminalSerial,
+ lastPayment,
+
+ // Actions
+ handleInitialize,
+ handlePayment,
+ handleCreditPayment,
+ handleDebitPayment,
+ handlePIXPayment,
+ handleInstallmentPayment,
+ handleRefund,
+ };
+};
diff --git a/example/src/utils/alerts.ts b/example/src/utils/alerts.ts
new file mode 100644
index 0000000..bbc808c
--- /dev/null
+++ b/example/src/utils/alerts.ts
@@ -0,0 +1,16 @@
+import { Alert } from 'react-native';
+
+export const showAlert = {
+ success: (title: string, message: string) => {
+ Alert.alert(`✅ ${title}`, message);
+ },
+ error: (title: string, message: string) => {
+ Alert.alert(`❌ ${title}`, message);
+ },
+ warning: (title: string, message: string) => {
+ Alert.alert(`⚠️ ${title}`, message);
+ },
+ info: (title: string, message: string) => {
+ Alert.alert(`ℹ️ ${title}`, message);
+ },
+};
diff --git a/example/src/utils/asyncOperations.ts b/example/src/utils/asyncOperations.ts
new file mode 100644
index 0000000..ef9133c
--- /dev/null
+++ b/example/src/utils/asyncOperations.ts
@@ -0,0 +1,23 @@
+/**
+ * Generic async operation handler with consistent error handling and loading states
+ */
+export const withAsyncOperation = async (
+ operation: () => Promise,
+ setLoading: (loading: boolean) => void,
+ onError?: (error: Error) => void
+): Promise => {
+ setLoading(true);
+ try {
+ const result = await operation();
+ return result;
+ } catch (error) {
+ if (onError) {
+ onError(error as Error);
+ } else {
+ console.error('Async operation failed:', error);
+ }
+ return null;
+ } finally {
+ setLoading(false);
+ }
+};
diff --git a/example/src/utils/index.ts b/example/src/utils/index.ts
new file mode 100644
index 0000000..0ebd597
--- /dev/null
+++ b/example/src/utils/index.ts
@@ -0,0 +1,3 @@
+export * from './alerts';
+export * from './asyncOperations';
+export * from './payment';
diff --git a/example/src/utils/payment.ts b/example/src/utils/payment.ts
new file mode 100644
index 0000000..6bb91d5
--- /dev/null
+++ b/example/src/utils/payment.ts
@@ -0,0 +1,106 @@
+import { theme } from '../constants/theme';
+import { PaymentType } from 'react-native-plugpag-nitro';
+import type { ButtonVariant } from '../components/Button';
+
+/**
+ * Formats currency value from cents to BRL format
+ */
+export const formatCurrency = (amountInCents: number): string => {
+ const amount = amountInCents / 100;
+ return new Intl.NumberFormat('pt-BR', {
+ style: 'currency',
+ currency: 'BRL',
+ }).format(amount);
+};
+
+/**
+ * Gets the appropriate color for payment event codes
+ */
+export const getEventColor = (code: number): string => {
+ if (code >= 1001 && code <= 1004) return theme.colors.cardEvent; // Card events
+ if (code >= 1010 && code <= 1012) return theme.colors.passwordEvent; // Password events
+ if (code >= 1020 && code <= 1023) return theme.colors.processing; // Processing
+ if (code >= 1030 && code <= 1032) return theme.colors.terminalResponse; // Terminal response
+ if (code >= 1040 && code <= 1043) return theme.colors.errorEvent; // Errors
+ return theme.colors.default; // Default
+};
+
+/**
+ * Gets status color based on initialization state
+ */
+export const getStatusColor = (isInitialized: boolean): string => {
+ return isInitialized ? theme.colors.ready : theme.colors.notInitialized;
+};
+
+/**
+ * Gets status text based on initialization state
+ */
+export const getStatusText = (isInitialized: boolean): string => {
+ return isInitialized ? '✅ Pronto' : '❌ Não Inicializado';
+};
+
+/**
+ * Gets payment type display text
+ */
+export const getPaymentTypeText = (type: PaymentType): string => {
+ switch (type) {
+ case PaymentType.CREDIT:
+ return 'Pagamento de crédito';
+ case PaymentType.DEBIT:
+ return 'Pagamento de débito';
+ case PaymentType.PIX:
+ return 'Pagamento PIX';
+ default:
+ return 'Transação';
+ }
+};
+
+/**
+ * Payment amounts configuration
+ */
+export const PAYMENT_AMOUNTS = {
+ credit: 2500, // R$ 25.00
+ debit: 1500, // R$ 15.00
+ pix: 1500, // R$ 15.00
+ installment: 10000, // R$ 100.00
+} as const;
+
+/**
+ * Payment button configurations
+ */
+export const PAYMENT_BUTTONS = [
+ {
+ id: 'credit',
+ title: `Pagamento Crédito - ${formatCurrency(PAYMENT_AMOUNTS.credit)}`,
+ variant: 'primary' as ButtonVariant,
+ paymentType: PaymentType.CREDIT,
+ amount: PAYMENT_AMOUNTS.credit,
+ },
+ {
+ id: 'debit',
+ title: `Pagamento Débito - ${formatCurrency(PAYMENT_AMOUNTS.debit)}`,
+ variant: 'primary' as ButtonVariant,
+ paymentType: PaymentType.DEBIT,
+ amount: PAYMENT_AMOUNTS.debit,
+ },
+ {
+ id: 'pix',
+ title: `PIX - ${formatCurrency(PAYMENT_AMOUNTS.pix)}`,
+ variant: 'secondary' as ButtonVariant,
+ paymentType: PaymentType.PIX,
+ amount: PAYMENT_AMOUNTS.pix,
+ },
+ {
+ id: 'installment',
+ title: `Pagamento Parcelado - ${formatCurrency(PAYMENT_AMOUNTS.installment)} (3x)`,
+ variant: 'primary' as ButtonVariant,
+ paymentType: PaymentType.CREDIT,
+ amount: PAYMENT_AMOUNTS.installment,
+ installments: 3,
+ },
+] as const;
+
+/**
+ * Terminal activation code
+ */
+export const TERMINAL_ACTIVATION_CODE = '403938';
diff --git a/package.json b/package.json
index ad8ec6b..d93f24c 100644
--- a/package.json
+++ b/package.json
@@ -85,7 +85,7 @@
"eslint": "^9.31.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.5.3",
- "jest": "^30.0.4",
+ "jest": "^30.0.5",
"nitro-codegen": "^0.26.4",
"prettier": "^3.6.2",
"react": "19.1.0",
@@ -94,7 +94,7 @@
"react-native-nitro-modules": "^0.26.4",
"release-it": "^19.0.4",
"turbo": "^2.5.5",
- "typescript": "^5.5.4"
+ "typescript": "^5.8.3"
},
"peerDependencies": {
"react": "*",
diff --git a/src/PlugpagNitro.nitro.ts b/src/PlugpagNitro.nitro.ts
index f8810cb..1a79458 100644
--- a/src/PlugpagNitro.nitro.ts
+++ b/src/PlugpagNitro.nitro.ts
@@ -85,6 +85,23 @@ export interface PlugpagPaymentData {
userReference?: string;
}
+export interface PlugpagStyleData {
+ headTextColor?: string;
+ headBackgroundColor?: string;
+ contentTextColor?: string;
+ contentTextValue1Color?: string;
+ contentTextValue2Color?: string;
+ positiveButtonTextColor?: string;
+ positiveButtonBackground?: string;
+ negativeButtonTextColor?: string;
+ negativeButtonBackground?: string;
+ genericButtonBackground?: string;
+ genericButtonTextColor?: string;
+ genericSmsEditTextBackground?: string;
+ genericSmsEditTextTextColor?: string;
+ lineColor?: string;
+}
+
export interface PlugpagTransactionResult {
result: ErrorCode;
errorCode?: string;
@@ -198,13 +215,21 @@ export interface PlugpagNitro extends HybridObject<{ android: 'kotlin' }> {
/**
* Abort the current ongoing transaction
*/
- doAbort(): Promise;
+ doAbort(): Promise;
+
+ /**
+ * Set custom style theme for PagBank SDK UI components
+ * Allows customization of colors for modal dialogs, buttons, and text
+ * @param styleData Style configuration with color customizations
+ */
+ setStyleTheme(styleData: PlugpagStyleData): Promise;
/**
- * Print a custom receipt from file path
- * @param filePath Path to the image file (PNG/JPEG)
+ * Print from a file path
+ * @param filePath Path to the file to print
+ * @param textSize Text size for printing
*/
- print(filePath: string): Promise;
+ print(filePath: string, textSize: number): Promise;
/**
* Reprint the last customer receipt
diff --git a/src/index.tsx b/src/index.tsx
index 4633fcb..4954993 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -5,8 +5,8 @@ import type {
PlugpagNitro,
PlugpagInitializationResult,
PlugpagTransactionResult,
- PlugpagAbortResult,
PlugpagConstants,
+ PlugpagStyleData,
PaymentEvent,
} from './PlugpagNitro.nitro';
import {
@@ -28,13 +28,16 @@ export {
export type {
PlugpagInitializationResult,
PlugpagTransactionResult,
- PlugpagAbortResult,
PlugpagConstants,
PlugpagPaymentData,
PlugpagVoidData,
+ PlugpagStyleData,
PaymentEvent,
} from './PlugpagNitro.nitro';
+// Export theme utilities
+export { ThemeUtils } from './themes';
+
const PlugpagNitroModule =
NitroModules.createHybridObject('PlugpagNitro');
@@ -192,15 +195,32 @@ export async function refundPayment(options: {
/**
* Abort the current ongoing transaction
*/
-export async function doAbort(): Promise {
+export async function doAbort(): Promise {
return safeModuleCall('doAbort', () => PlugpagNitroModule.doAbort());
}
+/**
+ * Set custom style theme for PagBank SDK UI components
+ * Allows customization of colors for modal dialogs, buttons, and text
+ */
+export async function setStyleTheme(
+ styleData: PlugpagStyleData
+): Promise {
+ return safeModuleCall('setStyleTheme', () =>
+ PlugpagNitroModule.setStyleTheme(styleData)
+ );
+}
+
/**
* Print a custom receipt from file path
*/
-export async function print(filePath: string): Promise {
- return safeModuleCall('print', () => PlugpagNitroModule.print(filePath));
+export async function print(
+ filePath: string,
+ textSize: number = 20
+): Promise {
+ return safeModuleCall('print', () =>
+ PlugpagNitroModule.print(filePath, textSize)
+ );
}
/**
@@ -230,6 +250,7 @@ export default {
doPayment,
refundPayment,
doAbort,
+ setStyleTheme,
print,
reprintCustomerReceipt,
diff --git a/src/themes.ts b/src/themes.ts
new file mode 100644
index 0000000..a542d4e
--- /dev/null
+++ b/src/themes.ts
@@ -0,0 +1,57 @@
+import type { PlugpagStyleData } from './PlugpagNitro.nitro';
+
+/**
+ * Theme utility functions for PagBank SDK styling
+ */
+export class ThemeUtils {
+ /**
+ * Validates that all color values in a theme are valid hex colors
+ * @param theme PlugpagStyleData to validate
+ * @returns Array of validation errors (empty if valid)
+ */
+ static validateTheme(theme: PlugpagStyleData): string[] {
+ const errors: string[] = [];
+ const hexColorRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;
+
+ Object.entries(theme).forEach(([key, value]) => {
+ if (value && typeof value === 'string' && !hexColorRegex.test(value)) {
+ errors.push(`Invalid hex color for ${key}: ${value}`);
+ }
+ });
+
+ return errors;
+ }
+
+ /**
+ * Merges a partial theme with a base theme
+ * @param baseTheme Base theme to extend
+ * @param partialTheme Partial theme with overrides
+ * @returns Complete merged theme
+ */
+ static mergeThemes(
+ baseTheme: PlugpagStyleData,
+ partialTheme: Partial
+ ): PlugpagStyleData {
+ return {
+ ...baseTheme,
+ ...partialTheme,
+ };
+ }
+
+ /**
+ * Creates a theme preview object for debugging
+ * @param theme Theme to preview
+ * @returns Object with color swatches for visualization
+ */
+ static createThemePreview(theme: PlugpagStyleData): Record {
+ const preview: Record = {};
+
+ Object.entries(theme).forEach(([key, value]) => {
+ if (value && typeof value === 'string') {
+ preview[key] = value;
+ }
+ });
+
+ return preview;
+ }
+}
diff --git a/yarn.lock b/yarn.lock
index 3c6d0c8..169f0a2 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2214,58 +2214,58 @@ __metadata:
languageName: node
linkType: hard
-"@jest/console@npm:30.0.4":
- version: 30.0.4
- resolution: "@jest/console@npm:30.0.4"
+"@jest/console@npm:30.0.5":
+ version: 30.0.5
+ resolution: "@jest/console@npm:30.0.5"
dependencies:
- "@jest/types": 30.0.1
+ "@jest/types": 30.0.5
"@types/node": "*"
chalk: ^4.1.2
- jest-message-util: 30.0.2
- jest-util: 30.0.2
+ jest-message-util: 30.0.5
+ jest-util: 30.0.5
slash: ^3.0.0
- checksum: e3e674fa58a6c4efab96414cfd60c289c8657f2de4201ceb9dc8a758137d671db4c2a26769a7353cf676b8819408400aa7603633e02b44fe27a5fb1bf5bfc404
+ checksum: 5ff6c56e30fd99f069d9f0d5a6a5834fc63c303a84737057d10dd9a912d3c6eb45b0f2bcb45b998cd69732ae50635ae96982265a4632a71a8b28d66d1df0a608
languageName: node
linkType: hard
-"@jest/core@npm:30.0.4":
- version: 30.0.4
- resolution: "@jest/core@npm:30.0.4"
+"@jest/core@npm:30.0.5":
+ version: 30.0.5
+ resolution: "@jest/core@npm:30.0.5"
dependencies:
- "@jest/console": 30.0.4
+ "@jest/console": 30.0.5
"@jest/pattern": 30.0.1
- "@jest/reporters": 30.0.4
- "@jest/test-result": 30.0.4
- "@jest/transform": 30.0.4
- "@jest/types": 30.0.1
+ "@jest/reporters": 30.0.5
+ "@jest/test-result": 30.0.5
+ "@jest/transform": 30.0.5
+ "@jest/types": 30.0.5
"@types/node": "*"
ansi-escapes: ^4.3.2
chalk: ^4.1.2
ci-info: ^4.2.0
exit-x: ^0.2.2
graceful-fs: ^4.2.11
- jest-changed-files: 30.0.2
- jest-config: 30.0.4
- jest-haste-map: 30.0.2
- jest-message-util: 30.0.2
+ jest-changed-files: 30.0.5
+ jest-config: 30.0.5
+ jest-haste-map: 30.0.5
+ jest-message-util: 30.0.5
jest-regex-util: 30.0.1
- jest-resolve: 30.0.2
- jest-resolve-dependencies: 30.0.4
- jest-runner: 30.0.4
- jest-runtime: 30.0.4
- jest-snapshot: 30.0.4
- jest-util: 30.0.2
- jest-validate: 30.0.2
- jest-watcher: 30.0.4
+ jest-resolve: 30.0.5
+ jest-resolve-dependencies: 30.0.5
+ jest-runner: 30.0.5
+ jest-runtime: 30.0.5
+ jest-snapshot: 30.0.5
+ jest-util: 30.0.5
+ jest-validate: 30.0.5
+ jest-watcher: 30.0.5
micromatch: ^4.0.8
- pretty-format: 30.0.2
+ pretty-format: 30.0.5
slash: ^3.0.0
peerDependencies:
node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
peerDependenciesMeta:
node-notifier:
optional: true
- checksum: cb590959e9154a95e578914eb54fdc24e982fd1f5be4065598f21b14ec6ab84f6dc472ae70a5a6596b9c24d9208b1868e18bf3e2947e79ac6a351e9fcae9e141
+ checksum: 3ef30db3b35ef554298293a6f61f8ea9d81e04a12430b92bf8cd84ec2c539999cbb67d35422cec428655bc74c19f0e70ec0781a56cbcbfca87f229c65b2ac342
languageName: node
linkType: hard
@@ -2285,15 +2285,15 @@ __metadata:
languageName: node
linkType: hard
-"@jest/environment@npm:30.0.4":
- version: 30.0.4
- resolution: "@jest/environment@npm:30.0.4"
+"@jest/environment@npm:30.0.5":
+ version: 30.0.5
+ resolution: "@jest/environment@npm:30.0.5"
dependencies:
- "@jest/fake-timers": 30.0.4
- "@jest/types": 30.0.1
+ "@jest/fake-timers": 30.0.5
+ "@jest/types": 30.0.5
"@types/node": "*"
- jest-mock: 30.0.2
- checksum: 4079b11c0b1a453ffec23a285d5c3f2e195c26d5853f2375b71b6b57bb2e2b8229e76709a75bd9e58fbf5a7cb8424bd97ba211ba8bf273082f37721ea3edef52
+ jest-mock: 30.0.5
+ checksum: 0c2c27a4ee3d4e5054e36202185da4943b1c7fb4b2f65ddf5ddbe25bcb29dcb9c3c6c1e8b53f93dd5753ccc707c36ef3db6fc0120bb055014fd3c854929d505c
languageName: node
linkType: hard
@@ -2318,27 +2318,36 @@ __metadata:
languageName: node
linkType: hard
-"@jest/expect@npm:30.0.4":
- version: 30.0.4
- resolution: "@jest/expect@npm:30.0.4"
+"@jest/expect-utils@npm:30.0.5":
+ version: 30.0.5
+ resolution: "@jest/expect-utils@npm:30.0.5"
dependencies:
- expect: 30.0.4
- jest-snapshot: 30.0.4
- checksum: a9eb9bb32b242e855c7b525866cd67c8573761dc7f539cb3c6cb4824abc98c9785548302f37a4604c16f91e9e10c9ad4980d3b740dc3173233528c75047b27bb
+ "@jest/get-type": 30.0.1
+ checksum: 8976ac5217edc58276d4eff7cc7a2523feb18427327710e47db4999a985ad535bddd5a00a0cb8c31300bfab9cdf166e94d92e4f3650d921cf41d1bd682294974
languageName: node
linkType: hard
-"@jest/fake-timers@npm:30.0.4":
- version: 30.0.4
- resolution: "@jest/fake-timers@npm:30.0.4"
+"@jest/expect@npm:30.0.5":
+ version: 30.0.5
+ resolution: "@jest/expect@npm:30.0.5"
dependencies:
- "@jest/types": 30.0.1
+ expect: 30.0.5
+ jest-snapshot: 30.0.5
+ checksum: a841d9a8bd1d099904c2df0f17bbee6be6374bc3f87cd2f4cb14cdabc78d165d4bb312a6d5676688b10e8bcb63801c979068de89bab9c679927dfe0230673b7d
+ languageName: node
+ linkType: hard
+
+"@jest/fake-timers@npm:30.0.5":
+ version: 30.0.5
+ resolution: "@jest/fake-timers@npm:30.0.5"
+ dependencies:
+ "@jest/types": 30.0.5
"@sinonjs/fake-timers": ^13.0.0
"@types/node": "*"
- jest-message-util: 30.0.2
- jest-mock: 30.0.2
- jest-util: 30.0.2
- checksum: 2caa9a6d92d4d5473dc2af9e96a44b51247692bde35db5540f96079709dded00f9af4ddb0064078f42adc7f9277b52116ed293916b4dc11750894d4d0a1b637f
+ jest-message-util: 30.0.5
+ jest-mock: 30.0.5
+ jest-util: 30.0.5
+ checksum: c748528b5cb04ebec28174e98009198e1a4a881e63627c7740ffbeefa8810eec674f9dc36401611431875a15a984b695b79c6efdf3f602ba9ab64a2920eb2c9b
languageName: node
linkType: hard
@@ -2363,15 +2372,15 @@ __metadata:
languageName: node
linkType: hard
-"@jest/globals@npm:30.0.4":
- version: 30.0.4
- resolution: "@jest/globals@npm:30.0.4"
+"@jest/globals@npm:30.0.5":
+ version: 30.0.5
+ resolution: "@jest/globals@npm:30.0.5"
dependencies:
- "@jest/environment": 30.0.4
- "@jest/expect": 30.0.4
- "@jest/types": 30.0.1
- jest-mock: 30.0.2
- checksum: 5dda77a36d768c57389603e1d22d05ac24c916f2e82f3766f57398ae8a835cfb69746c75b76906abdbc1e39da0c1863f68a93167c20317f38bade7500c861b71
+ "@jest/environment": 30.0.5
+ "@jest/expect": 30.0.5
+ "@jest/types": 30.0.5
+ jest-mock: 30.0.5
+ checksum: 44091f5d8386bf5cadd7d36e2fb36b0794b2dd1e0c866d4cecceaf12f9304bb139544a597b1d1edf4c8158baa5684042bcfda4bc9a5603bd2c41c17509c4151b
languageName: node
linkType: hard
@@ -2385,15 +2394,15 @@ __metadata:
languageName: node
linkType: hard
-"@jest/reporters@npm:30.0.4":
- version: 30.0.4
- resolution: "@jest/reporters@npm:30.0.4"
+"@jest/reporters@npm:30.0.5":
+ version: 30.0.5
+ resolution: "@jest/reporters@npm:30.0.5"
dependencies:
"@bcoe/v8-coverage": ^0.2.3
- "@jest/console": 30.0.4
- "@jest/test-result": 30.0.4
- "@jest/transform": 30.0.4
- "@jest/types": 30.0.1
+ "@jest/console": 30.0.5
+ "@jest/test-result": 30.0.5
+ "@jest/transform": 30.0.5
+ "@jest/types": 30.0.5
"@jridgewell/trace-mapping": ^0.3.25
"@types/node": "*"
chalk: ^4.1.2
@@ -2406,9 +2415,9 @@ __metadata:
istanbul-lib-report: ^3.0.0
istanbul-lib-source-maps: ^5.0.0
istanbul-reports: ^3.1.3
- jest-message-util: 30.0.2
- jest-util: 30.0.2
- jest-worker: 30.0.2
+ jest-message-util: 30.0.5
+ jest-util: 30.0.5
+ jest-worker: 30.0.5
slash: ^3.0.0
string-length: ^4.0.2
v8-to-istanbul: ^9.0.1
@@ -2417,7 +2426,7 @@ __metadata:
peerDependenciesMeta:
node-notifier:
optional: true
- checksum: 0b8fbe2d79398395bb4cf72d4584dd80d06f2155102f9e81887f4711370d48f5d114a2ff695f0fb39bba03ef63b39d1dbcaf340898781e906ef7ece06d6c37e4
+ checksum: 5b907de63acf59b7c45d0a43f267be1f275baadaa58e150dc1d417d7e2d4ecf04fc03cbac6e93da413991d8848627acf53adc4a709ed7dc132512e408da6baac
languageName: node
linkType: hard
@@ -2430,6 +2439,15 @@ __metadata:
languageName: node
linkType: hard
+"@jest/schemas@npm:30.0.5":
+ version: 30.0.5
+ resolution: "@jest/schemas@npm:30.0.5"
+ dependencies:
+ "@sinclair/typebox": ^0.34.0
+ checksum: 7a4fc4166f688947c22d81e61aaf2cb22f178dbf6ee806b0931b75136899d426a72a8330762f27f0cf6f79da0d2a56f49a22fe09f5f80df95a683ed237a0f3b0
+ languageName: node
+ linkType: hard
+
"@jest/schemas@npm:^29.6.3":
version: 29.6.3
resolution: "@jest/schemas@npm:29.6.3"
@@ -2439,15 +2457,15 @@ __metadata:
languageName: node
linkType: hard
-"@jest/snapshot-utils@npm:30.0.4":
- version: 30.0.4
- resolution: "@jest/snapshot-utils@npm:30.0.4"
+"@jest/snapshot-utils@npm:30.0.5":
+ version: 30.0.5
+ resolution: "@jest/snapshot-utils@npm:30.0.5"
dependencies:
- "@jest/types": 30.0.1
+ "@jest/types": 30.0.5
chalk: ^4.1.2
graceful-fs: ^4.2.11
natural-compare: ^1.4.0
- checksum: 2bfde456169a5512a27f798f050c882b73033bb52331b7cb282d0bf696d2937b9112062a56d6c3ea47a999b49ffe897c6036822c645566c2a334b570c92c7d7c
+ checksum: 94ab5b9f8a1bf82c7bed154abf4fda682ae8a9d06850501336724fcc67fdfc5da7045f076d976ef04e9cbebf24437eac66d9e7c1e0aff65958cbbced2516b613
languageName: node
linkType: hard
@@ -2462,50 +2480,50 @@ __metadata:
languageName: node
linkType: hard
-"@jest/test-result@npm:30.0.4":
- version: 30.0.4
- resolution: "@jest/test-result@npm:30.0.4"
+"@jest/test-result@npm:30.0.5":
+ version: 30.0.5
+ resolution: "@jest/test-result@npm:30.0.5"
dependencies:
- "@jest/console": 30.0.4
- "@jest/types": 30.0.1
+ "@jest/console": 30.0.5
+ "@jest/types": 30.0.5
"@types/istanbul-lib-coverage": ^2.0.6
collect-v8-coverage: ^1.0.2
- checksum: feae8c3518da2ec3330fe47d4f3871b6917d21cd459db6bef8e5279a5e9adae14593063e028e7d4d537393deb0dfdcc20102a74de94f258439be18d019628b74
+ checksum: 6608b03b18fe6219f80967c2a35766594d757d6aac9238185358551b47254a0c4246d61d0ec9e3ea26272c4ddef7b947b0ad1236d50d9d52fe7fac5174174453
languageName: node
linkType: hard
-"@jest/test-sequencer@npm:30.0.4":
- version: 30.0.4
- resolution: "@jest/test-sequencer@npm:30.0.4"
+"@jest/test-sequencer@npm:30.0.5":
+ version: 30.0.5
+ resolution: "@jest/test-sequencer@npm:30.0.5"
dependencies:
- "@jest/test-result": 30.0.4
+ "@jest/test-result": 30.0.5
graceful-fs: ^4.2.11
- jest-haste-map: 30.0.2
+ jest-haste-map: 30.0.5
slash: ^3.0.0
- checksum: 97c67c8b243e1fedbfa7137da4e276e8034d27d2a039d987c76c27dea7a61c74d617d52cbecd6e87d8c359de9b22745b739177810b42e08cab1f9e66435d3900
+ checksum: d183bb3c269372e86b283d3b676c5279788eacdfdca424ba8b7da55cb11da51d887a328507b86f33489b38b4422b6e4393ead65ec5ebc1b2d59ca0d39b165bd5
languageName: node
linkType: hard
-"@jest/transform@npm:30.0.4":
- version: 30.0.4
- resolution: "@jest/transform@npm:30.0.4"
+"@jest/transform@npm:30.0.5":
+ version: 30.0.5
+ resolution: "@jest/transform@npm:30.0.5"
dependencies:
"@babel/core": ^7.27.4
- "@jest/types": 30.0.1
+ "@jest/types": 30.0.5
"@jridgewell/trace-mapping": ^0.3.25
babel-plugin-istanbul: ^7.0.0
chalk: ^4.1.2
convert-source-map: ^2.0.0
fast-json-stable-stringify: ^2.1.0
graceful-fs: ^4.2.11
- jest-haste-map: 30.0.2
+ jest-haste-map: 30.0.5
jest-regex-util: 30.0.1
- jest-util: 30.0.2
+ jest-util: 30.0.5
micromatch: ^4.0.8
pirates: ^4.0.7
slash: ^3.0.0
write-file-atomic: ^5.0.1
- checksum: 949542f46ef5d5578004a205fd529d51f3d036f9bad1b79d25740b71ba7bdd7b10dcdf81c79eb0149ee96f1153a1bc844599a9041d9072f75f92237e1e4df553
+ checksum: a926cdd7627850e1ef9c2b75eebebe283a4042a633ab5d6b70e2603555567ca9fa558c0ef53f506def82aa878cfc2f2e5f28331d918e90cab5418e9bb61e38df
languageName: node
linkType: hard
@@ -2547,6 +2565,21 @@ __metadata:
languageName: node
linkType: hard
+"@jest/types@npm:30.0.5":
+ version: 30.0.5
+ resolution: "@jest/types@npm:30.0.5"
+ dependencies:
+ "@jest/pattern": 30.0.1
+ "@jest/schemas": 30.0.5
+ "@types/istanbul-lib-coverage": ^2.0.6
+ "@types/istanbul-reports": ^3.0.4
+ "@types/node": "*"
+ "@types/yargs": ^17.0.33
+ chalk: ^4.1.2
+ checksum: 59a7ad26a5ca4f0480961b4a9bde05c954c4b00b267231f05e33fd05ed786abdebc0a3cdcb813df4bf05b3513b0a29c77db79e97b246ac4ab31285e4253e8335
+ languageName: node
+ linkType: hard
+
"@jest/types@npm:^26.6.2":
version: 26.6.2
resolution: "@jest/types@npm:26.6.2"
@@ -4529,11 +4562,11 @@ __metadata:
languageName: node
linkType: hard
-"babel-jest@npm:30.0.4":
- version: 30.0.4
- resolution: "babel-jest@npm:30.0.4"
+"babel-jest@npm:30.0.5":
+ version: 30.0.5
+ resolution: "babel-jest@npm:30.0.5"
dependencies:
- "@jest/transform": 30.0.4
+ "@jest/transform": 30.0.5
"@types/babel__core": ^7.20.5
babel-plugin-istanbul: ^7.0.0
babel-preset-jest: 30.0.1
@@ -4542,7 +4575,7 @@ __metadata:
slash: ^3.0.0
peerDependencies:
"@babel/core": ^7.11.0
- checksum: 9d0048416b4499789fa0babd1dabcae03613acdc30c6269b3173cc53a22ee01a042dc119414196e3ac264743184a89fc27e64278770b8a942f9b22565df3e424
+ checksum: 7d7cecee857536cd802d6856dee15c84b19fc65f7b55bd7987e1dd9753cfb944e63ae331c4cc43280a740262d1614adb5293616cfcc2b587db7d904aaab213aa
languageName: node
linkType: hard
@@ -6638,7 +6671,21 @@ __metadata:
languageName: node
linkType: hard
-"expect@npm:30.0.4, expect@npm:^30.0.0":
+"expect@npm:30.0.5":
+ version: 30.0.5
+ resolution: "expect@npm:30.0.5"
+ dependencies:
+ "@jest/expect-utils": 30.0.5
+ "@jest/get-type": 30.0.1
+ jest-matcher-utils: 30.0.5
+ jest-message-util: 30.0.5
+ jest-mock: 30.0.5
+ jest-util: 30.0.5
+ checksum: 018b31125fd082f2c1d99d2f41bf77a510a62cabb7df023c5d30af2c20bdb35f0cb9598684fe28421f6ef4ddf01a6922b278490423e4c2983b531cb862d7859c
+ languageName: node
+ linkType: hard
+
+"expect@npm:^30.0.0":
version: 30.0.4
resolution: "expect@npm:30.0.4"
dependencies:
@@ -8276,58 +8323,58 @@ __metadata:
languageName: node
linkType: hard
-"jest-changed-files@npm:30.0.2":
- version: 30.0.2
- resolution: "jest-changed-files@npm:30.0.2"
+"jest-changed-files@npm:30.0.5":
+ version: 30.0.5
+ resolution: "jest-changed-files@npm:30.0.5"
dependencies:
execa: ^5.1.1
- jest-util: 30.0.2
+ jest-util: 30.0.5
p-limit: ^3.1.0
- checksum: 58a8e0101b11e074a2dd4eff2cdc7da9b6f620465d18f60a820ad19fade2fde23df2bda82d676692d88cf15a23ef6896ede5d55d115dd49bf22d4c755fec6b77
+ checksum: b535cc7fa9e65205e114ee083373af8c86304ec50e28ec6c285abd025a15a5deaebe0aa1fcdc1b7ed7c162adf2c4029312fa2beeb64f716bb11bff988fdc9cba
languageName: node
linkType: hard
-"jest-circus@npm:30.0.4":
- version: 30.0.4
- resolution: "jest-circus@npm:30.0.4"
+"jest-circus@npm:30.0.5":
+ version: 30.0.5
+ resolution: "jest-circus@npm:30.0.5"
dependencies:
- "@jest/environment": 30.0.4
- "@jest/expect": 30.0.4
- "@jest/test-result": 30.0.4
- "@jest/types": 30.0.1
+ "@jest/environment": 30.0.5
+ "@jest/expect": 30.0.5
+ "@jest/test-result": 30.0.5
+ "@jest/types": 30.0.5
"@types/node": "*"
chalk: ^4.1.2
co: ^4.6.0
dedent: ^1.6.0
is-generator-fn: ^2.1.0
- jest-each: 30.0.2
- jest-matcher-utils: 30.0.4
- jest-message-util: 30.0.2
- jest-runtime: 30.0.4
- jest-snapshot: 30.0.4
- jest-util: 30.0.2
+ jest-each: 30.0.5
+ jest-matcher-utils: 30.0.5
+ jest-message-util: 30.0.5
+ jest-runtime: 30.0.5
+ jest-snapshot: 30.0.5
+ jest-util: 30.0.5
p-limit: ^3.1.0
- pretty-format: 30.0.2
+ pretty-format: 30.0.5
pure-rand: ^7.0.0
slash: ^3.0.0
stack-utils: ^2.0.6
- checksum: 984383db3942499521905f029b4e1b5f6fd6181a1ef7f746ed4554b5dd7a505f790d61245ad6aaeb6209519bea54c693693d7979f51a0c47bf2c993f39b46c88
+ checksum: 049a3a0902aef9b638ec22b19ab9de17924316a537751e0528f8bb76c6aa21386a865668437f3a1745fc5369871bc634f8385acd1a06b173305817bf9d099b92
languageName: node
linkType: hard
-"jest-cli@npm:30.0.4":
- version: 30.0.4
- resolution: "jest-cli@npm:30.0.4"
+"jest-cli@npm:30.0.5":
+ version: 30.0.5
+ resolution: "jest-cli@npm:30.0.5"
dependencies:
- "@jest/core": 30.0.4
- "@jest/test-result": 30.0.4
- "@jest/types": 30.0.1
+ "@jest/core": 30.0.5
+ "@jest/test-result": 30.0.5
+ "@jest/types": 30.0.5
chalk: ^4.1.2
exit-x: ^0.2.2
import-local: ^3.2.0
- jest-config: 30.0.4
- jest-util: 30.0.2
- jest-validate: 30.0.2
+ jest-config: 30.0.5
+ jest-util: 30.0.5
+ jest-validate: 30.0.5
yargs: ^17.7.2
peerDependencies:
node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
@@ -8336,36 +8383,36 @@ __metadata:
optional: true
bin:
jest: ./bin/jest.js
- checksum: d1b1e97163927f52c3e3ebcef4ace2d808e7a2a57b721941e767cc2b335c9d3abb0b648922c5c6c0008e8571054fb2524a97a5129b8cf2099e6cfa2af5ea8933
+ checksum: 89789180aa7a3616a0b3685634de6683441b3826cfe918ff2e237935de2363850f61b3e4238fa5b1699653a3cb20dda1c002a4a1560d052d922430e90881265b
languageName: node
linkType: hard
-"jest-config@npm:30.0.4":
- version: 30.0.4
- resolution: "jest-config@npm:30.0.4"
+"jest-config@npm:30.0.5":
+ version: 30.0.5
+ resolution: "jest-config@npm:30.0.5"
dependencies:
"@babel/core": ^7.27.4
"@jest/get-type": 30.0.1
"@jest/pattern": 30.0.1
- "@jest/test-sequencer": 30.0.4
- "@jest/types": 30.0.1
- babel-jest: 30.0.4
+ "@jest/test-sequencer": 30.0.5
+ "@jest/types": 30.0.5
+ babel-jest: 30.0.5
chalk: ^4.1.2
ci-info: ^4.2.0
deepmerge: ^4.3.1
glob: ^10.3.10
graceful-fs: ^4.2.11
- jest-circus: 30.0.4
+ jest-circus: 30.0.5
jest-docblock: 30.0.1
- jest-environment-node: 30.0.4
+ jest-environment-node: 30.0.5
jest-regex-util: 30.0.1
- jest-resolve: 30.0.2
- jest-runner: 30.0.4
- jest-util: 30.0.2
- jest-validate: 30.0.2
+ jest-resolve: 30.0.5
+ jest-runner: 30.0.5
+ jest-util: 30.0.5
+ jest-validate: 30.0.5
micromatch: ^4.0.8
parse-json: ^5.2.0
- pretty-format: 30.0.2
+ pretty-format: 30.0.5
slash: ^3.0.0
strip-json-comments: ^3.1.1
peerDependencies:
@@ -8379,7 +8426,7 @@ __metadata:
optional: true
ts-node:
optional: true
- checksum: c3b5710a1cea2694146da79cc7228ffa9703767fe80cd5147ff43425eb84f3a8bef8f08374dd4f7414ebce19b3912820f3d0b17db5e439774c10518e467e648a
+ checksum: d6d447f4612c5b006e2dc1dd1e7921f315d35b15c37cb4960bbf8c25a3b5c314ce2c0f00f5a008109b72f80cd7a973794292514e765b4340482c5ed32dd97881
languageName: node
linkType: hard
@@ -8395,6 +8442,18 @@ __metadata:
languageName: node
linkType: hard
+"jest-diff@npm:30.0.5":
+ version: 30.0.5
+ resolution: "jest-diff@npm:30.0.5"
+ dependencies:
+ "@jest/diff-sequences": 30.0.1
+ "@jest/get-type": 30.0.1
+ chalk: ^4.1.2
+ pretty-format: 30.0.5
+ checksum: 799160780cc3ad18001eed355099679519135ecdbec261c195e1409331eee27812ecf8937247cb3c67d8d81373e711f72d95e7718003ffe11b740e1214eb7a18
+ languageName: node
+ linkType: hard
+
"jest-docblock@npm:30.0.1":
version: 30.0.1
resolution: "jest-docblock@npm:30.0.1"
@@ -8404,31 +8463,31 @@ __metadata:
languageName: node
linkType: hard
-"jest-each@npm:30.0.2":
- version: 30.0.2
- resolution: "jest-each@npm:30.0.2"
+"jest-each@npm:30.0.5":
+ version: 30.0.5
+ resolution: "jest-each@npm:30.0.5"
dependencies:
"@jest/get-type": 30.0.1
- "@jest/types": 30.0.1
+ "@jest/types": 30.0.5
chalk: ^4.1.2
- jest-util: 30.0.2
- pretty-format: 30.0.2
- checksum: 76765cd4a0bbc3bfa5e08c5300b7f0211f3e99f377bc53e5e9222450945e30a53e87638212c96ace2b6d0fa293b05dd297dc42e5716e4e4d9785d6a1f3b8f9d2
+ jest-util: 30.0.5
+ pretty-format: 30.0.5
+ checksum: 3774a3d218dc86b2caff306bf36a2201e9b058c6bda0a3ed22d318b6bde0816c1f75f4727226e61c74188dcc34f50e4dd21ffc303e91225f9b57756a2a13110f
languageName: node
linkType: hard
-"jest-environment-node@npm:30.0.4":
- version: 30.0.4
- resolution: "jest-environment-node@npm:30.0.4"
+"jest-environment-node@npm:30.0.5":
+ version: 30.0.5
+ resolution: "jest-environment-node@npm:30.0.5"
dependencies:
- "@jest/environment": 30.0.4
- "@jest/fake-timers": 30.0.4
- "@jest/types": 30.0.1
+ "@jest/environment": 30.0.5
+ "@jest/fake-timers": 30.0.5
+ "@jest/types": 30.0.5
"@types/node": "*"
- jest-mock: 30.0.2
- jest-util: 30.0.2
- jest-validate: 30.0.2
- checksum: 69eba1b9b6659a340eb3466ecb3256021bda5c67c371d3fa5ec62ffcfbf16bb07a0ad2fe885646aea44ab5be02b13e9467cee1da815f90146df05810956249b7
+ jest-mock: 30.0.5
+ jest-util: 30.0.5
+ jest-validate: 30.0.5
+ checksum: ad721c07780438c3bdf3c6f4361141ae868d90f59781dd187a912a14851d0084e027f48dd9f06fc1f6c10b7dfeff1b6d95d479ba70fa04b2014ecbac2d6660a3
languageName: node
linkType: hard
@@ -8453,25 +8512,25 @@ __metadata:
languageName: node
linkType: hard
-"jest-haste-map@npm:30.0.2":
- version: 30.0.2
- resolution: "jest-haste-map@npm:30.0.2"
+"jest-haste-map@npm:30.0.5":
+ version: 30.0.5
+ resolution: "jest-haste-map@npm:30.0.5"
dependencies:
- "@jest/types": 30.0.1
+ "@jest/types": 30.0.5
"@types/node": "*"
anymatch: ^3.1.3
fb-watchman: ^2.0.2
fsevents: ^2.3.3
graceful-fs: ^4.2.11
jest-regex-util: 30.0.1
- jest-util: 30.0.2
- jest-worker: 30.0.2
+ jest-util: 30.0.5
+ jest-worker: 30.0.5
micromatch: ^4.0.8
walker: ^1.0.8
dependenciesMeta:
fsevents:
optional: true
- checksum: daf0056035b8636463cd18abba4e2ae0190bd721e48ff71722ded4cbfdf19215c776b8a66f7f4a599ee103be6555030481a2c0997f3a95eb52294220793b12a3
+ checksum: 21137a000cee32c87965095777f3ef77abfb33fbc2699a7861597cb2e018c4d52038f499f2aa3c19497b005453d56f019575b7faa9f205032faa01f1fc51610f
languageName: node
linkType: hard
@@ -8498,13 +8557,13 @@ __metadata:
languageName: node
linkType: hard
-"jest-leak-detector@npm:30.0.2":
- version: 30.0.2
- resolution: "jest-leak-detector@npm:30.0.2"
+"jest-leak-detector@npm:30.0.5":
+ version: 30.0.5
+ resolution: "jest-leak-detector@npm:30.0.5"
dependencies:
"@jest/get-type": 30.0.1
- pretty-format: 30.0.2
- checksum: bb570d6aeb5187efa0a929d58104819e725ac7dbe4b57d0b9aa8a4ed456c75be64cf13ab28ced59f13a383a24ac87dcfa2867b4fcb2648f784bd2138e5756511
+ pretty-format: 30.0.5
+ checksum: 60ba8c0afb0a20c0cdd8665469aba7f6663d2e94b01db18174db4986b1f50c0f74e979fa1e70ab78c9215ec8e48e6f43de6b0cdd3b3546c53f47b5ea92e343f0
languageName: node
linkType: hard
@@ -8520,6 +8579,18 @@ __metadata:
languageName: node
linkType: hard
+"jest-matcher-utils@npm:30.0.5":
+ version: 30.0.5
+ resolution: "jest-matcher-utils@npm:30.0.5"
+ dependencies:
+ "@jest/get-type": 30.0.1
+ chalk: ^4.1.2
+ jest-diff: 30.0.5
+ pretty-format: 30.0.5
+ checksum: 46e05c7c94b00068627a906bb8627c7061fb88d9abdc8d43110a9b62d6531ddc4f0a16e2ac798255634ce85a03ccae318b08e9f376bc49d18ecee64aee9fab50
+ languageName: node
+ linkType: hard
+
"jest-message-util@npm:30.0.2":
version: 30.0.2
resolution: "jest-message-util@npm:30.0.2"
@@ -8537,6 +8608,23 @@ __metadata:
languageName: node
linkType: hard
+"jest-message-util@npm:30.0.5":
+ version: 30.0.5
+ resolution: "jest-message-util@npm:30.0.5"
+ dependencies:
+ "@babel/code-frame": ^7.27.1
+ "@jest/types": 30.0.5
+ "@types/stack-utils": ^2.0.3
+ chalk: ^4.1.2
+ graceful-fs: ^4.2.11
+ micromatch: ^4.0.8
+ pretty-format: 30.0.5
+ slash: ^3.0.0
+ stack-utils: ^2.0.6
+ checksum: 3acd0a99cbec60d1e37de884e0f3fb9e2126e6c10226d27f1247c1bdd83c40e15c9bb183a61609f136d03058d4aa758101dd1fbd42f2409626fbfe207672a5c5
+ languageName: node
+ linkType: hard
+
"jest-message-util@npm:^29.7.0":
version: 29.7.0
resolution: "jest-message-util@npm:29.7.0"
@@ -8565,6 +8653,17 @@ __metadata:
languageName: node
linkType: hard
+"jest-mock@npm:30.0.5":
+ version: 30.0.5
+ resolution: "jest-mock@npm:30.0.5"
+ dependencies:
+ "@jest/types": 30.0.5
+ "@types/node": "*"
+ jest-util: 30.0.5
+ checksum: 144077119e76dd28c2197169dc2bd6ec4c6980a50f32d9e24c79a6adf74e0d3b8bac72c02f6effc5aa27f520d3af7be12b3a06372d5296047f5e7b60fd26814b
+ languageName: node
+ linkType: hard
+
"jest-mock@npm:^29.7.0":
version: 29.7.0
resolution: "jest-mock@npm:29.7.0"
@@ -8602,118 +8701,118 @@ __metadata:
languageName: node
linkType: hard
-"jest-resolve-dependencies@npm:30.0.4":
- version: 30.0.4
- resolution: "jest-resolve-dependencies@npm:30.0.4"
+"jest-resolve-dependencies@npm:30.0.5":
+ version: 30.0.5
+ resolution: "jest-resolve-dependencies@npm:30.0.5"
dependencies:
jest-regex-util: 30.0.1
- jest-snapshot: 30.0.4
- checksum: 264c6796e7e3bc2a3e25b77b0dfe682334e942ef5af094d0b9228d79b3f7f82b9138f89c6367f8c55daee64b84fd3b436db577467d21afcb53473fdf89de1c69
+ jest-snapshot: 30.0.5
+ checksum: 89530cf8f58a3aed2ad0c43c151f2d7b4c852c60c1edff30f48e52430002864640ac522d3979f1b5dfa3a3af53486c3ffdb5fee4019a69028141237c464994ee
languageName: node
linkType: hard
-"jest-resolve@npm:30.0.2":
- version: 30.0.2
- resolution: "jest-resolve@npm:30.0.2"
+"jest-resolve@npm:30.0.5":
+ version: 30.0.5
+ resolution: "jest-resolve@npm:30.0.5"
dependencies:
chalk: ^4.1.2
graceful-fs: ^4.2.11
- jest-haste-map: 30.0.2
+ jest-haste-map: 30.0.5
jest-pnp-resolver: ^1.2.3
- jest-util: 30.0.2
- jest-validate: 30.0.2
+ jest-util: 30.0.5
+ jest-validate: 30.0.5
slash: ^3.0.0
unrs-resolver: ^1.7.11
- checksum: a79ccaa00925f0d5e59374d0d1dca9c5b96b91e9d0357ae6628a62fe4977ca50d260e914e50d4d9b25cd02588b887d26e5670a337d69efdbc64f05dee71f167b
+ checksum: 32c7f2a7e0c734cd5cbbe47a551eeb43980e4d9c9c85e68e3000c9aec689dbd4bbdacaa547c1e6c5071c57a8f0f827d12809c149f6c509eb529a275ba85cd37a
languageName: node
linkType: hard
-"jest-runner@npm:30.0.4":
- version: 30.0.4
- resolution: "jest-runner@npm:30.0.4"
+"jest-runner@npm:30.0.5":
+ version: 30.0.5
+ resolution: "jest-runner@npm:30.0.5"
dependencies:
- "@jest/console": 30.0.4
- "@jest/environment": 30.0.4
- "@jest/test-result": 30.0.4
- "@jest/transform": 30.0.4
- "@jest/types": 30.0.1
+ "@jest/console": 30.0.5
+ "@jest/environment": 30.0.5
+ "@jest/test-result": 30.0.5
+ "@jest/transform": 30.0.5
+ "@jest/types": 30.0.5
"@types/node": "*"
chalk: ^4.1.2
emittery: ^0.13.1
exit-x: ^0.2.2
graceful-fs: ^4.2.11
jest-docblock: 30.0.1
- jest-environment-node: 30.0.4
- jest-haste-map: 30.0.2
- jest-leak-detector: 30.0.2
- jest-message-util: 30.0.2
- jest-resolve: 30.0.2
- jest-runtime: 30.0.4
- jest-util: 30.0.2
- jest-watcher: 30.0.4
- jest-worker: 30.0.2
+ jest-environment-node: 30.0.5
+ jest-haste-map: 30.0.5
+ jest-leak-detector: 30.0.5
+ jest-message-util: 30.0.5
+ jest-resolve: 30.0.5
+ jest-runtime: 30.0.5
+ jest-util: 30.0.5
+ jest-watcher: 30.0.5
+ jest-worker: 30.0.5
p-limit: ^3.1.0
source-map-support: 0.5.13
- checksum: 529ec005bb74211f91af2eb7233f1d9da7b5a837d4c51e7d15f2512e906bbc68685365600b0f17063d85af7e77bc5213f050243b082b5f0f9be46d39f8a890d8
+ checksum: a65d8b02d7d870059235dbcd221e69cf45d3ae83272b7253dd6fd2a032bcfdb7f3bea968d64800fd52d630a5f6baf58abd4032f1a526bb87262b1704e9795b67
languageName: node
linkType: hard
-"jest-runtime@npm:30.0.4":
- version: 30.0.4
- resolution: "jest-runtime@npm:30.0.4"
+"jest-runtime@npm:30.0.5":
+ version: 30.0.5
+ resolution: "jest-runtime@npm:30.0.5"
dependencies:
- "@jest/environment": 30.0.4
- "@jest/fake-timers": 30.0.4
- "@jest/globals": 30.0.4
+ "@jest/environment": 30.0.5
+ "@jest/fake-timers": 30.0.5
+ "@jest/globals": 30.0.5
"@jest/source-map": 30.0.1
- "@jest/test-result": 30.0.4
- "@jest/transform": 30.0.4
- "@jest/types": 30.0.1
+ "@jest/test-result": 30.0.5
+ "@jest/transform": 30.0.5
+ "@jest/types": 30.0.5
"@types/node": "*"
chalk: ^4.1.2
cjs-module-lexer: ^2.1.0
collect-v8-coverage: ^1.0.2
glob: ^10.3.10
graceful-fs: ^4.2.11
- jest-haste-map: 30.0.2
- jest-message-util: 30.0.2
- jest-mock: 30.0.2
+ jest-haste-map: 30.0.5
+ jest-message-util: 30.0.5
+ jest-mock: 30.0.5
jest-regex-util: 30.0.1
- jest-resolve: 30.0.2
- jest-snapshot: 30.0.4
- jest-util: 30.0.2
+ jest-resolve: 30.0.5
+ jest-snapshot: 30.0.5
+ jest-util: 30.0.5
slash: ^3.0.0
strip-bom: ^4.0.0
- checksum: 9a8d66014871f1fb3f7f86cb309d2d9ae7f0917d01e2fefbd5c9740e2a43d2e838f14864f3792cc6a52d9bc1c1348df926168a765427ba8a72e20da7d293154b
+ checksum: f948fa6778f40b40804493555590f10beda7b51ee5b0394cc2c52c8b5e9bb515132fa220f55f1261d9fe996b5dfa7be815cc97983ce574c693f6769a067134a2
languageName: node
linkType: hard
-"jest-snapshot@npm:30.0.4":
- version: 30.0.4
- resolution: "jest-snapshot@npm:30.0.4"
+"jest-snapshot@npm:30.0.5":
+ version: 30.0.5
+ resolution: "jest-snapshot@npm:30.0.5"
dependencies:
"@babel/core": ^7.27.4
"@babel/generator": ^7.27.5
"@babel/plugin-syntax-jsx": ^7.27.1
"@babel/plugin-syntax-typescript": ^7.27.1
"@babel/types": ^7.27.3
- "@jest/expect-utils": 30.0.4
+ "@jest/expect-utils": 30.0.5
"@jest/get-type": 30.0.1
- "@jest/snapshot-utils": 30.0.4
- "@jest/transform": 30.0.4
- "@jest/types": 30.0.1
+ "@jest/snapshot-utils": 30.0.5
+ "@jest/transform": 30.0.5
+ "@jest/types": 30.0.5
babel-preset-current-node-syntax: ^1.1.0
chalk: ^4.1.2
- expect: 30.0.4
+ expect: 30.0.5
graceful-fs: ^4.2.11
- jest-diff: 30.0.4
- jest-matcher-utils: 30.0.4
- jest-message-util: 30.0.2
- jest-util: 30.0.2
- pretty-format: 30.0.2
+ jest-diff: 30.0.5
+ jest-matcher-utils: 30.0.5
+ jest-message-util: 30.0.5
+ jest-util: 30.0.5
+ pretty-format: 30.0.5
semver: ^7.7.2
synckit: ^0.11.8
- checksum: 0192c17d54cc9e25dbd4a240ee19e7b9014f9d14390be997ab50a9e67b6cd85e39817ffd5d4a43eebd943a44bf52df026b8746495c82780a82d27728afbb54ef
+ checksum: f1ffda5704f33049887779b91a50e03546b99eca83b429aea396467f1a118656ceb71c2a915d4316d2ff10e4c0ba1cffc1fc3313c7224e2e7ba1d37ef6164f56
languageName: node
linkType: hard
@@ -8731,6 +8830,20 @@ __metadata:
languageName: node
linkType: hard
+"jest-util@npm:30.0.5":
+ version: 30.0.5
+ resolution: "jest-util@npm:30.0.5"
+ dependencies:
+ "@jest/types": 30.0.5
+ "@types/node": "*"
+ chalk: ^4.1.2
+ ci-info: ^4.2.0
+ graceful-fs: ^4.2.11
+ picomatch: ^4.0.2
+ checksum: 16e059b849e8ac9a6eb0a62db18aa88cb8e9566d26fe7a4f2da1d166b322b937a4d4ee2e4881764cc270d3947d1734d319d444df75fb6964dbe2b99081f4e00a
+ languageName: node
+ linkType: hard
+
"jest-util@npm:^29.7.0":
version: 29.7.0
resolution: "jest-util@npm:29.7.0"
@@ -8745,17 +8858,17 @@ __metadata:
languageName: node
linkType: hard
-"jest-validate@npm:30.0.2":
- version: 30.0.2
- resolution: "jest-validate@npm:30.0.2"
+"jest-validate@npm:30.0.5":
+ version: 30.0.5
+ resolution: "jest-validate@npm:30.0.5"
dependencies:
"@jest/get-type": 30.0.1
- "@jest/types": 30.0.1
+ "@jest/types": 30.0.5
camelcase: ^6.3.0
chalk: ^4.1.2
leven: ^3.1.0
- pretty-format: 30.0.2
- checksum: ff5e2c0204158c1e4a0b133e0455eb2c0108ef1667676546164d931700926a1d7cdb7e538d44874bc3dab872f0c3b94e4cd7c0a510a060be68a6122fd998cf38
+ pretty-format: 30.0.5
+ checksum: b4fbf7281ddb27ade5688b8d52c5280c0107d7e8dba6430c1227cfcb808c09ff53a9316889c7bae89efb4982ea018c5b0a19988b931bbcc4411cd25138df83d7
languageName: node
linkType: hard
@@ -8773,32 +8886,32 @@ __metadata:
languageName: node
linkType: hard
-"jest-watcher@npm:30.0.4":
- version: 30.0.4
- resolution: "jest-watcher@npm:30.0.4"
+"jest-watcher@npm:30.0.5":
+ version: 30.0.5
+ resolution: "jest-watcher@npm:30.0.5"
dependencies:
- "@jest/test-result": 30.0.4
- "@jest/types": 30.0.1
+ "@jest/test-result": 30.0.5
+ "@jest/types": 30.0.5
"@types/node": "*"
ansi-escapes: ^4.3.2
chalk: ^4.1.2
emittery: ^0.13.1
- jest-util: 30.0.2
+ jest-util: 30.0.5
string-length: ^4.0.2
- checksum: 9eb5337174bf8a26c242a6128b43d7a8ee68cadfd3d5228083ac1c17acb2802db4afd5678228bdc53c7ae1549ccaeba15d65ed4fc21b5fe710d4104373a8c30e
+ checksum: 1f12d20a7d4d4e0734c78d31f93dde5f515297baf3513c72cb2ed0e1317906caa96557a620131a0bdc94f00e0fe554b552c8dc6d4b5812790d14417982c747e4
languageName: node
linkType: hard
-"jest-worker@npm:30.0.2":
- version: 30.0.2
- resolution: "jest-worker@npm:30.0.2"
+"jest-worker@npm:30.0.5":
+ version: 30.0.5
+ resolution: "jest-worker@npm:30.0.5"
dependencies:
"@types/node": "*"
"@ungap/structured-clone": ^1.3.0
- jest-util: 30.0.2
+ jest-util: 30.0.5
merge-stream: ^2.0.0
supports-color: ^8.1.1
- checksum: 5f6394b214d7b37077e40ce1214789769599c9c956ecc6c4af4a54d6591c999d6221321962ced68eb3b9f58327f1282305267d9672461f71d16a6c05dd2ae582
+ checksum: 5f76fb8941120d811f4830f278cf99c5fc50110767310a3ca9bf19f27db214d9b80bdf0cdec93e177c5f1e6166e298f9127a13975febeedcb6061536ae182e1f
languageName: node
linkType: hard
@@ -8814,14 +8927,14 @@ __metadata:
languageName: node
linkType: hard
-"jest@npm:^30.0.4":
- version: 30.0.4
- resolution: "jest@npm:30.0.4"
+"jest@npm:^30.0.5":
+ version: 30.0.5
+ resolution: "jest@npm:30.0.5"
dependencies:
- "@jest/core": 30.0.4
- "@jest/types": 30.0.1
+ "@jest/core": 30.0.5
+ "@jest/types": 30.0.5
import-local: ^3.2.0
- jest-cli: 30.0.4
+ jest-cli: 30.0.5
peerDependencies:
node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
peerDependenciesMeta:
@@ -8829,7 +8942,7 @@ __metadata:
optional: true
bin:
jest: ./bin/jest.js
- checksum: 23e75c16fe74b6cb1ed39473c8261e838121e21acd1daa2bdc15fb11c1f7510f9224ec2be2a1b0cada50168592db36391b956aa1afc168ec3be8e5b4e2653edf
+ checksum: 4f703c4a2d9c92e480ccb97c9bff16172443e0affb624b95c58a6db6cc27c21e69b91f322d812c2588c8a0f7f7aeaff4e3e84cb1a8c8681520b8c86f911dcae2
languageName: node
linkType: hard
@@ -10650,6 +10763,17 @@ __metadata:
languageName: node
linkType: hard
+"pretty-format@npm:30.0.5":
+ version: 30.0.5
+ resolution: "pretty-format@npm:30.0.5"
+ dependencies:
+ "@jest/schemas": 30.0.5
+ ansi-styles: ^5.2.0
+ react-is: ^18.3.1
+ checksum: 0772b7432ff4083483dc12b5b9a1904a1a8f2654936af2a5fa3ba5dfa994a4c7ef843f132152894fd96203a09e0ef80dab2e99dabebd510da86948ed91238fed
+ languageName: node
+ linkType: hard
+
"pretty-format@npm:^26.6.2":
version: 26.6.2
resolution: "pretty-format@npm:26.6.2"
@@ -10952,7 +11076,7 @@ __metadata:
eslint: ^9.31.0
eslint-config-prettier: ^10.1.8
eslint-plugin-prettier: ^5.5.3
- jest: ^30.0.4
+ jest: ^30.0.5
nitro-codegen: ^0.26.4
prettier: ^3.6.2
react: 19.1.0
@@ -10961,7 +11085,7 @@ __metadata:
react-native-nitro-modules: ^0.26.4
release-it: ^19.0.4
turbo: ^2.5.5
- typescript: ^5.5.4
+ typescript: ^5.8.3
peerDependencies:
react: "*"
react-native: "*"
@@ -12479,7 +12603,7 @@ __metadata:
languageName: node
linkType: hard
-"typescript@npm:^5.5.4":
+"typescript@npm:^5.8.3":
version: 5.8.3
resolution: "typescript@npm:5.8.3"
bin:
@@ -12489,7 +12613,7 @@ __metadata:
languageName: node
linkType: hard
-"typescript@patch:typescript@^5.5.4#~builtin":
+"typescript@patch:typescript@^5.8.3#~builtin":
version: 5.8.3
resolution: "typescript@patch:typescript@npm%3A5.8.3#~builtin::version=5.8.3&hash=14eedb"
bin: