passkit-wallet
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePassKit — Apple Pay & Wallet
PassKit — Apple Pay 与 Wallet
Accept Apple Pay payments for physical goods and services, and add passes to
the user's Wallet. Covers payment buttons, payment requests, authorization,
Wallet passes, and merchant configuration. Targets Swift 6.2 / iOS 26+.
支持为实体商品和服务接入Apple Pay支付,以及向用户的Wallet中添加凭证。内容涵盖支付按钮、支付请求、授权流程、Wallet凭证及商户配置。适配Swift 6.2 / iOS 26+版本。
Contents
目录
Setup
配置步骤
Project Configuration
项目配置
- Enable the Apple Pay capability in Xcode
- Create a Merchant ID in the Apple Developer portal (format: )
merchant.com.example.app - Generate and install a Payment Processing Certificate for your merchant ID
- Add the merchant ID to your entitlements
- 在Xcode中启用Apple Pay功能
- 在Apple开发者后台创建商户ID(格式:)
merchant.com.example.app - 为商户ID生成并安装支付处理证书
- 将商户ID添加到项目授权文件中
Availability Check
可用性检查
Always verify the device can make payments before showing Apple Pay UI.
swift
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界面之前,务必先验证设备是否支持支付功能。
swift
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
显示Apple Pay按钮
SwiftUI
SwiftUI
Use the built-in view in SwiftUI.
PayWithApplePayButtonswift
import SwiftUI
import PassKit
struct CheckoutView: View {
var body: some View {
PayWithApplePayButton(.buy) {
startPayment()
}
.payWithApplePayButtonStyle(.black)
.frame(height: 48)
.padding()
}
}使用SwiftUI内置的视图。
PayWithApplePayButtonswift
import SwiftUI
import PassKit
struct CheckoutView: View {
var body: some View {
PayWithApplePayButton(.buy) {
startPayment()
}
.payWithApplePayButtonStyle(.black)
.frame(height: 48)
.padding()
}
}UIKit
UIKit
Use for UIKit-based interfaces.
PKPaymentButtonswift
let button = PKPaymentButton(
paymentButtonType: .buy,
paymentButtonStyle: .black
)
button.cornerRadius = 12
button.addTarget(self, action: #selector(startPayment), for: .touchUpInside)Button types: , , , , , , , , , , , , , ,
.buy.setUp.inStore.donate.checkout.book.subscribe.reload.addMoney.topUp.order.rent.support.contribute.tip对于基于UIKit的界面,使用。
PKPaymentButtonswift
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.tipCreating a Payment Request
创建支付请求
Build a with your merchant details and the items being purchased.
PKPaymentRequestswift
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
}The last item in is treated as the total and its label appears as the merchant name on the payment sheet.
paymentSummaryItems构建包含商户信息和购买商品的。
PKPaymentRequestswift
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: "小部件", amount: 9.99),
PKPaymentSummaryItem(label: "配送费", amount: 4.99),
PKPaymentSummaryItem(label: "我的商店", amount: 14.98) // 总计(最后一项)
]
return request
}paymentSummaryItemsRequesting Shipping and Contact Info
请求配送与联系信息
swift
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, .servicePickupswift
request.requiredShippingContactFields = [.postalAddress, .emailAddress, .name]
request.requiredBillingContactFields = [.postalAddress]
request.shippingMethods = [
PKShippingMethod(label: "标准配送", amount: 4.99),
PKShippingMethod(label: "加急配送", amount: 9.99),
]
request.shippingMethods?[0].identifier = "standard"
request.shippingMethods?[0].detail = "5-7个工作日"
request.shippingMethods?[1].identifier = "express"
request.shippingMethods?[1].detail = "1-2个工作日"
request.shippingType = .shipping // .delivery, .storePickup, .servicePickupSupported Networks
支持的支付网络
| Network | Constant |
|---|---|
| Visa | |
| Mastercard | |
| American Express | |
| Discover | |
| China UnionPay | |
| JCB | |
| Maestro | |
| Electron | |
| Interac | |
Query available networks at runtime with .
PKPaymentRequest.availableNetworks()| 支付网络 | 常量 |
|---|---|
| Visa | |
| Mastercard | |
| American Express | |
| Discover | |
| 中国银联 | |
| JCB | |
| Maestro | |
| Electron | |
| Interac | |
可通过在运行时查询可用的支付网络。
PKPaymentRequest.availableNetworks()Presenting the Payment Sheet
展示支付表单
Use (works in both SwiftUI and UIKit, no view controller needed).
PKPaymentAuthorizationControllerswift
@MainActor
func startPayment() {
let request = createPaymentRequest()
let controller = PKPaymentAuthorizationController(paymentRequest: request)
controller.delegate = self
controller.present()
}使用(适用于SwiftUI和UIKit,无需视图控制器)。
PKPaymentAuthorizationControllerswift
@MainActor
func startPayment() {
let request = createPaymentRequest()
let controller = PKPaymentAuthorizationController(paymentRequest: request)
controller.delegate = self
controller.present()
}Handling Payment Authorization
处理支付授权
Implement to process the payment token.
PKPaymentAuthorizationControllerDelegateswift
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()
}
}实现来处理支付令牌。
PKPaymentAuthorizationControllerDelegateswift
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()
}
}Handling Shipping Changes
处理配送方式变更
swift
func paymentAuthorizationController(
_ controller: PKPaymentAuthorizationController,
didSelectShippingMethod shippingMethod: PKShippingMethod,
handler completion: @escaping (PKPaymentRequestShippingMethodUpdate) -> Void
) {
let updatedItems = recalculateItems(with: shippingMethod)
let update = PKPaymentRequestShippingMethodUpdate(paymentSummaryItems: updatedItems)
completion(update)
}swift
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
Wallet凭证
Adding a Pass to Wallet
向Wallet添加凭证
Load a file and present .
.pkpassPKAddPassesViewControllerswift
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)
}
}加载文件并展示。
.pkpassPKAddPassesViewControllerswift
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 Button
SwiftUI Wallet按钮
swift
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
}
}
}
}swift
import PassKit
import SwiftUI
struct AddPassButton: View {
let passData: Data
var body: some View {
Button("添加至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:
// 展示审核界面
break
case .didAddPasses:
// 凭证添加成功
break
case .didCancelAddPasses:
break
@unknown default:
break
}
}
}
}Checking Pass Library
检查凭证库
Use to inspect and manage passes the user already has.
PKPassLibraryswift
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 }使用检查和管理用户已有的凭证。
PKPassLibraryswift
let library = PKPassLibrary()
// 检查特定凭证是否已在Wallet中
let hasPass = library.containsPass(pass)
// 获取你的应用可访问的凭证
let passes = library.passes()
// 检查凭证库是否可用
guard PKPassLibrary.isPassLibraryAvailable() else { return }Common Mistakes
常见错误
DON'T: Use StoreKit for physical goods
错误做法:使用StoreKit售卖实体商品
Apple Pay (PassKit) is for physical goods and services. StoreKit is for digital
content, subscriptions, and in-app purchases. Using the wrong framework leads to
App Review rejection.
swift
// 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)
]Apple Pay(PassKit)适用于实体商品和服务。StoreKit则用于数字内容、订阅和内购。使用错误的框架会导致应用审核被拒。
swift
// 错误:使用StoreKit售卖实体产品
let product = try await Product.products(for: ["com.example.tshirt"])
// 正确:使用Apple Pay售卖实体商品
let request = PKPaymentRequest()
request.paymentSummaryItems = [
PKPaymentSummaryItem(label: "T恤", amount: 29.99),
PKPaymentSummaryItem(label: "我的商店", amount: 29.99)
]DON'T: Hardcode merchant ID in multiple places
错误做法:在多处硬编码商户ID
swift
// 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]
}swift
// 错误:商户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]
}DON'T: Forget the total line item
错误做法:遗漏总计行项目
The last item in is the total row. If you omit it, the
payment sheet shows no merchant name or total.
paymentSummaryItemsswift
// 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
]paymentSummaryItemsswift
// 错误:没有总计项
request.paymentSummaryItems = [
PKPaymentSummaryItem(label: "小部件", amount: 9.99)
]
// 正确:最后一项是包含商户显示名称的总计
request.paymentSummaryItems = [
PKPaymentSummaryItem(label: "小部件", amount: 9.99),
PKPaymentSummaryItem(label: "我的商店", amount: 9.99) // 总计
]DON'T: Skip the canMakePayments check
错误做法:跳过canMakePayments检查
swift
// 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 */ }
}swift
// 错误:未检查就显示Apple Pay按钮
PayWithApplePayButton(.buy) { startPayment() }
// 正确:仅在支持时显示
if PKPaymentAuthorizationController.canMakePayments(
usingNetworks: PaymentConfig.supportedNetworks
) {
PayWithApplePayButton(.buy) { startPayment() }
} else {
// 显示替代结账方式或配置按钮
Button("设置Apple Pay") { /* 引导用户 */ }
}DON'T: Dismiss the controller before completing authorization
错误做法:完成授权前关闭控制器
swift
// 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()
}swift
// 错误:在didAuthorizePayment中关闭控制器
func paymentAuthorizationController(
_ controller: PKPaymentAuthorizationController,
didAuthorizePayment payment: PKPayment,
handler completion: @escaping (PKPaymentAuthorizationResult) -> Void
) {
controller.dismiss() // 时机过早 -- 会导致表单空白
completion(.init(status: .success, errors: nil))
}
// 正确:仅在paymentAuthorizationControllerDidFinish中关闭控制器
func paymentAuthorizationControllerDidFinish(
_ controller: PKPaymentAuthorizationController
) {
controller.dismiss()
}Review Checklist
审核检查清单
- Apple Pay capability enabled and merchant ID configured in Developer portal
- Payment Processing Certificate generated and installed
- checked before showing Apple Pay button
canMakePayments(usingNetworks:) - Last item in is the total with merchant display name
paymentSummaryItems - Payment token sent to server for processing (never decoded client-side)
- dismisses the controller
paymentAuthorizationControllerDidFinish - Shipping method changes recalculate totals via delegate callback
- StoreKit used for digital goods; Apple Pay used for physical goods
- Wallet passes loaded from signed bundles
.pkpass - checked before pass operations
PKPassLibrary.isPassLibraryAvailable() - Apple Pay button uses system-provided or
PKPaymentButtonPayWithApplePayButton - Error states handled in authorization result (network failures, declined cards)
- 已启用Apple Pay功能,并在开发者后台配置了商户ID
- 已生成并安装支付处理证书
- 在显示Apple Pay按钮前检查了
canMakePayments(usingNetworks:) - 的最后一项是带有商户显示名称的总计
paymentSummaryItems - 支付令牌已发送至服务器处理(切勿在客户端解码)
- 负责关闭控制器
paymentAuthorizationControllerDidFinish - 配送方式变更时通过代理回调重新计算总计
- 数字商品使用StoreKit;实体商品使用Apple Pay
- Wallet凭证从已签名的包加载
.pkpass - 在操作凭证前检查了
PKPassLibrary.isPassLibraryAvailable() - Apple Pay按钮使用系统提供的或
PKPaymentButtonPayWithApplePayButton - 授权结果中处理了错误状态(网络故障、卡片被拒等)
References
参考资料
- Extended patterns (recurring payments, coupon codes, multi-merchant):
references/wallet-passes.md - PassKit framework
- PKPaymentRequest
- PKPaymentAuthorizationController
- PKPaymentButton
- PKPass
- PKAddPassesViewController
- PKPassLibrary
- PKPaymentNetwork
- 扩展模式( recurring payments、优惠券代码、多商户):
references/wallet-passes.md - PassKit框架
- PKPaymentRequest
- PKPaymentAuthorizationController
- PKPaymentButton
- PKPass
- PKAddPassesViewController
- PKPassLibrary
- PKPaymentNetwork