passkit について
PassKitは、Apple Pay決済とWalletパスの実装を解説するSwift開発ガイドです。実物商品、サービス、寄付、対象となる継続課金を扱うアプリ向けに、決済設定、承認、配送情報の更新、パス管理を説明します。
主な機能
- 01Apple Payボタンと決済リクエストの設定
- 02決済シートの表示と承認処理
- 03配送方法と合計額の更新
- 04署名済みWalletパスの追加
- 05パスライブラリの確認と置換
活用シーン
- 01実物商品の購入画面へのApple Pay導入
- 02決済承認トークンの処理
- 03Apple Walletでの署名済みパスの提供
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ dpearson2699/ swift-ios-skills --skill passkitインストール後、Claude Code で/passkitを入力して読み込まれたか確認します。
同梱ファイルevals/evals.jsonreferences/wallet-passes.md
詳細
PassKit
物理的な商品、現実世界のサービス、寄付、および対象となる定期支払いについて、Apple Pay 決済を受け付け、ユーザーの Wallet にパスを追加できます。決済ボタン、決済リクエスト、承認、Wallet パス、加盟店の設定を扱います。Swift 6.3 / iOS 26+ を対象としています。
高度な Apple Pay フローでは、1 つの PKPaymentRequest に設定できる高度なリクエストタイプは、定期支払い、自動チャージ、後払い、Apple Pay Later の利用可否、マルチトークンコンテキストのうち 1 つだけです。チェックアウトでこれらのモードを複数使用する場合は、別々の決済リクエストを使用してください。
目次
セットアップ
プロジェクトの設定
- Xcode で Apple Pay 機能を有効にします
- Apple Developer ポータルで Merchant ID を作成します(形式:
merchant.com.example.app) - 加盟店 ID 用の決済処理証明書を生成してインストールします
- 加盟店 ID をエンタイトルメントに追加します
利用可否の確認
Apple Pay UI を表示する前に、デバイスで決済できることを必ず確認してください。canMakePayments(usingNetworks:capabilities:) で有効なカードの有無を確認する場合、Apple の HIG では、その確認を行うすべての場所で Apple Pay を主要かつ目立つ決済方法として表示することが求められます。
import PassKit
func canMakePayments() -> Bool {
// Check device supports Apple Pay at all
guard PKPaymentAuthorizationController.canMakePayments() else {
return false
}
// Check user has cards for the networks you support
return PKPaymentAuthorizationController.canMakePayments(
usingNetworks: [.visa, .masterCard, .amex, .discover],
capabilities: .threeDSecure
)
}
Apple Pay ボタンの表示
SwiftUI
SwiftUI では、組み込みの PayWithApplePayButton ビューを使用します。Apple Pay とラベル付けするコントロールには、Apple が提供するボタン API を使用してください。カスタムボタンに Apple Pay のロゴや「Apple Pay」のテキストを含めてはいけません。
import SwiftUI
import PassKit
struct CheckoutView: View {
var body: some View {
PayWithApplePayButton(.buy) {
startPayment()
}
.payWithApplePayButtonStyle(.black)
.frame(height: 48)
.padding()
}
}
UIKit
UIKit ベースのインターフェイスでは PKPaymentButton を使用します。
let button = PKPaymentButton(
paymentButtonType: .buy,
paymentButtonStyle: .black
)
button.cornerRadius = 12
button.addTarget(self, action: #selector(startPayment), for: .touchUpInside)
ボタンの種類: .plain、.buy、.setUp、.inStore、.donate、.checkout、.continue、.book、.subscribe、.reload、.addMoney、.topUp、.order、.rent、.support、.contribute、.tip
決済リクエストの作成
加盟店の詳細と購入する商品を指定して PKPaymentRequest を作成します。PassKit の金額 API では Double ではなく NSDecimalNumber を使用します。
func createPaymentRequest() -> PKPaymentRequest {
let request = PKPaymentRequest()
request.merchantIdentifier = "merchant.com.example.app"
request.countryCode = "US"
request.currencyCode = "USD"
request.supportedNetworks = [.visa, .masterCard, .amex, .discover]
request.merchantCapabilities = .threeDSecure
request.paymentSummaryItems = [
PKPaymentSummaryItem(
label: "Widget",
amount: NSDecimalNumber(string: "9.99")
),
PKPaymentSummaryItem(
label: "Shipping",
amount: NSDecimalNumber(string: "4.99")
),
PKPaymentSummaryItem(
label: "My Store",
amount: NSDecimalNumber(string: "14.98")
) // Total
]
return request
}
paymentSummaryItems の最後の項目は合計として扱われ、そのラベルが決済シートの支払い欄に表示されます。
配送先と連絡先情報のリクエスト
注文の価格設定、履行、または法的な処理に必要な連絡先フィールドのみをリクエストしてください。 決済シートで正確に収集できない必須の商品選択、任意のメモ、商品ごとの配送先、受け取り場所は、Apple Pay ボタンより前に収集してください。
request.requiredShippingContactFields = [.postalAddress, .emailAddress, .name]
request.requiredBillingContactFields = [.postalAddress]
let standard = PKShippingMethod(
label: "Standard",
amount: NSDecimalNumber(string: "4.99")
)
standard.identifier = "standard"
standard.detail = "5-7 business days"
let express = PKShippingMethod(
label: "Express",
amount: NSDecimalNumber(string: "9.99")
)
express.identifier = "express"
express.detail = "1-2 business days"
request.shippingMethods = [standard, express]
request.shippingType = .shipping // .delivery, .storePickup, .servicePickup
対応ネットワーク
| ネットワーク | 定数 |
|---|---|
| Visa | .visa |
| Mastercard | .masterCard |
| American Express | .amex |
| Discover | .discover |
| China UnionPay | .chinaUnionPay |
| JCB | .JCB |
| Maestro | .maestro |
| Electron | .electron |
| Interac | .interac |
実行時に PKPaymentRequest.availableNetworks() を使用して、利用可能なネットワークを取得します。
決済シートの表示
PKPaymentAuthorizationController を使用します(SwiftUI と UIKit の両方で動作し、ビューコントローラーは不要です)。コントローラーのデリゲートは弱参照されるため、決済シートが表示されている間はコントローラーを保持してください。
final class CheckoutCoordinator: NSObject {
private var paymentController: PKPaymentAuthorizationController?
@MainActor
func startPayment() {
let controller = PKPaymentAuthorizationController(
paymentRequest: createPaymentRequest()
)
paymentController = controller
controller.delegate = self
controller.present { [weak self] presented in
if !presented {
self?.paymentController = nil
}
}
}
}
決済承認の処理
PKPaymentAuthorizationControllerDelegate を実装して、決済トークンを処理します。
extension CheckoutCoordinator: PKPaymentAuthorizationControllerDelegate {
func paymentAuthorizationController(
_ controller: PKPaymentAuthorizationController,
didAuthorizePayment payment: PKPayment,
handler completion: @escaping (PKPaymentAuthorizationResult) -> Void
) {
// Send payment.token.paymentData to your payment processor
Task {
do {
try await paymentService.process(payment.token)
completion(PKPaymentAuthorizationResult(status: .success, errors: nil))
} catch {
completion(PKPaymentAuthorizationResult(status: .failure, errors: [error]))
}
}
}
func paymentAuthorizationControllerDidFinish(
_ controller: PKPaymentAuthorizationController
) {
controller.dismiss { [weak self] in
self?.paymentController = nil
}
}
}
配送情報の変更処理
func paymentAuthorizationController(
_ controller: PKPaymentAuthorizationController,
didSelectShippingMethod shippingMethod: PKShippingMethod,
handler completion: @escaping (PKPaymentRequestShippingMethodUpdate) -> Void
) {
let updatedItems = recalculateItems(with: shippingMethod)
let update = PKPaymentRequestShippingMethodUpdate(paymentSummaryItems: updatedItems)
completion(update)
}
Wallet パス
Wallet へのパスの追加
署名済みの .pkpass データを読み込み、デバイスでパスを追加できることを確認してから、ユーザーがパスを追加する前に内容を確認できるよう、必要なタイミングで
PKAddPassesViewController を表示します。PKPass(data:) には署名済みのパスデータが必要で、無効なデータまたは無効な署名のエラーが発生することがあります。レビュー用のガイダンスでは、try? だけでエラーを隠さず、invalid-data と invalid-signature
の失敗を明示してください。
func addPassToWallet(data: Data) {
guard PKAddPassesViewController.canAddPasses() else {
return
}
do {
let pass = try PKPass(data: data)
guard let addController = PKAddPassesViewController(pass: pass) else {
return
}
addController.delegate = self
present(addController, animated: true)
} catch {
// Signed pass data is invalid or the signature cannot be validated.
showRecoverablePassError(error)
}
}
SwiftUI の Wallet ボタン
AddPassToWalletButton を、PKAddPassButton に相当する SwiftUI のボタンとして使用します。
import PassKit
import SwiftUI
struct AddPassButton: View {
let passData: Data
@State private var addedToWallet = false
var body: some View {
if PKAddPassesViewController.canAddPasses(),
let pass = try? PKPass(data: passData) {
AddPassToWalletButton([pass]) { added in
addedToWallet = added
}
.addPassToWalletButtonStyle(.blackOutline)
.frame(width: 250, height: 50)
}
}
}
パスライブラリの確認
PKPassLibrary を使用して、ユーザーがすでに所有しているパスを確認、管理します。パスライブラリを操作する前に
PKPassLibrary.isPassLibraryAvailable() を確認します。ただし、デバイスでパスを追加できるかどうかの判断には、PKAddPassesViewController.canAddPasses() を使用します。passes() が返すのは、アプリがエンタイトルメントを通じてアクセスできるパスのみです。既存のパスを置き換える場合は、replacePass(with:) の Boolean 戻り値を確認し、失敗を処理します。署名済みのパスバンドルの構築、Web サービスの更新、replacePass(with:) については、
references/wallet-passes.md を参照してください。
let library = PKPassLibrary()
// Check if a specific pass is already in Wallet
let hasPass = library.containsPass(pass)
// Retrieve passes your app can access
let passes = library.passes()
// Check if pass library is available
guard PKPassLibrary.isPassLibraryAvailable() else { return }
よくある間違い
しないこと:実物の商品に StoreKit を使用する
Apple Pay(PassKit)は、実物の商品、現実世界のサービス、寄付、および対象となる定期的な支払いに使用します。StoreKit は、仮想商品、アプリの機能、デジタルコンテンツのサブスクリプションに使用します。誤ったフレームワークを使用すると、App Review で却下されます。
しないこと:複数箇所に加盟店 ID をハードコードする
// WRONG: Merchant ID scattered across the codebase
let request1 = PKPaymentRequest()
request1.merchantIdentifier = "merchant.com.example.app"
// ...elsewhere:
let request2 = PKPaymentRequest()
request2.merchantIdentifier = "merchant.com.example.app" // easy to get out of sync
// CORRECT: Centralize configuration
enum PaymentConfig {
static let merchantIdentifier = "merchant.com.example.app"
static let countryCode = "US"
static let currencyCode = "USD"
static let supportedNetworks: [PKPaymentNetwork] = [.visa, .masterCard, .amex]
}
レビューチェックリスト
- Apple Pay の機能が有効で、Developer portal で加盟店 ID が設定されている
- Payment Processing Certificate が生成され、インストールされている
- Apple Pay ボタンを表示する前に
canMakePayments(usingNetworks:)を確認している - 利用可能なカードの有無を確認する箇所では、Apple Pay が目立つように表示されている
- 商品の選択、任意の詳細情報、複雑な配送方法の選択を、支払いシートの表示前に収集している
-
paymentSummaryItemsの最後の項目が、加盟店の表示名を含む合計金額になっている - 支払いの概要とトークンコンテキストの金額に
NSDecimalNumberを使用している - 支払いトークンをサーバーに送信して処理している(クライアント側では決してデコードしない)
- 表示中は
PKPaymentAuthorizationControllerを保持し、終了後に解放している -
paymentAuthorizationControllerDidFinishでコントローラーを閉じている - 配送方法の変更時に、デリゲートコールバックを通じて合計金額を再計算している
- 仮想商品やデジタルコンテンツには StoreKit を使用し、実物の商品、サービス、寄付、対象となる定期的な支払いには Apple Pay を使用している
- Wallet のパスを署名済みの
.pkpassバンドルから読み込んでいる -
PKPass(data:)の無効なデータおよび無効な署名の失敗を明示している - パスの操作に
PKPassLibrary.isPassLibraryAvailable()を使用し、パス追加の可否判定には使用していない - パス追加の UI を表示する前に
PKAddPassesViewController.canAddPasses()を確認している - パスを置き換える際に
PKPassLibrary.replacePass(with:)の Boolean 戻り値を確認している - Apple Pay ボタンに、システム提供の
PKPaymentButtonまたはPayWithApplePayButtonを使用している - Wallet への追加 UI に、システム提供の
PKAddPassButton、AddPassToWalletButton、またはPKAddPassesViewControllerを使用している - 承認結果でエラー状態(ネットワーク障害、カードの拒否)を処理している
参考資料
- 拡張パターン(定期支払い/後払い、クーポンコード、複数加盟店、パスバンドル、パスの更新):references/wallet-passes.md
- PassKit フレームワーク
- PKPaymentRequest
- PKPaymentAuthorizationController
- PKPaymentButton
- PayWithApplePayButton
- AddPassToWalletButton
- PKPass
- PKAddPassesViewController
- PKPassLibrary
- PKPaymentNetwork
- Apple Pay HIG