> 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/classic-push-provisioning/es/casos-de-uso/aprovisionamiento-push-para-carteras-xpay/ios/extensiones-de-aprovisionamiento-push.md).

# Extensiones de aprovisionamiento push

Con las extensiones de aplicación, puedes exponer funcionalidad y contenido personalizados más allá de la aplicación emisora. Esto permite que el usuario final acceda a ello mientras interactúa con otras apps o con iOS. Para obtener más información, consulta la [documentación de extensiones de Apple](https://developer.apple.com/library/archive/documentation/General/Conceptual/ExtensibilityPG/ExtensionOverview.html).

Con una extensión de Apple Wallet, los tokens pueden aprovisionarse directamente desde la app Apple Wallet en dispositivos iPhone y Apple Watch compatibles.

Aprovisionar tarjetas en la app Apple Wallet mejora la experiencia dentro de la app. Evita que el usuario final introduzca manualmente los datos de la tarjeta de pago.

> <i class="fa-info-circle">:info-circle:</i>
>
> #### Nota <a href="#note" id="note"></a>
>
> La extensión de Apple Wallet se instala cuando el usuario final instala la aplicación emisora. Para que Apple Wallet detecte los `pases` y muestre la extensión, el usuario final debe iniciar la aplicación emisora al menos una vez.

## Descripción general <a href="#overview" id="overview"></a>

<figure><img src="https://2719026056-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FC22AqJs7fn1cZC7pAaOE%2Fuploads%2FClVWgwHxoNjVpgU4feVG%2F90041e6e-f6cd-42d0-92d7-698007754247-f8088707-cd00-43fb-9650-f1d91182e7fb.webp?alt=media&amp;token=354f1d82-0cd3-4d00-9233-6ea3a1ec8c2b" alt=""><figcaption></figcaption></figure>

Cuando el usuario final inicia la app Apple Wallet:

1. Apple consulta con la aplicación emisora la disponibilidad de las tarjetas llamando a la [status](https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionhandler/3571367-status) API.
2. Cuando el usuario final selecciona la aplicación emisora, esta solicita autenticación del usuario mediante la [extensión de IU](https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionauthorizationproviding).
3. Apple consulta con la aplicación emisora las tarjetas disponibles y se las muestra al usuario final. Apple llama a la [passEntries](https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionhandler/3571366-passentries) API para iPhone o a la [remotePassEntries](https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionhandler/3606586-remotepassentries) para Watch.
4. El usuario final selecciona una o varias tarjetas. Para cada tarjeta seleccionada, Apple solicita la carga útil de aprovisionamiento a la aplicación emisora. Apple luego continúa con el flujo de aprovisionamiento llamando a la [generateAddPaymentPassRequest](https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionhandler/3571365-generateaddpaymentpassrequestfor) API.

> <i class="fa-info-circle">:info-circle:</i>
>
> #### Nota <a href="#note-1" id="note-1"></a>
>
> * El SDK de Push Provisioning usa la `generateAddPaymentPassRequestForInput` API para obtener la carga útil de aprovisionamiento para el paso 4.
> * Inicializa `GenerateAddPaymentPassRequestInput` proporcionando el esquema de la tarjeta como un `CardScheme` o `String`.
> * Si los datos de autenticación del paso 2 se usan para obtener los datos de la tarjeta en los pasos 3 y 4, establece el tiempo de expiración en un valor suficiente. Por ejemplo, 5 minutos.

La extensión de Apple Wallet requiere dos extensiones en la aplicación emisora:

* Una extensión sin IU para informar el estado de la extensión y la detectabilidad de la tarjeta en la aplicación emisora. Esto cubre los pasos 1, 3 y 4. Implémentala extendiendo la [PKIssuerProvisioningExtensionHandler](https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionhandler) superclase.
* Una extensión de IU para autenticar al usuario final en el paso 2. Esta es una pantalla independiente que usa las mismas credenciales de inicio de sesión de la aplicación emisora. Implémentala ajustándote al [PKIssuerProvisioningExtensionAuthorizationProviding](https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionauthorizationproviding) protocolo.

## Integración de Apple Wallet mediante Push Provisioning SDK <a href="#integrating-apple-wallet-via-push-provisioning-sdk" id="integrating-apple-wallet-via-push-provisioning-sdk"></a>

### Configuración de la aplicación <a href="#app-configuration" id="app-configuration"></a>

No se requiere configuración adicional para el SDK de Push Provisioning. Sin embargo, debes configurar un App Group ID en la aplicación emisora. Úsalo para configurar el contenedor compartido entre la aplicación emisora y sus extensiones.

Luego, la aplicación emisora puede usar este contenedor para almacenar los datos de la tarjeta (como los últimos 4 dígitos del PAN y la imagen de la tarjeta) y los datos de autenticación. La extensión puede recuperar los mismos datos.

De forma predeterminada, la aplicación emisora no comparte almacenamiento con sus extensiones. Tanto la aplicación emisora como sus extensiones deben usar el App Group para compartir almacenamiento. Para obtener más información, consulta la [documentación de extensiones de Apple](https://developer.apple.com/library/archive/documentation/General/Conceptual/ExtensibilityPG/ExtensionOverview.html).

En Xcode, añade el App Group ID en la pestaña Capabilities, como se muestra a continuación. Para obtener más información, consulta la [documentación de Apple App Groups](https://developer.apple.com/documentation/xcode/configuring-app-groups).

<figure><img src="https://2719026056-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FC22AqJs7fn1cZC7pAaOE%2Fuploads%2FxLDts5mIVODIZDfTF0Bg%2F0f247a6e-1998-43cf-86ac-85e9950cd147-d6e5f5b6-8c1a-445c-beba-78e70777ea3c.webp?alt=media&amp;token=df94afa7-8337-4408-b652-a90b250ec2b2" alt=""><figcaption></figcaption></figure>

Después de añadir el App Group ID en Xcode, la aplicación emisora puede almacenar datos en el grupo de acceso del llavero.

```swift
// guardar los datos de autenticación del usuario y los últimos 4 dígitos de la tarjeta (y otros datos estáticos
// información, por ejemplo, nombre del titular de la tarjeta, etc.) en el llavero
// para que se pueda acceder a ellos en las extensiones de Wallet
// por ejemplo, guardar los últimos 4 dígitos de la tarjeta
saveToKeychain(key: "cardID1_Last4", value: "1234")
saveToKeychain(key: "cardID2_Last4", value: "5678")

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: "BankServiceKechain",
        kSecAttrAccessGroup as String: "group.com.thalesgroup.gemalto.tpcsdk.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]
        let status = SecItemUpdate(query as CFDictionary, updatedData as CFDictionary)
    }
}
```

### Derecho de Apple Pay <a href="#apple-pay-entitlement" id="apple-pay-entitlement"></a>

De manera similar al flujo de aprovisionamiento de Apple Pay, el desarrollador de la aplicación debe añadir el `com.apple.developer.payment-pass-provisioning` derecho a los objetivos de la extensión con IU y sin IU. Para obtener más información, consulta [configuración del SDK](/classic-push-provisioning/es/guia-para-desarrolladores/configuracion-del-sdk/ios.md).

### Extensión de IU <a href="#ui-extension" id="ui-extension"></a>

La extensión de IU se usa para realizar la autenticación del usuario. Como el SDK no ofrece esa funcionalidad, la aplicación emisora debe implementar su propia IU de autenticación en esta extensión.

#### **Crear una extensión de IU**

1. En Xcode, añade un nuevo objetivo de tipo **Intents UI Extension**.
2. En el objetivo recién creado, añade el derecho y, opcionalmente, el App Group ID. Estos valores son los mismos que los definidos en la aplicación emisora.

<figure><img src="https://2719026056-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FC22AqJs7fn1cZC7pAaOE%2Fuploads%2Fjy2R54II1hn2RGh8NL91%2Ffefd0f42-6ecc-44e7-915e-36bf0aa0945f-44d3c789-aad0-4993-940f-7fd4e66cf8f9.webp?alt=media&amp;token=12e51dd8-01ba-4afe-b934-7035150fb6ed" alt=""><figcaption></figcaption></figure>

Ejemplo de WalletExtensionsUI.entitlements:

```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>
```

3. Reemplaza los valores en `Info.plist` con los siguientes valores:

| CLAVE                      | VALOR                                                                         |
| -------------------------- | ----------------------------------------------------------------------------- |
| NSExtensionPointIdentifier | com.apple.PassKit.issuer-provisioning.authorization                           |
| NSExtensionPrincipalClass  | Esta clase se ajusta a `PKIssuerProvisioningExtensionAuthorizationProviding`. |

Ejemplo de Info.plist de la extensión de IU de Wallet:

```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>
```

**Implementa la extensión de IU `PKIssuerProvisioningExtensionAuthorizationProviding` protocolo**

La autenticación del usuario final en la extensión de Apple Wallet se implementa ajustándose al protocolo [PKIssuerProvisioningExtensionAuthorizationProviding](https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionauthorizationproviding).

Aunque Apple lo marca como opcional, recomendamos autenticar al usuario final antes del aprovisionamiento.

Ejemplo de implementación de la extensión de IU de Wallet:

```swift
import UIKit
import PassKit

// referencia: https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionauthorizationproviding
class IntentViewController: UIViewController, PKIssuerProvisioningExtensionAuthorizationProviding {
    var completionHandler: ((PKIssuerProvisioningExtensionAuthorizationResult) -> Void)?
    private let authButton = UIButton(type: .system)
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Haz cualquier configuración adicional después de cargar la vista.

        /* configurar el diseño de la IU */
        authButton.addTarget(self, action: #selector(authButtonTouched), for: .touchUpInside)
    }

    @objc func authButtonTouched() {
        // Proporciona métodos de inicio de sesión manual y biométrico según los requisitos funcionales de Apple
        // ...

        // según la autenticación
        // si tiene éxito, envía el resultado .authorized
        self.completionHandler?(.authorized)
        // de lo contrario, envía el resultado .canceled
        //self.completionHandler?(.canceled)
    }
}
```

### Extensión sin IU <a href="#non-ui-extension" id="non-ui-extension"></a>

La extensión sin IU informa la disponibilidad de la tarjeta y los detalles de la tarjeta. También obtiene la carga útil de aprovisionamiento llamando a `generateAddPaymentPassRequestForInput`.

**Crear una extensión sin IU**

1. En Xcode, añade un nuevo objetivo de tipo **Extensión de Intents**. Borra la **casilla Incluir extensión de IU** porque la extensión de IU se ha creado en la sección anterior.
2. En el objetivo recién creado, añade el derecho y, opcionalmente, el App Group ID. Los valores son los mismos que los definidos en la aplicación emisora.

<figure><img src="https://2719026056-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FC22AqJs7fn1cZC7pAaOE%2Fuploads%2Fm2BI97W9kSyQ2wuKyGaQ%2Fe9aad370-dce6-4a2f-9b27-3a70cb280af8-2336fc95-33c2-453f-96b0-45da63079c2d.webp?alt=media&amp;token=d5a877fb-8f0a-43c9-9884-f7f269b5e7bf" alt=""><figcaption></figcaption></figure>

Ejemplo de WalletExtensions.entitlements:

```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>
```

3. Reemplaza los valores en `Info.plist` con los siguientes valores:

| CLAVE                      | VALOR                                                          |
| -------------------------- | -------------------------------------------------------------- |
| NSExtensionPointIdentifier | com.apple.PassKit.issuer-provisioning                          |
| NSExtensionPrincipalClass  | Esta clase se ajusta a `PKIssuerProvisioningExtensionHandler`. |

Ejemplo de Info.plist de la extensión sin IU de Wallet:

```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>
```

**Implementa una extensión sin IU `PKIssuerProvisioningExtensionHandler` superclase**

Apple requiere que la aplicación emisora extienda la [PKIssuerProvisioningExtensionHandler](https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionhandler) superclase para implementar la extensión de Apple Wallet. Las siguientes secciones describen cada una de las API utilizadas:

* [status](https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionhandler/3571367-status)
* [passEntries](https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionhandler/3571366-passentries)
* [remotePassEntries](https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionhandler/3606586-remotepassentries)
* [generateAddPaymentPassRequest](https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionhandler/3571365-generateaddpaymentpassrequestfor)

Ejemplo de implementación de la extensión sin IU de Wallet:

```swift
// referencia: https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionhandler
class IntentHandler: PKIssuerProvisioningExtensionHandler {

    override func status(completion: @escaping (PKIssuerProvisioningExtensionStatus) -> Void) {
        // Consulta la sección siguiente: 3. Implementar la API `status` de la extensión sin IU.
    }

    override func passEntries(completion: @escaping ([PKIssuerProvisioningExtensionPassEntry]) -> Void) {
        // Consulta la sección siguiente: 4. Implementar la API `passEntries` de la extensión sin IU.
    }

    // referencia:
    // https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionhandler/3606586-remotepassentries
    // Restricción de tiempo: 20 s
    override func remotePassEntries(completion: @escaping ([PKIssuerProvisioningExtensionPassEntry]) -> Void) {
        // Consulta la sección siguiente: 5. Implementar la API `remotePassEntries` de la extensión sin IU.
    }

    // referencia:
    // https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionhandler/3571365-generateaddpaymentpassrequestfor
    // Restricción de tiempo: 20 s
    override open func generateAddPaymentPassRequestForPassEntryWithIdentifier(
            _ identifier: String,
            configuration: PKAddPaymentPassRequestConfiguration,
            certificateChain certificates: [Data],
            nonce: Data,
            nonceSignature: Data,
            completionHandler completion: @escaping (PKAddPaymentPassRequest?) -> Void
    ) {
        // Consulta la sección siguiente: 6. Implementar la API `generateAddPaymentPassRequest` de la extensión sin IU.
    }

    func loadFromKeychain(key: String) -> String? {
        let getQuery: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
            kSecAttrService as String: "BankServiceKechain",
            kSecAttrAccessGroup as String: "group.com.thalesgroup.gemalto.tpcsdk.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
    }
}
```

**Implementa una extensión sin IU `status` API**

Apple llama a la [status](https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionhandler/3571367-status) API para comprobar con la aplicación emisora si hay tarjetas disponibles.

Apple impone un límite de tiempo de 100 ms para devolver el control desde el controlador. Si lo superas, es posible que la app Apple Wallet no muestre la aplicación emisora. Recomendamos recuperar la información de la tarjeta desde el grupo local del llavero. Luego usa el SDK de Push Provisioning `getToken` API para comprobar si la tarjeta está digitalizada.

Antes de llamar a `getToken`, configura el SDK de Push Provisioning llamando a `configure`.

Ejemplo de implementación de la API de la extensión sin IU de Wallet `status:`

```swift
class IntentHandler: PKIssuerProvisioningExtensionHandler {
    // referencia:
    // https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionhandler/3571367-status
    // Restricción de tiempo: 100 ms
    override func status(completion: @escaping (PKIssuerProvisioningExtensionStatus) -> Void) {
        let status = PKIssuerProvisioningExtensionStatus()
        status.requiresAuthentication = true
        status.passEntriesAvailable = false
        status.remotePassEntriesAvailable = false

        // configura TPCSDK, por ejemplo, para el entorno PPROD
        do {
            try TPCSDK.configure(url: "https://hapi.dbp-stg.thalescloud.io/mg/tpc", issuerId: "<issuerId>")
        } catch {
            // manejar el error
            completion(status)
        }

        // cargar la información de la tarjeta desde el llavero compartido
        // Consulta el paso anterior para ver el código de ejemplo de `loadFromKeychain`
        let last4Array = [
            loadFromKeychain(key: "cardID1_Last4"),
            loadFromKeychain(key: "cardID2_Last4")
        ].compactMap { $0 }

        let semaphore = DispatchSemaphore(value: 0)
        // para cada tarjeta, comprueba si está disponible (no digitalizada)
        for last4 in last4Array {
            // llama a TPC SDK getToken para comprobar el estado de digitalización
            TPCSDK.getToken(input: GetTokenInput(last4: last4)) { local, remote, error in
                if let error = error {
                    // manejar el error
                } else {
                    if local == nil {
                        // si al menos una no está digitalizada en iPhone,
                        // hay una tarjeta disponible para digitalización
                        status.passEntriesAvailable = true
                    }

                    if remote == nil {
                        // si al menos una no está digitalizada en Watch,
                        // hay una tarjeta disponible para digitalización
                        status.remotePassEntriesAvailable = true
                    }
                }

                semaphore.signal()
            }
            semaphore.wait()

            // si tanto pass como remotePass están disponibles, no hace falta seguir iterando.
            if status.passEntriesAvailable && status.remotePassEntriesAvailable {
                break
            }
        }

        completion(status)
    }
}
```

**Implementa una extensión sin IU `passEntries` API**

Apple llama a la [passEntries](https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionhandler/3571366-passentries) API para solicitar los datos de la tarjeta disponibles para añadir a un iPhone y mostrarlos al usuario final.

Debes proporcionar la siguiente información para que la tarjeta se muestre correctamente:

* imagen de la tarjeta
* nombre del titular de la tarjeta
* últimos 4 dígitos del PAN
* esquema de la tarjeta
* título del producto de la tarjeta
* identificador de la tarjeta, usado más tarde por la `generateAddPaymentPassRequest` API

Esta API tiene un límite de tiempo de 20 s para recuperar los datos de la tarjeta. Si lo superas, la app Apple Wallet lo tratará como si no hubiera tarjetas disponibles.

Ejemplo de implementación de la API de la extensión sin IU de Wallet `passEntries:`

```swift
class IntentHandler: PKIssuerProvisioningExtensionHandler {
    var urlSession: URLSession?
    // referencia:
    // https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionhandler/3571366-passentries
    // Restricción de tiempo: 20 s
    override func passEntries(completion: @escaping ([PKIssuerProvisioningExtensionPassEntry]) -> Void) {
        var entries: [PKIssuerProvisioningExtensionPassEntry] = []
        let semaphore = DispatchSemaphore(value: 0)

        // por ejemplo, cargar el cardID desde el llavero
        let cards = [
            "cardID1",
            "cardID2",
        ]

        // Si realizas llamadas de red, DEBES reinicializar urlSession
        // urlSession = ...

        // Cada tarjeta debe tener la siguiente información; de lo contrario, podría no mostrarse:
        // - imagen de la tarjeta
        // - nombre del titular de la tarjeta
        // - últimos 4 dígitos de la tarjeta
        // - esquema de la tarjeta
        // - título del producto de la tarjeta
        // - identificador de la tarjeta, identificador de la tarjeta para identificarla en la devolución de llamada generateAddPaymentPassRequestForPassEntryWithIdentifier(...)
        for cardID in cards {
            guard let last4 = loadFromKeychain(key: "\(cardID)_Last4") else {
                // si falla el llavero, pasa a las siguientes
                continue
            }
            // comprueba si la tarjeta está disponible (no digitalizada).
            var isAvailable = false
            TPCSDK.getToken(input: GetTokenInput(last4: last4)) { local, _, error in
                if let error = error {
                    // manejar el error
                } else {
                    if local == nil {
                        // aún no está digitalizada, así que está disponible
                        isAvailable = true
                    }
                }
                semaphore.signal()
            }
            semaphore.wait()
            guard isAvailable else {
                continue
            }

            // obtener el nombre del titular de la tarjeta
            let cardHolderName = "\(cardID)_cardHolderName"
            // obtener el esquema de la tarjeta
            let scheme = PKPaymentNetwork.masterCard
            // obtener el título del producto de la tarjeta
            let productTitle = "\(cardID)_productTitle"
            // obtener la imagen de la tarjeta
            let art = UIImage(systemName: "creditcard")!.cgImage!

            // crear la configuración
            guard let config = PKAddPaymentPassRequestConfiguration(encryptionScheme: .ECC_V2) else {
                // si falla, pasa a las siguientes
                continue
            }

            config.cardholderName = cardHolderName
            config.primaryAccountSuffix = last4
            config.paymentNetwork = scheme

            // crear la entrada real
            guard let entry = PKIssuerProvisioningExtensionPaymentPassEntry(identifier: cardID,
                    title: productTitle,
                    art: art,
                    addRequestConfiguration: config) else {
                // si falla, pasa a las siguientes
                continue
            }

            entries.append(entry)
        }

        completion(entries)
    }
}
```

**Implementa la extensión sin IU `remotePassEntries` API**

De forma similar a la `passEntries` API, la [remotePassEntries](https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionhandler/3606586-remotepassentries) API se usa para obtener los datos de la tarjeta. La diferencia es que esta API comprueba las tarjetas disponibles para ser aprovisionadas en un Apple Watch. Normalmente, la tarjeta se aprovisiona en el Apple Watch a través de la app Apple Watch (app puente).

Esta API también tiene un límite de tiempo de 20 s.

Ejemplo de implementación de la API de la extensión sin IU de Wallet `remotePassEntries:`

```swift
class IntentHandler: PKIssuerProvisioningExtensionHandler {
    var urlSession: URLSession?
    // referencia:
    // https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionhandler/3606586-remotepassentries
    // Restricción de tiempo: 20 s
    override func remotePassEntries(completion: @escaping ([PKIssuerProvisioningExtensionPassEntry]) -> Void) {
        var remoteEntries: [PKIssuerProvisioningExtensionPassEntry] = []
        let semaphore = DispatchSemaphore(value: 0)

        // por ejemplo, cargar el cardID desde el llavero
        let cards = [
            "cardID1",
            "cardID2",
        ]

        // Si realizas llamadas de red, DEBES reinicializar urlSession
        // urlSession = ...

        // Cada tarjeta debe tener la siguiente información; de lo contrario, podría no mostrarse:
        // - imagen de la tarjeta
        // - nombre del titular de la tarjeta
        // - últimos 4 dígitos de la tarjeta
        // - esquema de la tarjeta
        // - título del producto de la tarjeta
        // - identificador de la tarjeta, identificador de la tarjeta para identificarla en la devolución de llamada generateAddPaymentPassRequestForPassEntryWithIdentifier(...)
        for cardID in cards {
            guard let last4 = loadFromKeychain(key: "\(cardID)_Last4") else {
                // si falla el llavero, pasa a las siguientes
                continue
            }
            // comprueba si la tarjeta está disponible (no digitalizada).
            var isAvailable = false
            TPCSDK.getToken(input: GetTokenInput(last4: last4)) { _, remote, error in
                if let error = error {
                    // manejar el error
                } else {
                    if remote == nil {
                        // aún no está digitalizada, así que está disponible
                        isAvailable = true
                    }
                }
                semaphore.signal()
            }
            semaphore.wait()
            guard isAvailable else {
                continue
            }

            // obtener el nombre del titular de la tarjeta
            let cardHolderName = "\(cardID)_cardHolderName"
            // obtener el esquema de la tarjeta
            let scheme = PKPaymentNetwork.masterCard
            // obtener el título del producto de la tarjeta
            let productTitle = "\(cardID)_productTitle"
            // obtener la imagen de la tarjeta
            let art = UIImage(systemName: "creditcard")!.cgImage!

            // crear la configuración
            guard let config = PKAddPaymentPassRequestConfiguration(encryptionScheme: .ECC_V2) else {
                // si falla, pasa a las siguientes
                continue
            }

            config.cardholderName = cardHolderName
            config.primaryAccountSuffix = last4
            config.paymentNetwork = scheme

            // crear la entrada real
            guard let entry = PKIssuerProvisioningExtensionPaymentPassEntry(identifier: cardID,
                    title: productTitle,
                    art: art,
                    addRequestConfiguration: config) else {
                // si falla, pasa a las siguientes
                continue
            }

            remoteEntries.append(entry)
        }

        completion(remoteEntries)
    }
}
```

**Implementa la extensión sin IU `generateAddPaymentPassRequest` API**

Después de que el usuario final haya seleccionado la tarjeta que se va a aprovisionar, Apple llama a la [generateAddPaymentPassRequest](https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionhandler/3571365-generateaddpaymentpassrequestfor) API para obtener la carga útil de aprovisionamiento.

TPC SDK proporciona la `generateAddPaymentPassRequestForInput` API para obtener la carga útil de aprovisionamiento.

Esta API también tiene un límite de tiempo de 20 s.

Ejemplo de implementación de la API de la extensión sin IU de Wallet `generateAddPaymentPassRequest:`

```swift
class IntentHandler: PKIssuerProvisioningExtensionHandler {
    var urlSession: URLSession?
    // referencia:
    // https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionhandler/3571365-generateaddpaymentpassrequestfor
    // Restricción de tiempo: 20 s
    override open func generateAddPaymentPassRequestForPassEntryWithIdentifier(
            _ identifier: String,
            configuration: PKAddPaymentPassRequestConfiguration,
            certificateChain certificates: [Data],
            nonce: Data,
            nonceSignature: Data,
            completionHandler completion: @escaping (PKAddPaymentPassRequest?) -> Void
    ) {
        let cardID = identifier
        // según el cardID, obtiene encryptedPayload y authorizationCode, de forma similar a TPCSDK.provision(...)
        // - encryptedPayload, información de la tarjeta cifrada en PKCS7.
        // - authorizationCode, código de autorización proporcionado por el emisor.
        let encryptedPayload = "<PKCS7 cifrado>"
        let authorizationCode = "<código de autorización>"
        // esquema de la tarjeta
        let scheme = "MASTERCARD"

        // Si realizas llamadas de red, DEBES reinicializar urlSession
        // urlSession = ...

        // llamar a TPCSDK para generar la solicitud
        let sdkInput = GenerateAddPaymentPassRequestInput(schemeString: scheme,
                encryptedPayload: encryptedPayload,
                authorizationCode: authorizationCode,
                certificateChain: certificates,
                nonce: nonce,
                nonceSignature: nonceSignature)
        // Solo aplicable para el esquema nacional. Esto se puede obtener del sistema de gestión de tarjetas del banco.
        // de lo contrario, es nil
        sdkInput.productId = "<productId>"
        TPCSDK.generateAddPaymentPassRequestForInput(sdkInput) { request, error in
            if let error = error {
                // manejar el error
                completion(nil)
            } else {
                completion(request)
            }
        }
    }
}
```

### Requisitos funcionales de Apple <a href="#apple-functional-requirements" id="apple-functional-requirements"></a>

Para ajustarse a los requisitos funcionales de Apple, la aplicación del emisor debe adoptar las siguientes prácticas:

* Mostrar la imagen de la tarjeta y el nombre del titular.
* Usar bordes cuadrados para la imagen de la tarjeta y el icono de la app.
* Admitir métodos de inicio de sesión manual y biométrico para la autenticación en la extensión de la interfaz de usuario.
* Comprobar la elegibilidad de la tarjeta en un plazo de 100 ms después de que Apple invoque la API.

## Preguntas frecuentes <a href="#faq" id="faq"></a>

**La aplicación del emisor debe proporcionar información a Apple Wallet si las tarjetas están disponibles para su aprovisionamiento antes de que el usuario final inicie sesión. ¿Cómo lo hacen los emisores?**

El usuario final debe iniciar sesión en la aplicación del emisor al menos una vez. Esto actualiza la extensión con el estado de la tarjeta antes de que el usuario final use la extensión de Apple Wallet.

**Si falla un inicio de sesión, ¿dónde muestra la app el error?**

Se recomienda mostrar el error en la pantalla de inicio de sesión. La extensión de la interfaz de usuario sigue la misma política que un inicio de sesión típico de una app.

**¿Cuáles son los requisitos de tamaño y resolución para las imágenes de la tarjeta?**

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

* Usar un PDF vectorial (recomendado) o un PNG rasterizado.
* Usar una resolución de 1536 x 969.
* Mantener el tamaño de la imagen por debajo de 4 MB.
* Usar esquinas cuadradas (no redondeadas).
* Excluir elementos exclusivos de la tarjeta física, como el número de tarjeta, los caracteres en relieve, el holograma y los contactos del chip.
* Usar orientación horizontal. Si la tarjeta física usa orientación vertical, reorientarla a horizontal.
* Se puede añadir un indicador sin contacto donde se acepten pagos NFC.

**¿Las extensiones de Apple Wallet requieren un nuevo identificador de paquete?**

Sí. Añade los nuevos IDs de paquete a `associatedApplicationIdentifier` en el PNO. iOS admite identificadores de paquete comodín. Los emisores también deben actualizar los existentes `pases` en Apple Wallet usando las APIs de PNO.

Si el ID de paquete no está incluido en `associatedApplicationIdentifier`, la aplicación del emisor podría no poder acceder a la tarjeta que se añadió a través de la extensión de Apple Wallet (por ejemplo, mediante `getToken`).

**¿Qué podría estar mal si no se muestra el icono de la aplicación del emisor?**

Asegúrate de que la nueva aplicación del emisor esté instalada y se haya abierto al menos una vez para actualizar las extensiones de Apple Wallet.

**¿Los emisores necesitan implementar el aprovisionamiento en la app para que las extensiones de Apple Wallet funcionen?**

Sí. La aplicación del emisor debe implementar [Push Provisioning](/classic-push-provisioning/es/casos-de-uso/aprovisionamiento-push-para-carteras-xpay/ios/aprovisionamiento-push.md) antes de implementar la extensión de Apple Wallet.


---

# 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 by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.payments.thalescloud.io/classic-push-provisioning/es/casos-de-uso/aprovisionamiento-push-para-carteras-xpay/ios/extensiones-de-aprovisionamiento-push.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

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.
