关于 passkit
PassKit 是用于实现 Apple Pay 支付和 Wallet 卡券的 Swift 开发指南,涵盖支付配置、授权、配送更新和卡券管理,适用于处理实物商品、服务、捐赠或符合条件的定期付款的应用。
主要功能
- 01Apple Pay 按钮与支付请求配置
- 02支付界面展示与授权
- 03配送方式和总额更新
- 04添加已签名的 Wallet 卡券
- 05检查和替换卡券库内容
适用场景
- 01为实物商品结账接入 Apple Pay
- 02处理支付授权令牌
- 03在 Apple 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 流程,一个 PKPaymentRequest 只能设置一种可选的高级请求类型:定期付款、自动充值、延期付款、Apple Pay Later 可用性或多令牌上下文。结账流程需要使用多种模式时,请使用多个付款请求。
目录
设置
项目配置
- 在 Xcode 中启用 Apple Pay 功能
- 在 Apple Developer 门户中创建 Merchant ID(格式:
merchant.com.example.app) - 为 Merchant ID 生成并安装支付处理证书
- 将 Merchant ID 添加到你的授权文件中
可用性检查
显示 Apple Pay 界面前,务必确认设备可以付款。如果使用 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 使用 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")
) // Total
]
return request
}
paymentSummaryItems 中的最后一项会被视为总额,其标签会显示在付款表单的 Pay 行中。
请求配送和联系信息
仅请求用于确定价格、履行订单或依法处理订单所需的联系信息。 如果付款表单无法准确收集,请在 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:) 需要已签名的通行证数据,并且可能抛出数据无效或签名无效错误。在审查指南中,应明确指出 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 {
// Signed pass data is invalid or the signature cannot be validated.
showRecoverablePassError(error)
}
}
SwiftUI Wallet 按钮
使用 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)
}
}
}
检查通行证库
使用 PKPassLibrary 检查和管理用户已有的通行证。在执行通行证库操作前,检查
PKPassLibrary.isPassLibraryAvailable();但要使用 PKAddPassesViewController.canAddPasses() 判断设备是否可以添加通行证。passes() 仅返回应用可通过其
entitlements 访问的通行证。替换现有通行证时,检查 replacePass(with:) 返回的布尔值并处理失败情况。有关已签名通行证包的构建、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 门户中配置商户 ID
- 已生成并安装 Payment Processing Certificate
- 在显示 Apple Pay 按钮前检查了
canMakePayments(usingNetworks:) - 在检查可用活跃卡片的位置显著展示 Apple Pay
- 在付款界面显示前收集商品选项、可选详情和复杂配送选项
-
paymentSummaryItems中的最后一项是包含商户显示名称的总金额 - 付款摘要和令牌上下文中的金额使用
NSDecimalNumber - 将付款令牌发送到服务器处理(绝不在客户端解码)
- 展示期间保留
PKPaymentAuthorizationController,结束后清除 -
paymentAuthorizationControllerDidFinish会关闭控制器 - 配送方式更改时,通过代理回调重新计算总额
- 虚拟商品/数字内容使用 StoreKit;实物商品、服务、捐赠以及符合条件的定期付款使用 Apple Pay
- 从已签名的
.pkpass包加载 Wallet 通行证 - 显示
PKPass(data:)的数据无效和签名无效错误 - 通行证操作使用
PKPassLibrary.isPassLibraryAvailable(),而非添加通行证能力检查 - 显示添加通行证界面前检查
PKAddPassesViewController.canAddPasses() - 替换通行证时检查
PKPassLibrary.replacePass(with:)返回的布尔值 - Apple Pay 按钮使用系统提供的
PKPaymentButton或PayWithApplePayButton - 添加到 Wallet 的界面使用系统提供的
PKAddPassButton、AddPassToWalletButton或PKAddPassesViewController - 处理授权结果中的错误状态(网络故障、银行卡被拒)