> 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/ja/implement-push-provisioning/implement-push-to-digital-wallets/apple-wallet-extension.md).

# Apple Wallet拡張機能

## Apple Wallet拡張機能

Apple Wallet拡張機能を使用すると、エンドユーザーはイシュアアプリケーションに加えて、Apple Walletアプリケーションから直接カードを見つけてプロビジョニングできるようになります。

アプリケーション拡張機能を通じて、イシュアアプリケーションの外部にカスタム機能やコンテンツを公開し、エンドユーザーがApple Walletまたはシステムとやり取りしている間に利用できるようにできます。

Apple Walletアプリケーション内でカードをプロビジョニングすると、支払いカード情報を手動で入力する必要がなくなるため、アプリ内体験が向上します。

を参照してください。 [Apple拡張機能の概要](https://developer.apple.com/library/archive/documentation/General/Conceptual/ExtensibilityPG/ExtensionOverview.html) iOS拡張機能に関する一般的な情報については、

### ユーザー体験

Apple Wallet拡張機能のエンドツーエンドのフローには、主に2つのフェーズがあります。

1. Apple Walletは、イシュアアプリケーションにプロビジョニング可能なカードがあることを検出します。
2. エンドユーザーは認証を行い、追加するカードを選択します。その後、Apple WalletはD1 SDKを介してイシュアアプリケーションからプロビジョニングペイロードを要求します。

#### 検出と認証

<figure><img src="https://1486457345-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWDlYTPaq4dNHuiWtf8ux%2Fuploads%2Fk408nyZlESdWTOrsYplH%2FUX%20-%20Discover%20and%20authentication.svg?alt=media&amp;token=e3edd3ab-b5c5-44ab-9e26-f7a1cc492c42" alt="Apple Wallet showing issuer application option for adding a card"><figcaption><p>カードがプロビジョニング可能な場合、Apple Walletはイシュアアプリケーションを選択肢として表示します。</p></figcaption></figure>

{% stepper %}
{% step %}
**Apple Walletでイシュアアプリケーションを検出する**

Apple Walletは、カードが利用可能かどうかを判断するためにイシュアアプリケーションと照会します。カードが利用可能な場合、イシュアアプリケーションはオプションの一覧に表示されます。
{% endstep %}

{% step %}
**エンドユーザーがイシュアアプリケーションを選択する**

エンドユーザーがイシュアアプリケーションを選択すると、Apple WalletはイシュアアプリケーションのUI拡張機能を起動して、エンドユーザーを認証します。
{% endstep %}

{% step %}
**エンドユーザーを認証する**

イシュアアプリケーションのUI拡張機能は、メインのイシュアアプリケーションと同じ認証情報と方法（たとえば、手動ログインや生体認証）を使用してエンドユーザーを認証します。
{% endstep %}
{% endstepper %}

#### カードの選択とプロビジョニング

<figure><img src="https://1486457345-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWDlYTPaq4dNHuiWtf8ux%2Fuploads%2F31NDGz2qKRMoHFwIMZF4%2FUX%20-%20Selecting%20and%20provisioning%20cards.svg?alt=media&amp;token=d122f374-83a4-4fce-9f36-101b75ebb228" alt="Apple Wallet showing available cards from the issuer application"><figcaption><p>Apple Walletは、イシュアアプリケーションから利用可能なカードを取得して表示します。</p></figcaption></figure>

{% stepper %}
{% step %}
**Apple Walletが利用可能なカードを取得する**

Apple Walletは、非UI拡張機能を通じてイシュアアプリケーションから利用可能なカードの一覧を要求します。エンドユーザーには、追加するカードの一覧が表示されます。
{% endstep %}

{% step %}
**カードの詳細を表示**

エンドユーザーは、カードを追加する前にカードの詳細（たとえば、カードアート、商品名、下4桁の数字）を確認できます。
{% endstep %}

{% step %}
**利用規約に同意する**

エンドユーザーは、イシュアと決済ネットワークの利用規約を確認し、同意します。
{% endstep %}

{% step %}
**プロビジョニングペイロードとトークン化**

選択された各カードについて、Apple Walletはイシュアアプリケーションからプロビジョニングペイロードを要求し、その後、決済ネットワークのTSPとのトークン化フローを開始します。
{% endstep %}
{% endstepper %}

### 拡張機能

Apple Walletとの統合には、イシュアアプリケーションから2種類の拡張機能が必要です。

* **非UI拡張機能**\
  拡張機能フローの状態とカードの利用可能性を報告します。これには、カード検出とプロビジョニングペイロードの手順が含まれます。これは、D1 SDKのベースクラスを拡張して実装されます `D1IssuerProvisioningExtensionHandler`.
* **UI拡張機能**\
  エンドユーザーを認証します。これは、イシュアアプリケーションと同じログイン認証情報を使用する別画面です。次を実装します `D1IssuerProvisioningExtensionAuthorizationProviding` プロトコル。

{% hint style="info" %}
Apple Wallet拡張機能は、イシュアアプリケーションと同時にインストールされます。Apple Walletが追加するパスを検出して拡張機能を表示するには、エンドユーザーは少なくとも1回、イシュアアプリケーションを起動する必要があります。
{% endhint %}

D1 SDKは、非UI拡張機能向けの基本実装を提供するため、完全なロジックを自分で実装する必要はありません。イシュアアプリケーションは `D1Task.configure()` を `WalletExtensionConfig` とともにアプリケーション起動時に呼び出して、カードの詳細を拡張機能と共有します。この構成はインストール後に1回だけ必要ですが、カード情報を最新の状態に保つため、アプリケーション起動のたびに呼び出すことを推奨します。

<figure><img src="https://1486457345-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWDlYTPaq4dNHuiWtf8ux%2Fuploads%2FHtLpRmjTRrDY1JhzJxjN%2FSequence%20Diagram%20-%20%20Apple%20wallet%20extension%20-%20CONFIGURATION.svg?alt=media&amp;token=1f8f4c4d-efbc-48b6-aa19-a391543c584f" alt="High-level flow for configuring the Apple Wallet extension via D1 SDK"><figcaption><p>高レベルのフロー: イシュアアプリケーションがD1 SDKを構成し、Apple Walletがカードを検出できるようにします。</p></figcaption></figure>

UI拡張機能で認証に成功した後、イシュアアプリケーションは `D1IssuerProvisioningExtensionAuthorizationProviding.bindingHash()` を呼び出して、エンコードされたハッシュ済みクライアントバインディングペイロード（`cbp`）を取得する必要があります。イシュアアプリケーションはこの `cbp` イシュア トークンを要求する際のイシュア バックエンドへの値。その後、次を呼び出す必要があります `D1イシュアProvisioningExtensionAuthorizationProviding.login()` これにより、SDK は UI なしの拡張フローを継続できます。

{% hint style="warning" %}
この `bindingHash()` 値はイシュアアプリケーションと拡張機能で異なります。拡張機能用に別のイシュア アクセストークンを生成してください。
{% endhint %}

### シーケンス図

#### 1. Apple Wallet にイシュアアプリケーションのボタンが表示されます

<figure><img src="https://1486457345-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWDlYTPaq4dNHuiWtf8ux%2Fuploads%2Fo5Mj5yYzUGnlp201RcNE%2FSequence%20Diagram%20-%20%20Apple%20Wallet%20shows%20the%20issuer%20application%20button.svg?alt=media&amp;token=4d823af2-5f1c-4c61-b870-d086af07bb93" alt="Sequence diagram showing how Apple Wallet discovers the issuer application"><figcaption><p>Apple Walletは、イシュアアプリケーションにプロビジョニング可能なカードがあることを検出します。</p></figcaption></figure>

#### 2. エンドユーザー認証

<figure><img src="https://1486457345-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWDlYTPaq4dNHuiWtf8ux%2Fuploads%2F61Yv0rUgpTdf6kzAkK7H%2FSequence%20Diagram%20-%20End%20user%20authentication.svg?alt=media&amp;token=b3a0b382-124f-4ee3-acd7-4623e7443ebc" alt="Sequence diagram showing Apple Wallet triggering issuer UI extension for authentication"><figcaption><p>Apple Wallet は認証のためにイシュアアプリケーションの UI 拡張機能を起動します。</p></figcaption></figure>

#### 3. 利用可能なカードが表示されます

<figure><img src="https://1486457345-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWDlYTPaq4dNHuiWtf8ux%2Fuploads%2FMLtj5pDy0f2Rt4fjzRNd%2FSequence%20Diagram%20-%20%20Available%20cards%20are%20displayed.svg?alt=media&amp;token=7bea5b89-19f2-442b-a4f6-82a268a35f73" alt="Sequence diagram showing Apple Wallet requesting card list from issuer"><figcaption><p>Apple Wallet は利用可能なカードの一覧を要求し、エンドユーザーに表示します。</p></figcaption></figure>

#### 4. トークン化

<figure><img src="https://1486457345-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWDlYTPaq4dNHuiWtf8ux%2Fuploads%2Fh3Ti9cGW6ZDc3oaMMSQy%2FSequence%20Diagram%20-%20%20Tokenization.svg?alt=media&amp;token=134a7852-acdb-43be-9027-5aff4f25a722" alt="Sequence diagram showing Apple Wallet requesting provisioning payload and tokenizing"><figcaption><p>Apple Wallet はプロビジョニング ペイロードを要求し、選択したカードをトークン化します。</p></figcaption></figure>

### D1 SDK を介して Apple Wallet を統合する

#### SDK の設定

を実行するとき [iOS SDK の初期化](/push-provisioning/ja/integrate-the-d1-sdk/getting-started/configuration/3.-initialization/ios-initialization.md)、イシュアアプリケーションも次を呼び出す必要があります `configure(ConfigParams.walletExtensionConfig(...))` および以下を指定します:

**`cardParamsList`**

単一の `consumerId` および単一の `issuerId`:

* `cardParamsList` – カード ID、カードアート、商品タイトル、下 4 桁の一覧。カードアートと商品タイトルは Apple Wallet に表示されます。
* `appGroupID` – D1 SDK がイシュアアプリケーションとその拡張機能の間でデータを共有できるようにする共有コンテナー ID。

```swift
let coreConfig = ConfigParams.coreConfig(consumerID: "<obtained from server>")
let cardConfig = ConfigParams.cardConfig()

let card = ConfigParams.CardParams(cardID: "<obtained from server>",
        cardArt: UIImage(named: "<image_name>")!, productTitle: "<product title>", last4: "<last4>")
let walletExtensionConfig = ConfigParams.walletExtensionConfig(cardParamsList: [card],
        appGroupID: "<App Group ID defined in app capabilities>")

d1Task.configure([coreConfig, cardConfig, walletExtensionConfig]) { errors in
    if let errors = errors {
        for error in errors {
            // どの設定が失敗したかを特定するためにエラーを確認します
            // D1 SDK の統合エラー管理のセクションを参照してください
        }
    }
}

// wallet 拡張フローで同じエンドユーザーを認証するために consumerId を保存します
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: "<service associated, e.g. イシュアAppService>",
        kSecAttrAccessGroup as String: "<App Group ID defined in app capabilities>",
        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`**

複数の組み合わせをサポートする場合は、このバリアントを使用します `issuerId` と `consumerId`:

* `issuerParamsList` – の一覧 `cardParamsList` 対応する `issuerId` と `consumerId`.
* `appGroupID` – D1 SDK がイシュアアプリケーションとその拡張機能の間でデータを共有できるようにする共有コンテナー ID。

```swift
let coreConfig = ConfigParams.coreConfig(consumerID: "<obtained from server>")
let cardConfig = ConfigParams.cardConfig()

var issuerParamsList: [ConfigParams.イシュアParams] = []
let card1 = ConfigParams.CardParams(cardID: "<obtained from server>",
        cardArt: UIImage(named: "<image_name>")!, productTitle: "<product title>", last4: "<last4>")
let card2 = ConfigParams.CardParams(cardID: "<obtained from server>",
        cardArt: UIImage(named: "<image_name>")!, productTitle: "<product title>", last4: "<last4>")
let card3 = ConfigParams.CardParams(cardID: "<obtained from server>",
        cardArt: UIImage(named: "<image_name>")!, productTitle: "<product title>", last4: "<last4>")
let issuerParam1 = ConfigParams.イシュアParams(cardParams: card1, issuerID: "<issuerId 1>", consumerID: "<consumerId 1>")
let issuerParam2 = ConfigParams.イシュアParams(cardParams: card2, issuerID: "<issuerId 2>", consumerID: "<consumerId 2>")
let issuerParam3 = ConfigParams.イシュアParams(cardParams: card3, issuerID: "<issuerId 2>", consumerID: "<consumerId 3>")
issuerParamsList.append(issuerParam1)
issuerParamsList.append(issuerParam2)
issuerParamsList.append(issuerParam3)

let walletExtensionConfig = ConfigParams.walletExtensionConfig(issuerParamsList: issuerParamsList,
        appGroupID: "<App Group ID defined in app capabilities>")

d1Task.configure([coreConfig, cardConfig, walletExtensionConfig]) { errors in
    if let errors = errors {
        for error in errors {
            // どの設定が失敗したかを特定するためにエラーを確認します
            // D1 SDK の統合エラー管理のセクションを参照してください
        }
    }
}

// ウォレット拡張の認証時に使用するために issuerID の値を保存します
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: "<service associated, e.g. イシュアAppService>",
        kSecAttrAccessGroup as String: "<App Group ID defined in app capabilities>",
        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" %}
次の使用を強く推奨します `last4` パラメータ。

これがないと `last4`、SDK は共有デバイスのシナリオでトークン化状態を誤って解釈する可能性があります。たとえば、ユーザー A がカードをトークン化し、その後ユーザー B が同じデバイス上の同じイシュアアプリケーションにログインすると、SDK はユーザー B に対してトークン化がすでに完了していると誤って判断する可能性があります。
{% endhint %}

#### App Group ID

既定では、イシュアアプリケーションとその拡張機能はストレージを共有しません。両者でストレージを共有するには app groups を使用してください。詳細は [Apple拡張機能の概要](https://developer.apple.com/library/archive/documentation/General/Conceptual/ExtensibilityPG/ExtensionOverview.html) をご覧ください。

`appGroupID` イシュアアプリケーションとその拡張機能間の共有コンテナを識別します。Xcode では **機能**.

<figure><img src="https://1486457345-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWDlYTPaq4dNHuiWtf8ux%2Fuploads%2Fi2LKsGLuhSD1Fk2FT0Wa%2Fapple-extension-ide-config.png?alt=media&amp;token=84c4205a-2583-4327-9043-e023489fc634" alt="Xcode configuration for app groups used by issuer application and extensions"><figcaption><p>イシュアアプリケーションとその拡張機能のために Xcode で app group を構成する例。</p></figcaption></figure>

{% hint style="info" %}
で使用する前に、App Group ID が Xcode に正しく追加されていることを確認してください `configure(ConfigParams.walletExtensionConfig(...))`。そうでない場合、D1 SDK は `invalidAppGroupID` エラーを返します。
{% endhint %}

#### Apple Pay のエンタイトルメント

アプリ内 Apple Pay のプロビジョニングと同様に、次のエンタイトルメントを追加する必要があります `com.apple.developer.payment-pass-provisioning` を UI 拡張ターゲットと非 UI 拡張ターゲットの両方に追加してください。オンボーディングの詳細については、Apple Wallet のプロビジョニングドキュメントまたは **ウォレット向けプッシュプロビジョニングのオンボーディング**.

#### UI拡張機能

**UI 拡張機能を作成する**

<figure><img src="https://1486457345-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWDlYTPaq4dNHuiWtf8ux%2Fuploads%2F4fu4rpWs39nD2IhGoeg2%2Fapple-extension-ide-config-1.png?alt=media&amp;token=47e3023b-b9ce-4026-a934-f54269346f14" alt="Xcode dialog for creating an intents UI extension"><figcaption><p>Apple Wallet UI 拡張機能用に、Xcode で Intents UI Extension ターゲットを作成します。</p></figcaption></figure>

{% stepper %}
{% step %}
Xcode に次の種類の新しいターゲットを追加します **Intents UI Extension**.
{% endstep %}

{% step %}
新しいターゲットでは、イシュアアプリケーションと同じ App Group ID とエンタイトルメント値を有効にします。App Group ID が異なる場合、 `ログイン` APIは次を返します `walletExtensionAppGroupIDが見つかりません` エラーを返します。
{% endstep %}

{% step %}
拡張機能を更新 `Info.plist`:

* 設定 `NSExtensionPointIdentifier` に `com.apple.PassKit.発行者プロビジョニング.認可`.
* 設定 `NSExtensionPrincipalClass` 〜に準拠するクラスに `D1IssuerProvisioningExtensionAuthorizationProviding`.
  {% endstep %}
  {% endstepper %}

エンタイトルメントと拡張機能の設定の例:

```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>
        com.apple.PassKit.イシュア-プロビジョニング.承認
        <key>NSExtensionPrincipalClass</key>
        <string>$(PRODUCT_MODULE_NAME).IntentViewController</string>
    </dict>
</dict>
</plist>
```

**UI拡張機能用のSDKを統合する**

D1 SDK は提供します `D1IssuerProvisioningExtensionAuthorizationProviding` Apple のものを拡張するプロトコル、 [`PKIssuerProvisioningExtensionAuthorizationProviding`](https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionauthorizationproviding) および追加し `bindingHash()` と `login()` API。

UI 拡張では、次に準拠するビューコントローラを実装します `D1IssuerProvisioningExtensionAuthorizationProviding`、エンドユーザーを認証し、次を呼び出します `bindingHash()` を取得するために `cbp`、この情報を使ってイシュアのバックエンドからイシュアトークンを要求し、 `cbp`、その後次を呼び出します `ログイン`. あなたの **SDKログイン** トークン生成のためのドキュメント。

**単一の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()
        // UIレイアウトを構成
        authButton.addTarget(self, action: #selector(authButtonTouched), for: .touchUpInside)
    }

    @objc func authButtonTouched() {
        // Keychain から認証関連データ（少なくとも consumerID）を読み込む
        let consumerID = loadFromKeychain(key: "D1ConsumerID")

        // Apple の要件に従い、手動認証と生体認証の両方の方法を提供する

        Task {
            do {
                let cbp = try await self.bindingHash()
                var issuerToken = getAccessToken(sub: consumerID!, cbp: cbp)
                try await self.login(&issuerToken)

                await MainActor.run {
                    self.completionHandler?(.authorized)
                }
            } catch {
                await MainActor.run {
                    self.completionHandler?(.canceled)
                }
            }
        }
    }

    func loadFromKeychain(key: String) -> String? {
        let getQuery: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
            kSecAttrService as String: "<service associated, e.g. イシュアAppService>",
            kSecAttrAccessGroup as String: "<App Group ID defined in app capabilities>",
            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, cbp: String) -> Data? {
        // イシュアのバックエンドからイシュアトークンを要求する際に cbp を使用する
        return Data()
    }
}
```

**複数のイシュアIDでログイン**

```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()
        // UIレイアウトを構成
        authButton.addTarget(self, action: #selector(authMultiIssuerButtonTouched), for: .touchUpInside)
    }

    @objc func authMultiIssuerButtonTouched() {
        // Keychain からイシュアIDを読み込む
        let issuerId1 = loadFromKeychain(key: "issuerID1")
        let issuerId2 = loadFromKeychain(key: "issuerID2")

        // Apple の要件に従い、手動認証と生体認証の両方の方法を提供する

        Task {
            do {
                let cbp = try await self.bindingHash()
                let issuerToken1 = getAccessToken(iss: issuerId1!, cbp: cbp)
                let issuerToken2 = getAccessToken(iss: issuerId2!, cbp: cbp)
                var issuerTokens = [issuerToken1, issuerToken2]

                try await self.login(&issuerTokens)

                await MainActor.run {
                    self.completionHandler?(.authorized)
                }
            } catch {
                await MainActor.run {
                    self.completionHandler?(.canceled)
                }
            }
        }
    }

    func loadFromKeychain(key: String) -> String? {
        let getQuery: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
            kSecAttrService as String: "<service associated, e.g. イシュアAppService>",
            kSecAttrAccessGroup as String: "<App Group ID defined in app capabilities>",
            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, cbp: String) -> Data {
        // イシュアのバックエンドからイシュアトークンを要求する際に cbp を使用する
        return Data()
    }
}
```

{% hint style="info" %}
カードが同じ `issuerId` しかし異なる `consumerId` 値を持つ場合、1つの `イシュアトークン` をその `issuerId`.
{% endhint %}

#### 非UI拡張機能

**UIなしの拡張機能を作成**

<figure><img src="https://1486457345-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWDlYTPaq4dNHuiWtf8ux%2Fuploads%2FkcVbmYb3VtlvavC2FHhr%2Fapple-extension-ide-config-2.png?alt=media&amp;token=622bd0c3-ca14-484a-b91d-a6e3ac502e60" alt="Xcode dialog for creating an intents extension without UI"><figcaption><p>Apple Wallet の UIなし拡張機能用に、Xcode で Intents Extension ターゲットを作成します。</p></figcaption></figure>

{% stepper %}
{% step %}
Xcode に次の種類の新しいターゲットを追加します **Intents Extension**. クリア **UI拡張機能を含める** UI拡張機能が以前に作成されていたため。
{% endstep %}

{% step %}
新しいターゲットでは、発行元アプリケーションと同じ App Group ID とエンタイトルメント値を有効にします。App Group ID が異なる場合、D1 SDK の操作は次を返します `walletExtensionAppGroupIDが見つかりません` エラーを返します。
{% endstep %}

{% step %}
拡張機能を更新 `Info.plist`:

* 設定 `NSExtensionPointIdentifier` に `com.apple.PassKit.issuer-provisioning`.
* 設定 `NSExtensionPrincipalClass` 拡張するクラスへ `D1IssuerProvisioningExtensionHandler`.
  {% endstep %}
  {% endstepper %}

エンタイトルメントと拡張機能の設定の例:

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

**UI以外の拡張機能用のSDKを統合する**

D1 SDK は基底クラスを提供します `D1IssuerProvisioningExtensionHandler`、Appleのものを拡張する [`PKIssuerProvisioningExtensionHandler`](https://developer.apple.com/documentation/passkit/pkissuerprovisioningextensionhandler) そして、必要な API を実装します。ほとんどの場合、このクラスを継承するだけで十分です。

```swift
import D1

class IntentHandler: D1IssuerProvisioningExtensionHandler {
}
```

#### エラー処理

必要に応じてオーバーライドします `errorEncountered(_:)` UI なしの拡張で、拡張フロー中に受信したエラーをログに記録したり追跡したりします。

```swift
import D1

class IntentHandler: D1IssuerProvisioningExtensionHandler {

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

典型的なエラーケースには次が含まれます:

* この `appGroupID` 無効です、または見つかりません。
* D1バックエンドがエラーを返します。

```console
# 例: ステータスAPIでのエラー
SDKエラー = ステータスAPIでのエラー。原因: [2](-25300) ストレージへのアクセスに失敗しました。 
エラー: [20009]appGroupIDがApple Pay Wallet UIおよびNon-UI拡張機能で見つかりません。 
いくつかの理由が考えられます。メインアプリケーションにウォレット拡張機能がありません 
`D1Task.configure(_:completion:)` を呼び出して設定します。 
`ConfigParams.walletExtensionConfig(cardParamsList:appGroupID:)` または 
メインアプリケーションの機能で定義されたappGroupIDがUIで定義されていません 
およびNon-UI拡張ターゲットの機能です。

# 例: passEntries API でのエラー
SDK エラー = passEntries API でのエラー。原因: [3]サーバーエラーを受信しました 
HTTPエラーコード400とメッセージ 'Server error'。エラー: [40001]エラーが発生しました 
カード操作の実行中に。

# 例: remotePassEntries API でのエラー
SDK エラー = remotePassEntries API でのエラー。原因: [3]サーバーエラーを受信しました 
HTTPエラーコード400とメッセージ 'Server error' を伴って。エラー: [40001]エラーが発生しました 
カード操作の実行中に。

# 例: generate API でのエラー
SDK エラー = generate API でのエラー。原因: [3]サーバーエラーを受信しました 
HTTPエラーコード400とメッセージ '911. Operation failed .1'。エラー: [40001]エラーが発生しました 
カード操作の実行中に。
```

#### Appleの機能要件

Appleの機能要件に合わせるため、D1 SDK `D1IssuerProvisioningExtensionHandler` およびイシュアアプリケーションは、次の手法を採用します:

* カードアートとカード所有者名を表示します。
* AppleがAPIを呼び出してから100ミリ秒以内に、カードが利用可能かどうか（有効性）を判定します。
* カードアートとアプリアイコンは角を直角にして提供します。
* UI拡張機能での認証に、手動ログインと生体認証ログインの両方をサポートします。

### FAQ

<details>

<summary><strong>エンドユーザーがログインする前に、イシュアアプリケーションはどのようにしてApple Walletにカードの利用可否を提供できますか？</strong></summary>

エンドユーザーは、拡張機能がカードのステータスで更新されるように、少なくとも一度はイシュアアプリケーションにログインする必要があります。イシュアアプリケーションは `configure(ConfigParams.walletExtensionConfig())` D1 SDK にカード一覧を提供するため。

</details>

<details>

<summary><strong>ログインに失敗した場合、エラーはどこに表示されますか？</strong></summary>

ログインエラーは UI 拡張のログイン画面に直接表示することを推奨します。UI 拡張は、イシュアアプリケーションのログインと同じ UX ガイドラインに従う必要があります。

</details>

<details>

<summary><strong>カード画像のサイズと解像度の要件は何ですか？</strong></summary>

デジタルカード画像は Apple の機能要件に従う必要があります：

* 画像は PNG（推奨）またはベクター PDF で提供してください。
  * Wallet 拡張は、メインアプリよりも厳しいメモリ制限があります。
  * PDF ファイルは、OS が PNG に変換するため、メモリ使用量が増加します。
* 解像度は 1536 × 969 を使用してください。
* 画像サイズが 4 MB 未満であることを確認してください。
* 角は四角形（丸めない）にしてください。
* 物理カードにのみ存在する要素（たとえば、PAN、エンボス文字、ホログラム、チップ接点）は含めないでください。
* 横向きで使用してください。物理カードが縦向きの場合は、横向きに変更してください。
* NFC 支払いがサポートされている場合は、必要に応じて非接触インジケーターを追加してください。

</details>

<details>

<summary><strong>拡張機能には新しいバンドル識別子が必要ですか？</strong></summary>

はい。新しいバンドル識別子は、 `associatedApplicationIdentifier` 支払いネットワークオペレーター（PNO）の。iOS はワイルドカードのバンドル識別子をサポートしています。

イシュアは、PNO API を使用して Apple Wallet 内の既存のパスも更新する必要があります。バンドル ID が含まれていない場合、Apple Wallet 拡張経由で追加されたカードにはイシュアアプリケーションからアクセスできません（たとえば、デジタル化状態の確認など）。

</details>

<details>

<summary><strong>イシュアアプリケーションのアイコンが Apple Wallet に表示されない場合、何が問題なのでしょうか？</strong></summary>

次の点を確認してください：

* イシュアアプリケーションがインストールされており、少なくとも一度は起動されている。
* イシュアアプリケーションが呼び出す `configure(ConfigParams.walletExtensionConfig())` カード一覧を更新するために
* イシュアアプリケーションと両方の拡張機能が同じ `appGroupID` で `configure(ConfigParams.walletExtensionConfig())` および Xcode の機能設定で。

</details>

<details>

<summary><strong>Apple Wallet 拡張を機能させるには、アプリ内プロビジョニングが必要ですか？</strong></summary>

はい。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/ja/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.
