PAYATHON 2026

Third-party payment gateway 与 In-App Purchase 如何选择?

支付阿杰

结论

如果这些 license 用来解锁应用内的数字功能,例如创建孩子或看护人账号,并允许他们登录和使用服务,那么在 iOS 应用内销售时,原则上需要使用 Apple 的 In-App Purchase(IAP)。

如果直接在 iOS App 内接入 Stripe、PayPal 等 third-party payment gateway,或通过 WebView 完成付款,App 审核被拒的风险较高。购买后的权益即使能在 iOS、Android 和 Web 之间通用,也不代表可以绕过 IAP。

比较稳妥的做法是:

  • 在 iOS App 内购买:使用 Apple IAP。
  • 在 Android App 内购买:遵守 Google Play 的计费政策。
  • 在 Web portal 购买:继续使用 third-party payment gateway。
  • 各渠道的购买结果:统一写入服务端的 license entitlement,由服务端管理跨平台权益。

跨平台同步和支付渠道并不矛盾。用户通过 Apple IAP 购买后,服务端仍可将 license 绑定到业务账号,让用户在 Android 和 Web 上使用。

为什么通常必须使用 IAP

判断依据不是商品是否叫 “license”,而是用户实际买到了什么。

这里的 license 可以让用户:

  • 创建额外的辅助用户账号;
  • 允许这些账号登录应用;
  • 使用应用提供的数字服务。

这些权益通常属于应用内消费的数字内容或数字功能。只要购买入口位于 iOS App 内,Apple 一般就会要求使用 IAP 完成交易。

下面这些理由通常不能构成 IAP 豁免:

  • 服务同时支持 iOS、Android 和 Web;
  • 账号和数据可以在多个平台同步;
  • 用户可能会在其他平台使用购买的 license;
  • Web 端已经接入 Stripe 等支付服务;
  • 商品名称是 license、seat、credit 或 membership。

审核人员关注的是权益的实际用途,而不是产品名称或背后的技术实现。

哪些实现容易被拒绝

如果没有适用的地区政策、特殊 entitlement 或明确例外,以下设计的审核风险较高:

  • 在 iOS App 中嵌入 Stripe Checkout;
  • 使用 WebView 打开 Web portal 的付款页面;
  • 提供 “购买 license” 按钮并跳转到外部支付页面;
  • 在 App 内引导用户前往网站付款;
  • 通过二维码、客服提示或其他方式规避 IAP;
  • 将购买按钮伪装成账号管理或升级入口。

外部链接、替代支付和相关提示是否允许,可能取决于 App Store storefront、应用类别以及当地监管要求。某个地区允许的实现不能直接用于全球版本。提交审核前,应根据目标发行地区核对届时有效的 App Store Review Guidelines、entitlement 要求和 StoreKit 文档。

推荐的业务架构

支付渠道和 license 权益应分开设计:

Apple IAP ───────┐
Google Play ─────┼──> Payment Verification ──> License Entitlement Service
Web Gateway ─────┘                                  │
                                                    ├── iOS
                                                    ├── Android
                                                    └── Web

服务端统一维护权益记录,例如:

{
  "accountId": "parent_123",
  "totalLicenses": 2,
  "assignedLicenses": [
    {
      "licenseId": "lic_001",
      "assignedTo": "kid_456",
      "status": "active"
    },
    {
      "licenseId": "lic_002",
      "assignedTo": "caretaker_789",
      "status": "active"
    }
  ]
}

license 可以来自不同渠道,但权限判断应保持一致:

{
  "entitlementId": "ent_10001",
  "accountId": "parent_123",
  "quantity": 2,
  "source": "apple_iap",
  "sourceTransactionId": "transaction_id",
  "status": "active"
}

不要让客户端自行增加 license 数量。客户端只负责发起购买,服务端验证交易成功后再发放权益。

iOS 端的实现步骤

1. 在 App Store Connect 配置商品

商品类型要根据业务模式选择:

  • 如果 license 永久有效,且每份只能购买一次,可以研究 non-consumable。但它不适合通过反复购买同一个商品来增加数量。
  • 如果用户可以多次购买 license 包,通常需要按数量设计不同商品,也可以根据实际模型评估 consumable。
  • 如果 license 按月或按年持续有效,通常更接近 auto-renewable subscription。

商品类型要与退款、恢复购买、重复购买和到期规则相匹配,不能只考虑哪种类型接入更方便。

例如,可以配置:

com.example.licenses.1
com.example.licenses.2
com.example.licenses.5

2. 使用 StoreKit 2 发起购买

import StoreKit

enum PurchaseError: Error {
    case productNotFound
    case verificationFailed
}

func purchaseLicensePack(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):
        guard case .verified(let transaction) = verification else {
            throw PurchaseError.verificationFailed
        }

        try await sendTransactionToServer(
            transactionId: String(transaction.id),
            productId: transaction.productID
        )

        await transaction.finish()

    case .pending:
        // 等待 Ask to Buy 或其他待处理付款流程
        break

    case .userCancelled:
        break

    @unknown default:
        break
    }
}

func sendTransactionToServer(
    transactionId: String,
    productId: String
) async throws {
    // 通过 HTTPS 将交易信息发送到业务服务端。
    // 服务端验证交易后再增加 license entitlement。
}

客户端显示购买成功,并不意味着服务端可以直接发放 license。服务端仍需验证交易,并保证处理过程具备幂等性。

3. 服务端验证并发放权益

服务端可以按以下流程处理:

接收交易信息
    ↓
验证 Apple 交易
    ↓
检查 transactionId 是否已经处理
    ↓
确定 productId 对应的 license 数量
    ↓
写入 entitlement
    ↓
返回最新 license 状态

伪代码示例:

async function grantAppleLicenses({ accountId, transaction }) {
  const verifiedTransaction = await verifyAppleTransaction(transaction);

  if (!verifiedTransaction.valid) {
    throw new Error("Invalid Apple transaction");
  }

  const transactionId = verifiedTransaction.transactionId;

  return database.transaction(async (db) => {
    const existing = await db.purchases.findByTransactionId(transactionId);

    if (existing) {
      return db.entitlements.findByAccountId(accountId);
    }

    const quantity = getLicenseQuantity(
      verifiedTransaction.productId
    );

    await db.purchases.insert({
      accountId,
      transactionId,
      productId: verifiedTransaction.productId,
      source: "apple_iap"
    });

    await db.entitlements.incrementLicenses(accountId, quantity);

    return db.entitlements.findByAccountId(accountId);
  });
}

生产环境还要处理退款、撤销、订阅续期、订阅到期和 App Store Server Notifications。具体字段及验证接口应以接入时的 Apple 官方文档为准。

已在 Web 购买的用户怎么办

可以让用户登录 iOS App,并使用此前在 Web 上合法取得的权益。通常没有必要让用户重复购买同一份 license。

需要区分以下两种行为:

  • “登录并使用已有权益”:通常属于正常的跨平台服务场景。
  • “在 iOS App 内引导用户去 Web 购买”:可能受到外部购买链接和支付引导规则的限制。

因此,iOS App 可以从服务端读取已有 license:

GET /v1/me/entitlements
Authorization: Bearer <access_token>

返回:

{
  "licenses": {
    "total": 2,
    "assigned": 1,
    "available": 1
  }
}

App 内能否显示外部购买入口,则要结合应用类别、销售地区和当时适用的规则单独判断。

可能适用 third-party payment gateway 的情况

如果用户购买的是现实世界中的商品或服务,例如交通、送餐、线下护理或现场课程,通常不属于 IAP 的典型适用范围。

某些企业服务、特定类型的应用,以及部分地区的替代支付规则,也可能有例外。不过,当前描述中的 license 面向家长等普通终端用户,用于解锁应用内账号和数字服务,因此更接近消费者数字权益。不能仅凭 “多平台使用” 就将其认定为企业服务或线下服务。

如果这些账号实际上由学校、医院或企业统一采购,而 iOS App 只供签约机构的人员登录,结论可能不同。这种情况需要重新评估销售对象、合同关系、应用内是否提供购买入口,以及服务的实际交付方式。

审核时应准备的说明

在 App Review Notes 中应清楚说明:

  • 主账号如何购买 license;
  • 一个 license 能创建哪类辅助账号;
  • 辅助账号如何登录;
  • iOS、Android 和 Web 如何共享 entitlement;
  • iOS 购买使用了哪些 IAP 商品;
  • 测试账号和完整的测试路径;
  • 已有 Web 用户如何访问既有权益;
  • App 内是否存在外部付款入口。

不要只写 “license 可以跨平台使用,所以没有使用 IAP”。这种解释没有回答 Apple 对数字功能支付方式的审核问题。

注意事项

  • 通过 IAP 购买的权益可以绑定业务账号并同步到其他平台,无需为了跨平台访问而改用 third-party payment gateway。
  • license 的分配、回收和剩余数量必须由服务端管理。
  • 交易验证必须具备幂等性,以免重复发放权益。
  • 需要明确退款后如何处理已经创建的辅助账号,避免直接删除数据,造成无法恢复的结果。
  • 对于订阅型 license,需要定义到期后的宽限期、降级方式和辅助账号状态。
  • “恢复购买” 只能恢复可恢复的 Apple 商品,并不等于恢复全部 Web 或 Android 订单。跨渠道权益应通过登录业务账号恢复。
  • Android 端也有自己的数字商品支付政策。iOS 使用 IAP,并不代表 Android 可以任意使用 third-party payment gateway。
  • App Store 政策和地区规则可能调整。最终方案应根据计划上线时的发行地区和最新官方规则确认,必要时在正式开发前向 Apple 咨询具体业务模型。

备注:内容仅供参考。