# Pagamento por Biometria Guide (iOS SDK + API)

Upcoming Release
This documentation covers features from our upcoming release. While the core functionality and workflow described here will remain unchanged, you may notice some refinements before the final release, such as:

- iOS SDK updates and optimizations
- Documentation improvements (links, terminology, diagrams)
- API reference updates for enrollment endpoints


With Belvo's Pagamento por Biometria, collecting payments from users becomes seamless, removing the need for users to navigate to their financial institution to approve each individual payment request.

The first step in enabling biometric payment collection is to **enroll** the user’s device with their institution. During enrollment, key data about the device and the user's public key credentials is securely registered with their institution, ensuring that future payments can be confirmed using biometric authentication alone. Once enrollment is complete, you can start requesting payments directly from the user’s device.

In this guide, we’ll take you through each step, from device enrollment to successfully initiating a payment request on an iOS device.

SDK + API Usage
This guide demonstrates using both the Belvo SDK and API to enroll devices as well as making payments. However, you can complete the entire flow using just the Belvo SDK. For more information, see our Pagamento por Biometria iOS SDK Only guide.

## Prerequisites

Before starting, ensure you have:

1. **Generated your Belvo Payments API Keys**
2. **Set up Webhooks** to receive payment and enrollment status updates
3. **Generated an SDK Access Token** (see below)
4. **Installed the Belvo iOS SDK** (see below)
5. **Created a Belvo Customer** for each user you want to enroll


### SDK Access Token

SDK Authentication
The Biometric payments SDK requires an access token to authenticate API requests. Generate this token from your backend server and pass it to the SDK during initialization.

Generate an SDK access token from your 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..."
}
```

Token Best Practices
Never hardcode tokens in your app. Always generate them server-side and implement secure storage and refresh logic.

### SDK Installation

**Minimum Requirements**

- **iOS**: 15.0 or higher
- **Swift**: 5.0 or higher


**Instructions:**

To integrate the Belvo Biometric payments SDK into your Xcode project using Swift Package Manager:

1. In Xcode, select **File** → **Add Packages...**.
2. Enter the package repository URL: `https://github.com/belvo-finance-opensource/biometric-pix-ios-sdk`.
3. Select the version requirements and click **Add Package**.
4. Choose the **BiometricPixSDK** product and click **Add Package**.


**Add the required entitlements to your app's entitlements file:**

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

**Add location usage description in your `Info.plist` file:**

```xml
  <key>NSLocationWhenInUseUsageDescription</key>
  <string>This app uses location for security and fraud prevention purposes.</string>
```

**Share your Team ID and Bundle Identifier with Belvo in the following format:**

```shell
   ## Format
   TEAM_ID.BUNDLE_ID

   ## Example
   ABCDEFGHIJ.com.yourcompany.appname
```

## Enrollment

Create a Customer First
Before creating an enrollment, you must create a customer using the Belvo API (in order to have a `customer.id` to associate the enrollment with.) For details on how to create a customer, see the Create Customer (Brazil) request, making sure to select the **V1 - Create Customer** request body.

Enrollment is the process of registering a user’s device in their institution to allow for biometric payments for a given merchant. During the process, you will use a **combination** of the Belvo Payments iOS SDK and API to retrieve key details about the device as well as the  biometric public data.

1. **List Institutions:** Prompt the user to select their desired financial institution to enroll in, using the Belvo API to display the available options.
2. **Request Location Permissions and Collect Risk Signals:** Prompt the user for necessary permissions and collect risk signals, including the device ID.
3. **Create and Update Enrollment:** Send the collected risk signals, along with the customer ID, institution ID, and a callback URL, to Belvo's server to create the enrollment. Then, redirect the user to their institution's app for approval. Once they're redirected back to your callback URL with details, send these to Belvo to update the enrollment state.
4. **Poll for FIDO Options:** Continuously poll the Belvo API (`GET /enrollments/{id}/fido-registration-options/`) to retrieve the necessary FIDO options for biometric registration.
⚠️ **Polling Strategy:** We recommend polling our server every two seconds for up to two minutes. If no response is received within this timeframe, instruct the user to try again.
5. **Prompt for Biometrics:** Take the FIDO options from Belvo's API and use the Belvo iOS SDK `startRegistration(fidoOptions)` method to prompt the user for their biometric gesture.
6. **Finalize Enrollment:** Send the biometric public data to Belvo using `POST /enrollments/{id}/confirm/`. After that, poll `GET /enrollments/{id}/` until a response is received (enrollment `status` = `SUCCEEDED` or `FAILED`).


```mermaid
sequenceDiagram
    autonumber

    participant EndUser
    participant ClientAppBackend
    participant BiometricPixSDK
    participant Payments
    participant BankAPP

    Note over EndUser,BankAPP: 0. Prompt the user to select the institution to enroll in (Belvo API)

    ClientAppBackend ->> Payments: /institutions/
    Payments -->> ClientAppBackend: List of institutions
    ClientAppBackend ->> EndUser: Display list of institutions
    EndUser -->> ClientAppBackend: Selected institution

    Note over EndUser,BankAPP: 1. Request Location Permissions and Collect Risk Signals (iOS SDK)

    BiometricPixSDK ->> EndUser: requestPermission()
    EndUser -->> BiometricPixSDK: Grants permission to collect risk signals
    ClientAppBackend ->> BiometricPixSDK: collectRiskSignals(accountTenure)
    BiometricPixSDK -->> ClientAppBackend: Returns riskSignals + deviceId (encrypted)
    ClientAppBackend ->> ClientAppBackend: Persist deviceId (encrypted)

    Note over EndUser,BankAPP: 2. Send Risk Signals to Belvo (Belvo API)

    ClientAppBackend ->> Payments: POST /enrollments/ (riskSignals, callback_url)
    Payments -->> ClientAppBackend: 201 Created (enrollment_id, redirect_url)
    ClientAppBackend ->> ClientAppBackend: Persist enrollment_id associated with deviceId (encrypted)

    Note over EndUser,BankAPP: 3. Redirect user to their APP and update enrollment

    ClientAppBackend ->> EndUser: Redirect to BankAPP (using the redirect_url)
    EndUser ->> BankAPP: Approves enrollment
    BankAPP ->> ClientAppBackend: Institution redirects to callback_url with details in query parameters
    ClientAppBackend ->> Payments: Update enrollment with received values using POST /enrollments/complete-redirection/
    Payments -->> ClientAppBackend: Returns Enrollment updated payload

    Note over EndUser,BankAPP: 4. Poll Belvo API for FIDO options

    ClientAppBackend ->> Payments: Poll GET /enrollments/{id}/fido-registration-options/
    Payments -->> ClientAppBackend: Returns fido_options

    Note over EndUser,BankAPP: 5. Prompt for Biometrics (iOS SDK)

    ClientAppBackend ->> BiometricPixSDK: startRegistration(fidoOptions)
    BiometricPixSDK ->> EndUser: Request biometric data
    EndUser -->> BiometricPixSDK: Provides biometric (face/fingerprint/PIN)
    BiometricPixSDK -->> ClientAppBackend: encodedId, rawId, encodedAttestationObject, encodedClientDataJSON

    Note over EndUser,BankAPP: 6. Send biometrics to finalize enrollment and poll for response

    ClientAppBackend ->> Payments: POST /enrollments/{id}/confirm/ (attestationObject, clientDataJSON, credential)
    Payments -->> ClientAppBackend: 204 - No Content
    ClientAppBackend ->> Payments: Poll GET /enrollments/{id}/
    Payments -->> ClientAppBackend: status = SUCCEEDED
```

## Prompt the user to select the institution to enroll in (Belvo API)

SDK Convenience Method Available
The iOS SDK provides a `getPaymentInstitutions()` method that simplifies fetching institutions. This method handles the API call internally and returns a list of `Institution` objects. See the SDK method reference section for details.

In your application, prompt your user to select the institution where they want to enroll the device in. Use the List all payment institutions request to get a list of all the possible institutions. Once the user selects the institution, save the id of the institution (required in Send Risk Signals to Belvo (API) step).

## Request Location Permissions and Collect Risk Signals (iOS SDK)

Next, in your application, you will need to make the following calls:

### requestPermission()

This `requestPermission()` method creates and launches a permission request for location and phone state permissions.

```swift
import SwiftUI
import BiometricPixSDK
import Foundation

struct PermissionRequestExample: View {
    @State private var permissionGranted: Bool? = nil
    private let sdk = BiometricPixSDK()

    private func requestPermission() {
        sdk.requestPermission { granted in
            DispatchQueue.main.async {
								// If needed, cast granted to Bool
                permissionGranted = granted as? Bool
								// If needed, handle UI
            }
        }
    }
}
```

When the user grants their permission, you can then extract the device’s risk signals using `collectRiskSignals(accountTenure)`.

### collectRiskSignals(accountTenure)

The `collectRiskSignals(accountTenure)` method gathers comprehensive device fingerprinting data and security signals. The collected data includes device ID, security status, hardware information, and behavioral signals, which are crucial for the institution to perform risk assessment and fraud detection. The method returns a RiskSignals object which you need save and forward on to Belvo’s servers in an API call. Additionally, you need to persist the value of `deviceId` that the the RiskSignals object returns so that later you can associate it with the Enrollment ID (later, when listing Enrollments, you need to provide the `deviceID` to receive all Enrollments).

accountTenure Parameter
In the `accountTenure` argument, you must pass the date that the user was created as a Customer in Belvo’s API, in `YYYY-MM-DD format`.

This is derived from the Customer created_at timestamp. However, you only need to send the first 10 characters corresponding to the year, month, and date (`YYYY-MM-DD`). A handy regex to extract this from the `created_at` parameter could be: `\d{4}-\d{2}-\d{2}`.

```swift
import SwiftUI
import BiometricPixSDK
import Foundation

struct RiskSignalsView: View {
    private let sdk = BiometricPixSDK()

    private func collectRiskSignals() {
        do {
            // Collect risk signals with the user creation date
            signals = sdk.collectRiskSignals(
                accountTenure: "YYYY-MM-DD" // [!code highlight]
            )
        } catch {
            // Handle errors that might occur during risk signal collection.
            // Possible errors include:
            // - Invalid `accountTenure` format
            // - Network connectivity issues
            // Consider logging the error or displaying an appropriate message to the user.
        }
    }
}
```

Once you have the risk signals and device ID, you can forward this information to Belvo using Create Enrollment method.

## Create Enrollment Using Risk Signals (API)

SDK Convenience Method Available
The iOS SDK provides a `createEnrollment()` method that simplifies the enrollment process. This method:

- Automatically collects risk signals internally
- Accepts CPF directly (no need to create Customer first)
- Handles the API call to create the enrollment
- Returns an enriched `Enrollment` object with institution data


**Example:**

```swift
let enrollment = sdk.createEnrollment(
    cpf: "12345678900",
    institution: institutionId,
    accountTenure: "2024-01-15",
    callbackUrl: "https://myapp.com/callback"
)
```

```curl
POST /enrollments/
```

```json
// Request Body
{
    "type": "open_finance_biometric_pix",
    "details": {
        "customer": "{{created_customer_uuid}}",
        "institution": "{{selected_institution_uuid}}",
        "name": "Name for the enrollment",
        "platform": "IOS",
        "callback_url": "{{https://deeplink_to_your_application}}",
        "risk_signals": {} // [!code highlight]
    }
}
```

| Parameter | Type | Description |
|  --- | --- | --- |
| `type` | string (enum) | The type of enrollment. For Pagamento por Biometria, this must be set to `open_finance_biometric_pix`. |
| `details` | object | Details regarding the device enrollment. |
| `details.customer` | string (uuid) | The Belvo ID for your user. |
| `details.institution` | string (uuid) | The Belvo ID for the institution your user selected for the enrollment. |
| `details.callback_url` | string (uri) | The deeplink to where your user should be redirected to in your application after they approve the enrollment in their institutions application. Must be HTTPS compliant. |
| `details.name` | string | A human-readable name for the enrollment. |
| `details.platform` | string | The platform that this enrollment relates to. For iOS devices, this must be set to `IOS`. |
| `details.risk_signals` | object | The `RiskSignals` object (converted to JSON) you received after using the `collectRiskSignals` method. |


Register your callback_url
The `callback_url` you provide **must** be registered in your applinks. For details on how to register your callback URL, please refer to Apple's documentation on applinks.

In the response payload, you will receive a `redirect_url` that you need to display to your user so that they can be redirected to their institution to confirm their enrollment.

```json
// 201 Created
{
  "id": "82666cde-3f80-4350-b0f7-24cb8e9294c9",
  "created_by": "56689ef8-4c92-44ae-b2c1-60505da4a7e1",
  "created_at": "2024-11-26T11:20:57.389056Z",
  "updated_at": "2024-11-26T11:20:57.389056Z",
  "type": "open_finance_biometric_pix",
  "status": "PENDING",
  "details": {
    "status": "AWAITING_ACCOUNT_HOLDER_VALIDATION",
    "customer": "f78b14f3-5c1a-409a-966f-7b052b067cf0",
    "institution": "188716fb-39ad-44a7-a992-6c278d2b24a4",
    "platform": "IOS",
    "name": "First Enrollment",
    "callback_url": "deeplink-to-your-application",
    "redirect_url": "https://www.user-banking-institituon.com/?enrollment_request=true...", // [!code highlight]
    "risk_signals": "*****"
  }
}
```

## Redirect user to their APP and update enrollment

You now need to redirect your user to their institution using the `redirect_url` so that they can confirm the enrollment process. During the process, they will  log in to their institution, review the enrollment request, and then authorize it. Once the user authorizes the enrollment, the institution will redirect them back to the `callback_url` you provided.

Success Example
```json
https://redirect.clientApplication.com/
	?state=<state>
	&code=<code>
	&id_token=<long_id_token>
```

Error Example
```json
https://redirect.clientApplication.com/
	?state=<state>
	&error=<error>
	&error_description=<error_description>
```

The institution will pass data in the query parameters that you must forward on to Belvo using the **Update Enrollment State** API request. We recommend transforming the query parameters into a JSON object and sending it directly through to Belvo.

## Update Enrollment State

SDK Convenience Method Available
The iOS SDK provides a `completeEnrollmentAfterRedirection()` method that simplifies handling OAuth callbacks:

- Accepts the full callback URL and automatically parses parameters
- Or accepts individual `state`, `code`, and `idToken` parameters
- Handles the API call internally
- Returns an enriched `Enrollment` object


**Example:**

```swift
// Option 1: Parse callback URL automatically
let enrollment = sdk.completeEnrollmentAfterRedirection(
    callbackUrl: callbackUrl // Full URL with query parameters
)

// Option 2: Provide parameters individually
let enrollment = sdk.completeEnrollmentAfterRedirection(
    state: state,
    code: code,
    idToken: idToken
)
```

With the value of the query string saved as a JSON object, you can make the following request:

```json
POST /enrollments/complete-redirection/
```

Success Example
```json Success Request Body
{
    "state": "{{state}}",
    "code": "{{code}}",
    "id_token": "{{id_token}}",
}
```

In the case that it was a successful callback, in the request response the `status` of the enrollment will still be set to `PENDING`.

```json Successful Enrollment Status Update
// 200 OK
{
    "id": "{{enrollment.id}}", // [!code highlight]
    "type": "open_finance_biometric_pix",
    "status": "PENDING",  // [!code highlight]
    "details": {
        "callback_url": "https://merchant.com/enrollment-success/",
        "customer": "{{customer.id}}",
        "expires_at": "2022-10-31T00:00:00Z",
        "institution": "uuid",
        "name": "My Enrollment",
        "payer_information": {
            "bank_account": {
                "institution_id": "{{institution.id}}",
                "agency": "1234",
                "number": "*****6789",
                "account_type": "CHECKINGS"
            }
        },
        "platform": "IOS",
        "redirect_url": "https://example.com/redirect-enrollment/",
        "risk_signals": "*******",
        "status": "AWAITING_ACCOUNT_HOLDER_VALIDATION"
    },
    "external_id": null,
    "metadata": {},
    "status_reason_code": null,
    "status_reason_message": null,
    "created_by": "{{belvo_client.id}}",
    "created_at": "{{timestamp}}",
    "updated_at": "{{timestamp}}"
}
```

The institution will now process the enrollment data and provide Belvo the FIDO Options that are required to generate the biometric challenge. You will need to poll our API to retrieve this data to then request biometric data from your user.

Error Example
```json
{
    "state": "{{state}}",
    "error": "{{error}}",  
    "error_description": "{{error_description}}"
}
```

In the case that it was a error callback, our API will still respond with a `200 - OK` with the `status` of the enrollment will still be set to `FAILED`. Additionally, the `status_reason_code` and `status_reason_message` will be set to provide more information about the failure.

```json Failed Enrollment Status Update
// 200 OK
{
    "id": "{{enrollment.id}}", // [!code highlight]
    "type": "open_finance_biometric_pix",
    "status": "FAILED",  // [!code highlight]
    "details": {
        "callback_url": "https://merchant.com/enrollment-success/",
        "customer": "{{customer.id}}",
        "expires_at": "2022-10-31T00:00:00Z",
        "institution": "uuid",
        "name": "My Enrollment",
        "payer_information": {
            "bank_account": {
                "institution_id": "{{institution.id}}",
                "agency": "1234",
                "number": "*****6789",
                "account_type": "CHECKINGS"
            }
        },
        "platform": "IOS",
        "redirect_url": "https://example.com/redirect-enrollment/",
        "risk_signals": "*******",
        "status": "AWAITING_ACCOUNT_HOLDER_VALIDATION"
    },
    "external_id": null,
    "metadata": {},
    "status_reason_code": "insufficient_funds", // [!code highlight]
    "status_reason_message": "No funds", // [!code highlight]
    "created_by": "{{belvo_client.id}}",
    "created_at": "{{timestamp}}",
    "updated_at": "{{timestamp}}"
}
```

## Poll Belvo API for FIDO options (API)

SDK Convenience Method with Auto-Polling
The iOS SDK provides a `getFidoRegistrationOptions()` method that simplifies FIDO option retrieval:

- Automatically polls every 1 second for up to 5 minutes
- Returns `FidoRegistrationOptions` when ready, or `nil` if polling times out
- Eliminates the need for manual polling logic


**Example:**

```swift
let fidoOptions = sdk.getFidoRegistrationOptions(enrollmentId: enrollmentId)
if let fidoOptions = fidoOptions {
    // Options ready, proceed with registration
    sdk.startRegistration(fidoResponseString: fidoOptions.toJsonString(), callback: self)
} else {
    // Polling failed or timed out
}
```

Manual Polling Tips
If implementing manual polling: Send a request every two seconds until you receive a response or two minutes pass with no response. If you do not receive response after two minutes, display a "Try again" screen to your user and restart the process. In the background, the Enrollment will transition to the `status` = `FAILED`.

After you receive the successful response from the **Update Enrollment State** request, you need to poll the endpoint below in order to receive the FIDO registration options required to prompt for biometric data.

```bash
GET /enrollments/{enrollment_id}/fido-registration-options/
```

You will receive the following `200 - OK` response from our API. Make sure to save the object (`fidoOptions`) as it is a required parameter for the `startRegistration()` SDK method.

```json
// 200 OK
{
    "rp": {
        "id": "belvo.com",
        "name": "Raidiam Mockbank - Pipeline NRJ"
    },
    "user": {
        "id": "a5bd0ef9-f8ab-41a2-b968-489761a91de6",
        "name": "Ralph Bragg",
        "displayName": "Ralph Bragg"
    },
    "challenge": "R3dsT2REOE5oZ25JbVE",
    "pubKeyCredParams": [
        {
            "alg": -257,
            "type": "public-key"
        },
        {
            "alg": -7,
            "type": "public-key"
        }
    ],
    "extensions": {
        "appid": "true"
    }
}
```

## Prompt for Biometrics (iOS SDK)

With the payload received, you need to use the **startRegistration(fidoOptions)** method. This method starts the biometric credential registration using FIDO2 protocols. It processes the FIDO registration options (a JSON string) received from your backend server and launches the device's native biometric authentication flow (e.g., fingerprint or face scan).

```swift
import Foundation
import BiometricPixSDK

class FidoRegistrationViewModel : NSObject, ObservableObject {
    private let sdk = BiometricPixSDK()

    func startRegistration(fidoOptions: String) {
        do {
            sdk.startRegistration(
                fidoResponseString: fidoOptions, // [!code highlight]
                callback: self // Callback instance
            )
        } catch {
            // Handle errors
        }
    }
}

// Implementing callback for registration
extension FidoRegistrationViewModel: FidoRegistrationCallback {
    func onError(error: String) {
            // Handle registration errors
    }

    func onSuccess(response attestationResponse: AttestationResponse) {
            // Handle successful registration
            // The attestationResponse object contains the data needed for the next step.
            let encodedId = attestationResponse.encodedId
            let rawId = attestationResponse.rawId
            let encodedAttestationObject = attestationResponse.encodedAttestationObject
            let encodedClientDataJSON = attestationResponse.encodedClientDataJSON
            
            // Persist these values to be sent to your backend.
    }
}
```

When using this view model in SwiftUI views, declare them as @ObservedObject properties to ensure they aren't destroyed during view re-renders. This is critical for callbacks to work properly.

```swift
import SwiftUI

struct MyView: View {
    @ObservedObject var registrationViewModel = FidoRegistrationViewModel()
// ...
}
```

You need to store the following values in variables as they are used to confirm the Enrollment in the following step:

- `encodedId`
- `rawId`
- `encodedAttestationObject`
- `encodedClientDataJSON`


## Send biometrics to finalize enrollment and poll for response (API)

SDK Convenience Method Available
The iOS SDK provides a `confirmEnrollment()` method that simplifies confirming the enrollment:

- Automatically handles payload creation from FIDO credentials
- Handles the API call internally
- Returns `Bool` indicating success


**Example:**

```swift
extension ViewModel: FidoRegistrationCallback {
    func onSuccess(credential: PublicKeyCredential, response: AuthenticatorAttestationResponse) {
        // SDK handles payload creation automatically
        let success = sdk.confirmEnrollment(enrollmentId: enrollmentId, credential: credential, response: response)
        if success {
            // Enrollment confirmed
        }
    }
    
    func onError(error: String) {
        // Handle error
    }
}
```

To complete the Enrollment process you will need to send the values you received to the following endpoint:

```bash
POST /payments/br/enrollments/{enrollment_id}/confirm/
```

```json
// Request Body
{
  "confirmation_data": {
    "authenticatorAttachment": "platform",
    "id": "{{encodedId}}",
    "rawId": "{{rawId}}",
    "type": "public-key",
    "response": {
      "attestationObject": "{{encodedAttestationObject}}",
      "clientDataJSON": "{{encodedClientDataJSON}}"
    }
  }
}
```

| Parameter | Type | Description |
|  --- | --- | --- |
| `authenticatorAttachment` | string | The type of authenticator. Must be set to `platform`. |
| `id` | string | The `encodedId` you received from the `startRegistration()` method. |
| `rawId` | string | The `rawId` you received from the `startRegistration()` method. |
| `type` | string | The type of FIDO credential being generated.  Must be set to `public-key`. |
| `response.attestationObject` | string | The `encodedAttestationObject` you received from the `startRegistration()` method. |
| `response.clientDataJSON` | string | The `encodedClientDataJSON` you received from the `startRegistration()` method. |


Belvo will respond with a `204 - No Content` and forward the information to the institution to complete the enrollment process.

Polling Tips
Send a request every two seconds until you receive a response or two minutes pass with no response. If you do not receive  response after two minutes, display a “Try again” screen to your user and restart the process. In the background, the Enrollment will transition to the `status` = `FAILED`.

You will need to poll the following endpoint until you receive a response from Belvo’s API. Once you receive a response, check the `status` field.

```bash
GET /enrollments/{enrollment_id}/
```

If the `status` is `SUCCEEDED`, perfect! The enrollment is ready and you can start making payments!

## Making a Payment

Once a user's device is successfully enrolled, you can initiate payment requests using their stored biometric credentials. This process involves:

1. Selecting an Enrollment
2. Creating a payment intent
3. Collecting biometric authentication data
4. Authorizing the payment


```mermaid
sequenceDiagram
    autonumber

    participant EndUser
    participant ClientAppBackend
    participant BiometricPixSDK
    participant Payments

    %% 1 - Payment Intent Creation using authorized enrollment
    Note over EndUser,Payments: 1. User selects Enrollment (API)

    EndUser ->> ClientAppBackend: Clicks "Realizar Pagamento"
    ClientAppBackend ->> Payments: GET /enrollments/?device_id=1234
    Payments -->> ClientAppBackend: Returns list of enrollments
    ClientAppBackend -->> EndUser: Show list of enrollments
    EndUser ->> ClientAppBackend: Chooses from enrollment list

    Note over EndUser,Payments: 2. Create Payment Intent (API)

    ClientAppBackend ->> Payments: POST /payment-intents/ (enrollment_id)
    Payments -->> ClientAppBackend: Returns payment_intent data (id, fido_options)
    ClientAppBackend ->> ClientAppBackend: Persist Payment Intent ID

    %% 2 - Payment Intent Authorization using AttestationObject + RiskSignals
    Note over EndUser,Payments: 3. Collect biometric data (SDK)

    ClientAppBackend ->> BiometricPixSDK: startSigning(fidoOptions)
    ClientAppBackend ->> BiometricPixSDK: collectRiskSignals(accountTenure)
    BiometricPixSDK -->> ClientAppBackend: Returns credentialId, attestationObject, clientDataJSON, riskSignals

    Note over EndUser,Payments: 4. Authorize payment (API)

    ClientAppBackend ->> Payments: POST /payment-intents/{id}/authorize/ (credentialId, attestationObject, clientDataJSON, riskSignals)
    Payments -->> ClientAppBackend: 204 - No Content
    ClientAppBackend ->> Payments: Poll GET /payment-intents/{id}/
    Payments -->> ClientAppBackend: status = SUCCEEDED

    ClientAppBackend ->> EndUser: Show success screen
```

## **Select Enrollment (API)**

SDK Convenience Method Available
The iOS SDK provides a `listEnrollments()` method that simplifies fetching enrollments:

- Automatically handles the API call with device ID
- Returns `[Enrollment]` with enriched institution data
- Each enrollment includes optional full `Institution` object


**Example:**

```swift
let enrollments = sdk.listEnrollments(deviceId: deviceId)
enrollments.forEach { enrollment in
    if let institution = enrollment.institution {
        print("\(institution.displayName): \(enrollment.status)")
    }
}
```

Use the List all enrollments API method, with the required `device_id` query parameter, to request all the enrollments your user has made using your application and their current device. Display this list of enrollments to the user, allowing them to choose which enrollment to use for the payment. Save the `id` of that enrollment (used in the following Create Payment Intent step).

```bash
GET /enrollments/?device_id={device_id}
```

## **Create Payment Intent (API)**

SDK Convenience Method Available
The iOS SDK provides a `createPaymentIntent()` method that simplifies creating payment intents:

- Handles the API call internally
- Returns complete `PaymentIntent` object with FIDO options
- Supports all payment intent parameters


**Example:**

```swift
let payload = CreatePaymentIntentPayload(
    amount: 100.50,
    customer: Customer(identifier: "12345678900"),
    description: "Payment for services",
    allowedPaymentMethodTypes: ["open_finance_biometric_pix"],
    paymentMethodDetails: PaymentMethodDetails(
        openFinanceBiometricPix: OpenFinanceBiometricPixPaymentMethodDetails(
            beneficiaryBankAccount: "bank-account-id",
            enrollment: "enrollment-id"
        )
    )
)
let paymentIntent = sdk.createPaymentIntent(payload: payload)
```

Once you have the user's selected Enrollment, you can create a Payment Intent:

```shell
POST /payments/br/payment-intents/
```

```json
{
    "amount": 0.13,
    "allowed_payment_method_types": [
        "open_finance_biometric_pix"
    ],
    "customer": "{{customer.id}}",
    "description": "Test Payment Intent with Enrollment",
    "statement_description": "Description to show on statement",
    "payment_method_details": {
        "open_finance_biometric_pix": {
            "beneficiary_bank_account": "{{bank_account.id}}",
            "enrollment": "{{enrollment.id}}"
        }
    },
    "confirm": true
}
```

| Parameter | Type | Required | Description |
|  --- | --- | --- | --- |
| `amount` | number | true | The amount to pay. |
| `allowed_payment_method_types` | string | true | The type of payment method. Must be set to `open_finance_biometric_pix`. |
| `customer` | string (uuid) | true | The `id` of the customer from whom you are requesting payments. |
| `description` | string | true | Your description for the payment. |
| `statement_description` | string | true | The description that will appear on your user’s bank account statement. |
| `payment_method_details.open_finance_biometric_pix.beneficiary_bank_account` | string (uuid) | true | The `id` of the bank account that will receive the funds. |
| `payment_method_details.open_finance_biometric_pix.enrollment` | string (uuid) | true | The `id` of the Enrollment the user selected. |
| `confirm` | boolean | true | Confirms that the payment is ready to be processed. Must be set to `true`. |


In the response, Belvo will return the `payment_intent.id`  and the `fido_options` object that are required for the next step of biometric authentication. You need to:

- Persist the `payment_intent.id` on your backend.
- Save the `fido_options` in a variable to be used in the next step in the Belvo SDK.


```json
{
  "id": "uuid", // [!code highlight]
  "status": "PENDING",
  "payment_method_information": {
	  "open_finance_biometric_pix": {
	      "provider": "belvo",
	      "consent_id": "urn:nubank:023230b9-1211-3420-bf6d-e7d56e87bdf1",
	      "fido_options": { // [!code highlight]
	          "rpId": "belvo.com",
	          "timeout": 300000,
	          "challenge": "oGW096Hvr8sVUIOf-10iqWI7ZfSx2GhoU359bBRK9h4",
	          "allowCredentials": [
	              {
	                  "id": "AfD-uI4LUzJAuzyLBRrPncocLusMgZ8yHNuuUl-7NSFbBlqrW2rMF0D_Ao-orNqdX3YZVf8_wk1jj--HuNH1uKE",
	                  "type": "public-key"
	              }
	          ]
	      },
	      "end_to_end_id": "E432158152025061315009OzwiMmDSO7",
	      "external_payment_id": "bde3bb4d-5b48-4875-b69d-7f2beee4fb42",
	      "provider_request_id": "afc99a8b-e0c7-4a8b-85d7-193bd70e4cc0"
	  }
	}
}
```

## Collect Biometric Data and Risk Signals (SDK)

Using the `fido_options` received from the payment intent, initiate the biometric authentication process using the `startSigning(fidoOptions)` method of the Belvo iOS SDK. After the user authenticates, you will also need to call `collectRiskSignals(accountTenure)` to gather device information.

The results from these methods contain the values you will need to send to your backend to authorize the payment.

```swift
import Foundation
import BiometricPixSDK

class FidoAuthenticationViewModel : NSObject, ObservableObject {
    private let sdk = BiometricPixSDK()

    func startSigning(fidoOptions: String) {
        do {
            // Note: The method to initiate payment authentication is startSigning.
            sdk.startSigning(
                fidoResponseString: fidoOptions,// json string requested from the backend api
                fallbackCredential: nil,//fallback credential string if avaliable
                callback: self// callback instance
            )
        } catch {
                        // Error handling
        }
    }
    
    private func collectRiskSignals() {
        do {
            // Collect risk signals with the user creation date
            let signals = sdk.collectRiskSignals(
                accountTenure: "YYYY-MM-DD"
            )
            // Send the signals to your backend to be used in the authorize step.
        } catch {
            // handle error
        }
    }
}

// implementing callback for authentication
extension FidoAuthenticationViewModel: FidoAuthenticationCallback {
    func onError(error: String) {
                // Handle authentication errors
    }

    func onSuccess(response assertionResponse: AssertionResponse) {
                // Handle successful authentication
        // Extract data from assertionResponse and send to your backend.
        // Then, call collectRiskSignals.
        collectRiskSignals()
    }
}
```

When using this view model in SwiftUI views, declare them as @ObservedObject properties to ensure they aren't destroyed during view re-renders. This is critical for callbacks to work properly.

```swift
import SwiftUI

struct MyView: View {
    @ObservedObject var registrationViewModel = FidoRegistrationViewModel()
// ...
}
```

## Authorize Payment (API)

SDK Convenience Method Available
The iOS SDK provides an `authorizePaymentIntent()` method that simplifies payment authorization:

- Handles the API call internally
- Accepts `AuthorizePaymentIntentPayload` with platform, risk signals, and assertion
- Returns `Bool` indicating success


**Example:**

```swift
let payload = AuthorizePaymentIntentPayload(
    platform: "ios",
    riskSignals: riskSignals,
    assertion: assertion
)
let success = sdk.authorizePaymentIntent(paymentIntentId: paymentIntentId, payload: payload)
```

After retrieving all the required information in the Collect Biometric Data and Risk Signals (SDK) step, you can now authorize the payment using Belvo's API.

```shell
POST /payment-intents/{payment_intent_id}/authorize/
```

- Belvo will process the authorization. You will need to poll `GET /payment-intents/{id}/` until the `status` of the payment intent becomes `SUCCEEDED`.
**Polling Strategy:** Similar to enrollment, we recommend polling the payment intent status (`GET /payment-intents/{payment_intent_id}/`) every two seconds for up to two minutes until the status is `SUCCEEDED`. If the status does not change or an error occurs, inform the user and suggest retrying.
- Once the payment is successful, display a confirmation screen to the End User.


```json
// POST /payment-intents/{id}/authorize/ Request Body Example
{
  "risk_signals": {}, // The Risk Signals object collected
  "assertion": {
    "authenticatorAttachment": "platform",
    "id": "{{encodedId}}",
    "rawId": "{{rawId}}",
    "response": {
      "authenticatorData": "{{encodedAuthenticatorData}}",
      "clientDataJSON": "{{encodedClientDataJSON}}",
      "signature": "{{encodedSignature}}",
      "userHandle": "{{userHandle}}"
      
    },
    "type": "public-key"
  }
}
```

Belvo's API will return a `204 - Not Content`. After which, you need to poll the following endpoint in order to retrieve the final status of the payment:

```shell
GET /payment-intents/{payment_intent_id}/
```

Polling Tips
Send a request every two seconds until you receive a response or two minutes pass with no response. If you do not receive  response after two minutes, display a “Try again” screen to your user and restart the process. In the background, the Payment Intent and associated Charge will transition to the `status` = `FAILED`.