
passkit
熱門使用 PassKit 整合 Apple Pay 付款與 Wallet 票卡(Passes)。適用於新增 Apple Pay 按鈕、建立付款請求、處理付款授權、將票卡加入 Wallet、設定商家功能、管理運送/聯絡資訊欄位,或使用 PKPaymentRequest、PKPaymentAuthorizationController、PKPaymentButton、AddPassToWalletButton、PKPass、PKAddPassesViewController、PKPassLibrary、Wallet 票卡發行,以及實體商品、真實世界服務、捐款與符合條件的定期付款等 Apple Pay 結帳流程。
使用 PassKit 整合 Apple Pay 付款與 Wallet 票卡(Passes)。適用於新增 Apple Pay 按鈕、建立付款請求、處理付款授權、將票卡加入 Wallet、設定商家功能、管理運送/聯絡資訊欄位,或使用 PKPaymentRequest、PKPaymentAuthorizationController、PKPaymentButton、AddPassToWalletButton、PKPass、PKAddPassesViewController、PKPassLibrary、Wallet 票卡發行,以及實體商品、真實世界服務、捐款與符合條件的定期付款等 Apple Pay 結帳流程。
PassKit
支援實體商品、真實世界服務、捐款以及符合條件的定期付款之 Apple Pay 結帳,並可將票卡加入使用者的 Wallet。涵蓋付款按鈕、付款請求、授權、Wallet 票卡與商家設定。適用於 Swift 6.3 / iOS 26+。
針對進階的 Apple Pay 流程,單一 PKPaymentRequest 只能設定一種可選的進階請求類型:定期付款(recurring)、自動儲值(automatic reload)、延期付款(deferred)、Apple Pay Later 可用性,或多 Token 上下文(multi-token contexts)。當結帳流程需要上述多種模式時,請使用獨立的付款請求。
Contents
- Setup
- Displaying the Apple Pay Button
- Creating a Payment Request
- Presenting the Payment Sheet
- Handling Payment Authorization
- Wallet Passes
- Checking Pass Library
- Common Mistakes
- Review Checklist
- References
Setup
Project Configuration
- 在 Xcode 中啟用 Apple Pay capability
- 在 Apple Developer Portal 建立 Merchant ID(格式:
merchant.com.example.app) - 為您的 Merchant ID 產生並安裝 Payment Processing Certificate
- 將 Merchant ID 新增至您的 entitlements
Availability Check
在顯示 Apple Pay UI 之前,務必先確認裝置是否支援付款。若您使用 canMakePayments(usingNetworks:capabilities:) 檢查是否有可用卡片,依 Apple HIG 規範,在使用該檢查的任何地方,Apple Pay 都應作為主要且顯眼的付款選項。
import PassKit
func canMakePayments() -> Bool {
// 檢查裝置是否支援 Apple Pay
guard PKPaymentAuthorizationController.canMakePayments() else {
return false
}
// 檢查使用者是否有您所支援發卡機構網路的卡片
return PKPaymentAuthorizationController.canMakePayments(
usingNetworks: [.visa, .masterCard, .amex, .discover],
capabilities: .threeDSecure
)
}
Displaying the Apple Pay Button
SwiftUI
在 SwiftUI 中使用內建的 PayWithApplePayButton View。任何標示為 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
Creating a Payment Request
建構包含您的商家詳細資訊以及購買項目的 PKPaymentRequest。
PassKit 金額相關 API 接收 NSDecimalNumber,而非 Double。
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")
) // 總額
]
return request
}
paymentSummaryItems 中的最後一個項目會被視為總額,其標籤會顯示在付款單的付款明細列中。
Requesting Shipping and Contact Info
僅請求計價、履約或法律上處理訂單所必需的聯絡資訊欄位。
當付款單無法精確收集時,請在點擊 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
Supported Networks
| 發卡機構網路 | 常數 |
|---|---|
| Visa | .visa |
| Mastercard | .masterCard |
| American Express | .amex |
| Discover | .discover |
| 中國銀聯 (China UnionPay) | .chinaUnionPay |
| JCB | .JCB |
| Maestro | .maestro |
| Electron | .electron |
| Interac | .interac |
可在執行期使用 PKPaymentRequest.availableNetworks() 查詢可用的網路。
Presenting the Payment Sheet
使用 PKPaymentAuthorizationController(同時適用於 SwiftUI 與 UIKit,不需要 View Controller)。控制器的 delegate 為弱參照(weak),因此在付款單開啟期間需保持強參照以保留該控制器。
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
}
}
}
}
Handling Payment Authorization
實作 PKPaymentAuthorizationControllerDelegate 以處理付款 Token。
extension CheckoutCoordinator: PKPaymentAuthorizationControllerDelegate {
func paymentAuthorizationController(
_ controller: PKPaymentAuthorizationController,
didAuthorizePayment payment: PKPayment,
handler completion: @escaping (PKPaymentAuthorizationResult) -> Void
) {
// 將 payment.token.paymentData 傳送至您的金流服務商
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
}
}
}
Handling Shipping Changes
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 Passes
Adding a Pass to Wallet
載入已簽署的 .pkpass 資料,確認裝置支援新增票卡,並在希望使用者於加入前預覽票卡時呈現 PKAddPassesViewController。PKPass(data:) 預期接收已簽署的票卡資料,且可能拋出資料無效或簽章無效的錯誤。在審查指引中請明確列出 invalid-data 與 invalid-signature 失敗原因,而非使用單純的 try? 掩蓋錯誤。
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 {
// 簽署的票卡資料無效或無法驗證簽章。
showRecoverablePassError(error)
}
}
SwiftUI Wallet Button
使用 AddPassToWalletButton 作為 SwiftUI 對應 PKAddPassButton 的元件。
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)
}
}
}
Checking Pass Library
使用 PKPassLibrary 檢查與管理使用者已擁有的票卡。在進行票卡庫操作前先檢查 PKPassLibrary.isPassLibraryAvailable(),但判定裝置是否能加入票卡時請使用 PKAddPassesViewController.canAddPasses()。passes() 只會回傳您 App 透過其 entitlement 權限可存取的票卡。替換既有票卡時,請檢查 replacePass(with:) 返回的 Boolean 結果並處理失敗情況。關於已簽署票卡包建構、更新 Web 服務與 replacePass(with:) 的說明,請參考 references/wallet-passes.md。
let library = PKPassLibrary()
// 檢查特定票卡是否已在 Wallet 中
let hasPass = library.containsPass(pass)
// 取得您的 App 可存取的票卡
let passes = library.passes()
// 檢查票卡庫是否可用
guard PKPassLibrary.isPassLibraryAvailable() else { return }
Common Mistakes
DON'T: Use StoreKit for physical goods
Apple Pay (PassKit) 用於實體商品、真實世界服務、捐款以及符合條件的定期付款。StoreKit 則用於虛擬商品、App 功能與數位內容訂閱。使用錯誤的框架將導致 App 審查(App Review)被拒絕。
DON'T: Hardcode merchant ID in multiple places
// 錯誤:Merchant ID 散落於程式碼庫各處
let request1 = PKPaymentRequest()
request1.merchantIdentifier = "merchant.com.example.app"
// ...其他地方:
let request2 = PKPaymentRequest()
request2.merchantIdentifier = "merchant.com.example.app" // 容易導致設定不一致
// 正確:集中管理設定
enum PaymentConfig {
static let merchantIdentifier = "merchant.com.example.app"
static let countryCode = "US"
static let currencyCode = "USD"
static let supportedNetworks: [PKPaymentNetwork] = [.visa, .masterCard, .amex]
}
Review Checklist
- [ ] 已啟用 Apple Pay capability 並於 Developer Portal 完成 Merchant ID 設定
- [ ] 已產生並安裝 Payment Processing Certificate
- [ ] 在顯示 Apple Pay 按鈕前已先檢查
canMakePayments(usingNetworks:) - [ ] 在任何確認卡片可用性的地方,Apple Pay 皆處於顯眼位置
<!-- truncated for translation batch; full body continues in source -->



