mcpField
P
passkit-wallet

passkit-wallet

github.com/dpearson2699開発1.2k401 回インストール13 週間前に更新
リポジトリ

passkit-wallet について

Swift 開発者向けの PassKit スキルガイドです。実物の商品やサービスの Apple Pay 決済と Wallet パス管理を扱い、設定、認証フロー、実装上の確認事項を説明します。

主な機能

  1. 01Apple Pay の加盟店情報設定
  2. 02決済リクエストの作成と表示
  3. 03認証と配送変更の処理
  4. 04Wallet パスの追加と確認
  5. 05実装上のよくある誤りの確認

活用シーン

  1. 01実物商品の Apple Pay 決済の導入
  2. 02配送方法変更後の注文金額更新
  3. 03署名済みパスの Wallet への追加
  4. 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+ が対象です。

目次

セットアップ

プロジェクトの設定
  1. Xcode で Apple Pay 機能を有効にします
  2. Apple Developer ポータルで Merchant ID を作成します(形式:merchant.com.example.app)
  3. Merchant ID 用の Payment Processing Certificate を生成してインストールします
  4. 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 を使用する
  • 支払い承認結果のエラー状態(ネットワーク障害、カードの拒否など)に対処する

参考資料

コメント