passkit-wallet について
Swift 開発者向けの PassKit スキルガイドです。実物の商品やサービスの Apple Pay 決済と Wallet パス管理を扱い、設定、認証フロー、実装上の確認事項を説明します。
主な機能
- 01Apple Pay の加盟店情報設定
- 02決済リクエストの作成と表示
- 03認証と配送変更の処理
- 04Wallet パスの追加と確認
- 05実装上のよくある誤りの確認
活用シーン
- 01実物商品の Apple Pay 決済の導入
- 02配送方法変更後の注文金額更新
- 03署名済みパスの Wallet への追加
- 04既存パスと決済実装の確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ dpearson2699/ swift-ios-skills --skill passkit-walletインストール後、Claude Code で/passkit-walletを入力して読み込まれたか確認します。
同梱ファイルreferences/wallet-passes.md
詳細
PassKit — Apple Pay と Wallet
Apple Pay で物理的な商品やサービスの支払いを受け付け、パスをユーザーの Wallet に追加します。決済ボタン、決済リクエスト、承認、Wallet パス、加盟店の設定を扱います。Swift 6.2 / iOS 26+ が対象です。
目次
セットアップ
プロジェクトの設定
- Xcode で Apple Pay 機能を有効にします
- Apple Developer ポータルで Merchant ID を作成します(形式:
merchant.com.example.app) - Merchant ID 用の Payment Processing Certificate を生成してインストールします
- Merchant ID をエンタイトルメントに追加します
利用可能かどうかの確認
Apple Pay UI を表示する前に、デバイスで決済できることを必ず確認してください。
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 ビューを使用します。
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)
ボタンの種類: .buy、.setUp、.inStore、.donate、.checkout、.book、.subscribe、.reload、.addMoney、.topUp、.order、.rent、.support、.contribute、.tip
決済リクエストの作成
加盟店の詳細と購入商品を指定して PKPaymentRequest を作成します。
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: 9.99),
PKPaymentSummaryItem(label: "Shipping", amount: 4.99),
PKPaymentSummaryItem(label: "My Store", amount: 14.98) // Total (last item)
]
return request
}
paymentSummaryItems の最後の項目は合計として扱われ、そのラベルが決済シートに加盟店名として表示されます。
配送先と連絡先情報のリクエスト
request.requiredShippingContactFields = [.postalAddress, .emailAddress, .name]
request.requiredBillingContactFields = [.postalAddress]
request.shippingMethods = [
PKShippingMethod(label: "Standard", amount: 4.99),
PKShippingMethod(label: "Express", amount: 9.99),
]
request.shippingMethods?[0].identifier = "standard"
request.shippingMethods?[0].detail = "5-7 business days"
request.shippingMethods?[1].identifier = "express"
request.shippingMethods?[1].detail = "1-2 business days"
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 の両方で動作し、ビューコントローラーは不要です)。
@MainActor
func startPayment() {
let request = createPaymentRequest()
let controller = PKPaymentAuthorizationController(paymentRequest: request)
controller.delegate = self
controller.present()
}
決済承認の処理
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()
}
}
配送先変更の処理
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 を表示します。
func addPassToWallet(data: Data) {
guard let pass = try? PKPass(data: data) else { return }
if let addController = PKAddPassesViewController(pass: pass) {
addController.delegate = self
present(addController, animated: true)
}
}
SwiftUI の Wallet ボタン
import PassKit
import SwiftUI
struct AddPassButton: View {
let passData: Data
var body: some View {
Button("Add to Wallet") {
addPass()
}
}
func addPass() {
guard let pass = try? PKPass(data: passData) else { return }
let library = PKPassLibrary()
library.addPasses([pass]) { status in
switch status {
case .shouldReviewPasses:
// Present review UI
break
case .didAddPasses:
// Passes added successfully
break
case .didCancelAddPasses:
break
@unknown default:
break
}
}
}
}
パスライブラリの確認
PKPassLibrary を使用して、ユーザーがすでに所有しているパスを確認、管理します。
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 で却下されます。
// WRONG: Using StoreKit to sell a physical product
let product = try await Product.products(for: ["com.example.tshirt"])
// CORRECT: Use Apple Pay for physical goods
let request = PKPaymentRequest()
request.paymentSummaryItems = [
PKPaymentSummaryItem(label: "T-Shirt", amount: 29.99),
PKPaymentSummaryItem(label: "My Store", amount: 29.99)
]
禁止:複数箇所に Merchant 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]
}
禁止:合計の項目を忘れる
paymentSummaryItems の最後の項目が合計行です。省略すると、決済シートに加盟店名も合計も表示されません。
// WRONG: No total item
request.paymentSummaryItems = [
PKPaymentSummaryItem(label: "Widget", amount: 9.99)
]
// CORRECT: Last item is the total with your merchant name
request.paymentSummaryItems = [
PKPaymentSummaryItem(label: "Widget", amount: 9.99),
PKPaymentSummaryItem(label: "My Store", amount: 9.99) // Total
]
禁止:canMakePayments の確認を省略する
// WRONG: Show Apple Pay button without checking
PayWithApplePayButton(.buy) { startPayment() }
// CORRECT: Only show when available
if PKPaymentAuthorizationController.canMakePayments(
usingNetworks: PaymentConfig.supportedNetworks
) {
PayWithApplePayButton(.buy) { startPayment() }
} else {
// Show alternative checkout or setup button
Button("Set Up Apple Pay") { /* guide user */ }
}
禁止:承認が完了する前にコントローラーを閉じる
// WRONG: Dismissing inside didAuthorizePayment
func paymentAuthorizationController(
_ controller: PKPaymentAuthorizationController,
didAuthorizePayment payment: PKPayment,
handler completion: @escaping (PKPaymentAuthorizationResult) -> Void
) {
controller.dismiss() // Too early -- causes blank sheet
completion(.init(status: .success, errors: nil))
}
// CORRECT: Dismiss only in paymentAuthorizationControllerDidFinish
func paymentAuthorizationControllerDidFinish(
_ controller: PKPaymentAuthorizationController
) {
controller.dismiss()
}
レビューチェックリスト
- Apple Pay の機能を有効にし、Developer ポータルでマーチャント ID を設定する
- Payment Processing Certificate を生成してインストールする
- Apple Pay ボタンを表示する前に
canMakePayments(usingNetworks:)を確認する -
paymentSummaryItemsの最後の項目を、マーチャントの表示名を含む合計金額にする - 支払いトークンをサーバーに送信して処理する(クライアント側では決してデコードしない)
-
paymentAuthorizationControllerDidFinishでコントローラーを閉じる - デリゲートコールバックを介して、配送方法の変更時に合計金額を再計算する
- デジタル商品には StoreKit を使用し、物理的な商品には Apple Pay を使用する
- 署名済みの
.pkpassバンドルから Wallet パスを読み込む - パスの操作を行う前に
PKPassLibrary.isPassLibraryAvailable()を確認する - Apple Pay ボタンには、システム提供の
PKPaymentButtonまたはPayWithApplePayButtonを使用する - 支払い承認結果のエラー状態(ネットワーク障害、カードの拒否など)に対処する
参考資料
- 拡張パターン(継続課金、クーポンコード、複数マーチャント):
references/wallet-passes.md - PassKit フレームワーク
- PKPaymentRequest
- PKPaymentAuthorizationController
- PKPaymentButton
- PKPass
- PKAddPassesViewController
- PKPassLibrary
- PKPaymentNetwork