proximity-reader

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

ProximityReader iOS Development Skill

ProximityReader iOS开发技能

Build contactless payment, loyalty, and ID verification features using Apple's ProximityReader framework on iPhone — no additional hardware required.
在iPhone上使用Apple的ProximityReader框架构建非接触式支付、会员服务和身份验证功能——无需额外硬件。

When to Use This Skill

何时使用此技能

  • Integrating Tap to Pay on iPhone (contactless payment acceptance)
  • Reading loyalty cards / VAS passes from Apple Wallet
  • Implementing Store and Forward for offline payment scenarios
  • Building ID verification with the Verifier API (mobile driver's licenses, national IDs)
  • Showing merchant education UI via
    ProximityReaderDiscovery
  • Debugging
    PaymentCardReaderError
    or
    MobileDocumentReaderError
  • 集成iPhone上的Tap to Pay(接受非接触式支付)
  • 读取Apple Wallet中的会员卡/VAS凭证
  • 为离线支付场景实现Store and Forward(存储转发)功能
  • 使用Verifier API构建身份验证(移动驾照、国民身份证)
  • 通过
    ProximityReaderDiscovery
    展示商家引导UI
  • 调试
    PaymentCardReaderError
    MobileDocumentReaderError

Framework Overview

框架概述

ProximityReader (iOS 15.4+, iPadOS 15.4+, Mac Catalyst 15.4+) enables an iPhone to act as a contactless reader for:
DomainKey ClassesMin iOS
Payments
PaymentCardReader
,
PaymentCardReaderSession
15.4
Loyalty (VAS)
VASRequest
,
VASReadResult
15.4
Store & Forward
StoreAndForwardPaymentCardReaderSession
,
PaymentCardReaderStore
17.0+
ID Verification
MobileDocumentReader
,
MobileDocumentReaderSession
17.0+
Merchant Discovery
ProximityReaderDiscovery
18.0+
ProximityReader(iOS 15.4+、iPadOS 15.4+、Mac Catalyst 15.4+)可让iPhone充当非接触式读取器,支持以下场景:
领域核心类最低iOS版本
支付
PaymentCardReader
,
PaymentCardReaderSession
15.4
会员服务(VAS)
VASRequest
,
VASReadResult
15.4
存储转发
StoreAndForwardPaymentCardReaderSession
,
PaymentCardReaderStore
17.0+
身份验证
MobileDocumentReader
,
MobileDocumentReaderSession
17.0+
商家发现
ProximityReaderDiscovery
18.0+

Prerequisites & Entitlements

前提条件与权限

Before writing any code, ensure:
  1. Entitlement: Request the Tap to Pay on iPhone entitlement from Apple via your developer account. Without it, the framework won't function.
  2. Payment Service Provider (PSP): You must coordinate with a Level 3 certified PSP (e.g. Stripe, Adyen, Square, Windcave). The PSP provides the reader token (JWT) required to initialize the reader.
  3. Device: iPhone XS or later. No additional NFC hardware needed.
  4. Xcode: Add the
    com.apple.developer.proximity-reader.payment.acceptance
    entitlement to your app's entitlements file.
For the Verifier API (ID reading), a separate entitlement and server-side reader token generation is required. Read
references/verifier-api.md
for details.
编写代码前,请确保满足以下要求:
  1. 权限:通过开发者账户向Apple申请iPhone上的Tap to Pay权限。没有此权限,框架将无法正常工作。
  2. 支付服务提供商(PSP):您必须与Level 3认证的PSP(如Stripe、Adyen、Square、Windcave)合作。PSP提供初始化读取器所需的reader token(JWT)。
  3. 设备:iPhone XS或更高版本。无需额外NFC硬件。
  4. Xcode:在应用的权限文件中添加
    com.apple.developer.proximity-reader.payment.acceptance
    权限。
对于Verifier API(身份证读取),需要单独的权限和服务器端reader token生成。详情请阅读
references/verifier-api.md

Architecture at a Glance

架构概览

┌─────────────────────────────────────────────────┐
│                   Your App                       │
├──────────┬──────────┬───────────┬───────────────┤
│ Payment  │ Loyalty  │ Store &   │  ID           │
│ Flow     │ (VAS)    │ Forward   │  Verification │
├──────────┴──────────┴───────────┴───────────────┤
│              ProximityReader Framework           │
├─────────────────────────────────────────────────┤
│         Secure Element / NFC Hardware            │
└─────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│                   您的应用                       │
├──────────┬──────────┬───────────┬───────────────┤
│ 支付流程 │ 会员服务 │ 存储转发  │ 身份验证       │
│          │ (VAS)  │           │               │
├──────────┴──────────┴───────────┴───────────────┤
│              ProximityReader 框架               │
├─────────────────────────────────────────────────┤
│         安全元件 / NFC 硬件                     │
└─────────────────────────────────────────────────┘

Quick Reference: Common Patterns

快速参考:常见模式

1. Payment Card Reading (Tap to Pay)

1. 支付卡读取(Tap to Pay)

swift
import ProximityReader

// 1. Create and configure the reader
let reader = PaymentCardReader()

// 2. Obtain token from your PSP
let token = PaymentCardReader.Token(rawValue: pspProvidedJWT)

// 3. Link the merchant account (first use only)
if try await !reader.isAccountLinked(using: token) {
    try await reader.linkAccount(using: token)
}

// 4. Create a session and prepare
let session = try await PaymentCardReaderSession(reader: reader, token: token)
try await session.prepare()

// 5. Read a payment card
let request = PaymentCardTransactionRequest(
    amount: Decimal(29.99),
    currencyCode: "USD",
    type: .purchase
)

let result: PaymentCardReadResult = try await session.readPaymentCard(request)
// Send result.paymentCardData to your PSP for processing
swift
import ProximityReader

// 1. 创建并配置读取器
let reader = PaymentCardReader()

// 2. 从PSP获取token
let token = PaymentCardReader.Token(rawValue: pspProvidedJWT)

// 3. 关联商家账户(仅首次使用)
if try await !reader.isAccountLinked(using: token) {
    try await reader.linkAccount(using: token)
}

// 4. 创建会话并准备
let session = try await PaymentCardReaderSession(reader: reader, token: token)
try await session.prepare()

// 5. 读取支付卡
let request = PaymentCardTransactionRequest(
    amount: Decimal(29.99),
    currencyCode: "USD",
    type: .purchase
)

let result: PaymentCardReadResult = try await session.readPaymentCard(request)
// 将result.paymentCardData发送至PSP进行处理

2. Loyalty Card (VAS) Reading

2. 会员卡(VAS)读取

swift
// Can be combined with payment or standalone
let vasRequest = VASRequest(
    merchantIdentifier: "pass.com.example.loyalty",
    localizedDescription: "Example Loyalty Program"
)

// Read loyalty card alongside payment
let result = try await session.readPaymentCard(
    request,
    vasRequest: vasRequest
)

// Access loyalty data
if let vasResult = result.vasReadResult {
    // Process loyalty identifiers
}
swift
// 可与支付结合或独立使用
let vasRequest = VASRequest(
    merchantIdentifier: "pass.com.example.loyalty",
    localizedDescription: "Example Loyalty Program"
)

// 读取支付卡的同时读取会员卡
let result = try await session.readPaymentCard(
    request,
    vasRequest: vasRequest
)

// 访问会员卡数据
if let vasResult = result.vasReadResult {
    // 处理会员标识符
}

3. Store and Forward (Offline)

3. 存储转发(离线)

swift
let sfSession = try await StoreAndForwardPaymentCardReaderSession(
    reader: reader,
    token: token
)
try await sfSession.prepare()

// Transactions are stored locally
let result = try await sfSession.readPaymentCard(request)

// Later, when online: retrieve and send batches
let store = PaymentCardReaderStore()
let batches: [StoreAndForwardBatch] = try await store.allBatches()

for batch in batches {
    // Send batch.data to PSP
    // On success, delete the batch
    try await store.delete(using: batch.deletionToken)
}
swift
let sfSession = try await StoreAndForwardPaymentCardReaderSession(
    reader: reader,
    token: token
)
try await sfSession.prepare()

// 交易将存储在本地
let result = try await sfSession.readPaymentCard(request)

// 后续联网时:检索并发送批量数据
let store = PaymentCardReaderStore()
let batches: [StoreAndForwardBatch] = try await store.allBatches()

for batch in batches {
    // 将batch.data发送至PSP
    // 成功后,删除该批次
    try await store.delete(using: batch.deletionToken)
}

4. Mobile Document / ID Reading (Verifier API)

4. 移动文档/身份证读取(Verifier API)

Read
references/verifier-api.md
for the full setup including server-side reader token generation.
swift
import ProximityReader

let docReader = MobileDocumentReader()
let readerToken = try await fetchReaderToken() // From your server

let session = try await MobileDocumentReaderSession(
    reader: docReader,
    readerToken: readerToken
)
try await session.prepare()

// Request a driver's license display
let displayRequest = MobileDriversLicenseDisplayRequest(
    retainedElements: [.givenName, .familyName, .portrait, .dateOfBirth, .ageOver21]
)
try await session.readDocument(displayRequest)

// Or request validated data
let dataRequest = MobileDriversLicenseDataRequest(
    retainedElements: [.givenName, .familyName, .dateOfBirth]
)
let documentResult = try await session.readDocument(dataRequest)
完整设置(包括服务器端reader token生成)请阅读
references/verifier-api.md
swift
import ProximityReader

let docReader = MobileDocumentReader()
let readerToken = try await fetchReaderToken() // 从您的服务器获取

let session = try await MobileDocumentReaderSession(
    reader: docReader,
    readerToken: readerToken
)
try await session.prepare()

// 请求显示驾照信息
let displayRequest = MobileDriversLicenseDisplayRequest(
    retainedElements: [.givenName, .familyName, .portrait, .dateOfBirth, .ageOver21]
)
try await session.readDocument(displayRequest)

// 或请求验证后的数据
let dataRequest = MobileDriversLicenseDataRequest(
    retainedElements: [.givenName, .familyName, .dateOfBirth]
)
let documentResult = try await session.readDocument(dataRequest)

5. Merchant Discovery UI

5. 商家发现UI

swift
// iOS 18+: Show Apple-provided merchant education
let discovery = ProximityReaderDiscovery()
try await discovery.present()
swift
// iOS 18+:展示Apple提供的商家引导界面
let discovery = ProximityReaderDiscovery()
try await discovery.present()

Error Handling

错误处理

Always wrap ProximityReader calls in do-catch. The two main error types are:
swift
do {
    try await session.readPaymentCard(request)
} catch let error as PaymentCardReaderError {
    switch error {
    case .notAllowed:
        // Missing entitlement or not authorized
    case .unsupported:
        // Device doesn't support Tap to Pay
    case .networkError:
        // Connectivity issue
    case .invalidReaderToken:
        // Token from PSP is invalid or expired
    case .readerBusy:
        // Another read session is active
    case .backgrounded:
        // App moved to background during read — must call prepare() again
    default:
        break
    }
} catch let error as PaymentCardReaderSession.ReadError {
    switch error {
    case .cancelled:
        // Customer cancelled the tap
    case .invalidAmount:
        // Amount was negative or zero
    case .notReady:
        // prepare() was not called
    default:
        break
    }
}
For the Verifier API:
swift
catch let error as MobileDocumentReaderError {
    // Handle session preparation and document request errors
}
始终将ProximityReader调用包裹在do-catch块中。主要有两种错误类型:
swift
do {
    try await session.readPaymentCard(request)
} catch let error as PaymentCardReaderError {
    switch error {
    case .notAllowed:
        // 缺少权限或未授权
    case .unsupported:
        // 设备不支持Tap to Pay
    case .networkError:
        // 连接问题
    case .invalidReaderToken:
        // PSP提供的token无效或已过期
    case .readerBusy:
        // 另一个读取会话正在进行中
    case .backgrounded:
        // 读取过程中应用进入后台——必须再次调用prepare()
    default:
        break
    }
} catch let error as PaymentCardReaderSession.ReadError {
    switch error {
    case .cancelled:
        // 客户取消了刷卡操作
    case .invalidAmount:
        // 金额为负数或零
    case .notReady:
        // 未调用prepare()
    default:
        break
    }
}
对于Verifier API:
swift
catch let error as MobileDocumentReaderError {
    // 处理会话准备和文档请求错误
}

Critical Implementation Notes

关键实现注意事项

  • Always call
    prepare()
    after the app returns to the foreground.
    The reader session is invalidated when backgrounded. Safe to call multiple times.
  • Call
    prepare()
    after each transaction
    to reset internal state for the next transaction.
  • The reader token (JWT) comes from your PSP, not from Apple directly. Each PSP has their own token generation flow.
  • PIN entry is supported on iOS 16.4+ for contactless cards that require it.
  • Testing: Use
    ProximityReaderStub
    for simulator testing. Real NFC reads require a physical device.
  • Thread safety: ProximityReader uses Swift concurrency (async/await). All calls should be made from the main actor or appropriate actor context.
  • Store and Forward: Batches persist on device. Always delete after successful processing to avoid data accumulation.
  • 应用回到前台后务必调用
    prepare()
    。读取器会话在后台时会失效,多次调用是安全的。
  • 每笔交易后调用
    prepare()
    ,以重置内部状态为下一笔交易做准备。
  • reader token(JWT)来自您的PSP,并非直接来自Apple。每个PSP都有自己的token生成流程。
  • PIN输入在iOS 16.4+上支持,适用于需要PIN的非接触式卡。
  • 测试:使用
    ProximityReaderStub
    进行模拟器测试。真实NFC读取需要物理设备。
  • 线程安全:ProximityReader使用Swift并发(async/await)。所有调用应在主actor或合适的actor上下文中进行。
  • 存储转发:批次数据会持久化在设备上。处理成功后务必删除,避免数据堆积。

Deeper Reference Docs

深入参考文档

For more detailed implementation guides, read these reference files:
ReferenceWhen to Read
references/api-reference.md
Full class/struct/enum listing with all properties and methods
references/verifier-api.md
Complete Verifier API setup including server-side token generation
references/integration-patterns.md
PSP integration patterns (Stripe, Adyen, Square), SwiftUI patterns, MVVM architecture
references/troubleshooting.md
Common errors, debugging tips, device compatibility
如需更详细的实现指南,请阅读以下参考文件:
参考文档阅读时机
references/api-reference.md
完整的类/结构体/枚举列表,包含所有属性和方法
references/verifier-api.md
Verifier API的完整设置,包括服务器端token生成
references/integration-patterns.md
PSP集成模式(Stripe、Adyen、Square)、SwiftUI模式、MVVM架构
references/troubleshooting.md
常见错误、调试技巧、设备兼容性

Code Generation Guidelines

代码生成指南

When generating ProximityReader code:
  1. Always
    import ProximityReader
  2. Use Swift concurrency (async/await) — the entire API is async
  3. Wrap all reader operations in do-catch with specific error handling
  4. Include
    prepare()
    calls after foregrounding and after each transaction
  5. Never hardcode PSP tokens — always fetch from server/PSP SDK
  6. For SwiftUI: use
    .task {}
    modifier or
    @MainActor
    view models
  7. Always check device capability before attempting to use the reader
  8. Follow Apple HIG: the tap payment sheet is system-provided — don't recreate it
生成ProximityReader代码时:
  1. 务必
    import ProximityReader
  2. 使用Swift并发(async/await)——整个API都是异步的
  3. 将所有读取器操作包裹在do-catch块中,并处理特定错误
  4. 包含应用回到前台后和每笔交易后的
    prepare()
    调用
  5. 切勿硬编码PSP token——始终从服务器/PSP SDK获取
  6. 对于SwiftUI:使用
    .task {}
    修饰符或
    @MainActor
    视图模型
  7. 在尝试使用读取器前,务必检查设备能力
  8. 遵循Apple HIG:刷卡支付界面由系统提供——请勿自行重建