> For the complete documentation index, see [llms.txt](https://docs.payments.thalescloud.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.payments.thalescloud.io/push-provisioning/es/implement-push-provisioning/implement-push-to-digital-wallets/apple-wallet-extension.md).

# Extensión de Apple Wallet

## Extensión de Apple Wallet

Utilice las extensiones de Apple Wallet para permitir a los usuarios finales descubrir y provisionar tarjetas directamente desde la aplicación Apple Wallet, además de la aplicación del emisor.

A través de extensiones de la aplicación, puede exponer funcionalidad y contenido personalizados fuera de la aplicación del emisor y ponerlos a disposición mientras el usuario final interactúa con Apple Wallet o el sistema.

Provisionar tarjetas dentro de la aplicación Apple Wallet mejora la experiencia dentro de la aplicación al evitar la introducción manual de los datos de la tarjeta de pago.

Consulte [visión general de extensiones de Apple](https://developer.apple.com/library/archive/documentation/General/Conceptual/ExtensibilityPG/ExtensionOverview.html) para información general sobre las extensiones de iOS.

### Experiencia del usuario

El flujo de extremo a extremo de la extensión de Apple Wallet tiene dos fases principales:

1. Apple Wallet detecta que la aplicación del emisor tiene tarjetas disponibles para provisionar.
2. El usuario final se autentica y selecciona qué tarjetas añadir; Apple Wallet solicita entonces la carga útil de provisión a la aplicación del emisor a través del SDK D1.

#### Descubrimiento y autenticación

<figure><img src="/files/4d46caf9aa1ebca0bbe8e91d3f2e9ae1d2603682" alt="Apple Wallet showing issuer application option for adding a card"><figcaption><p>Apple Wallet muestra la aplicación del emisor como una opción cuando hay tarjetas disponibles para provisionar.</p></figcaption></figure>

{% stepper %}
{% step %}

#### Descubrir la aplicación del emisor en Apple Wallet

Apple Wallet consulta con la aplicación del emisor para determinar si hay tarjetas disponibles. Si hay tarjetas disponibles, la aplicación del emisor aparece en la lista de opciones.
{% endstep %}

{% step %}

#### El usuario final selecciona la aplicación del emisor

Cuando el usuario final selecciona la aplicación del emisor, Apple Wallet activa la extensión de UI de la aplicación del emisor para autenticar al usuario final.
{% endstep %}

{% step %}

#### Autenticar al usuario final

La extensión de UI de la aplicación del emisor autentica al usuario final usando las mismas credenciales y métodos (por ejemplo, inicio de sesión manual y biometría) que la aplicación principal del emisor.
{% endstep %}
{% endstepper %}

#### Seleccionar y provisionar tarjetas

<figure><img src="/files/05bb6c1bf64cf91555ed4de7dcd6634b8395eb79" alt="Apple Wallet showing available cards from the issuer application"><figcaption><p>Apple Wallet obtiene y muestra las tarjetas disponibles de la aplicación del emisor.</p></figcaption></figure>

{% stepper %}
{% step %}

#### Apple Wallet obtiene las tarjetas disponibles

Apple Wallet solicita la lista de tarjetas disponibles a la aplicación del emisor a través de la extensión sin UI. El usuario final ve una lista de tarjetas para añadir.
{% endstep %}

{% step %}

#### Ver detalles de la tarjeta

El usuario final puede revisar los detalles de la tarjeta (por ejemplo, diseño de la tarjeta, título del producto y los últimos cuatro dígitos) antes de añadir la tarjeta.
{% endstep %}

{% step %}

#### Aceptar términos y condiciones

El usuario final revisa y acepta los términos y condiciones del emisor y de la red de pago.
{% endstep %}

{% step %}

#### Carga útil de provisión y tokenización

Para cada tarjeta seleccionada, Apple Wallet solicita una carga útil de provisión a la aplicación del emisor y luego inicia el flujo de tokenización con el TSP de la red de pago.
{% endstep %}
{% endstepper %}

### Extensiones

La integración con Apple Wallet requiere dos tipos de extensiones de la aplicación del emisor:

* **Extensión sin UI**\
  Informa el estado del flujo de la extensión y la disponibilidad de tarjetas. Esto cubre los pasos de descubrimiento de la tarjeta y la carga útil de provisión. Se implementa extendiendo la clase base del SDK D1 `D1IssuerProvisioningExtensionHandler`.
* **Extensión de UI**\
  Autentica al usuario final. Esta es una pantalla separada que usa las mismas credenciales de inicio de sesión que la aplicación del emisor. Implementa el `D1IssuerProvisioningExtensionAuthorizationProviding` protocolo.

{% hint style="info" %}
Las extensiones de Apple Wallet se instalan al mismo tiempo que la aplicación del emisor. Para que Apple Wallet detecte que hay pases para añadir y muestre la extensión, el usuario final debe iniciar la aplicación del emisor al menos una vez.
{% endhint %}

El SDK D1 proporciona una implementación base para la extensión sin UI para que no tenga que implementar la lógica completa usted mismo. La aplicación del emisor llama a `D1Task.configure()` con `WalletExtensionConfig` durante el lanzamiento de la aplicación para compartir detalles de las tarjetas con la extensión. Aunque esta configuración solo es necesaria una vez después de la instalación, recomendamos llamarla en cada lanzamiento de la aplicación para mantener la información de las tarjetas actualizada.

<figure><img src="/files/4ffefaa1e5eaf61591438330f23e01e8fec1b357" alt="High-level flow for configuring the Apple Wallet extension via D1 SDK"><figcaption><p>Flujo a alto nivel: la aplicación del emisor configura el SDK D1 para que Apple Wallet pueda descubrir las tarjetas.</p></figcaption></figure>

Después de una autenticación exitosa en la extensión de UI, la aplicación del emisor debe llamar a `D1IssuerProvisioningExtensionAuthorizationProviding.login()` para que el SDK pueda continuar con los pasos de la extensión sin UI.

### Diagramas de secuencia

#### 1. Apple Wallet muestra el botón de la aplicación del emisor

<figure><img src="/files/435cdbd4ac0ea59c93b4cdb8b9163f827763b188" alt="Sequence diagram showing how Apple Wallet discovers the issuer application"><figcaption><p>Apple Wallet detecta que la aplicación del emisor tiene tarjetas disponibles para provisionar.</p></figcaption></figure>

#### 2. Autenticación del usuario final

<figure><img src="/files/cc457112483ad1ef50db2c9d289d411c9e3c630d" alt="Sequence diagram showing Apple Wallet triggering issuer UI extension for authentication"><figcaption><p>Apple Wallet inicia la extensión de UI de la aplicación del emisor para la autenticación.</p></figcaption></figure>

#### 3. Se muestran las tarjetas disponibles

<figure><img src="/files/3f8f9ab2c8a1e3727fc0cc6be1a7fada5091990e" alt="Sequence diagram showing Apple Wallet requesting card list from issuer"><figcaption><p>Apple Wallet solicita la lista de tarjetas disponibles y las muestra al usuario final.</p></figcaption></figure>

#### 4. Tokenización

<figure><img src="/files/3d717630d3380f447d190656b76b3d47371e1dfa" alt="Sequence diagram showing Apple Wallet requesting provisioning payload and tokenizing"><figcaption><p>Apple Wallet solicita cargas útiles de provisión y tokeniza las tarjetas seleccionadas.</p></figcaption></figure>

### Integre Apple Wallet mediante el SDK D1

#### Configuración del SDK

Cuando realiza [inicialización del SDK de iOS](/push-provisioning/es/integrate-the-d1-sdk/getting-started/configuration/3.-initialization/ios-initialization.md), la aplicación del emisor también debe llamar a `configure(ConfigParams.walletExtensionConfig(...))` y proporcionar:

**`cardParamsList`**

Use esta variante cuando tenga un único `consumerId` y un único `issuerId`:

* `cardParamsList` – Lista de IDs de tarjeta, diseño de la tarjeta, títulos de producto y los últimos cuatro dígitos. El diseño de la tarjeta y el título del producto se muestran en Apple Wallet.
* `appGroupID` – El ID del contenedor compartido para que el SDK D1 pueda compartir datos entre la aplicación del emisor y sus extensiones.

```swift
let coreConfig = ConfigParams.coreConfig(consumerID: "<obtenido del servidor>")
let cardConfig = ConfigParams.cardConfig()

let card = ConfigParams.CardParams(cardID: "<obtenido del servidor>",
        cardArt: UIImage(named: "<nombre_de_imagen>")!, productTitle: "<título del producto>", last4: "<últimos4>")
let walletExtensionConfig = ConfigParams.walletExtensionConfig(cardParamsList: [card],
        appGroupID: "<ID de App Group definido en las capacidades de la app>")

d1Task.configure([coreConfig, cardConfig, walletExtensionConfig]) { errors in
    if let errors = errors {
        for error in errors {
            // Inspeccione los errores para identificar qué configuración falló
            // Consulte la sección de gestión de errores de integración del SDK D1
        }
    }
}

// Guarde consumerId para autenticar al mismo usuario final en el flujo de la extensión de Wallet
saveToKeychain(key: "D1ConsumerID", value: "<consumer_ID>")

func saveToKeychain(key: String, value: String) {
    guard let valueData = value.data(using: .utf8) else {
        return
    }
    let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
        kSecAttrService as String: "<servicio asociado, p. ej. IssuerAppService>",
        kSecAttrAccessGroup as String: "<ID de App Group definido en las capacidades de la app>",
        kSecAttrAccount as String: key]
    var addQuery = query
    addQuery[kSecValueData as String] = valueData
    let status = SecItemAdd(addQuery as CFDictionary, nil)

    if status == errSecDuplicateItem  {
        let updatedData: [String: Any] = [kSecValueData as String: valueData]
        SecItemUpdate(query as CFDictionary, updatedData as CFDictionary)
    }
}
```

**`issuerParamsList`**

Use esta variante cuando admita múltiples combinaciones de `issuerId` y `consumerId`:

* `issuerParamsList` – Lista de `cardParamsList` con los correspondientes `issuerId` y `consumerId`.
* `appGroupID` – El ID del contenedor compartido para que el SDK D1 pueda compartir datos entre la aplicación del emisor y sus extensiones.

```swift
let coreConfig = ConfigParams.coreConfig(consumerID: "<obtenido del servidor>")
let cardConfig = ConfigParams.cardConfig()

var issuerParamsList: [ConfigParams.IssuerParams] = []
let card1 = ConfigParams.CardParams(cardID: "<obtenido del servidor>",
        cardArt: UIImage(named: "<nombre_de_imagen>")!, productTitle: "<título del producto>", last4: "<últimos4>")
let card2 = ConfigParams.CardParams(cardID: "<obtenido del servidor>",
        cardArt: UIImage(named: "<nombre_de_imagen>")!, productTitle: "<título del producto>", last4: "<últimos4>")
let card3 = ConfigParams.CardParams(cardID: "<obtenido del servidor>",
        cardArt: UIImage(named: "<nombre_de_imagen>")!, productTitle: "<título del producto>", last4: "<últimos4>")
let issuerParam1 = ConfigParams.IssuerParams(cardParams: card1, issuerID: "<issuerId 1>", consumerID: "<consumerId 1>")
let issuerParam2 = ConfigParams.IssuerParams(cardParams: card2, issuerID: "<issuerId 2>", consumerID: "<consumerId 2>")
let issuerParam3 = ConfigParams.IssuerParams(cardParams: card3, issuerID: "<issuerId 2>", consumerID: "<consumerId 3>")
issuerParamsList.append(issuerParam1)
issuerParamsList.append(issuerParam2)
issuerParamsList.append(issuerParam3)

let walletExtensionConfig = ConfigParams.walletExtensionConfig(issuerParamsList: issuerParamsList,
        appGroupID: "<ID de App Group definido en las capacidades de la app>")

d1Task.configure([coreConfig, cardConfig, walletExtensionConfig]) { errors in
    if let errors = errors {
        for error in errors {
            // Inspeccione los errores para identificar qué configuración falló
            // Consulte la sección de gestión de errores de integración del SDK D1
        }
    }
}

// Guarde los valores issuerId para usarlos durante la autenticación de la extensión de Wallet
saveToKeychain(key: "issuerID1", value: "<issuerId 1>")
saveToKeychain(key: "issuerID2", value: "<issuerId 2>")

func saveToKeychain(key: String, value: String) {
    guard let valueData = value.data(using: .utf8) else {
        return
    }
    let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
        kSecAttrService as String: "<servicio asociado, p. ej. IssuerAppService>",
        kSecAttrAccessGroup as String: "<ID de App Group definido en las capacidades de la app>",
        kSecAttrAccount as String: key]
    var addQuery = query
    addQuery[kSecValueData as String] = valueData
    let status = SecItemAdd(addQuery as CFDictionary, nil)

    if status == errSecDuplicateItem  {
        let updatedData: [String: Any] = [kSecValueData as String: valueData]
        SecItemUpdate(query as CFDictionary, updatedData as CFDictionary)
    }
}
```

{% hint style="info" %}
Recomendamos encarecidamente usar el `last4` parámetro.

Sin `last4`, el SDK puede interpretar incorrectamente el estado de tokenización en escenarios de dispositivos compartidos. Por ejemplo, si el Usuario A tokeniza una tarjeta y luego el Usuario B inicia sesión en la misma aplicación del emisor en el mismo dispositivo, el SDK podría asumir incorrectamente que la tokenización ya está completa para el Usuario B.
{% endhint %}

#### ID de App Group

Por defecto, la aplicación del emisor y sus extensiones no comparten almacenamiento. Use App Groups para compartir almacenamiento entre ellas. Vea el [visión general de extensiones de Apple](https://developer.apple.com/library/archive/documentation/General/Conceptual/ExtensibilityPG/ExtensionOverview.html) para más detalles.

`appGroupID` identifica el contenedor compartido entre la aplicación del emisor y sus extensiones. Se configura en Xcode bajo **Capacidades**.

<figure><img src="/files/82f5552dc6125db387cb158de290abb3e7ef471e" alt="Xcode configuration for app groups used by issuer application and extensions"><figcaption><p>Ejemplo de configuración de un App Group en Xcode para la aplicación del emisor y sus extensiones.</p></figcaption></figure>

{% hint style="info" %}
Asegúrese de que el ID de App Group esté correctamente añadido a Xcode antes de usarlo en `configure(ConfigParams.walletExtensionConfig(...))`. De lo contrario, el SDK D1 devuelve el `invalidAppGroupID` error.
{% endhint %}

#### Derecho de Apple Pay

De manera similar a la provisión de Apple Pay dentro de la app, debe añadir el derecho `com.apple.developer.payment-pass-provisioning` a los objetivos de la extensión de UI y de la extensión sin UI. Para detalles de incorporación, consulte la documentación de provisión de Apple Wallet o **Incorporación a la provisión push para carteras**.

#### Extensión de UI

**Crear una extensión de UI**

<figure><img src="/files/181e8345e17fe71095e2af4a6d2dfe45e53cd309" alt="Xcode dialog for creating an intents UI extension"><figcaption><p>Cree un objetivo Intents UI Extension en Xcode para la extensión de UI de Apple Wallet.</p></figcaption></figure>

{% stepper %}
{% step %}
Añada un nuevo objetivo en Xcode de tipo **Intents UI Extension**.
{% endstep %}

{% step %}
En el nuevo objetivo, habilite el mismo ID de App Group y los mismos valores de derechos que en la aplicación del emisor. Si el ID de App Group difiere, la `login` API devuelve el `walletExtensionAppGroupIDNotFound` error.
{% endstep %}

{% step %}
Actualice el `Info.plist`:

* Establezca `NSExtensionPointIdentifier` a `com.apple.PassKit.issuer-provisioning.authorization`.
* Establezca `NSExtensionPrincipalClass` a una clase que se conforme a `D1IssuerProvisioningExtensionAuthorizationProviding`.
  {% endstep %}
  {% endstepper %}

Ejemplo de derechos y configuración de la extensión:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.developer.payment-pass-provisioning</key>
    <true/>
    <key>com.apple.security.application-groups</key>
    <array>
        <string>group.com.example.IssuerApp</string>
    </array>
</dict>
</plist>
```

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>NSExtension</key>
    <dict>
        <key>NSExtensionPointIdentifier</key>
        <string>com.apple.PassKit.issuer-provisioning.authorization</string>
        <key>NSExtensionPrincipalClass</key>
        <string>$(PRODUCT_MODULE_NAME).IntentViewController</string>
    </dict>
</dict>
</plist>
```

**Integre el SDK para la extensión de UI**

El SDK D1 proporciona el `D1IssuerProvisioningExtensionAuthorizationProviding` protocolo, que extiende el [`PKIssuerProvisioningExtensionAuthorizationProviding`](https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionauthorizationproviding) de Apple y añade una `login` API.

En la extensión de UI, implemente un controlador de vista que se conforme a `D1IssuerProvisioningExtensionAuthorizationProviding`, autentique al usuario final y llame a `login` con un token de acceso D1 (por ejemplo, un JWT de su backend). Consulte su **documentación de inicio de sesión del SDK** para la generación de tokens.

**Inicio de sesión con un único issuerId**

```swift
import UIKit
import PassKit
import D1

class IntentViewController: UIViewController, D1IssuerProvisioningExtensionAuthorizationProviding {
    var completionHandler: ((PKIssuerProvisioningExtensionAuthorizationResult) -> Void)?
    private let authButton = UIButton(type: .system)

    override func viewDidLoad() {
        super.viewDidLoad()
        // Configure el diseño de la UI
        authButton.addTarget(self, action: #selector(authButtonTouched), for: .touchUpInside)
    }

    @objc func authButtonTouched() {
        // Cargue los datos relacionados con la autenticación desde el Keychain (como mínimo consumerID)
        let consumerID = loadFromKeychain(key: "D1ConsumerID")

        // Proporcione métodos de autenticación manual y biométricos según lo requiera Apple

        // Tras una autenticación exitosa, obtenga el token de acceso D1 para este consumidor
        var issuerToken = getAccessToken(sub: consumerID!)

        // Use el token de acceso para iniciar sesión en la solución D1
        login(&issuerToken) { [weak self] error in
            if let _ = error {
                self?.completionHandler?(.canceled)
            } else {
                self?.completionHandler?(.authorized)
            }
        }
    }

    func loadFromKeychain(key: String) -> String? {
        let getQuery: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
            kSecAttrService as String: "<servicio asociado, p. ej. IssuerAppService>",
            kSecAttrAccessGroup as String: "<ID de App Group definido en las capacidades de la app>",
            kSecAttrAccount as String: key,
            kSecReturnData as String: true]
        var item: AnyObject?
        let status = SecItemCopyMatching(getQuery as CFDictionary, &item)
        if status == errSecSuccess,
           let data = item as? Data {
            return String(data: data, encoding: .utf8)
        }
        return nil
    }

    func getAccessToken(sub: String) -> Data? {
        // Implemente la recuperación del token de acceso D1 para el sujeto dado
        return Data()
    }
}
```

**Inicio de sesión con múltiples issuerIds**

```swift
import UIKit
import PassKit
import D1

class IntentViewController: UIViewController, D1IssuerProvisioningExtensionAuthorizationProviding {
    var completionHandler: ((PKIssuerProvisioningExtensionAuthorizationResult) -> Void)?
    private let authButton = UIButton(type: .system)

    override func viewDidLoad() {
        super.viewDidLoad()
        // Configure el diseño de la UI
        authButton.addTarget(self, action: #selector(authMultiIssuerButtonTouched), for: .touchUpInside)
    }

    @objc func authMultiIssuerButtonTouched() {
        // Cargue los IDs de emisor desde el Keychain
        let issuerId1 = loadFromKeychain(key: "issuerID1")
        let issuerId2 = loadFromKeychain(key: "issuerID2")

        // Proporcione métodos de autenticación manual y biométricos según lo requiera Apple

        // Tras una autenticación exitosa, obtenga tokens de acceso D1 para cada emisor
        let issuerToken1 = getAccessToken(iss: issuerId1!)
        let issuerToken2 = getAccessToken(iss: issuerId2!)
        var issuerTokens = [issuerToken1, issuerToken2]

        // Use los tokens de acceso para iniciar sesión en la solución D1
        login(&issuerTokens) { [weak self] error in
            if let _ = error {
                self?.completionHandler?(.canceled)
            } else {
                self?.completionHandler?(.authorized)
            }
        }
    }

    func loadFromKeychain(key: String) -> String? {
        let getQuery: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
            kSecAttrService as String: "<servicio asociado, p. ej. IssuerAppService>",
            kSecAttrAccessGroup as String: "<ID de App Group definido en las capacidades de la app>",
            kSecAttrAccount as String: key,
            kSecReturnData as String: true]
        var item: AnyObject?
        let status = SecItemCopyMatching(getQuery as CFDictionary, &item)
        if status == errSecSuccess,
           let data = item as? Data {
            return String(data: data, encoding: .utf8)
        }
        return nil
    }

    func getAccessToken(iss: String) -> Data {
        // Implemente la recuperación del token de acceso D1 para el emisor dado
        return Data()
    }
}
```

{% hint style="info" %}
Si las tarjetas comparten el mismo `issuerId` pero tienen diferentes `consumerId` valores, genere un único `issuerToken` para ese `issuerId`.
{% endhint %}

#### Extensión sin UI

**Crear una extensión sin UI**

<figure><img src="/files/a200fa7b2ab9fff001aeefb74d606dade77aae81" alt="Xcode dialog for creating an intents extension without UI"><figcaption><p>Cree un objetivo Intents Extension en Xcode para la extensión sin UI de Apple Wallet.</p></figcaption></figure>

{% stepper %}
{% step %}
Añada un nuevo objetivo en Xcode de tipo **Intents Extension**. Borre **Incluir extensión de UI** porque la extensión de UI se creó previamente.
{% endstep %}

{% step %}
En el nuevo objetivo, habilite el mismo ID de App Group y los mismos valores de derechos que en la aplicación del emisor. Si el ID de App Group difiere, las operaciones del SDK D1 devuelven el `walletExtensionAppGroupIDNotFound` error.
{% endstep %}

{% step %}
Actualice el `Info.plist`:

* Establezca `NSExtensionPointIdentifier` a `com.apple.PassKit.issuer-provisioning`.
* Establezca `NSExtensionPrincipalClass` a una clase que extienda `D1IssuerProvisioningExtensionHandler`.
  {% endstep %}
  {% endstepper %}

Ejemplo de derechos y configuración de la extensión:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.developer.payment-pass-provisioning</key>
    <true/>
    <key>com.apple.security.application-groups</key>
    <array>
        <string>group.com.example.IssuerApp</string>
    </array>
</dict>
</plist>
```

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>NSExtension</key>
    <dict>
        <key>NSExtensionPointIdentifier</key>
        <string>com.apple.PassKit.issuer-provisioning</string>
        <key>NSExtensionPrincipalClass</key>
        <string>$(PRODUCT_MODULE_NAME).IntentHandler</string>
    </dict>
</dict>
</plist>
```

**Integre el SDK para la extensión sin UI**

El SDK D1 proporciona la clase base `D1IssuerProvisioningExtensionHandler`, que extiende el [`PKIssuerProvisioningExtensionHandler`](https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionhandler) de Apple y implementa las APIs requeridas. En la mayoría de los casos, solo necesita extender esta clase.

```swift
import D1

class IntentHandler: D1IssuerProvisioningExtensionHandler {
}
```

#### Manejo de errores

Opcionalmente, anule `errorEncountered(_:)` en la extensión sin UI para registrar o rastrear los errores recibidos durante el flujo de la extensión.

```swift
import D1

class IntentHandler: D1IssuerProvisioningExtensionHandler {

    override func errorEncountered(_ error: D1Error) {
        print("Error = \(error.localizedDescription)")
    }
}
```

Los casos de error típicos incluyen:

* El `appGroupID` es inválido o no se encuentra.
* El backend D1 devuelve un error.

```console
# Ejemplo: error en la API de estado
Error del SDK = Error en la API de estado. Causa: [2](-25300) Acceso al almacenamiento fallido. 
Error: [20009]El appGroupID no se encuentra en la UI de Apple Pay Wallet y en la extensión sin UI. 
Podría deberse a varias razones: en la aplicación principal no existe la extensión de wallet 
configuración llamando a `D1Task.configure(_:completion:)` con 
`ConfigParams.walletExtensionConfig(cardParamsList:appGroupID:)` O 
el appGroupID definido en las capacidades de la aplicación principal no está definido en las capacidades de los objetivos de extensión de UI 
y sin UI.

# Ejemplo: error en la API passEntries
Error del SDK = Error en la API passEntries. Causa: [3]Se recibió un error del servidor con 
código de error HTTP 400 y mensaje 'Server error'. Error: [40001]Errores encontrados 
durante la ejecución de operaciones de tarjetas.

# Ejemplo: error en la API remotePassEntries
Error del SDK = Error en la API remotePassEntries. Causa: [3]Se recibió un error del servidor 
con código de error HTTP 400 y mensaje 'Server error'. Error: [40001]Errores encontrados 
durante la ejecución de operaciones de tarjetas.

# Ejemplo: error en la API generate
Error del SDK = Error en la API generate. Causa: [3]Se recibió un error del servidor con 
código de error HTTP 400 y mensaje '911. Operation failed .1'. Error: [40001]Errores encontrados 
durante la ejecución de operaciones de tarjetas.
```

#### Requisitos funcionales de Apple

Para alinearse con los requisitos funcionales de Apple, el SDK D1 `D1IssuerProvisioningExtensionHandler` y la aplicación del emisor adoptan las siguientes prácticas:

* Mostrar el diseño de la tarjeta y el nombre del titular.
* Determinar si hay tarjetas disponibles (elegibilidad) en 100 ms desde que Apple invoca la API.
* Proporcionar diseño de tarjeta e iconos de la app con bordes cuadrados.
* Soportar métodos de inicio de sesión manual y biométrico para la autenticación en la extensión de UI.

### Preguntas frecuentes

<details>

<summary><strong>¿Cómo puede la aplicación del emisor proporcionar la disponibilidad de tarjetas a Apple Wallet antes de que el usuario final inicie sesión?</strong></summary>

El usuario final debe iniciar sesión en la aplicación del emisor al menos una vez para que la extensión pueda actualizarse con el estado de las tarjetas. La aplicación del emisor llama a `configure(ConfigParams.walletExtensionConfig())` para proporcionar la lista de tarjetas al SDK D1.

</details>

<details>

<summary><strong>Si un inicio de sesión falla, ¿dónde se muestra el error?</strong></summary>

Recomendamos mostrar los errores de inicio de sesión directamente en la pantalla de inicio de sesión de la extensión de UI. La extensión de UI debe seguir las mismas directrices de UX que el inicio de sesión de la aplicación del emisor.

</details>

<details>

<summary><strong>¿Cuáles son los requisitos de tamaño y resolución para las imágenes de las tarjetas?</strong></summary>

La imagen de la tarjeta digital debe seguir los requisitos funcionales de Apple:

* Proporcione imágenes en PNG (recomendado) o PDF vectorial.
  * Las extensiones de Wallet tienen límites de memoria más estrictos que las apps principales.
  * Los archivos PDF incrementan el uso de memoria porque el sistema operativo los convierte a PNG.
* Use una resolución de 1536 × 969.
* Asegúrese de que el tamaño de la imagen sea inferior a 4 MB.
* Use esquinas cuadradas (no redondeadas).
* Excluya elementos solo de la tarjeta física (por ejemplo, PAN, caracteres en relieve, holograma, contactos del chip).
* Use orientación horizontal. Si la tarjeta física es vertical, reoriente a horizontal.
* Opcionalmente agregue el indicador sin contacto cuando se admitan pagos NFC.

</details>

<details>

<summary><strong>¿Requieren las extensiones nuevos identificadores de paquete (bundle)?</strong></summary>

Sí. Los nuevos identificadores de paquete deben incluirse en el `associatedApplicationIdentifier` del operador de la red de pago (PNO). iOS admite identificadores de paquete comodín.

Los emisores también deben actualizar los pases existentes en Apple Wallet usando las APIs del PNO. Si el ID de paquete no está incluido, las tarjetas añadidas mediante la extensión de Apple Wallet no podrán ser accedidas por la aplicación del emisor (por ejemplo, para comprobaciones del estado de digitalización).

</details>

<details>

<summary><strong>¿Qué podría estar mal si el icono de la aplicación del emisor no aparece en Apple Wallet?</strong></summary>

Compruebe lo siguiente:

* La aplicación del emisor está instalada y se ha abierto al menos una vez.
* La aplicación del emisor llama a `configure(ConfigParams.walletExtensionConfig())` para actualizar la lista de tarjetas.
* La aplicación del emisor y ambas extensiones usan el mismo `appGroupID` en `configure(ConfigParams.walletExtensionConfig())` y en las capacidades de Xcode.

</details>

<details>

<summary><strong>¿Es obligatorio el aprovisionamiento dentro de la app para que las extensiones de Apple Wallet funcionen?</strong></summary>

Sí. La aplicación del emisor debe primero implementar el aprovisionamiento push dentro de la app a las carteras antes de añadir las extensiones de Apple Wallet.

</details>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.payments.thalescloud.io/push-provisioning/es/implement-push-provisioning/implement-push-to-digital-wallets/apple-wallet-extension.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
