关于 passkit-wallet
面向 Swift 开发者的 PassKit 技能指南,涵盖实体商品与服务的 Apple Pay 支付,以及 Wallet 通行证管理。内容包括配置、授权流程和常见错误检查。
主要功能
- 01配置 Apple Pay 商户信息
- 02构建并展示支付请求
- 03处理支付授权与配送变更
- 04添加及检查 Wallet 通行证
- 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 生成并安装支付处理证书
- 将 Merchant ID 添加到你的 entitlements
可用性检查
显示 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 视图。
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 内购买。使用错误的框架会导致 App 审核被拒。
// 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()
}
检查清单
- 已在 Developer 门户中启用 Apple Pay 功能并配置商户 ID
- 已生成并安装支付处理证书
- 显示 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