Paso a paso: cómo integrar el SDK de ChequeUI en su aplicación móvil
Una guía completa para desarrolladores de iOS y Android que implementan la captura de cheques y las capacidades OCR
1. Introducción
El SDK móvil de ChequeUI permite a los desarrolladores integrar potentes capacidades de procesamiento de cheques directamente en sus aplicaciones móviles. Ya sea que esté creando una aplicación bancaria, una solución de procesamiento de pagos o una herramienta de contabilidad, el SDK proporciona todo lo que necesita para capturar, validar y extraer datos de cheques con precisión de nivel empresarial.
Resumen de capacidades del SDK
El SDK de ChequeUI ofrece un conjunto completo de funciones de procesamiento de cheques:
- Captura inteligente: Detección automática y captura de bordes del cheque con detección de bordes en tiempo real
- Extracción de datos OCR: reconocimiento basado en IA de todos los campos del cheque, incluidos detalles bancarios, información del beneficiario, fechas y montos.
- Verificación de montos: validación cruzada entre montos escritos y numéricos para detectar discrepancias
- Detección de firma: verificación de presencia de firma basada en visión por computadora
- Procesamiento por lotes: maneje múltiples cheques de manera eficiente para operaciones masivas
- Soporte sin conexión: Procese cheques sin conectividad de red con sincronización automática
- Diseño que prioriza la seguridad: cifrado de extremo a extremo y manejo seguro de tokens
- UI personalizable: tema la interfaz de captura para que coincida con la identidad de su marca
¿Por qué elegir SDK de ChequeUI?
A diferencia de las soluciones genéricas OCR, ChequeUI está diseñada específicamente para documentos financieros:
| Característica | Genérico OCR | SDK de ChequeUI |
|---|---|---|
| Precisión de campo | 70-80% | 95%+ |
| Validación de importe | manuales | Automático |
| Detección de bordes | Básico | Avanzado con corrección de perspectiva |
| Reconocimiento Bancario | Ninguno | Base de datos integrada de más de 10.000 bancos |
| Cumplimiento | Autogestionado | SOC 2, PCI DSS listo |
| Tiempo de integración | 2-4 semanas | 2-3 días |
2. Requisitos previos
Antes de integrar SDK de ChequeUI, asegúrese de tener implementados los siguientes requisitos previos.
Requisitos del sistema
iOS
- Versión mínima: iOS 13.0+
- Xcode: Versión 14.0 o posterior
- Swift: Versión 5.5 o posterior
- Capacidades del dispositivo: Cámara con enfoque automático, se recomienda un mínimo de 8 MP
androide
- Nivel mínimo de API: API 24 (Android 7.0)
- Nivel API objetivo: se recomienda API 34 (Android 14)
- Kotlin: Versión 1.8.0 o posterior
- Android Studio: Hedgehog (2023.1.1) o posterior
- Capacidades del dispositivo: compatibilidad con Camera2 API, se recomienda un mínimo de 8 MP
Credenciales de API
Para utilizar el SDK de ChequeUI, necesita credenciales de API válidas:
- Regístrese en https://chequedb.com/developer
- Crea un nuevo proyecto en el panel de desarrollador
- Genere claves API para sus plataformas de destino
- Configurar ID de paquetes permitidos (iOS) y nombres de paquetes (Android)
Sus credenciales incluirán:
CHEQUEUI_API_KEY=your_api_key_here
CHEQUEUI_SECRET_KEY=your_secret_key_here
CHEQUEUI_ENVIRONMENT=sandbox
# or 'production'
Nota de seguridad: Nunca envíe su
SECRET_KEYal control de versiones. Utilice variables de entorno o gestión secreta segura.
Configuración del entorno de desarrollo
Configuración de iOS
- Asegúrese de tener instalado CocoaPods o Swift Package Manager
- Verifique que su perfil de aprovisionamiento incluya permisos de cámara
- Agregue lo siguiente a su
Info.plist:
<key>NSCameraUsageDescription</key>
<string>This app uses the camera to capture cheques for processing.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app accesses photos to upload cheque images.</string>
Configuración de Android
- Asegúrese de que su
AndroidManifest.xmlincluya los permisos necesarios:
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-feature android:name="android.hardware.camera" android:required="true" />
<uses-feature android:name="android.hardware.camera.autofocus" />
- Para Android 6.0+, implemente solicitudes de permiso de tiempo de ejecución para el permiso
CAMERA.
3. Instalación
Instalación de iOS
Usando CocoaPods
Agregue lo siguiente a su Podfile:
platform :ios, '13.0'
use_frameworks!
target 'YourApp' do
pod 'ChequeUI', '~> 2.5.0'
end
Luego ejecuta:
pod install
Usando el Administrador de paquetes Swift
En Xcode, vaya a Archivo → Agregar dependencias del paquete e ingrese:
https://github.com/chequedb/chequeui-ios-sdk.git
Seleccione la versión 2.5.0 o especifique su regla de versión:
// Package.swift dependencies
dependencies: [
.package(url: "https://github.com/chequedb/chequeui-ios-sdk.git", from: "2.5.0")
]
Instalación de Android
Usando Gradle (build.gradle.kts)
Agregue el repositorio y la dependencia ChequeUI:
// settings.gradle.kts
pluginManagement {
repositories {
google()
mavenCentral()
maven { url = uri("https://chequedb.jfrog.io/artifactory/chequeui-gradle") }
}
}
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven { url = uri("https://chequedb.jfrog.io/artifactory/chequeui-gradle") }
}
}
// app/build.gradle.kts
dependencies {
implementation("com.chequedb:chequeui-sdk:2.5.0")
implementation("com.chequedb:chequeui-camera:2.5.0")
implementation("com.chequedb:chequeui-mlkit:2.5.0")
}
Usando Maven
<repositories>
<repository>
<id>chequeui</id>
<url>https://chequedb.jfrog.io/artifactory/chequeui-maven</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.chequedb</groupId>
<artifactId>chequeui-sdk</artifactId>
<version>2.5.0</version>
</dependency>
</dependencies>
Gestión de versiones
Recomendamos utilizar una definición de versión centralizada:
iOS (archivo Pod)
chequeui_version = '2.5.0'
pod 'ChequeUI', "~> #{chequeui_version}"
Android (gradle/libs.versions.toml)
[versions]
chequeui = "2.5.0"
[libraries]
chequeui-sdk = { module = "com.chequedb:chequeui-sdk", version.ref = "chequeui" }
4. Configuración de autenticación
Configuración de clave API
iOS (rápido)
import ChequeUI
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let config = ChequeUIConfiguration(
apiKey: "your_api_key_here",
environment: .sandbox, // or .production
region: .usEast
)
ChequeUI.initialize(with: config)
return true
}
}
Android (Kotlin)
import com.chequedb.chequeui.ChequeUI
import com.chequedb.chequeui.config.ChequeUIConfiguration
class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
val config = ChequeUIConfiguration.Builder()
.apiKey("your_api_key_here")
.environment(Environment.SANDBOX) // or Environment.PRODUCTION
.region(Region.US_EAST)
.build()
ChequeUI.initialize(this, config)
}
}
Actualización de token
El SDK maneja automáticamente la actualización del token. Sin embargo, puede implementar un manejo de token personalizado para casos de uso avanzados:
iOS
ChequeUI.shared.setTokenProvider { completion in
// Your custom token fetching logic
fetchFreshToken { token, error in
completion(token, error)
}
}
androide
ChequeUI.setTokenProvider(object : TokenProvider {
override suspend fun getToken(): String {
// Your custom token fetching logic
return fetchFreshToken()
}
override suspend fun refreshToken(): String {
return refreshAccessToken()
}
})
Mejores prácticas de seguridad
- Separación de entornos: nunca utilice credenciales de producción en desarrollo
- Almacenamiento de claves: use el llavero de iOS o el almacén de claves de Android para valores confidenciales
- Fijación de certificados: habilitar para entornos de producción
- Ofuscación: habilite ProGuard/R8 en Android para proteger los componentes internos de SDK
// iOS - Enable certificate pinning
let config = ChequeUIConfiguration(
apiKey: apiKey,
environment: .production,
certificatePinning: true,
pinnedCertificates: ["sha256/..."]
)
// Android - Enable certificate pinning
val config = ChequeUIConfiguration.Builder()
.apiKey(apiKey)
.environment(Environment.PRODUCTION)
.certificatePinning(true)
.addPinnedCertificate("sha256/...")
.build()
5. Integración básica
SDK Inicialización
Una inicialización adecuada garantiza que el SDK esté listo antes de cualquier operación de verificación:
iOS
import ChequeUI
class ChequeService {
static let shared = ChequeService()
func initializeSDK() {
guard ChequeUI.isInitialized else {
print("SDK not initialized")
return
}
// Configure logging
ChequeUI.shared.logLevel = .debug
// Set default options
ChequeUI.shared.defaultCaptureOptions = CaptureOptions(
autoCapture: true,
guidanceMode: .enhanced,
compressionQuality: 0.9
)
}
}
androide
import com.chequedb.chequeui.ChequeUI
import com.chequedb.chequeui.config.CaptureOptions
class ChequeService(private val context: Context) {
fun initializeSDK() {
if (!ChequeUI.isInitialized()) {
throw IllegalStateException("SDK not initialized")
}
// Configure logging
ChequeUI.setLogLevel(LogLevel.DEBUG)
// Set default options
val defaultOptions = CaptureOptions.Builder()
.autoCapture(true)
.guidanceMode(GuidanceMode.ENHANCED)
.compressionQuality(0.9f)
.build()
ChequeUI.setDefaultCaptureOptions(defaultOptions)
}
}
Opciones de configuración
| Opción | Tipo | Predeterminado | Descripción |
|---|---|---|---|
autoCapture | Booleano | true | Captura automáticamente cuando se detectan bordes |
guidanceMode | Enumeración | .standard | Complejidad de superposición de guía visual |
compressionQuality | Flotador | 0.9 | Compresión JPEG (0,0-1,0) |
maxImageDimension | internacional | 2048 | Ancho/alto máximo en píxeles |
enableFlash | booleano | true | Permitir alternancia de flash en la interfaz de usuario |
captureSound | booleano | true | Reproducir sonido del obturador al capturar |
Configuración de manejo de errores
Configure el manejo de errores globales para detectar problemas de nivel SDK:
iOS
ChequeUI.shared.errorHandler = { error in
switch error {
case .unauthorized:
// Handle authentication failure
self.refreshCredentials()
case .networkError(let underlying):
// Handle connectivity issues
self.showOfflineMode()
case .rateLimitExceeded:
// Implement exponential backoff
self.scheduleRetry()
default:
print("ChequeUI Error: \(error.localizedDescription)")
}
}
androide
ChequeUI.setErrorHandler { error ->
when (error) {
is ChequeUIError.Unauthorized -> refreshCredentials()
is ChequeUIError.NetworkError -> showOfflineMode()
is ChequeUIError.RateLimitExceeded -> scheduleRetry()
else -> Log.e("ChequeUI", "Error: ${error.message}")
}
}
6. Verificar captura
Integración de cámara
El SDK proporciona un controlador de vista/actividad de cámara listo para usar con funciones avanzadas.
iOS
import ChequeUI
import UIKit
class CaptureViewController: UIViewController {
func presentChequeCapture() {
let captureVC = ChequeCaptureViewController()
captureVC.delegate = self
captureVC.captureOptions = CaptureOptions(
autoCapture: true,
guidanceMode: .enhanced,
showFlashToggle: true
)
present(captureVC, animated: true)
}
}
extension CaptureViewController: ChequeCaptureDelegate {
func chequeCapture(
_ controller: ChequeCaptureViewController,
didCapture image: UIImage,
withMetadata metadata: CaptureMetadata
) {
// Handle captured image
print("Capture quality score: \(metadata.qualityScore)")
print("Edge detection confidence: \(metadata.edgeConfidence)")
// Dismiss and process
controller.dismiss(animated: true) {
self.processCapturedCheque(image)
}
}
func chequeCapture(
_ controller: ChequeCaptureViewController,
didFailWithError error: ChequeUIError
) {
controller.dismiss(animated: true)
showError(error)
}
}
androide
import com.chequedb.chequeui.capture.ChequeCaptureActivity
import com.chequedb.chequeui.capture.CaptureContract
class MainActivity : AppCompatActivity() {
private val captureLauncher = registerForActivityResult(
CaptureContract()
) { result ->
when (result) {
is CaptureResult.Success -> {
val image = result.image
val metadata = result.metadata
Log.d("ChequeUI", "Quality score: ${metadata.qualityScore}")
Log.d("ChequeUI", "Edge confidence: ${metadata.edgeConfidence}")
processCapturedCheque(image)
}
is CaptureResult.Error -> {
showError(result.error)
}
is CaptureResult.Cancelled -> {
Log.d("ChequeUI", "User cancelled capture")
}
}
}
fun launchCapture() {
val options = CaptureOptions.Builder()
.autoCapture(true)
.guidanceMode(GuidanceMode.ENHANCED)
.showFlashToggle(true)
.build()
captureLauncher.launch(options)
}
}
Captura automática frente a manual
El SDK admite modos de captura automática y manual:
// Auto-capture: Triggered when edges are stable and quality is good
let autoOptions = CaptureOptions(autoCapture: true, stabilityThreshold: 0.95)
// Manual capture: User taps shutter button
let manualOptions = CaptureOptions(autoCapture: false)
```Requisitos de captura automática:
- Confianza en la detección de bordes ≥ 85%
- Estabilidad de la imagen durante 1,5 segundos
- Se alcanzó el umbral mínimo de brillo
- No se detecta desenfoque de movimiento
## Validación de la calidad de la imagen
El SDK realiza controles de calidad en tiempo real:
```swift
// Quality thresholds
let options = CaptureOptions(
minQualityScore: 0.75, // Overall image quality
minEdgeConfidence: 0.80, // Edge detection confidence
minBrightness: 0.3, // Relative brightness (0-1)
maxBlurScore: 0.4, // Blur detection (lower is sharper)
requireAllCorners: true // All four corners must be visible
)
Detección de bordes
Detección avanzada de bordes con corrección de perspectiva:
// Configure edge detection sensitivity
val edgeOptions = EdgeDetectionOptions.Builder()
.sensitivity(Sensitivity.HIGH)
.perspectiveCorrection(true)
.cornerRefinement(true)
.build()
val captureOptions = CaptureOptions.Builder()
.edgeDetection(edgeOptions)
.build()
7. Extracción de datos
Procesamiento síncrono versus asíncrono
Elija el modo de procesamiento según su caso de uso:
Sincrónico (en tiempo real)
// iOS - Synchronous extraction
do {
let result = try await ChequeUI.shared.extract(
from: image,
options: ExtractionOptions(syncMode: true)
)
updateUI(with: result)
} catch {
handleExtractionError(error)
}
// Android - Synchronous extraction
lifecycleScope.launch {
try {
val result = ChequeUI.extract(
image = image,
options = ExtractionOptions(syncMode = true)
)
updateUI(result)
} catch (e: ExtractionException) {
handleExtractionError(e)
}
}
Asíncrono (por lotes/en segundo plano)
// Submit for background processing
let jobId = try await ChequeUI.shared.submitExtraction(
image: image,
webhookUrl: "https://your-api.com/webhooks/cheque"
)
// Check status later
let status = try await ChequeUI.shared.checkExtractionStatus(jobId: jobId)
Mapeo de campos
Asigne campos extraídos a sus modelos de datos:
struct ChequeData: Codable {
let bankName: String
let branch: String
let date: Date
let payee: String
let amountInWords: String
let amountInNumerals: Decimal
let accountNumber: String?
let chequeNumber: String?
}
// Field mapping configuration
let mapping = FieldMapping()
.map(.bankName, to: \.bankName)
.map(.branch, to: \.branch)
.map(.date, to: \.date, formatter: dateFormatter)
.map(.amountInWords, to: \.amountInWords)
.map(.amountInNumerals, to: \.amountInNumerals)
let result = try await ChequeUI.shared.extract(
from: image,
mapping: mapping
)
Umbrales de confianza
Establezca niveles mínimos de confianza para la extracción de campo:
let extractionOptions = ExtractionOptions(
confidenceThresholds: [
.bankName: 0.85,
.amountInNumerals: 0.95,
.amountInWords: 0.90,
.date: 0.80,
.payee: 0.75
],
rejectOnLowConfidence: true
)
8. Manejo de errores
Errores de red
extension ChequeViewController {
func handleNetworkError(_ error: ChequeUIError) {
switch error {
case .networkError(let urlError):
switch urlError.code {
case .notConnectedToInternet:
enableOfflineMode()
case .timedOut:
showRetryDialog(message: "Connection timed out")
default:
showError("Network error: \(urlError.localizedDescription)")
}
default:
break
}
}
}
Mala calidad de imagen
func handleQualityError(_ error: ChequeUIError) {
switch error {
case .poorImageQuality(let issues):
var messages: [String] = []
if issues.contains(.blurry) {
messages.append("Image is blurry. Hold steady and retake.")
}
if issues.contains(.poorLighting) {
messages.append("Insufficient lighting. Try using flash.")
}
if issues.contains(.edgesNotDetected) {
messages.append("Cannot detect cheque edges. Place on dark background.")
}
if issues.contains(.glareDetected) {
messages.append("Glare detected. Adjust angle to avoid reflections.")
}
showGuidance(messages: messages)
default:
break
}
}
Cheques no válidos
func validateCheque(_ result: ExtractionResult) -> ValidationStatus {
// Check amount matching
guard result.amountsMatch else {
return .invalid(reason: "Written and numeric amounts do not match")
}
// Check date validity
guard result.date <= Date() else {
return .invalid(reason: "Cheque date is in the future")
}
// Check expiry (typically 6 months)
let sixMonthsAgo = Calendar.current.date(byAdding: .month, value: -6, to: Date())!
guard result.date >= sixMonthsAgo else {
return .invalid(reason: "Cheque may be stale-dated")
}
return .valid
}
Reintentar lógica
Implementar un retroceso exponencial para fallas transitorias:
class ChequeProcessor {
private val maxRetries = 3
private val baseDelay = 1000L // 1 second
suspend fun processWithRetry(image: Bitmap): ExtractionResult {
var lastError: Exception? = null
repeat(maxRetries) { attempt ->
try {
return ChequeUI.extract(image)
} catch (e: NetworkException) {
lastError = e
if (attempt < maxRetries - 1) {
val delay = baseDelay * (2.0.pow(attempt)).toLong()
delay(delay)
}
}
}
throw lastError ?: Exception("Extraction failed")
}
}
9. Funciones avanzadas
Procesamiento por lotes
Procese múltiples cheques de manera eficiente:
// iOS Batch Processing
let batchRequest = BatchExtractionRequest(
images: chequeImages,
options: BatchOptions(
parallelProcessing: true,
maxConcurrent: 3,
continueOnError: true
)
)
let batchResult = try await ChequeUI.shared.extractBatch(request: batchRequest)
for (index, result) in batchResult.results.enumerated() {
switch result {
case .success(let extraction):
print("Cheque \(index): Extracted \(extraction.payee)")
case .failure(let error):
print("Cheque \(index): Failed - \(error)")
}
}
// Android Batch Processing
val batchRequest = BatchExtractionRequest(
images = chequeImages,
options = BatchOptions(
parallelProcessing = true,
maxConcurrent = 3,
continueOnError = true
)
)
ChequeUI.extractBatch(batchRequest)
.onEach { progress ->
updateProgress(progress.completed, progress.total)
}
.collect { result ->
when (result) {
is BatchResult.Success -> processExtraction(result.extraction)
is BatchResult.Failure -> logFailedCheque(result.error)
}
}
Modo sin conexión
Habilite el procesamiento sin conexión con sincronización automática:
// Configure offline support
let config = ChequeUIConfiguration(
apiKey: apiKey,
environment: .production,
offlineMode: .enabled(
maxStorageMB: 100,
autoSync: true,
syncOnWifiOnly: false
)
)
// Queue for offline processing
ChequeUI.shared.queueForOffline(image: image, metadata: metadata)
// Listen for sync events
ChequeUI.shared.offlineSyncDelegate = self
extension MyClass: OfflineSyncDelegate {
func offlineSync(didCompleteSync results: [OfflineResult]) {
for result in results {
print("Synced cheque: \(result.chequeId)")
}
}
}
Temas de interfaz de usuario personalizados
Personalice la interfaz de captura para que coincida con su marca:
// iOS Theming
var theme = ChequeUITheme()
theme.primaryColor = UIColor.systemIndigo
theme.accentColor = UIColor.systemOrange
theme.backgroundColor = UIColor.systemBackground
theme.textColor = UIColor.label
theme.cornerRadius = 12.0
theme.font = UIFont.systemFont(ofSize: 16, weight: .medium)
// Overlay customization
theme.overlayStyle = .roundedRectangle(cornerRadius: 20)
theme.guideColor = UIColor.green.withAlphaComponent(0.7)
theme.guideWidth = 3.0
ChequeUI.shared.applyTheme(theme)
// Android Theming
val theme = ChequeUITheme.Builder()
.primaryColor(ContextCompat.getColor(context, R.color.brand_primary))
.accentColor(ContextCompat.getColor(context, R.color.brand_accent))
.backgroundColor(ContextCompat.getColor(context, R.color.background))
.textColor(ContextCompat.getColor(context, R.color.text_primary))
.cornerRadius(12f)
.fontFamily(R.font.brand_font)
.overlayStyle(OverlayStyle.ROUNDED_RECTANGLE)
.guideColor(ContextCompat.getColor(context, R.color.guide_green))
.build()
ChequeUI.applyTheme(theme)
10. Pruebas
Pruebas unitarias
Pruebe su lógica de integración:
// iOS Unit Tests
import XCTest
@testable import ChequeUI
class ChequeServiceTests: XCTestCase {
var chequeService: ChequeService!
var mockChequeUI: MockChequeUI!
override func setUp() {
super.setUp()
mockChequeUI = MockChequeUI()
chequeService = ChequeService(chequeUI: mockChequeUI)
}
func testAmountValidation() async {
// Given
let extraction = ExtractionResult(
amountInWords: "One Thousand Dollars",
amountInNumerals: 1000.00,
amountsMatch: true
)
mockChequeUI.mockResult = extraction
// When
let result = try? await chequeService.processCheque(image: testImage)
// Then
XCTAssertNotNil(result)
XCTAssertEqual(result?.amount, 1000.00)
XCTAssertTrue(result?.isValid ?? false)
}
func testAmountMismatchDetection() async {
// Given
let extraction = ExtractionResult(
amountInWords: "One Thousand Dollars",
amountInNumerals: 100.00,
amountsMatch: false
)
mockChequeUI.mockResult = extraction
// When
let result = try? await chequeService.processCheque(image: testImage)
// Then
XCTAssertNotNil(result)
XCTAssertFalse(result?.isValid ?? true)
}
}
// Android Unit Tests
@RunWith(MockitoJUnitRunner::class)
class ChequeServiceTest {
@Mock
lateinit var mockChequeUI: ChequeUI
lateinit var chequeService: ChequeService
@Before
fun setup() {
chequeService = ChequeService(mockChequeUI)
}
@Test
fun `amount validation succeeds when amounts match`() = runTest {
// Given
val extraction = ExtractionResult(
amountInWords = "One Thousand Dollars",
amountInNumerals = BigDecimal("1000.00"),
amountsMatch = true
)
whenever(mockChequeUI.extract(any(), any())).thenReturn(extraction)
// When
val result = chequeService.processCheque(testImage)
// Then
assertTrue(result.isValid)
assertEquals(BigDecimal("1000.00"), result.amount)
}
}
Pruebas de integración
Pruebe los flujos de un extremo a otro:
// iOS UI Tests
class ChequeCaptureUITests: XCTestCase {
var app: XCUIApplication!
override func setUp() {
super.setUp()
app = XCUIApplication()
app.launchArguments = ["--uitesting", "--mock-camera"]
app.launch()
}
func testCompleteCaptureFlow() {
// Tap capture button
app.buttons["Capture Cheque"].tap()
// Wait for camera
XCTAssertTrue(app.otherElements["ChequeCameraView"].waitForExistence(timeout: 5))
// Trigger mock capture
app.buttons["MockCapture"].tap()
// Verify results screen
XCTAssertTrue(app.staticTexts["Extraction Results"].waitForExistence(timeout: 10))
XCTAssertTrue(app.staticTexts["Bank Name"].exists)
}
}
Respuestas simuladas
// Mock ChequeUI for testing
class MockChequeUI: ChequeUIProtocol {
var mockResult: ExtractionResult?
var mockError: Error?
func extract(from image: UIImage, options: ExtractionOptions?) async throws -> ExtractionResult {
if let error = mockError {
throw error
}
return mockResult ?? ExtractionResult()
}
}
11. Despliegue de producción
Ajuste de rendimiento
Optimice las cargas de trabajo de producción:
// iOS Performance Configuration
let perfConfig = PerformanceConfig(
imageCompression: 0.85, // Balance quality vs size
maxImageDimension: 1920, // Reduce processing time
cacheSizeMB: 50, // Enable response caching
enableImagePreprocessing: true // Auto-enhance before OCR
)
ChequeUI.shared.configurePerformance(perfConfig)
// Android Performance Configuration
val perfConfig = PerformanceConfig.Builder()
.imageCompression(0.85f)
.maxImageDimension(1920)
.cacheSizeMB(50)
.enableImagePreprocessing(true)
.bitmapPoolEnabled(true) // Reuse bitmap allocations
.build()
ChequeUI.configurePerformance(perfConfig)
Monitoreo
Integre con su plataforma de análisis:
// iOS Analytics Integration
ChequeUI.shared.analyticsDelegate = self
extension MyApp: ChequeUIAnalyticsDelegate {
func chequeUI(didCaptureCheque event: CaptureEvent) {
Analytics.track("cheque_captured", properties: [
"quality_score": event.qualityScore,
"processing_time_ms": event.processingTime,
"auto_capture": event.usedAutoCapture
])
}
func chequeUI(didExtractData event: ExtractionEvent) {
Analytics.track("cheque_extracted", properties: [
"confidence_avg": event.averageConfidence,
"fields_extracted": event.fieldCount,
"amounts_matched": event.amountsMatch
])
}
}
Métricas del panel de análisis
Realice un seguimiento de estas métricas clave en producción:
| Métrica | Objetivo | Descripción |
|---|---|---|
| Tasa de éxito de captura | >95% | Porcentaje de capturas exitosas |
| Precisión de extracción | >92% | Precisión OCR a nivel de campo |
| Latencia de extremo a extremo | <3s | Captura a extracción completa |
| Tasa de igualación de importe | >98% | Acuerdo escrito vs numérico |
| SDK Tasa de accidentes | <0,1% | Métrica de estabilidad |
12. Ejemplos de código completos
iOS (Swift): integración completa
import UIKit
import ChequeUI
class ChequeScannerViewController: UIViewController {
// MARK: - Properties
private let chequeService = ChequeService()
private var currentImage: UIImage?
// MARK: - UI Components
private lazy var captureButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Capture Cheque", for: .normal)
button.backgroundColor = .systemIndigo
button.setTitleColor(.white, for: .normal)
button.layer.cornerRadius = 12
button.addTarget(self, action: #selector(captureTapped), for: .touchUpInside)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
private lazy var resultView: ChequeResultView = {
let view = ChequeResultView()
view.isHidden = true
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
initializeSDK()
}
// MARK: - Setup
private func setupUI() {
view.backgroundColor = .systemBackground
title = "Cheque Scanner"
view.addSubview(captureButton)
view.addSubview(resultView)
NSLayoutConstraint.activate([
captureButton.centerXAnchor.constraint(equalTo: view.centerXAnchor),
captureButton.centerYAnchor.constraint(equalTo: view.centerYAnchor),
captureButton.widthAnchor.constraint(equalToConstant: 200),
captureButton.heightAnchor.constraint(equalToConstant: 50),
resultView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
resultView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
resultView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
resultView.bottomAnchor.constraint(equalTo: captureButton.topAnchor, constant: -20)
])
}
private func initializeSDK() {
guard ChequeUI.isInitialized else {
showAlert(message: "SDK not initialized")
return
}
ChequeUI.shared.logLevel = .info
ChequeUI.shared.errorHandler = { [weak self] error in
self?.handleSDKError(error)
}
}
// MARK: - Actions
@objc private func captureTapped() {
presentChequeCapture()
}
private func presentChequeCapture() {
let captureVC = ChequeCaptureViewController()
captureVC.delegate = self
captureVC.captureOptions = CaptureOptions(
autoCapture: true,
guidanceMode: .enhanced,
minQualityScore: 0.75,
minEdgeConfidence: 0.80
)
present(captureVC, animated: true)
}
// MARK: - Processing
private func processCheque(_ image: UIImage) {
showLoading(true)
Task {
do {
let extraction = try await ChequeUI.shared.extract(
from: image,
options: ExtractionOptions(
confidenceThresholds: [
.amountInNumerals: 0.95,
.amountInWords: 0.90
]
)
)
await MainActor.run {
self.showLoading(false)
self.displayResults(extraction)
}
} catch {
await MainActor.run {
self.showLoading(false)
self.handleExtractionError(error)
}
}
}
}
private func displayResults(_ result: ExtractionResult) {
resultView.configure(with: result)
resultView.isHidden = false
// Log analytics
Analytics.track("cheque_processed", properties: [
"bank": result.bankName,
"amount": result.amountInNumerals,
"confidence": result.averageConfidence
])
}
// MARK: - Error Handling
private func handleSDKError(_ error: ChequeUIError) {
switch error {
case .unauthorized:
showAlert(message: "Session expired. Please log in again.")
case .networkError:
showAlert(message: "Network error. Please check your connection.")
case .rateLimitExceeded:
showAlert(message: "Too many requests. Please try again later.")
default:
showAlert(message: error.localizedDescription)
}
}
private func handleExtractionError(_ error: Error) {
if let chequeError = error as? ChequeUIError {
switch chequeError {
case .poorImageQuality(let issues):
let message = issues.map { $0.description }.joined(separator: "\n")
showAlert(message: "Image quality issues:\n\(message)")
default:
showAlert(message: chequeError.localizedDescription)
}
} else {
showAlert(message: "An unexpected error occurred")
}
}
private func showLoading(_ loading: Bool) {
captureButton.isEnabled = !loading
captureButton.setTitle(loading ? "Processing..." : "Capture Cheque", for: .normal)
}
private func showAlert(message: String) {
let alert = UIAlertController(title: "Notice", message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default))
present(alert, animated: true)
}
}
// MARK: - ChequeCaptureDelegate
extension ChequeScannerViewController: ChequeCaptureDelegate {
func chequeCapture(
_ controller: ChequeCaptureViewController,
didCapture image: UIImage,
withMetadata metadata: CaptureMetadata
) {
controller.dismiss(animated: true) { [weak self] in
self?.currentImage = image
self?.processCheque(image)
}
}
func chequeCapture(
_ controller: ChequeCaptureViewController,
didFailWithError error: ChequeUIError
) {
controller.dismiss(animated: true) { [weak self] in
self?.handleSDKError(error)
}
}
}
Android (Kotlin): integración completa
package com.example.chequeapp
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import com.chequedb.chequeui.ChequeUI
import com.chequedb.chequeui.capture.CaptureContract
import com.chequedb.chequeui.capture.CaptureOptions
import com.chequedb.chequeui.capture.CaptureResult
import com.chequedb.chequeui.config.ChequeUIConfiguration
import com.chequedb.chequeui.config.Environment
import com.chequedb.chequeui.config.ExtractionOptions
import com.chequedb.chequeui.config.GuidanceMode
import com.chequedb.chequeui.error.ChequeUIError
import com.chequedb.chequeui.model.ExtractionResult
import com.example.chequeapp.databinding.ActivityChequeScannerBinding
import kotlinx.coroutines.launch
class ChequeScannerActivity : AppCompatActivity() {
private lateinit var binding: ActivityChequeScannerBinding
private val captureLauncher = registerForActivityResult(CaptureContract()) { result ->
when (result) {
is CaptureResult.Success -> {
processCheque(result.image)
}
is CaptureResult.Error -> {
handleCaptureError(result.error)
}
is CaptureResult.Cancelled -> {
Toast.makeText(this, "Capture cancelled", Toast.LENGTH_SHORT).show()
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityChequeScannerBinding.inflate(layoutInflater)
setContentView(binding.root)
initializeSDK()
setupUI()
}
private fun initializeSDK() {
if (!ChequeUI.isInitialized()) {
val config = ChequeUIConfiguration.Builder()
.apiKey(BuildConfig.CHEQUEUI_API_KEY)
.environment(if (BuildConfig.DEBUG) Environment.SANDBOX else Environment.PRODUCTION)
.build()
ChequeUI.initialize(applicationContext, config)
}
ChequeUI.setLogLevel(LogLevel.INFO)
ChequeUI.setErrorHandler { error ->
handleSDKError(error)
}
}
private fun setupUI() {
binding.captureButton.setOnClickListener {
launchCapture()
}
binding.retryButton.setOnClickListener {
launchCapture()
}
binding.resultView.isVisible = false
}
private fun launchCapture() {
val options = CaptureOptions.Builder()
.autoCapture(true)
.guidanceMode(GuidanceMode.ENHANCED)
.minQualityScore(0.75f)
.minEdgeConfidence(0.80f)
.showFlashToggle(true)
.build()
captureLauncher.launch(options)
}
private fun processCheque(image: Bitmap) {
showLoading(true)
lifecycleScope.launch {
try {
val extraction = ChequeUI.extract(
image = image,
options = ExtractionOptions(
confidenceThresholds = mapOf(
ExtractionField.AMOUNT_IN_NUMERALS to 0.95f,
ExtractionField.AMOUNT_IN_WORDS to 0.90f,
ExtractionField.BANK_NAME to 0.85f
)
)
)
showLoading(false)
displayResults(extraction)
} catch (e: Exception) {
showLoading(false)
handleExtractionError(e)
}
}
}
private fun displayResults(result: ExtractionResult) {
binding.resultView.apply {
isVisible = true
setBankName(result.bankName)
setBranch(result.branch)
setPayee(result.payee)
setAmount("${result.amountInWords} / $${result.amountInNumerals}")
setDate(result.date)
setConfidence(result.averageConfidence)
setAmountsMatch(result.amountsMatch)
}
// Log analytics
Analytics.logEvent("cheque_processed") {
param("bank", result.bankName)
param("amount", result.amountInNumerals.toDouble())
param("confidence", result.averageConfidence.toDouble())
}
}
private fun handleSDKError(error: ChequeUIError) {
val message = when (error) {
is ChequeUIError.Unauthorized -> "Session expired. Please log in again."
is ChequeUIError.NetworkError -> "Network error. Please check your connection."
is ChequeUIError.RateLimitExceeded -> "Too many requests. Please try again later."
else -> error.message ?: "An error occurred"
}
runOnUiThread {
Toast.makeText(this, message, Toast.LENGTH_LONG).show()
}
}
private fun handleCaptureError(error: ChequeUIError) {
when (error) {
is ChequeUIError.CameraPermissionDenied -> {
Toast.makeText(this, "Camera permission required", Toast.LENGTH_LONG).show()
}
else -> handleSDKError(error)
}
}
private fun handleExtractionError(error: Exception) {
val message = when (error) {
is ChequeUIError.PoorImageQuality -> {
val issues = error.issues.joinToString("\n") { "• ${it.description}" }
"Image quality issues:\n$issues"
}
is ChequeUIError.LowConfidence -> {
"Could not read cheque clearly. Please retake."
}
else -> "Failed to process cheque. Please try again."
}
Toast.makeText(this, message, Toast.LENGTH_LONG).show()
binding.errorView.isVisible = true
}
private fun showLoading(loading: Boolean) {
binding.captureButton.isEnabled = !loading
binding.progressBar.visibility = if (loading) View.VISIBLE else View.GONE
binding.captureButton.text = if (loading) "Processing..." else "Capture Cheque"
}
}
13. Guía de solución de problemas
Problemas comunes y soluciones
| Problema | Posible causa | Solución |
|---|---|---|
| SDK Error de inicialización | Clave API no válida | Verificar las credenciales en el panel de desarrollador |
| La cámara no se abre | Permiso denegado | Consulte Info.plist / AndroidManifest.xml |
| Precisión deficiente de OCR | Problemas de calidad de imagen | Utilice captura automática y validación de calidad |
| Procesamiento lento | Tamaño de imagen grande | Reducir maxImageDimension en configuración |
| Vencimiento del token | Desviación del reloj | Sincronizar la hora del dispositivo con NTP |
| Errores de compilación | Conflictos de versión | Actualización a la última versión SDK |
Consejos de depuración
-
Habilitar registro detallado:
ChequeUI.shared.logLevel = .verbose -
Verifique la versión SDK:
print("SDK de ChequeUI Version: \(ChequeUI.version)") -
Validar configuración:
let validation = ChequeUI.shared.validateConfiguration() print(validation.diagnostics)
Recursos de soporte
- Documentación: https://docs.chequedb.com/sdk
- Página de estado: https://status.chequedb.com
- Correo electrónico de soporte: sdk-support@chequedb.com
- Problemas de GitHub: github.com/chequedb/chequeui-sdk
14. Conclusión
El SDK móvil de ChequeUI proporciona una solución integral lista para producción para integrar capacidades de procesamiento de cheques en sus aplicaciones iOS y Android. Con sus funciones de captura inteligente, OCR de alta precisión y amplias opciones de personalización, puede ofrecer a sus usuarios una experiencia de procesamiento de cheques perfecta.
Conclusiones clave
- Integración rápida: comience en horas, no en semanas, con nuestro intuitivo SDK
- Listo para producción: manejo de errores integrado, soporte fuera de línea y funciones de seguridad
- Alta precisión: modelos de aprendizaje automático especialmente diseñados y optimizados para documentos financieros
- Arquitectura flexible: compatibilidad con flujos de trabajo sincrónicos y asincrónicos
- Soporte integral: documentación extensa, ejemplos y soporte dedicado
Próximos pasos
- Regístrese para obtener una cuenta de desarrollador en chequedb.com/developer
- Descargue las aplicaciones de muestra de nuestros repositorios de GitHub
- Experimente con el entorno sandbox
- Integre el SDK en su aplicación
- Implemente en producción con confianza
Recursos adicionales
- ¡Feliz codificación! Si tiene alguna pregunta o comentario, nos encantaría saber de usted en desarrolladores@chequedb.com*