passkit-wallet by dpearson2699/swift-ios-skills
npx skills add https://github.com/dpearson2699/swift-ios-skills --skill passkit-wallet为实体商品和服务接受 Apple Pay 支付,并将通行证添加到用户的 Wallet。涵盖支付按钮、支付请求、授权、Wallet 通行证和商家配置。目标版本为 Swift 6.2 / iOS 26+。
merchant.com.example.app)在显示 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
)
}
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
在 SwiftUI 中使用内置的 PayWithApplePayButton 视图。
import SwiftUI
import PassKit
struct CheckoutView: View {
var body: some View {
PayWithApplePayButton(.buy) {
startPayment()
}
.payWithApplePayButtonStyle(.black)
.frame(height: 48)
.padding()
}
}
对于基于 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: "小部件", amount: 9.99),
PKPaymentSummaryItem(label: "运费", amount: 4.99),
PKPaymentSummaryItem(label: "我的商店", amount: 14.98) // 总计(最后一项)
]
return request
}
paymentSummaryItems 中的 最后一项 被视为总计,其标签在支付表单上显示为商家名称。
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, .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
) {
// 将 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()
}
}
func paymentAuthorizationController(
_ controller: PKPaymentAuthorizationController,
didSelectShippingMethod shippingMethod: PKShippingMethod,
handler completion: @escaping (PKPaymentRequestShippingMethodUpdate) -> Void
) {
let updatedItems = recalculateItems(with: shippingMethod)
let update = PKPaymentRequestShippingMethodUpdate(paymentSummaryItems: updatedItems)
completion(update)
}
加载 .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)
}
}
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
}
}
}
}
使用 PKPassLibrary 来检查和操作用户已有的通行证。
let library = PKPassLibrary()
// 检查特定通行证是否已在 Wallet 中
let hasPass = library.containsPass(pass)
// 检索你的应用可以访问的通行证
let passes = library.passes()
// 检查通行证库是否可用
guard PKPassLibrary.isPassLibraryAvailable() else { return }
Apple Pay (PassKit) 用于 实体商品和服务。StoreKit 用于数字内容、订阅和应用内购买。使用错误的框架会导致应用审核被拒。
// 错误:使用 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)
]
// 错误:商家 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]
}
paymentSummaryItems 中的最后一项是总计行。如果省略它,支付表单将不显示商家名称或总计。
// 错误:没有总计项
request.paymentSummaryItems = [
PKPaymentSummaryItem(label: "小部件", amount: 9.99)
]
// 正确:最后一项是带有商家名称的总计
request.paymentSummaryItems = [
PKPaymentSummaryItem(label: "小部件", amount: 9.99),
PKPaymentSummaryItem(label: "我的商店", amount: 9.99) // 总计
]
// 错误:未检查就显示 Apple Pay 按钮
PayWithApplePayButton(.buy) { startPayment() }
// 正确:仅在可用时显示
if PKPaymentAuthorizationController.canMakePayments(
usingNetworks: PaymentConfig.supportedNetworks
) {
PayWithApplePayButton(.buy) { startPayment() }
} else {
// 显示替代结账或设置按钮
Button("设置 Apple Pay") { /* 引导用户 */ }
}
// 错误:在 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()
}
canMakePayments(usingNetworks:)paymentSummaryItems 中的最后一项是带有商家显示名称的总计paymentAuthorizationControllerDidFinish 关闭控制器.pkpass 包加载PKPassLibrary.isPassLibraryAvailable()PKPaymentButton 或 PayWithApplePayButtonreferences/wallet-passes.md每周安装量
325
代码仓库
GitHub 星标
269
首次出现
2026年3月8日
安全审计
安装于
codex322
opencode319
github-copilot319
amp319
cline319
kimi-cli319
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+.
merchant.com.example.app)Always verify the device can make payments before showing 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
)
}
Use the built-in PayWithApplePayButton view in SwiftUI.
import SwiftUI
import PassKit
struct CheckoutView: View {
var body: some View {
PayWithApplePayButton(.buy) {
startPayment()
}
.payWithApplePayButtonStyle(.black)
.frame(height: 48)
.padding()
}
}
Use PKPaymentButton for UIKit-based interfaces.
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, , ,
Build a PKPaymentRequest with your merchant details and the items being purchased.
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 paymentSummaryItems is treated as the total and its label appears as the merchant name on the payment sheet.
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
| Network | Constant |
|---|---|
| Visa | .visa |
| Mastercard | .masterCard |
| American Express | .amex |
| Discover | .discover |
| China UnionPay | .chinaUnionPay |
| JCB | .JCB |
| Maestro |
Query available networks at runtime with PKPaymentRequest.availableNetworks().
Use PKPaymentAuthorizationController (works in both SwiftUI and UIKit, no view controller needed).
@MainActor
func startPayment() {
let request = createPaymentRequest()
let controller = PKPaymentAuthorizationController(paymentRequest: request)
controller.delegate = self
controller.present()
}
Implement PKPaymentAuthorizationControllerDelegate to process the payment token.
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)
}
Load a .pkpass file and present 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)
}
}
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
}
}
}
}
Use PKPassLibrary to inspect and manage passes the user already has.
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 }
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.
// 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)
]
// 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]
}
The last item in paymentSummaryItems is the total row. If you omit it, the payment sheet shows no merchant name or total.
// 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
]
// 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()
}
canMakePayments(usingNetworks:) checked before showing Apple Pay buttonpaymentSummaryItems is the total with merchant display namepaymentAuthorizationControllerDidFinish dismisses the controller.pkpass bundlesPKPassLibrary.isPassLibraryAvailable() checked before pass operationsPKPaymentButton or PayWithApplePayButtonreferences/wallet-passes.mdWeekly Installs
325
Repository
GitHub Stars
269
First Seen
Mar 8, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykWarn
Installed on
codex322
opencode319
github-copilot319
amp319
cline319
kimi-cli319
股票分析工具 - 一键生成NVDA、TSLA、AAPL等股票情感分析报告
835 周安装
.support.contribute.tip.maestro |
| Electron | .electron |
| Interac | .interac |