> 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 extensiones de la aplicación, puede exponer funcionalidad y contenido personalizado más allá de la aplicación del emisor. Esto permite al usuario final acceder a ello mientras interactúa con otras aplicaciones o iOS. Para obtener más información, consulte el [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 aplicación Apple Wallet en dispositivos iPhone y Apple Watch compatibles.

Aprovisionar tarjetas en la aplicación Apple Wallet mejora la experiencia dentro de la aplicación. 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 del emisor. Para que Apple Wallet detecte las `pases` y muestre la extensión, el usuario final debe iniciar la aplicación del emisor al menos una vez.

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

<figure><img src="/files/4d41dde30b6580b5baf781ff45e17d65a1eff85a" alt=""><figcaption></figcaption></figure>

Cuando el usuario final inicia la aplicación Apple Wallet:

1. Apple consulta con la aplicación del emisor la disponibilidad de las tarjetas llamando a la [API de estado](https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionhandler/3571367-status) API.
2. Cuando el usuario final selecciona la aplicación del emisor, el emisor solicita la autenticación del usuario utilizando la [extensión de IU](https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionauthorizationproviding).
3. Apple consulta con la aplicación del emisor las tarjetas disponibles y muestra las tarjetas al usuario final. Apple llama a la [API passEntries](https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionhandler/3571366-passentries) API para iPhone o [remotePassEntries](https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionhandler/3606586-remotepassentries) para Watch.
4. El usuario final selecciona una o más tarjetas. Para cada tarjeta seleccionada, Apple solicita la carga útil de aprovisionamiento a la aplicación del emisor. Luego Apple procede 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.
> * Si los datos de autenticación del paso 2 se usan para obtener los detalles de la tarjeta en los pasos 3 y 4, establezca 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 del emisor:

* Una extensión sin IU para informar el estado de la extensión y la descubribilidad de la tarjeta en la aplicación del emisor. Esto cubre los pasos 1, 3 y 4. Impléntela extendiendo la [superclase 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 separada que usa las mismas credenciales de inicio de sesión de la aplicación del emisor. Implémentela cumpliendo con el [protocolo PKIssuerProvisioningExtensionAuthorizationProviding](https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionauthorizationproviding) protocolo.

## Integración de Apple Wallet a través del SDK de Push Provisioning <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, debe configurar un ID de App Group en la aplicación del emisor. Úselo para configurar el contenedor compartido entre la aplicación del emisor y sus extensiones.

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

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

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

<figure><img src="/files/c3e13df7f209f226d7493c681e879e1285a07cff" alt=""><figcaption></figcaption></figure>

Después de agregar el ID de App Group en Xcode, la aplicación del emisor puede almacenar datos en el grupo de acceso al llavero.

```swift
// guardar datos de autenticación del usuario y los últimos4 de la tarjeta (y otros
// datos estáticos, p. ej. nombre del titular de la tarjeta, etc..) en el llavero
// para que puedan ser accedidos en las extensiones de Wallet
// p. ej. guardar los últimos4 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)
    }
}
```

### Apple Pay Entitlement <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 agregar la `concesión com.apple.developer.payment-pass-provisioning` concesión a ambos objetivos de extensión con y sin IU. Para obtener más información, consulte [configuración del SDK](/classic-push-provisioning/es/guia-del-desarrollador/configuracion-del-sdk/ios.md).

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

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

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

1. En Xcode, agregue un nuevo objetivo de tipo **Extensión de IU de Intents**.
2. En el objetivo recién creado, agregue la concesión y, opcionalmente, el ID de App Group. Estos valores son los mismos que los definidos en la aplicación del emisor.

<figure><img src="/files/b7ff738c7dc8048804c6594f5190ecfb3e586e8c" 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. Reemplace los valores en `Info.plist` con los siguientes valores:

| CLAVE                      | VALOR                                                                                  |
| -------------------------- | -------------------------------------------------------------------------------------- |
| NSExtensionPointIdentifier | com.apple.PassKit.issuer-provisioning.authorization                                    |
| NSExtensionPrincipalClass  | Esta clase cumple con `protocolo 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>
```

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

La autenticación del usuario final en la extensión Apple Wallet se implementa cumpliendo con el protocolo [protocolo PKIssuerProvisioningExtensionAuthorizationProviding](https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionauthorizationproviding).

Aunque Apple marca esto 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()
        // Realice cualquier configuración adicional después de cargar la vista.

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

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

        // basados en la autenticación
        // si tiene éxito, envíe el resultado .authorized
        self.completionHandler?(.authorized)
        // de lo contrario, envíe 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, agregue un nuevo objetivo de tipo **Extensión de Intents**. Borre la **Incluir extensión de IU** casilla de verificación ya que la extensión de IU se ha creado en la sección anterior.
2. En el objetivo recién creado, agregue la concesión y, opcionalmente, el ID de App Group. Los valores son los mismos que los definidos en la aplicación del emisor.

<figure><img src="/files/3a05dfddc6c82cb0fb940d8bc41e25a8f82e3484" 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. Reemplace los valores en `Info.plist` con los siguientes valores:

| CLAVE                      | VALOR                                                                        |
| -------------------------- | ---------------------------------------------------------------------------- |
| NSExtensionPointIdentifier | com.apple.PassKit.issuer-provisioning                                        |
| NSExtensionPrincipalClass  | Esta clase que cumple con `superclase 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>
```

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

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

* [API de estado](https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionhandler/3571367-status)
* [API 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) {
        // Consulte la sección abajo: 3. Implementar la API `status` de la Extensión sin IU.
    }

    override func passEntries(completion: @escaping ([PKIssuerProvisioningExtensionPassEntry]) -> Void) {
        // Consulte la sección abajo: 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: 20s
    override func remotePassEntries(completion: @escaping ([PKIssuerProvisioningExtensionPassEntry]) -> Void) {
        // Consulte la sección abajo: 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: 20s
    override open func generateAddPaymentPassRequestForPassEntryWithIdentifier(
            _ identifier: String,
            configuration: PKAddPaymentPassRequestConfiguration,
            certificateChain certificates: [Data],
            nonce: Data,
            nonceSignature: Data,
            completionHandler completion: @escaping (PKAddPaymentPassRequest?) -> Void
    ) {
        // Consulte la sección abajo: 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
    }
}
```

**Implemente una extensión sin IU `API de estado` API**

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

Apple impone un límite de tiempo de 100 ms para devolver la respuesta del manejador. Si lo excede, la aplicación Apple Wallet podría no mostrar la aplicación del emisor. Recomendamos recuperar la información de la tarjeta del grupo de llavero local. Luego use la SDK de Push Provisioning `API getToken` API para comprobar si la tarjeta está digitalizada.

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

Ejemplo de implementación de la API de la Extensión sin IU de Wallet `estado:`

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

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

        // cargar la información de la tarjeta desde el llavero compartido
        // Consulte el paso anterior, para 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, comprobar si está disponible (no digitalizada)
        for last4 in last4Array {
            // llamar 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 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 es necesario iterar más.
            if status.passEntriesAvailable && status.remotePassEntriesAvailable {
                break
            }
        }

        completion(status)
    }
}
```

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

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

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

* arte 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 adelante por la `generateAddPaymentPassRequest` API

Esta API tiene un límite de tiempo de 20 s para recuperar los detalles de la tarjeta. Si lo excede, la aplicación Apple Wallet lo trata 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: 20s
    override func passEntries(completion: @escaping ([PKIssuerProvisioningExtensionPassEntry]) -> Void) {
        var entries: [PKIssuerProvisioningExtensionPassEntry] = []
        let semaphore = DispatchSemaphore(value: 0)

        // p. ej. cargar el cardID desde el llavero
        let cards = [
            "cardID1",
            "cardID2",
        ]

        // Si realiza llamadas de red, DEBE reinicializar urlSession
        // urlSession = ...

        // Cada tarjeta necesita tener la siguiente información, de lo contrario podría no mostrarse:
        // - arte de la tarjeta
        // - nombre del titular de la tarjeta
        // - últimos4 de la tarjeta
        // - esquema de la tarjeta
        // - título del producto de la tarjeta
        // - identificador de la tarjeta, identificador para la tarjeta, para identificar la tarjeta en la devolución de llamada generateAddPaymentPassRequestForPassEntryWithIdentifier(...)
        for cardID in cards {
            guard let last4 = loadFromKeychain(key: "\(cardID)_Last4") else {
                // si falla el llavero, continuar con las siguientes
                continue
            }
            // comprobar si la tarjeta está disponible (no digitalizada).
            var isAvailable = false
            TPCSDK.getToken(input: GetTokenInput(last4: last4)) { local, _, error in
                if let error = error {
                    // manejar error
                } else {
                    if local == nil {
                        // aún no digitalizada, por lo 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 el arte de la tarjeta
            let art = UIImage(systemName: "creditcard")!.cgImage!

            // crear la configuración
            guard let config = PKAddPaymentPassRequestConfiguration(encryptionScheme: .ECC_V2) else {
                // si falla, continuar con 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, continuar con las siguientes
                continue
            }

            entries.append(entry)
        }

        completion(entries)
    }
}
```

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

De forma similar a la `API passEntries` API, la [remotePassEntries](https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionhandler/3606586-remotepassentries) API se utiliza para obtener los detalles 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 aplicación Apple Watch (aplicación 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: 20s
    override func remotePassEntries(completion: @escaping ([PKIssuerProvisioningExtensionPassEntry]) -> Void) {
        var remoteEntries: [PKIssuerProvisioningExtensionPassEntry] = []
        let semaphore = DispatchSemaphore(value: 0)

        // p. ej. cargar el cardID desde el llavero
        let cards = [
            "cardID1",
            "cardID2",
        ]

        // Si realiza llamadas de red, DEBE reinicializar urlSession
        // urlSession = ...

        // Cada tarjeta necesita tener la siguiente información, de lo contrario podría no mostrarse:
        // - arte de la tarjeta
        // - nombre del titular de la tarjeta
        // - últimos4 de la tarjeta
        // - esquema de la tarjeta
        // - título del producto de la tarjeta
        // - identificador de la tarjeta, identificador para la tarjeta, para identificar la tarjeta en la devolución de llamada generateAddPaymentPassRequestForPassEntryWithIdentifier(...)
        for cardID in cards {
            guard let last4 = loadFromKeychain(key: "\(cardID)_Last4") else {
                // si falla el llavero, continuar con las siguientes
                continue
            }
            // comprobar si la tarjeta está disponible (no digitalizada).
            var isAvailable = false
            TPCSDK.getToken(input: GetTokenInput(last4: last4)) { _, remote, error in
                if let error = error {
                    // manejar error
                } else {
                    if remote == nil {
                        // aún no digitalizada, por lo 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 el arte de la tarjeta
            let art = UIImage(systemName: "creditcard")!.cgImage!

            // crear la configuración
            guard let config = PKAddPaymentPassRequestConfiguration(encryptionScheme: .ECC_V2) else {
                // si falla, continuar con 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, continuar con las siguientes
                continue
            }

            remoteEntries.append(entry)
        }

        completion(remoteEntries)
    }
}
```

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

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

El SDK TPC 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: 20s
    override open func generateAddPaymentPassRequestForPassEntryWithIdentifier(
            _ identifier: String,
            configuration: PKAddPaymentPassRequestConfiguration,
            certificateChain certificates: [Data],
            nonce: Data,
            nonceSignature: Data,
            completionHandler completion: @escaping (PKAddPaymentPassRequest?) -> Void
    ) {
        let cardID = identifier
        // basado en cardID, obtener encryptedPayload y authorizationCode, similar en TPCSDK.provision(...)
        // - encryptedPayload, información de la tarjeta cifrada en PKCS7.
        // - authorizationCode, código de autorización proporcionado por el emisor.
        let encryptedPayload = "<PKCS7 encrypted>"
        let authorizationCode = "<authorization code>"
        // esquema de la tarjeta
        let scheme = CardScheme.Mastercard

        // Si realiza llamadas de red, DEBE reinicializar urlSession
        // urlSession = ...

        // llamar a TPCSDK para generar la solicitud
        let sdkInput = GenerateAddPaymentPassRequestInput(scheme: scheme,
                encryptedPayload: encryptedPayload,
                authorizationCode: authorizationCode,
                certificateChain: certificates,
                nonce: nonce,
                nonceSignature: nonceSignature)
        // Solo aplicable para esquema doméstico. Esto puede recuperarse 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 error
                completion(nil)
            } else {
                completion(request)
            }
        }
    }
}
```

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

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

* Mostrar el arte de la tarjeta y el nombre del titular.
* Usar bordes cuadrados para el arte de la tarjeta y el icono de la aplicación.
* Compatibilizar métodos de inicio de sesión manual y biométrico para la autenticación en la extensión de IU.
* Comprobar la elegibilidad de la tarjeta dentro de los 100 ms después de que Apple invoque la API.

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

**La aplicación del emisor necesita proporcionar información a Apple Wallet si las tarjetas están disponibles para aprovisionamiento antes de que el usuario final inicie sesión. ¿Cómo hacen esto 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 Apple Wallet.

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

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

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

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

* Usar un PDF vectorial (recomendado) o un PNG rasterizado.
* Usar resolución 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, caracteres en relieve, holograma y 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 contactless donde se acepten pagos NFC.

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

Sí. Agregue 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 Apple Wallet (por ejemplo, a través de `API getToken`).

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

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

**¿Los emisores necesitan implementar Aprovisionamiento dentro de la aplicación 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 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/classic-push-provisioning/es/casos-de-uso/aprovisionamiento-push-para-carteras-xpay/ios/extensiones-de-aprovisionamiento-push.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.
