# Guía de Pagamento por Biometria (Integración solo con iOS SDK)

Próximo Lanzamiento
Esta documentación cubre características de nuestro próximo lanzamiento. Aunque la funcionalidad principal y el flujo de trabajo descritos aquí permanecerán sin cambios, es posible que note algunas mejoras antes del lanzamiento final.

Con Pagamento por Biometria de Belvo, la recolección de pagos de los usuarios se vuelve fluida, eliminando la necesidad de que los usuarios naveguen a su institución financiera para aprobar cada solicitud de pago individual.

Esta guía demuestra la integración utilizando los métodos de conveniencia del SDK de iOS de Belvo. El SDK maneja la comunicación con el backend, los flujos de OAuth y el registro FIDO internamente, proporcionando un camino de integración simplificado.

El primer paso para habilitar la recolección de pagos biométricos es **registrar** el dispositivo del usuario con su institución. Durante el registro, se registra de manera segura información clave sobre el dispositivo y las credenciales de clave pública del usuario con su institución, asegurando que los pagos futuros puedan confirmarse utilizando solo la autenticación biométrica.

Una vez que el registro esté completo, puedes comenzar a solicitar pagos directamente desde el dispositivo del usuario.

## Prerrequisitos

Antes de comenzar, asegúrate de tener:

1. **Generadas tus claves de la API de Belvo Payments**
2. **Configurados los Webhooks** para recibir actualizaciones de estado de pagos y registros
3. **Generado un SDK Access Token** (ver abajo)
4. **Instalado el SDK de iOS de Belvo**


### Token de Acceso del SDK

Autenticación del SDK
El SDK de pagos biométricos requiere un token de acceso para autenticar las solicitudes de la API. Genera este token desde tu servidor backend y pásalo al SDK durante la inicialización.

Genera un token de acceso del SDK desde tu backend:

```bash
POST https://api.belvo.com/payments/api/widget-token/
Authorization: Basic <base64_encoded_secret_id:secret_password>
Content-Type: application/json
```

```json
{
  "access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
```

Mejores Prácticas para Tokens
Nunca codifiques tokens directamente en tu aplicación. Siempre genéralos del lado del servidor e implementa lógica segura de almacenamiento y actualización.

### Instalación del SDK

**Requisitos Mínimos:**

- iOS 15.0 o superior
- Swift 5.0 o superior


**Instalación a través de Swift Package Manager:**

1. En Xcode, selecciona **File** → **Add Packages...**
2. Ingresa: `https://github.com/belvo-finance-opensource/biometric-pix-ios-sdk`
3. Selecciona los requisitos de versión y haz clic en **Add Package**
4. Elige el producto **BiometricPixSDK** y haz clic en **Add Package**


### Configuración de la App

**Agrega permisos a tu archivo `.entitlements`:**

```xml
<key>com.apple.developer.associated-domains</key>
<array>
  <string>webcredentials:belvo.com</string>
</array>
```

**Agrega la descripción del uso de ubicación a `Info.plist`:**

```xml
<key>NSLocationWhenInUseUsageDescription</key>
<string>Esta aplicación utiliza la ubicación para propósitos de seguridad y prevención de fraude.</string>
```

**Comparte tu Team ID y Bundle Identifier con Belvo:**

```
Formato: TEAM_ID.BUNDLE_ID
Ejemplo: ABCDEFGHIJ.com.yourcompany.appname
```

## Inicialización del SDK

Inicializa el SDK una vez en tu aplicación, típicamente en tu modelo de vista o capa de servicio:

```swift
import BiometricPixSDK

class BiometricPixService {
    private let sdk: BiometricPixSDK
    
    init(accessToken: String) {
        self.sdk = BiometricPixSDK(accessToken: accessToken)
    }
    
    deinit {
        sdk.cleanup()
    }
}
```

Gestión de Recursos
Siempre llama a `cleanup()` cuando hayas terminado con el SDK (por ejemplo, en `deinit` o al cerrar sesión) para liberar adecuadamente los recursos.

## Flujo de Inscripción (7 Pasos)

El proceso de inscripción registra el dispositivo de un usuario con su institución para pagos biométricos.

```mermaid
sequenceDiagram
    autonumber

    participant User
    participant YourApp
    participant BiometricPixSDK
    participant BelvoAPI
    participant Institution
    
    Note over YourApp,BiometricPixSDK: Llamada 1: getPaymentInstitutions()
    User->>YourApp: Inicia inscripción
    YourApp->>BiometricPixSDK: getPaymentInstitutions()
    BiometricPixSDK->>BelvoAPI: Obtener instituciones
    BelvoAPI-->>BiometricPixSDK: Lista de instituciones
    BiometricPixSDK-->>YourApp: [Institution]
    YourApp->>User: Mostrar selector de institución
    User-->>YourApp: Selecciona institución
    
    Note over YourApp,BiometricPixSDK: Llamada 2: requestPermission()
    YourApp->>BiometricPixSDK: requestPermission()
    BiometricPixSDK->>User: Solicitar permiso de ubicación
    User-->>BiometricPixSDK: Otorga permiso
    BiometricPixSDK-->>YourApp: Permiso concedido
    
    Note over YourApp,BiometricPixSDK: Llamada 3: createEnrollment()
    YourApp->>BiometricPixSDK: createEnrollment(cpf, institution, accountTenure, callbackUrl)
    BiometricPixSDK->>BiometricPixSDK: Recopilar señales de riesgo internamente
    BiometricPixSDK->>BelvoAPI: POST /enrollments/
    BelvoAPI-->>BiometricPixSDK: Inscripción creada (redirect_url)
    BiometricPixSDK-->>YourApp: Objeto de inscripción
    
    Note over YourApp,Institution: Redirigir a la Institución
    YourApp->>User: Redirigir a la institución (redirect_url)
    User->>Institution: Aprobar inscripción en la app de la institución
    Institution-->>YourApp: Callback OAuth (code, state, id_token)
    
    Note over YourApp,BiometricPixSDK: Llamada 4: completeEnrollmentAfterRedirection()
    YourApp->>BiometricPixSDK: completeEnrollmentAfterRedirection(callbackUrl)
    BiometricPixSDK->>BelvoAPI: POST /enrollments/complete-redirection/
    BelvoAPI-->>BiometricPixSDK: Inscripción actualizada
    BiometricPixSDK-->>YourApp: Objeto de inscripción
    
    Note over YourApp,BiometricPixSDK: Llamada 5: getFidoRegistrationOptions()
    YourApp->>BiometricPixSDK: getFidoRegistrationOptions(enrollmentId)
    BiometricPixSDK->>BelvoAPI: Consultar opciones FIDO (reintento automático)
    BelvoAPI-->>BiometricPixSDK: Opciones de registro FIDO
    BiometricPixSDK-->>YourApp: FidoRegistrationOptions
    
    Note over YourApp,BiometricPixSDK: Llamada 6: startRegistration()
    YourApp->>BiometricPixSDK: startRegistration(fidoOptions, callback)
    BiometricPixSDK->>User: Solicitar biometría (Face ID/Touch ID)
    User-->>BiometricPixSDK: Proporciona biometría
    BiometricPixSDK-->>YourApp: Credencial vía callback
    
    Note over YourApp,BiometricPixSDK: Llamada 7: confirmEnrollment()
    YourApp->>BiometricPixSDK: confirmEnrollment(enrollmentId, credential)
    BiometricPixSDK->>BelvoAPI: POST /enrollments/{id}/confirm/
    BelvoAPI->>Institution: Registrar credencial FIDO
    Institution-->>BelvoAPI: Registro confirmado
    BelvoAPI-->>BiometricPixSDK: Inscripción EXITOSA
    BiometricPixSDK-->>YourApp: Éxito (Booleano)
    YourApp->>User: Mostrar pantalla de éxito
```

### Paso 1: Obtener Instituciones de Pago

Obtén la lista de instituciones que soportan pagos biométricos:

```swift
import BiometricPixSDK

class EnrollmentViewModel: ObservableObject {
    private let sdk: BiometricPixSDK
    @Published var institutions: [Institution] = []
    @Published var selectedInstitution: Institution?
    
    init(sdk: BiometricPixSDK) {
        self.sdk = sdk
    }
    
    func loadInstitutions() {
        do {
            institutions = try sdk.getPaymentInstitutions()
        } catch {
            // Manejar error (red, autenticación, etc.)
            print("Error al cargar instituciones: \(error)")
        }
    }
}
```

**Mostrar instituciones al usuario:**

```swift
struct InstitutionPickerView: View {
    @ObservedObject var viewModel: EnrollmentViewModel
    
    var body: some View {
        List(viewModel.institutions) { institution in
            Button(action: { 
                viewModel.selectedInstitution = institution 
            }) {
                HStack {
                    AsyncImage(url: URL(string: institution.iconLogo))
                        .frame(width: 40, height: 40)
                    Text(institution.displayName)
                }
            }
        }
        .onAppear {
            viewModel.loadInstitutions()
        }
    }
}
```

### Paso 2: Solicitar Permisos

Solicitar permisos de ubicación requeridos para la evaluación de riesgos:

```swift
func requestPermissions() {
    sdk.requestPermission { granted in
        DispatchQueue.main.async {
            if let permissionGranted = granted as? Bool, permissionGranted {
                // Permisos concedidos, proceder con el registro
                self.startEnrollment()
            } else {
                // Manejar la denegación de permisos
                self.showPermissionDeniedAlert()
            }
        }
    }
}
```

### Paso 3: Crear Inscripción

Crea la inscripción con la siguiente llamada al método (el SDK maneja la recopilación de señales de riesgo internamente):

```swift
func startEnrollment() {
    guard let institution = selectedInstitution else { return }
    
    do {
        let enrollment = try sdk.createEnrollment(
            cpf: userCPF,                           // CPF del usuario
            institution: institution.id,             // ID de la institución seleccionada
            accountTenure: customerCreatedDate,      // Formato "YYYY-MM-DD"
            callbackUrl: "https://myapp.com/callback"
        )
        
        // Guarda el ID de inscripción y el ID de dispositivo para más tarde
        self.enrollmentId = enrollment.id
        self.deviceId = enrollment.details.riskSignals.deviceId
        
        // Redirige al usuario a la institución
        if let redirectUrl = enrollment.details.redirectUrl {
            self.openInstitutionApp(url: redirectUrl)
        }
        
    } catch {
        // Manejar error
        print("La creación de la inscripción falló: \(error)")
    }
}
```

Formato de Antigüedad de Cuenta
El parámetro `accountTenure` debe ser la fecha en que el usuario fue creado como Cliente de Belvo, en formato `YYYY-MM-DD`. Extrae esto del timestamp `created_at` del Cliente (primeros 10 caracteres).

### Paso 4: Redirigir a la Institución

Abre la aplicación de la institución usando el `redirect_url`:

```swift
func openInstitutionApp(url: String) {
    guard let url = URL(string: url) else { return }
    
    if UIApplication.shared.canOpenURL(url) {
        UIApplication.shared.open(url)
    }
}
```

La institución redirigirá de vuelta a tu `callbackUrl` con parámetros OAuth.

### Paso 5: Completar el Registro Después de la Redirección

Maneja el callback de OAuth en tu aplicación y completa el registro:

```swift
// En tu SceneDelegate o App delegate
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
    guard let url = URLContexts.first?.url else { return }
    
    // Pasa la URL completa del callback al SDK
    handleEnrollmentCallback(url: url)
}

func handleEnrollmentCallback(url: URL) {
    do {
        let enrollment = try sdk.completeEnrollmentAfterRedirection(
            callbackUrl: url.absoluteString  // El SDK analiza los parámetros automáticamente
        )
        
        // Verifica si fue exitoso
        if enrollment.status == "PENDING" {
            // Éxito - procede al registro FIDO
            self.getFidoOptions(enrollmentId: enrollment.id)
        } else if enrollment.status == "FAILED" {
            // Maneja el fallo
            self.showEnrollmentError(
                code: enrollment.statusReasonCode,
                message: enrollment.statusReasonMessage
            )
        }
        
    } catch {
        print("Error al completar el registro: \(error)")
    }
}
```

Alternativa: Parámetros Manuales
Si prefieres analizar la URL del callback tú mismo, puedes pasar los parámetros individualmente:

```swift
let enrollment = try sdk.completeEnrollmentAfterRedirection(
    state: stateParam,
    code: codeParam,
    idToken: idTokenParam
)
```

### Paso 6: Obtener Opciones de Registro FIDO

Recupera las opciones FIDO necesarias para el registro biométrico. El SDK realiza sondeos automáticamente durante un máximo de 5 minutos:

```swift
func getFidoOptions(enrollmentId: String) {
    // El SDK realiza sondeos automáticamente cada 1 segundo durante un máximo de 5 minutos
    if let fidoOptions = sdk.getFidoRegistrationOptions(enrollmentId: enrollmentId) {
        // Opciones FIDO recibidas, proceder al registro biométrico
        self.startBiometricRegistration(fidoOptions: fidoOptions)
    } else {
        // Tiempo de sondeo agotado (han pasado 5 minutos)
        self.showTimeoutError()
    }
}
```

Sondeo Automático
El método `getFidoRegistrationOptions()` maneja toda la lógica de sondeo automáticamente. Verifica cada 1 segundo durante un máximo de 5 minutos, por lo que no necesitas implementar ninguna lógica de reintento.

### Paso 7: Registrar Biometría y Confirmar

Solicita al usuario los datos biométricos y confirma la inscripción:

```swift
class EnrollmentViewModel: NSObject, ObservableObject {
    private let sdk: BiometricPixSDK
    private var enrollmentId: String?
    
    func startBiometricRegistration(fidoOptions: FidoRegistrationOptions) {
        do {
            try sdk.startRegistration(
                fidoResponseString: fidoOptions.toJsonString(),
                callback: self
            )
        } catch {
            print("Failed to start registration: \(error)")
        }
    }
}

// Implementar callback FIDO
extension EnrollmentViewModel: FidoRegistrationCallback {
    func onSuccess(credential: PublicKeyCredential, response: AuthenticatorAttestationResponse) {
        // El SDK maneja la creación de payload automáticamente
        guard let enrollmentId = self.enrollmentId else { return }
        
        let success = sdk.confirmEnrollment(
            enrollmentId: enrollmentId,
            credential: credential,
            response: response
        )
        
        if success {
            DispatchQueue.main.async {
                self.showEnrollmentSuccess()
            }
        } else {
            DispatchQueue.main.async {
                self.showEnrollmentError()
            }
        }
    }
    
    func onError(error: String) {
        DispatchQueue.main.async {
            self.showBiometricError(message: error)
        }
    }
}
```

Ciclo de Vida del View Model
Al usar view models con callbacks en SwiftUI, decláralos como `@ObservedObject` o `@StateObject` para evitar que sean destruidos durante las re-renderizaciones de la vista:

```swift
struct EnrollmentView: View {
    @StateObject private var viewModel: EnrollmentViewModel
}
```

¡Inscripción Completa!
El dispositivo ahora está inscrito y listo para pagos biométricos.

## Flujo de Pago (4 Pasos)

Una vez inscrito, iniciar pagos requiere cuatro llamadas a métodos (como puedes ver en el diagrama de secuencia a continuación):

```mermaid
sequenceDiagram
    autonumber

    participant Usuario
    participant TuApp
    participant BiometricPixSDK
    participant BelvoAPI
    participant Institución

    Note over TuApp,BiometricPixSDK: Llamada 1: listEnrollments()
    Usuario->>TuApp: Inicia pago
    TuApp->>BiometricPixSDK: listEnrollments(deviceId)
    BiometricPixSDK->>BelvoAPI: GET /enrollments/?device_id=...
    BelvoAPI-->>BiometricPixSDK: [Enrollment]
    BiometricPixSDK-->>TuApp: Lista de inscripciones
    TuApp->>Usuario: Mostrar selector de inscripción
    Usuario-->>TuApp: Selecciona inscripción
    
    Note over TuApp,BiometricPixSDK: Llamada 2: createPaymentIntent()
    TuApp->>BiometricPixSDK: createPaymentIntent(payload)
    BiometricPixSDK->>BelvoAPI: POST /payment-intents/
    BelvoAPI-->>BiometricPixSDK: PaymentIntent (con opciones FIDO)
    BiometricPixSDK-->>TuApp: Objeto PaymentIntent
    
    Note over TuApp,BiometricPixSDK: Llamada 3: startSigning() + collectRiskSignals()
    TuApp->>BiometricPixSDK: startSigning(fidoOptions, callback)
    BiometricPixSDK->>Usuario: Solicitar biometría (Face ID/Touch ID)
    Usuario-->>BiometricPixSDK: Proporciona biometría
    BiometricPixSDK-->>TuApp: Afirmación vía callback
    TuApp->>BiometricPixSDK: collectRiskSignals(accountTenure)
    BiometricPixSDK-->>TuApp: RiskSignals
    
    Note over TuApp,BiometricPixSDK: Llamada 4: authorizePaymentIntent()
    TuApp->>BiometricPixSDK: authorizePaymentIntent(paymentIntentId, payload)
    BiometricPixSDK->>BelvoAPI: POST /payment-intents/{id}/authorize/
    BelvoAPI->>Institución: Procesar pago
    Institución-->>BelvoAPI: Pago confirmado
    BelvoAPI-->>BiometricPixSDK: Pago SUCCEEDED
    BiometricPixSDK-->>TuApp: Autorización exitosa (Boolean)
    TuApp->>Usuario: Mostrar confirmación de pago
```

### Paso 1: Listar Inscripciones

Obtén todas las inscripciones para el dispositivo actual y permite que el usuario seleccione una:

```swift
class PaymentViewModel: ObservableObject {
    private let sdk: BiometricPixSDK
    @Published var enrollments: [Enrollment] = []
    @Published var selectedEnrollment: Enrollment?
    
    func loadEnrollments(deviceId: String) {
        do {
            enrollments = try sdk.listEnrollments(deviceId: deviceId)
        } catch {
            print("Failed to load enrollments: \(error)")
        }
    }
}
```

**Mostrar inscripciones al usuario:**

```swift
struct EnrollmentSelectionView: View {
    @ObservedObject var viewModel: PaymentViewModel
    
    var body: some View {
        List(viewModel.enrollments) { enrollment in
            Button(action: { 
                viewModel.selectedEnrollment = enrollment 
            }) {
                HStack {
                    if let institution = enrollment.institution {
                        AsyncImage(url: URL(string: institution.iconLogo))
                            .frame(width: 40, height: 40)
                        VStack(alignment: .leading) {
                            Text(institution.displayName)
                            Text("Estado: \(enrollment.status)")
                                .font(.caption)
                                .foregroundColor(.gray)
                        }
                    }
                }
            }
        }
        .onAppear {
            viewModel.loadEnrollments(deviceId: savedDeviceId)
        }
    }
}
```

### Paso 2: Crear una Intención de Pago

Crea una intención de pago con todos los detalles del pago:

```swift
func createPayment(amount: Double, enrollmentId: String, beneficiaryAccountId: String) {
    let payload = CreatePaymentIntentPayload(
        amount: amount,
        customer: Customer(identifier: userCPF),  // CPF del usuario
        description: "Pago por servicios",
        statementDescription: "Compra ACME Corp",
        allowedPaymentMethodTypes: ["open_finance_biometric_pix"],
        paymentMethodDetails: PaymentMethodDetails(
            openFinanceBiometricPix: OpenFinanceBiometricPixPaymentMethodDetails(
                beneficiaryBankAccount: beneficiaryAccountId,
                enrollment: enrollmentId
            )
        ),
        confirm: true
    )
    
    do {
        let paymentIntent = try sdk.createPaymentIntent(payload: payload)
        
        // Guardar ID de la intención de pago
        self.paymentIntentId = paymentIntent.id
        
        // Extraer opciones FIDO para el siguiente paso
        if let fidoOptions = paymentIntent.paymentMethodInformation?.openFinanceBiometricPix?.fidoOptions {
            self.promptForBiometric(fidoOptions: fidoOptions)
        }
        
    } catch {
        print("Error al crear la intención de pago: \(error)")
    }
}
```

### Paso 3: Recopilar Señales Biométricas y de Riesgo

Solicitar autenticación biométrica y recopilar señales de riesgo:

```swift
class PaymentViewModel: NSObject, ObservableObject {
    private let sdk: BiometricPixSDK
    private var paymentIntentId: String?
    private var riskSignals: RiskSignals?
    private var assertionResponse: AssertionResponse?
    
    func promptForBiometric(fidoOptions: FidoOptions) {
        // Convertir opciones FIDO a cadena JSON
        let fidoJsonString = fidoOptions.toJsonString()
        
        do {
            try sdk.startSigning(
                fidoResponseString: fidoJsonString,
                fallbackCredential: nil,  // Opcional: proporcionar si tienes uno
                callback: self
            )
        } catch {
            print("Error al iniciar la firma: \(error)")
        }
    }
    
    func collectRiskSignals() {
        do {
            self.riskSignals = try sdk.collectRiskSignals(
                accountTenure: customerCreatedDate  // "YYYY-MM-DD"
            )
            
            // Una vez que tenemos tanto la afirmación como las señales de riesgo, autorizar el pago
            if assertionResponse != nil && riskSignals != nil {
                self.authorizePayment()
            }
        } catch {
            print("Error al recopilar señales de riesgo: \(error)")
        }
    }
}

// Implementar callback de autenticación FIDO
extension PaymentViewModel: FidoAuthenticationCallback {
    func onSuccess(response: AssertionResponse) {
        // Almacenar respuesta de afirmación
        self.assertionResponse = response
        
        // Recopilar señales de riesgo
        self.collectRiskSignals()
    }
    
    func onError(error: String) {
        DispatchQueue.main.async {
            self.showPaymentError(message: error)
        }
    }
}
```

### Paso 4: Autorizar Pago

Autoriza el pago con los datos recopilados:

```swift
func authorizePayment() {
    guard let paymentIntentId = self.paymentIntentId,
          let riskSignals = self.riskSignals,
          let assertion = self.assertionResponse else {
        return
    }
    
    let payload = AuthorizePaymentIntentPayload(
        platform: "ios",
        riskSignals: riskSignals,
        assertion: assertion
    )
    
    let success = sdk.authorizePaymentIntent(
        paymentIntentId: paymentIntentId,
        payload: payload
    )
    
    DispatchQueue.main.async {
        if success {
            self.showPaymentSuccess()
        } else {
            self.showPaymentError(message: "Authorization failed")
        }
    }
}
```

¡Flujo de Pago Completo!
El pago ahora está autorizado y en proceso. Necesitas monitorear los eventos de webhook para rastrear su estado final.

## Manejo de Errores

Todos los métodos del SDK que realizan operaciones de red pueden lanzar excepciones. Asegúrate de manejarlas adecuadamente:

```swift
do {
    let institutions = try sdk.getPaymentInstitutions()
    // Éxito
} catch let error as BiometricPixSDKError {
    switch error {
    case .networkError(let message):
        // Manejar problemas de red
        print("Error de red: \(message)")
    case .authenticationError:
        // Manejar token inválido o expirado
        print("Autenticación fallida - el token puede estar expirado")
    case .invalidParameters(let message):
        // Manejar entrada inválida
        print("Parámetros inválidos: \(message)")
    case .unknown(let message):
        // Manejar errores desconocidos
        print("Error: \(message)")
    }
} catch {
    print("Error inesperado: \(error)")
}
```

## Webhooks

Aunque el SDK maneja la mayor parte del flujo de trabajo, aún debes escuchar las notificaciones de webhook para manejar actualizaciones asíncronas:

- **Cambios en el estado de inscripción:** tipo de webhook `ENROLLMENTS`
- **Cambios en el estado de pago:** tipo de webhook `PAYMENT_INTENTS`


Para obtener la documentación completa de los webhooks, consulta Webhooks de Pagos (Brasil).

## Referencia de Métodos del SDK

### Inicialización

**`BiometricPixSDK(accessToken: String)`**

- Crea una nueva instancia del SDK con el token de acceso proporcionado
- Debe inicializarse una vez y reutilizarse en toda tu aplicación
- Token de acceso obtenido del endpoint `/payments/api/widget-token/`


**`cleanup()`**

- Libera los recursos del SDK
- Llamar en `deinit` o cuando el usuario cierra sesión


### Métodos de Inscripción

**`getPaymentInstitutions() throws -> [Institution]`**

- Obtiene todas las instituciones que soportan pagos biométricos
- Devuelve un array de objetos `Institution` con `id`, `displayName`, `iconLogo`, etc.
- Lanza excepciones en errores de red o autenticación


**`createEnrollment(cpf: String, institution: String, accountTenure: String, callbackUrl: String) throws -> Enrollment`**

- Crea la inscripción y recopila señales de riesgo automáticamente
- `cpf`: Número de CPF del usuario
- `institution`: ID de la institución de `getPaymentInstitutions()`
- `accountTenure`: Fecha de creación del cliente en formato "YYYY-MM-DD"
- `callbackUrl`: Enlace profundo para el callback de OAuth (debe estar registrado en applinks)
- Devuelve un objeto `Enrollment` con `id`, `redirect_url`, `device_id`


**`completeEnrollmentAfterRedirection(callbackUrl: String) throws -> Enrollment`**

- Completa la inscripción después del callback de OAuth de la institución
- Analiza automáticamente los parámetros de OAuth desde la URL completa del callback
- Alternativa: `completeEnrollmentAfterRedirection(state: String, code: String, idToken: String)`


**`getFidoRegistrationOptions(enrollmentId: String) -> FidoRegistrationOptions?`**

- Consulta las opciones de FIDO (reintento automático: intervalo de 1 segundo, tiempo de espera de 5 minutos)
- Devuelve `FidoRegistrationOptions` cuando está listo
- Devuelve `nil` si el tiempo de espera de la consulta se agota


**`startRegistration(fidoResponseString: String, callback: FidoRegistrationCallback) throws`**

- Inicia el flujo de registro biométrico (Face ID/Touch ID)
- `fidoResponseString`: Cadena JSON de `FidoRegistrationOptions.toJsonString()`
- `callback`: Delegado para recibir callbacks de éxito/error


**`confirmEnrollment(enrollmentId: String, credential: PublicKeyCredential, response: AuthenticatorAttestationResponse) -> Bool`**

- Confirma la inscripción con la credencial FIDO
- Devuelve `true` en caso de éxito, `false` en caso de fallo


### Métodos de Pago

**`listEnrollments(deviceId: String) throws -> [Enrollment]`**

- Obtiene todas las inscripciones para un dispositivo
- Devuelve un array de objetos `Enrollment` con datos enriquecidos de la institución
- Filtrar por `status == "SUCCEEDED"` para mostrar solo inscripciones activas


**`createPaymentIntent(payload: CreatePaymentIntentPayload) throws -> PaymentIntent`**

- Crea una intención de pago
- Devuelve `PaymentIntent` con `id` y `paymentMethodInformation.openFinanceBiometricPix.fidoOptions`


**`startSigning(fidoResponseString: String, fallbackCredential: String?, callback: FidoAuthenticationCallback) throws`**

- Inicia la autenticación biométrica para el pago
- `fallbackCredential`: Credencial opcional para escenarios de reintento
- `callback`: Delegado para recibir la respuesta de la afirmación


**`collectRiskSignals(accountTenure: String) throws -> RiskSignals`**

- Recoge huellas digitales del dispositivo y señales de seguridad
- `accountTenure`: Fecha de creación del cliente en formato "YYYY-MM-DD"
- Devuelve un objeto `RiskSignals` para la carga útil de autorización


**`authorizePaymentIntent(paymentIntentId: String, payload: AuthorizePaymentIntentPayload) -> Bool`**

- Autoriza el pago con afirmación biométrica y señales de riesgo
- Devuelve `true` en caso de éxito, `false` en caso de fallo