ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
ウォレットとApple Payの最新情報
ウォレットとApple Payの最新情報をご覧ください。AppやWebサイトでウォレットのオーダーをサポートする方法や、Identity Verification APIを使用して、ユーザーの年齢やIDを安全に検証する方法を紹介します。また、SwiftUIのPassKitサポートを紹介し、自動支払いを使用して、Apple Payエクスペリエンスを向上させる方法についても解説します。
リソース
- Apple Pay
- Apple Pay Merchant Token Management API
- Apple Pay on the Web
- Apple Pay on the Web Interactive Demo
- ApplePayPaymentRequest
- Example Order Packages
- Human Interface Guidelines: Wallet
- PassKit (Apple Pay and Wallet)
- Requesting identity data from a Wallet pass
- Verifying Wallet identity requests
- Wallet Orders
関連ビデオ
WWDC23
-
ダウンロード
♪ 明るい音楽 ヒップホップのミュージック♪ ♪ こんにちは 私の名前はLaisと申します Davidと申します ウォレットおよびApple Payの 今年新しくなった機能 について説明します 2014年にApple Payを発表し 店舗 やオンライン そしてApp内で迅速かつ安全に プライベート決済を行うための 新しいベンチマークを 打ち立てました それ以降 Apple Payは 世界中に広がっています Apple Payは現在72カ国と 地域で利用可能で 毎日100万件以上の 取引が行われています 本日はウォレットと Apple Payに 興味深い新機能と APIを導入したことをお話しします Laisが詳しく 説明します ありがとうございます David 本日は主要な アジェンダを見ていきます まず最初に簡単な アップデートの話をします 一度の決済で複数の 加盟店への支払いに 対応する機能を 追加しました またサブスクリプションなどの 自動支払いへの 対応も大幅に強化しています 注文の追跡により ユーザーの購入後の体験を より充実させること ができます そして最後に DavidがウォレットのIDによる 本人認証について 説明します まずは いくつかの興味深い 最新情報をお伝えします Tap to Pay on iPhoneは 今年初めに発表され 米国ではiOS 15.4に 搭載されました Tap to Pay on iPhoneは安全で プライバシーが守られており 簡潔に非接触型決済を 行うことができます これをAppに 統合することで シームレスで安全な非接触型 決済を簡単に実現できます これにはApple Pay 非接触型クレジットカード デビットカードなどの デジタルウォレットが 含まれます iPhoneにタップするだけで 支払いが完了するため 追加のハードウェアや 決済端末は必要ありません macOS 13では Apple Payの エクスペリエンスを 再設計しました 昨年デザインを再設計した iOSのペイメントシートは 大きな成功を収めましたが 今年はmacOSでも 同様の体験を提供します SwiftUIを使用して これを実装することで iOSと同時に macOSにも 新機能を搭載する ことができました 本日ご紹介する Apple Payのすべての機能は Macにも 対応しています 新しいSwiftUI API を紹介します SwiftUIで構築されたAppで Add to Apple Walletや Apple Payのボタンを 統合することがさらに簡単になりました これらの新しいAPIは 書く必要がある コードの量を 大幅に削減します エアラインパスの追加を ユーザーに促すボタンを 追加する方法について 見てみましょう まず最初に パスを作成します 正常に読み込めなかった場合 処理をする必要があります これはパスデータが 不正であったり 署名が適切でなかった場合 などに生じる場合があります 次に パスの配列で AddPassToWalletButtonを呼び出します この例では要素が 1つだけの配列ですが 同じボタンに複数のパスを 設定することができます 結果はBoolとして送信され ユーザーがパスを 追加したかどうかに基づき Appに保存・ログするなどの アクションをトリガできます この例では それをstate varに保存します これだけです 最小値のセット内で ボタンのサイズとスタイル をカスタマイズできます デフォルトのサイズは 幅250、高さ50です それをより幅広く
または より高くすることもできます
SwiftUIで Add to Apple Walletボタンを 追加する方法について 説明しました Pay with Apple Payボタンを 追加する方法を見ていきましょう まず最初に PKPaymentRequest クラスを使用して ペイメントリクエストを作成し それに通常の設定を行います そしてauthorizationChange メソッドを作成します これら2つが準備できたので ボタンを表示する コードを追加してみます PayWithApplePayButton の呼び出しを追加し ラベル paymentRequestオブジェクト authorizationChange メソッドを送信します Apple Payが現在のデバイスに 対応していない場合に 処理するために フォールバックビューを送信できます Add Passボタン のように サイズやスタイルを カスタマイズできます すべてで17種類のラベルが 用意されているため ユースケースに合わせて 支払いボタンを カスタマイズできます iOS・iPadOS・macOS watchOSで利用可能です ここからはマルチ決済 について説明します iOS 16では 同じ決済取引で 異なる加盟店に対して 複数のペイメントトークンを 要求する機能を導入します オンラインマーケットプレイス 旅行予約 チケット販売サービス などに便利です 例を見ていきましょう Allisonが旅行を計画 していると想像してください 旅行代理店のホームページに アクセスすると 航空券 ホテル レンタカーなど 必要なものが すべて提供されています Allisonは合計500ドル を支払うだけです Allisonは旅行代理店に クレジットカードの 全情報を提供します 旅行会社がAllisonの クレジットカードに 500ドルを請求し 関係各社に支払うと 想像するかと思います しかし一般的には 旅行会社が 各社にクレジットカード 情報を伝え 個別決済しています これも機能しますが Allisonのプライバシーや セキュリティにとって クレジットカード情報を 共有されるのは あまり良いことではありません ここで新たに マルチ決済用のAPIが登場し 取引に関わる各加盟店の ペイメントトークンを要求することが 可能になりました このペイメントトークンを使って 複数の企業がそれぞれ Allisonに承認された 該当金額を請求する ことができます AllisonはApple Payが 提供するプライバシーと セキュリティの利点を 活用しながら 旅行の予約と決済を 行うことがが可能になりました ペイメントシートも更新され 取引に関与した サブマーチャントの内訳 をユーザーに表示します ユーザーは合計欄 をタップすると 支払サマリーに移動できます ここでは 決済取引に関わる すべての加盟店の内訳と それぞれの加盟店に対する 承認した金額を確認できます Appにマルチ決済 を追加する 方法について説明します まず最初にPKPaymentRequest クラスを使用して ペイメントリクエストを作成し 通常の設定を行います 次に合計金額など決済のための サマリー項目を追加します 次に 新しいPKPaymentTokenContext クラスを使用して 決済取引に関与する それぞれの追加のマーチャントの ペイメントトークン コンテキストを作成します 各マーチャントの詳細と それぞれについて承認する 金額を記入してください 最後に ペイメントリクエストに ペイメントトークンの コンテキストを設定します すべてのペイメントトークン コンテキストの金額の合計は ペイメントリクエスト自体の 合計金額以下または同じであることを 必ず確認してください Appでその加盟店の ペイメントトークンを リクエストする際には 常に同じ加盟店の同じ外部識別子 を使用する必要があります WebでApple Payによる マルチ決済を 導入する場合は Apple Pay JS APIの ドキュメントをご確認ください 次は 自動支払いにおける 改善点について 見ていきます iOS 16では ウォレット Appから 加盟店に設定した自動支払いを 表示および管理できる 機能を導入しています 今回のリリースでは サブスクや分割払い 継続課金などの 定期支払いと 店舗カードの残高補充などの 自動リロード支払いの 2種類の自動支払いに 対応しています ペイメントリクエストの際に 自動支払いの設定を リクエストできる 新しいAPIを導入します ユーザーのApple ID と連結した 新しい種類の ペイメントトークンである Apple Payマーチャント トークンを導入し より確実に継続的に ユーザーに課金できるようにします Apple Payの マーチャントトークンが どのように役立つか 詳しく見ていきましょう JulieがiPhoneの Apple Payを使って ブッククラブの会費を 支払っているとします ブッククラブが ペイメントリクエストをして Julieが支払い を承認すると ブッククラブは ペイメントトークンを受け取り 毎月そのトークンを使い Julieに会費を請求します このペイメントトークンは 決済を承認するために 使用されたJulieの デバイスに接続されています Julieが新しいiPhoneを 購入した場合はどうなりますか 新しい自動支払い機能では Julieの決済ネットワーク が対応している場合 代わりにブッククラブが Apple Payのマーチャント トークンを受け取ります このペイメントトークンは JulieのiPhoneではなく Apple IDと 紐付けられており 継続的な認証を 確実に保証します つまり JulieがiPhoneを 機種変更したり 今使っている携帯電話 を解約した場合でも ブッククラブは Julieに確実に月会費を 請求し続けることができます このようなApple Payでの 決済に対応している場合は 自動支払いを導入することで ユーザーに確実に継続して 請求することができ サービスに支障を きたさないようにもできます 今回のリリースで対応する 最初の自動支払いの種類は 定期支払いです 定期支払いは 毎週・毎月・毎年など 定期的なスケジュールで 課金される 固定または変更できる 金額設定があります これらの決済は指定した日に 終了することも キャンセルされるまで 継続することも可能です トライアル期間や導入期間 もサポートします サブスク 分割払い 定期的課金などの 用途に最適な 決済方法です 自動支払いを使って Appで定期支払いを 設定する方法を 見ていきましょう PKRecurringPaymentSummaryItem クラスを使用して 定期支払い の金額と期間を 指定すること から説明します 定期支払いには 通常の請求期間だけでなく 導入期間やトライアル期間 を指定することができます startDateおよびendDate プロパティを使用して トライアル期間 の終了日と 通常の課金期間の開始日 を示すことができます 次に 新しいPKRecurringPaymentRequest クラスを使用して 定期支払い リクエストを作成します 決済の説明 定期的な請求期間 およびユーザーが 定期支払いの方法を 更新または削除する ウェブページの 管理URLを提供します オプションで トライアル課金期間や 課金契約テキストを 用意することで ユーザーに決済条件を 理解してもらいやすくなります そして オプションで トークン通知URLを指定し 決済用Apple Pay マーチャントトークンが 発行された場合 サーバーがライフサイクル 通知を受け取る ことが可能です 例えば カード発行会社や ユーザーがトークンを 削除した場合に 通知を受け取ることができます マーチャントトークンのライフサイクル 通知に関する詳細は Apple Pay Merchant Token Management APIの ドキュメントを 参照してください paymentRequest オブジェクトに 定期支払い リクエストを設定します サマリー項目に関する 簡単な留意点として 定期支払いは ペイメントリクエストの サマリー項目に自動的に 追加されることはありません サマリー項目の配列に 手動でアイテム項目を追加してください ペイメントリクエストの合計は ユーザーに請求される 最初の金額です この例では合計が トライアル期間中の 金額を表示するように 設定されています ペイメントシートにはユーザーへの 定期支払いの 詳細が表示され ユーザーは請求内容 セクションをタップして 詳細を確認する ことができます 今回のリリースで サポートする 2つ目の種類の 自動支払いである 自動リロード決済 について説明します この決済により 残高が一定の基準額を 下回る際は自動的に 一定の金額が上乗せされます 自動リロード決済は 店舗カードのチャージや プリペイド残高の 管理などに最適です 自動リロード決済の 設定をリクエストするには 新しいPKAutomatic ReloadPaymentSummaryItemクラスを使用して リロード額と閾値を指定する ところから始めます 新しいPKAutomatic ReloadPaymentRequest クラスを使用して 自動リロード決済 リクエストを作成し 定期支払いのように 決済の説明 請求書 および管理URLを提示します オプションで課金契約 文章と トークン通知URLを 指定できます そして ペイメントリクエスト オブジェクトに 自動リロード決済 を設定します 繰り返しますが 自動リロード決済を サマリー項目に含め 請求合計金額を適切に 設定してください ウェブ上でApple Payによる 自動支払いをする場合は Apple Pay JS APIの ドキュメントをご確認ください ここで 自動リロード決済が ユーザーのペイメントシートに どのように表示されるのか を説明します Appで自動支払いをする際に ユーザーに最高の体験を 提供するために 留意していただきたいことを いくつかご紹介します 自動支払いのサマリー項目は システムにより追加されないため 気をつけてください 合計請求金額は ユーザーが最初に請求される 金額です 課金規約の文章は 短くすることが適切です ペイメントシートには最初の 500文字だけが表示されます 課金契約の文章は ユーザーの通常の課金契約や 法的契約に置き換わる ものではありません 各地域の 定期支払いに関する 法律に準拠するかどうかは 皆さま次第です ユーザーに提示する 法的契約書がある場合 それはペイメントシートの前に お客様に 提示するべきものです 1回の取引で リクエストできる 自動支払いの種類は 1つだけです マルチ決済では 自動支払いは ご利用いただけません 決済用に発行された Apple Payマーチャントトークンの ライフサイクル通知 を受け取りたい場合 必ずトークン 通知URLを指定して Apple Pay Merchant Token Management APIを サーバーに 導入してください これらの新しいAPIと Apple Payの マーチャントトークンの利点を 気に入っていただけると思います 自動支払いを採用する パートナー企業の一部をご紹介します Apple Pay マーチャントトークンは American Express・Discover Mastercard・Visaに対応し 将来的に他の決済ネットワーク にも対応する予定です 購入後の体験を 充実させるために 次に 注文の追跡機能を 導入しました iOS 16の新機能である 注文の追跡は 参加加盟店への注文を 追跡できます ウォレット内でアクティブな注文 最近完了した注文 過去の注文の概要を 直感的に把握 できるようになりました あるベーカリー商品の注文が 1件現在処理中になっています 注文がまだ処理中ですが 後ほど戻って確認します 今はPet Avenueで 私の猫のために おもちゃやアクセサリ を購入したいと思います Apple Payで購入することを 選択しました 決済承認してすぐに ウォレットにその注文を 追跡するための通知が届きました その通知から 注文に関する詳細 現在の状況が確認できます 配送や追跡情報を含む 注文の情報が確認でき 注文したアイテムの リストも確認できます 下記にはPet Avenueの 連絡先や決済情報の確認 Pet Avenue Appに戻る といった複数の選択肢があります Pet Avenueは 注文処理が本当に早く すぐに商品を発送したと 想像してください Pet Avenueは 発送してすぐに 現在確認できる 情報を更新しました 「発送済み」に状態が 変更されたことを確認でき 6月10日に到着予定 ということも確認できます 配送における カスタムメッセージや 追跡情報も 共有できます 私のベーカリー商品 のことを覚えていますか ちょうど受け取り準備が できたと通知が来ました 確認しましょう 受け取り設定でベーカリー 商品を注文していました 受け取り準備が できたようです Bake My Breath Awayは 受け取り画面 受け取り説明 到着した物を受け取る バーコードを提示します Apple Payでシームレスに 注文の追跡を確認できました 注文の追跡をどのように ユーザーのエクスペリエンスに 統合していくのか 見ていきましょう 注文の追跡について 見ていくには まず最初に ディベロッパアカウントの Order Type IDを 作成する必要があります Order Type IDは エンティティとして 組織を識別し 注文情報を提示します 複数のOrder Type ID を登録でき 例えば 複数の加盟店に代わって 注文情報を提供します Order Type ID Certificateも作成します その認証情報を使用して 注文パッケージの構築や 注文を更新します 注文は注文パッケージ として割り当てられます 注文パッケージには 注文のメタデータや 情報すべて含まれます 配送 受け取り オーダーフルフィルメントなど 幅広いシナリオで 表示されます 注文パッケージには ロゴやラインアイテム などの画像も含まれます。 ローカライゼーション を追加することで 多様なユーザーに 対応することも可能です すべての注文パッケージは そのオリジンを 確認するために 暗号化された署名が必要です すべてが揃ったところで 注文パッケージを 展開用に圧縮します このセッションに付随する 注文パッケージのサンプル を確認してください 注文パッケージに関する 詳細につきましては デベロッパードキュメント を確認ください Apple Payでシームレスに ウォレットに注文を追加します ユーザーが決済を承認した際に Appまたはホームページで 決済情報を受け取り サーバーへ処理するために 送られます 決済情報が正常に 処理されると サーバーは注文といくつか メタデータを作成します サーバーはAppや ホームページに 結果を含めた 注文についての詳細を返します 注文の詳細により デバイスでサーバーから 注文を非同期リクエスト することができます サーバーはデバイスに 注文パッケージを返します サーバーは注文を 作成する際に Order Type IDの 名前空間内で 固有のOrder ID を割り当てます サーバーに安全性のある認証トークン を生成することも必要です これは 注文の詳細の一部でもある 共有された秘密です デバイスは注文を リクエストした際に デバイス自体を認証するため トークン を使用します 決済認証の結果を返す 例を見ていきましょう ユーザーが決済を認証した際に Appは決済情報を サーバーへ送り 注文を作成するよう 要求します サーバーの結果が成功を 示しているかどうかを確認し サーバーから返された エラーを処理します サーバーが成功を示した場合 適切な認証結果で 決済を完了してください 注文の詳細で 決済の認証結果を返すには まず初めにサーバーの結果から 抽出する必要があります Order Type ID Order ID サーバーへのURL および認証トークンを含む PKPaymentOrderDetails オブジェクトを作成します PKPaymentOrderDetails オブジェクトを PKPaymentAuthorizationResultの 新しいorderDetailsプロパティに割り当てます それだけです ウェブ上で注文の詳細による 決済を完了することもできます 以前と同様にサーバーの結果から 注文の詳細を抽出します 決済を完了するデータに 注文内容を記載してください 注文を更新するために 自動更新の サポートを示す 注文パッケージを作成します 注文が追加されると デバイスは更新するために それに登録します サーバーは登録に関する店舗情報を 保管する必要があります 後ほどサーバーが 注文を更新する際に 登録情報を使用して 更新するために登録した デバイスを示します デバイスがプッシュ通知を 受け取ると サーバーから注文リクエストを 再度行うことができます サーバーは更新した 注文パッケージを デバイスに返します ユーザーと皆さまだけが それらが注文されたことを 知る必要があります 注文の追跡を非公開で 設計しています 注文情報は直接 デバイスとサーバー間で やり取りされます iCloudを介して 注文が同期された場合 エンドツーエンドで 暗号化されています 最高のカスタマー エクスペリエンスを提供するために これらのプラクティス を実施してください Appと提供する 注文を関連付けます Appが通知を配信し インストールされた場合 注文の追跡通知は 無効にできます これにより重複した 通知を防ぐことができます ユーザーの好みに関する 知識を利用して 適切なローカライゼーション だけを提供します 注文パッケージのサイズには 気をつけてください 小さいサイズを 維持するようにして 高いネットワークコストを 削減してください 注文を更新する際に 更新するために登録した デバイスの通知を促します ウォレット内の注文は注文の\実際の状態と 一致している必要があります 注文の追跡のためにHIGを 確認することも忘れないでください プラットフォームは 注文の追跡の統合を より簡潔に実施できます 秋までに Shopify Narvar Routeが 注文の追跡に対応する ことを発表します 注文の追跡に対応する プラットフォームが 次月に増えますので 注目していてください 注文の追跡は ユーザーにとって 購入後の体験を強化する 最適な方法です 自動更新により ユーザーは注文の状態について 常に最新のものを確認できます ユーザーはこの体験を きっと喜ぶと思います 皆さまが受けた注文で 活用されることを楽しみにしています ここからは Davidに代わります ありがとうございます Lais! iOS 16での ウォレットのIDを追加する 新しい機能について 話していきたいと思います 今年初めにiOS 15.4の ウォレット内にIDを搭載しました 米国の該当する州の ユーザーは 運転免許証や州IDを ウォレットに追加できます ウォレット内のIDは ユーザーの物理的IDと 同じ発行機関によって 発行されています 米国では 各州のDepartment of Motor Vehicles それに相当する 組織が該当します iOS 16では Appや App Clipがユーザーの年齢や 本人確認を行う ためにウォレット内のIDに 情報を要求できる 新しいAPIが追加されました Appは情報を リクエストして ユーザーはリクエストを 確認し承認します その後Appは 復号化および検証のため サーバーにレスポンスを 送ります ユーザーIDから データ要素の数を リクエストできます これらには氏名 住所 生年月日 顔写真 発行機関 身分証明書の番号と有効期限 運転免許証の種類 (該当する場合) などが含まれます IDのユースケースとして 非常に一般的なのは年齢確認です 物理的なIDの場合 生年月日を見ます 生年月日は年齢を確認する ためだけの必要な情報よりも 多くの情報を明確にします 私の年齢を確認する場合 実際に生まれた日や年 年齢を正確に知る 必要はありません 十分な年齢に達しているか どうかだけを知る必要があります ウォレット内のIDで直接 その質問に回答することができます Appはユーザーが特定の 年齢以上であるかどうかを示す ブール値のデータ要素を 要求することができ 完全な生年月日を 確認するよりも プライバシーを保護する 方法で年齢認証ができます AppがAPIを 呼び出すと どんな情報を リクエストしたかが シートに表示されます その情報を保存する 意図があるかどうか 保存する期間も 表示されます これによりユーザーは情報を Appと共有するかどうか 十分な情報を得た上で 判断することができます Face IDやTouch IDを使って 相手が明確に承認するまで 情報が共有される ことはありません あなたが受け取る レスポンスには リクエストした要素だけが 含まれています 物理的なIDカードを スキャンするなどの 他の本人認証の仕組みでは IDに記載されている内容を すべて共有することになります ウォレット内のIDにより 必要な情報だけを 限定して共有することで ユーザーにとって よりプライバシーが守られ サーバーで安全に保管するべき 機密情報の量も 減らすことができます このレスポンスには IDの発行元が署名しているため その情報が本物であることを 簡単に確認すること ができます 発行機関はIDを 作成しますが APIを呼び出す時点では 関与しません ユーザーが自身の情報をいつ 誰と共有したかを 知ることはできません APIを利用するには ディベロッパアカウントを介して エンタイトルメントを リクエストする必要があります マーチャントIDや 暗号化証明書を 設定する 必要があります このプロセスは Apple PayでApp内決済の 設定をする際に とても似ています IDと認証の活用について 後ほど話したいと思います ここからは検証フロー について話していきます ハイレベルで 4つのステップで構成されています まず最初にAppはPassKit フレームワークでAPIを起動して リクエストしている 情報を特定します システムはシートを表示して ユーザーにリクエストを 承認することを促します リクエストが承認されると Appは 暗号化された 応答を受け取ります Appは復号化や 検証を行うために サーバーに応答を送ります まず最初にPassKitにAPIを 使用する方法について話します AppがSwiftUIする場合 Appの VerifyIdentityWithWalletButton SwiftUIビューを使用する必要があります ボタンを押すと 本人確認フローが 起動するボタンが 表示されます Pay with Apple Payや Add Pass to Walletボタンを追加するように ウォレットボタンによる 個人認証の検証はAPIを使用する すべてのAppで馴染みのある 一貫した体験を提供します 4つの異なる ラベルから選択して ユースケースに適した ボタンを表示できます 利用可能なスペースに応じて 1行および複数行のバージョン を自動的に切り替えます ボタンを作成する際に PKIdentityRequestオブジェクト を指定する必要があります それには リクエストしたい情報と その返送方法が 記述されています その作成方法について 見ていきましょう 探している データ要素を記述した PKIdentityDrivers LicenseDescriptor を作成することから始めます addElements メソッドを使用して リクエストしたい要素と その要素を保存するか どうかを指定します addElementsメソッドを 複数回起動することで 保管するため 意図の異なる 要素セットを 指定できます この例では 2回呼び出しています まず最初に「最低でも18歳」 要素を追加し それは保存される ことはありません 再度addElements メソッドを呼び出し ユーザー名 苗字 顔写真を要求しており これらはすべて 最大30日間保存可能です 記述子はその後 PKIdentityRequestに入ります 次のステップは使用する マーチャント識別子を特定することです マーチャント識別子は 暗号化認証を示し それによりAPI応答は 暗号化されます ディベロッパアカウントを介して マーチャント識別子と 暗号化認証で構成します APIから受け取る レスポンスに紐づく nonceを指定する 必要があります 応答の再送を防止し 特定のユーザーセッション に結びつけるために 使用される重要な セキュリティ機能です nonceをどのように 管理するかは ご自身のセキュリティ要件に 基づき決定してください 多くの場合 これはサーバーからです 後ほどサーバーは nonceが有効であること を強化する ことに対応します これらのプロパティを 設定することで PKIdentityRequest があります これからはボタン について振り返ります 個人認証が有効の場合 Appにボタンが表示され タップすると リクエストによる 個人認証フローが 開始されます 個人認証が 有効でない場合 指定したフォールバックビューが 代わりに表示されます 例えばiPhoneに ウォレットのIDがない場合 これは発生します フォールバックビューを 使用して 個人を認証する 他の方法を提供します 個人認証が有効だったとして ユーザーはボタンを タップします システムはリクエスト によりシートを表示し それには リクエストした要素や それらを保管の 意図が含まれます ユーザーはFace IDまたは Touch IDでリクエストを承認したり 承認なしにシートを 閉じることができます コードはリクエストの 結果を含む 結果のオブジェクト を受け取ります リクエストが承認されると 成功結果を受け取ります これは暗号化応答を含む PKIdentityDocument オブジェクトによるものであり Appはサーバーに 復号化や認証のために 送信します リクエストが成功 しなかった場合 失敗の結果を 受け取ります 失敗の最も 共通の原因は リクエストが 承認されないことであり キャンセルされたエラーを 受ける場合に発生します それは VerifyIdentityWithWalletButtonであり APIのSwiftUI バージョンです それを使用して ボタンを表示し 個人認証フローを起動し ウォレットのIDから 情報をリクエストします Appで SwiftUIを使用しない場合 PKIdentityButtonや PKIdentityAuthorization Controllerクラスを使用して 同じことを 完了することができます 今まで情報をリクエストし ユーザーはリクエストを承認し Appは暗号化応答を サーバーに 送信してきました 次にサーバーが応答を 復号化および検証するために すべきことについて 話していきます このトピックでは簡潔に 概要を説明するだけなので 詳しくはディベロッパ ドキュメントを確認してください 対応形式はいくつかの 国際基準を用いるため それらについて 理解することを 強く推奨します 受け取る応答データは CBORで暗号化された エンベロープにあります CBORはRFC 8949で 定義されたデータ形式です JSONに 類似していますが オブジェクトをエンコードするために バイナリデータを使用します 暗号化エンベロープは 復号化処理に必要な メタデータを含んでおり 暗号化データ自体も 同時に含んでいます HPKEを使用して データは暗号化され それはRFC 9180で 定義された暗号スキームになります サーバーは秘密鍵を使用して このデータを復号化します 復号化するとmdoc応答 オブジェクトを取得します mdoc応答はモバイル運転免許証 および州IDのISO規格である ISO 18013 part 5に定義されています mdoc応答オブジェクトは リクエストした データ要素を含みます 応答が本物であることを 確認するために サーバーが検証するべき 多くのセキュリティ 機能が含まれています サーバー自身が復号化を行い 検証します Appleサーバーや発行機関 のサーバーは どちらも関与しません 復号化と応答の検証について 話す前に セッショントランスクリプト について話す必要があります これは応答ペイロードと 特定のAppの 特定のリクエストを結ぶ CBOR構造です サーバーはこの構造を構築し 復号化および検証の際に 使用する必要があります セッショントランスクリプトは 先ほどPKIdentityRequestで使用した ものと同じ nonceやマーチャントIDを含みます ディベロッパチームの チームIDと 暗号化証明書の 公開鍵の SHA256ハッシュも 含みます セッショントランスクリプト を構築する際 サーバーは使用した 入力がすべて有効であるか 確認する必要があります nonceはすでに 使用されておらず 現在のユーザーと 結びついている必要があります 他の値は ディベロッパアカウントの内容と 一致している必要があります ここからは暗号化データの 復号化について話します 作成したばかりのセッション トランスクリプトが 暗号化エンベロープの メタデータと同様に必要になります 秘密鍵も必要です これは先ほど ディベロッパアカウントで 設定した証明書に 対応する秘密鍵です ユーザー情報の守秘義務 を守るために 秘密鍵は常に非公開である ことを確認してください サーバーに保存して守り Appには 保存しないでください 秘密鍵が漏洩した場合は 直ちにディベロッパアカウント 証明書を失効させてください 暗号化データの復号化後 2つの暗号署名と リクエストされた データ要素を含む mdoc応答オブジェクト を受け取ります データ要素を使用する前に mdoc応答でどちらの 署名も確認する必要があります まず初めに 発行者の署名を確認します これはユーザーIDの 発行機関の署名です この署名を確認することで 応答のデータが 本当の発行機関のものであり 改ざんされていないこと を検証します その署名が有効であるかを 確認するだけでなく 信頼できる発行機関の署名 であるかも確認する必要があります ウォレットのIDを使用した 発行機関の証明書についての 詳細は ドキュメントを確認してください 次にデバイスの署名を 検証する必要があります この署名は ユーザーのiPhoneの Secure Elementの 鍵によって作成されました それは受け取った応答が 発行機関がもともと IDを発行した iPhoneと同じものであること を証明するものです ここではセッション トランスクリプトを 発行機関の署名による情報と 同時に再度使用する必要があります これでリクエストした データ要素を使用する 準備が整いました これらの要素を 発行機関およびデバイスの 署名の検証なしに 絶対に使用しないでください そうしなければ 受け取ったデータが 本物かどうか わからないからです これらのステップは完了しました これで完了です Appは情報をリクエストし サーバーは応答を 復号化および検証しました ウォレットにIDがない場合 どのように実装をテストするのか 疑問を持つかと思います これについては いくつかメカニズムがあります まず初めに iOS シミュレータでテストできます そこではAPIが mock応答を返します この応答は実際のものと 類似していますが 実際の署名が 不足しています 同様にテストプロファイル を使用して iPhoneのウォレットに IDがない場合でさえも iPhoneに実際にmock応答 を受け取ることができます これについての詳細は ドキュメントをご確認ください サーバーはこれらの mock応答を決して 本物のように処理しては いけませんので注意してください サーバーの実装に 役立つように ドキュメントには応答例と それを復号化および 検証するために必要なもの がすべて含まれています iOS 16では ウォレットのIDで 個人認証が できるようになりました AppでAPIを 使用する方法や サーバーで応答を 処理する方法や 実装をテストする方法 について話しました 今年はウォレット およびApple Payに たくさんの新しい 機能を導入しました これらにはマルチ決済や 自動支払いの改善や 注文の追跡 個人認証が含まれます 詳細につきましては デベロッパ ドキュメントを確認ください ご視聴ありがとうございました 素晴らしいWWDCを! ♪
-
-
2:39 - AddPassToWalletButton
@State var addedToWallet: Bool @ViewBuilder private var airlineButton: some View { if let pass = createAirlinePass() { AddPassToWalletButton([pass]) { added in addedToWallet = added } .frame(width: 250, height: 50) .addPassToWalletButtonStyle(.blackOutline) } else { // Fallback } }
-
3:40 - PayWithApplePayButton
// Create a payment request let paymentRequest = PKPaymentRequest() // ... // Create a payment authorization change method func authorizationChange(phase: PayWithApplePayButtonPaymentAuthorizationPhase) { ... } PayWithApplePayButton( .plain, request: paymentRequest, onPaymentAuthorizationChange: authorizationChange ) { // Fallback } .frame(width: 250, height: 50) .payWithApplePayButtonStyle(.automatic)
-
6:34 - Multi-merchant payments
// Create a payment request let paymentRequest = PKPaymentRequest() // ... // Set total amount paymentRequest.paymentSummaryItems = [ PKPaymentSummaryItem(label: "Total", amount: 500) ] // Create a multi token context for each additional merchant in the payment let multiTokenContexts = [ PKPaymentTokenContext( merchantIdentifier: "com.example.air-travel", externalIdentifier: "com.example.air-travel", merchantName: "Air Travel", merchantDomain: "air-travel.example.com", amount: 150 ), PKPaymentTokenContext( merchantIdentifier: "com.example.hotel", externalIdentifier: "com.example.hotel", merchantName: "Hotel", merchantDomain: "hotel.example.com", amount: 300 ), PKPaymentTokenContext( merchantIdentifier: "com.example.car-rental", externalIdentifier: "com.example.car-rental", merchantName: "Car Rental", merchantDomain: "car-rental.example.com", amount: 50 ) ] paymentRequest.multiTokenContexts = multiTokenContexts
-
10:14 - Automatic Payments - Recurring payment request
// Specify the amount and billing periods let regularBilling = PKRecurringPaymentSummaryItem(label: "Membership", amount: 20) let trialBilling = PKRecurringPaymentSummaryItem(label: "Trial Membership", amount: 10) let trialEndDate = Calendar.current.date(byAdding: .month, value: 1, to: Date.now) trialBilling.endDate = trialEndDate regularBilling.startDate = trialEndDate // Create a recurring payment request let recurringPaymentRequest = PKRecurringPaymentRequest( paymentDescription: "Book Club Membership", regularBilling: regularBilling, managementURL: URL(string: "https://www.example.com/managementURL")! ) recurringPaymentRequest.trialBilling = trialBilling recurringPaymentRequest.billingAgreement = """ 50% off for the first month. You will be charged $20 every month after that until you cancel. \ You may cancel at any time to avoid future charges. To cancel, go to your Account and click \ Cancel Membership. """ recurringPaymentRequest.tokenNotificationURL = URL( string: "https://www.example.com/tokenNotificationURL" )! // Update the payment request let paymentRequest = PKPaymentRequest() // ... paymentRequest.recurringPaymentRequest = recurringPaymentRequest // Include in the summary items let total = PKRecurringPaymentSummaryItem(label: "Book Club", amount: 10) total.endDate = trialEndDate paymentRequest.paymentSummaryItems = [trialBilling, regularBilling, total]
-
12:39 - Automatic Payments - Automatic reload payment request
// Specify the reload amount and threshold let automaticReloadBilling = PKAutomaticReloadPaymentSummaryItem( label: "Coffee Shop Reload", amount: 25 ) reloadItem.thresholdAmount = 5 // Create an automatic reload payment request let automaticReloadPaymentRequest = PKAutomaticReloadPaymentRequest( paymentDescription: "Coffee Shop", automaticReloadBilling: automaticReloadBilling, managementURL: URL(string: "https://www.example.com/managementURL")! ) automaticReloadPaymentRequest.billingAgreement = """ Coffee Shop will add $25.00 to your card immediately, and will automatically reload your \ card with $25.00 whenever the balance falls below $5.00. You may cancel at any time to avoid \ future charges. To cancel, go to your Account and click Cancel Reload. """ automaticReloadPaymentRequest.tokenNotificationURL = URL( string: "https://www.example.com/tokenNotificationURL" )! // Update the payment request let paymentRequest = PKPaymentRequest() // ... paymentRequest.automaticReloadPaymentRequest = automaticReloadPaymentRequest // Include in the summary items let total = PKAutomaticReloadPaymentSummaryItem( label: "Coffee Shop", amount: 25 ) total.thresholdAmount = 5 paymentRequest.paymentSummaryItems = [total]
-
19:17 - Order Tracking (swift)
func onAuthorizationChange(phase: PayWithApplePayButtonPaymentAuthorizationPhase) { switch phase { // ... case .didAuthorize(let payment, let resultHandler): server.createOrder(with: payment) { serverResult in guard case .success(let orderDetails) = serverResult else { /* handle error */ } let result = PKPaymentAuthorizationResult(status: .success, errors: nil) result.orderDetails = PKPaymentOrderDetails( orderTypeIdentifier: orderDetails.orderTypeIdentifier, orderIdentifier: orderDetails.orderIdentifier, webServiceURL: orderDetails.webServiceURL, authenticationToken: orderDetails.authenticationToken, ) resultHandler(result) } } }
-
20:13 - Order Tracking (JS)
paymentRequest.show().then((response) => { server.createOrder(response).then((orderDetails) => { let details = { }; if (response.methodName === "https://apple.com/apple-pay") { details.data = { orderDetails: { orderTypeIdentifier: orderDetails.orderTypeIdentifier, orderIdentifier: orderDetails.orderIdentifier, webServiceURL: orderDetails.webServiceURL, authenticationToken: orderDetails.authenticationToken, }, }; } response.complete("success", details); }); });
-
27:05 - VerifyIdentityWithWalletButton 2
@ViewBuilder var verifiyIdentityButton: some View { VerifyIdentityWithWalletButton( .verifyIdentity, request: createRequest(), ) { result in // ... } fallback: { // verify identity another way } }
-
27:18 - Create a PKIdentityRequest
func createRequest() -> PKIdentityRequest { let descriptor = PKIdentityDriversLicenseDescriptor() descriptor.addElements([.age(atLeast: 18)], intentToStore: .willNotStore) descriptor.addElements([.givenName, .familyName, .portrait], intentToStore: .mayStore(days: 30)) let request = PKIdentityRequest() request.descriptor = descriptor request.merchantIdentifier = // configured in Developer account request.nonce = // bound to user session }
-
27:19 - VerifyIdentityWithWalletButton 3
@ViewBuilder var verifiyIdentityButton: some View { VerifyIdentityWithWalletButton( .verifyIdentity, request: createRequest(), ) { result in // ... } fallback: { // verify identity another way } }
-
29:37 - VerifyIdentityWithWalletButton 4
@ViewBuilder var verifiyIdentityButton: some View { VerifyIdentityWithWalletButton( .verifyIdentity, request: createRequest(), ) { result in switch result { case .success(let document): // send document to server for decryption and verification case .failure(let error): switch error { case PKIdentityError.cancelled: // handle cancellation default: // handle other errors } } } fallback: { // verify identity another way } }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。