在 iOS apps 中实现电商支付系统是否符合 Apple 政策?
支付小周
结论
可以,具体是否合规要看销售的商品类型:
- 服装、食品、家具等需要配送或线下交付的实体商品,可以在 App 内使用 Apple Pay、信用卡、支付宝等支付方式,无须使用 Apple 的 In-App Purchase。
- 会员权限、虚拟货币、课程内容、电子书、功能解锁等在 App 内消费的数字内容、功能或服务,通常必须通过 StoreKit 提供的 In-App Purchase 购买,不能用普通网页支付或第三方支付绕过这一机制。
因此,用户在 iOS App 中浏览电商网站上的实体商品并完成购买,一般符合电商场景的审核要求。更容易违反审核政策的情况,是通过外部支付销售供用户在 App 内消费的数字商品。
推荐实现方式
销售实体商品时,可以采用以下流程:
- App 从服务端获取商品、库存和价格。
- 用户在原生页面或嵌入页面中选择商品。
- App 创建订单,由服务端生成支付会话。
- 使用 Apple Pay 或合规的第三方支付 SDK 完成付款。
- 服务端验证支付结果并更新订单。
- App 从服务端查询订单状态,不能只依据客户端返回的”支付成功”结果。
如果只是用 WKWebView 包装整个电商网站,支付方式本身未必违规,但 App 仍可能因为缺少原生功能、体验较差或过于接近网页壳而影响审核。更稳妥的方案是加入原生导航、购物车、账户、订单状态或通知等 App 级功能。
Apple Pay 示例
下面是实体商品场景中创建 Apple Pay 支付请求的简化示例:
import PassKit
func startApplePay() {
guard PKPaymentAuthorizationController.canMakePayments() else {
return
}
let request = PKPaymentRequest()
request.merchantIdentifier = "merchant.com.example.store"
request.supportedNetworks = [.visa, .masterCard, .amex]
request.merchantCapabilities = [.capability3DS]
request.countryCode = "CN"
request.currencyCode = "CNY"
request.paymentSummaryItems = [
PKPaymentSummaryItem(
label: "商品金额",
amount: NSDecimalNumber(string: "99.00")
),
PKPaymentSummaryItem(
label: "Example Store",
amount: NSDecimalNumber(string: "99.00")
)
]
let controller = PKPaymentAuthorizationController(paymentRequest: request)
controller.delegate = self
controller.present()
}
处理授权结果时,应将支付令牌发送给支付服务商或自己的后端,不要由客户端自行判断交易是否完成:
extension CheckoutViewController: PKPaymentAuthorizationControllerDelegate {
func paymentAuthorizationController(
_ controller: PKPaymentAuthorizationController,
didAuthorizePayment payment: PKPayment
) async -> PKPaymentAuthorizationResult {
do {
try await paymentService.process(
token: payment.token.paymentData
)
return PKPaymentAuthorizationResult(
status: .success,
errors: nil
)
} catch {
return PKPaymentAuthorizationResult(
status: .failure,
errors: [error]
)
}
}
func paymentAuthorizationControllerDidFinish(
_ controller: PKPaymentAuthorizationController
) {
controller.dismiss()
}
}
示例中的商户标识、支付网络、国家和币种都需要按实际业务配置。Apple Pay 的商户证书、支付令牌处理方式和支付服务商接口,也必须在服务端正确配置。
如果销售数字商品
数字商品应使用 StoreKit。例如,一次 StoreKit 2 购买可以按以下结构发起:
import StoreKit
func purchase(productID: String) async throws {
let products = try await Product.products(for: [productID])
guard let product = products.first else {
throw PurchaseError.productNotFound
}
let result = try await product.purchase()
switch result {
case .success(let verification):
let transaction = try checkVerified(verification)
// 将交易信息发送到服务端记录和验证
await transaction.finish()
case .pending:
// 等待家长批准或其他延迟授权
break
case .userCancelled:
break
@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 productNotFound
case failedVerification
}
实体商品可以使用外部支付,并不意味着数字会员、虚拟权益或 App 功能解锁也能放入同一个网页结账流程。审核通常看购买内容的实际用途,而不是支付页面位于 App、网页还是第三方 SDK。
容易踩坑的情况
- 实体商品附带数字权益时,需要明确区分价格和交付内容。如果数字权益才是主要购买目的,仍可能被认定为数字商品。
- 不要将网站上的数字订阅页面嵌入
WKWebView,借此规避 In-App Purchase。 - 支付密钥、API Secret 和订单签名密钥不能放在 App 中。
- 商品价格、优惠金额、库存和订单总额必须由服务端重新校验。
- 支付回调可能重复或延迟,服务端应通过订单号和交易号进行幂等处理。
- 客户端显示付款成功前,最好先由服务端确认支付状态。
- Apple Pay 不能替代 In-App Purchase。它适用于实体商品及其他允许使用外部支付的场景。
- 不同国家或地区可能有特殊规则、授权方案或外部购买链接政策,不能直接照搬其他地区的审核经验。
提交审核前,应根据商品性质检查当时有效的 App Review Guidelines,尤其是支付和购买相关条款。Apple 会更新政策,部分例外也取决于 App 类型、销售地区和相关授权。如果商品同时具有实体与数字属性,建议在 App Review Notes 中清楚说明商品内容、交付方式和支付流程。
备注:内容仅供参考。
