PAYATHON 2026

通过 Web Services 传输支付信息是否符合 Apple 规定?

支付老李

明确结论

是否符合 Apple 规定,不取决于是否使用 Web Services,而取决于用户购买的服务类型:

  • 用户购买应用内使用的数字内容、功能、订阅或虚拟服务时,通常必须使用 Apple 的 In-App Purchase(StoreKit)。通过 Web Services 将支付信息发送到第三方支付门户,一般不能替代应用内购买,还可能导致 App Store 审核被拒。
  • 用户购买实体商品或在应用外实际履行的服务,例如外卖、交通、酒店或线下预约时,通常可以使用第三方支付。
  • “符合 Apple 审核规则”与”法律上允许”是两回事。即使 Apple 允许使用第三方支付,开发者仍需遵守支付牌照、PCI DSS、隐私保护、消费者权益和当地数据保护法规。

部分地区或特定类型的应用可能适用外部购买链接、特殊 entitlement 或其他例外。不过,相关规则会随销售地区和提交时间变化,不能因此认定所有应用都可以绕过 In-App Purchase。

先判断服务属于哪一类

可以按以下标准判断:

购买内容通常采用的支付方式
应用内会员、数字课程、虚拟道具、功能解锁、云端数字服务In-App Purchase
实体商品、外卖、打车、酒店、线下活动或预约服务第三方支付
用户之间实时提供的服务需要结合具体业务模式判断
企业内部服务、阅读器类应用、特定地区的外部购买可能存在例外,需核对适用条件

如果用户付款后获得的权益主要在该 App 内使用,通常应将其视为数字服务。把支付页面重定向到浏览器、WebView 或第三方门户,并不会改变交易的性质。

推荐实现方式

数字服务使用 StoreKit

使用 StoreKit 2 发起购买时,应由服务端校验交易并发放权益。客户端不能只根据本地回调直接开通服务。

import StoreKit

func purchase(_ product: Product) async throws {
    let result = try await product.purchase()

    switch result {
    case .success(let verification):
        let transaction = try checkVerified(verification)

        // 将 transaction.id 或签名交易数据发送至服务端校验
        await sendTransactionToServer(transaction)

        await transaction.finish()

    case .pending:
        print("Purchase is pending")

    case .userCancelled:
        print("User cancelled the purchase")

    @unknown default:
        break
    }
}

func checkVerified<T>(
    _ result: VerificationResult<T>
) throws -> T {
    switch result {
    case .verified(let value):
        return value
    case .unverified:
        throw PurchaseError.failedVerification
    }
}

enum PurchaseError: Error {
    case failedVerification
}

服务端应使用 Apple 提供的交易验证机制确认交易真实性,并处理退款、撤销、续订和订阅状态变化。

允许第三方支付时使用托管结账页

如果交易涉及实体商品或应用外服务,建议让合规支付服务商直接收集银行卡信息,应用服务器只接收支付令牌。

struct CheckoutRequest: Codable {
    let orderId: String
    let paymentToken: String
}

func submitPayment(orderId: String, paymentToken: String) async throws {
    let endpoint = URL(string: "https://api.example.com/payments")!

    var request = URLRequest(url: endpoint)
    request.httpMethod = "POST"
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")

    request.httpBody = try JSONEncoder().encode(
        CheckoutRequest(
            orderId: orderId,
            paymentToken: paymentToken
        )
    )

    let (_, response) = try await URLSession.shared.data(for: request)

    guard let httpResponse = response as? HTTPURLResponse,
          (200...299).contains(httpResponse.statusCode) else {
        throw PaymentError.requestFailed
    }
}

enum PaymentError: Error {
    case requestFailed
}

这里的 paymentToken 应由支付服务商的 SDK 或托管页面生成,不能直接使用银行卡号、CVV 等原始支付数据。

安全与合规要求

不要通过普通 Web Services 自行传输或保存完整的银行卡资料,尤其不要:

  • 在日志、崩溃报告或分析事件中记录银行卡号、CVV 或完整支付请求。
  • 将支付信息写入 UserDefaults、本地数据库或未加密缓存。
  • 自行保存 CVV。
  • 仅根据客户端返回的”支付成功”状态发放权益。
  • 在不符合 Apple 规则的场景中,通过 WebView 隐藏第三方数字支付入口。

网络通信至少应使用 HTTPS,并正确验证服务器证书。后台还应使用订单签名、幂等键、服务端支付结果校验和 webhook 验签,防止重复扣款和伪造付款结果。

只要系统接触、处理或存储银行卡数据,就可能进入 PCI DSS 的合规范围。使用支付服务商提供的托管页面、原生 SDK 和令牌化方案,通常可以明显缩小合规范围,但不会自动免除所有责任。

上架前需要确认的事项

提交审核前,应明确以下信息:

  1. 用户具体购买了什么。
  2. 权益是在 App 内使用,还是在线下或 App 外履行。
  3. 支付页面由谁提供,应用服务器是否会接触原始银行卡数据。
  4. 支付完成后如何验证交易、处理退款和恢复订单。
  5. 目标销售地区是否允许相应的外部购买方式。
  6. 是否需要 Apple 的特定 entitlement,以及应用是否符合申请条件。

如果”特定服务”实际属于应用内数字功能,建议直接使用 StoreKit。如果属于实体商品或线下服务,可以接入第三方支付,但应采用令牌化或托管结账方案,并完成 PCI DSS、隐私政策和当地法律方面的评估。最终审核结果仍取决于应用的具体业务模式、销售地区,以及提交时有效的 App Review Guidelines。

备注:内容仅供参考。