PAYATHON 2026

如何在 iPhone app 中通过信用卡提供订阅?

支付老李

结论

iOS 不提供读取或录入用户 Apple Account 信用卡信息的 API。

如果月度订阅解锁的是 App 内数字功能、数字内容或会员权益,通常需要使用 Apple 的 In-App Purchase,并在 App Store Connect 中创建自动续期订阅。用户最终通过信用卡、账户余额还是其他方式付款,由 Apple 的支付系统决定,App 无法指定。

只有订阅对应实体商品、线下服务,或者符合特定地区及 App Store 政策例外时,才可能在 App 内接入 Stripe 等第三方信用卡支付。不能为了绕开 Apple 计费,直接把数字订阅改成信用卡付款。

为什么其他 App 看起来可以使用信用卡

通常有以下几种情况:

  • 用户在网站上购买订阅,iOS App 只供已有用户登录。
  • 收费对象是实体商品或现实世界服务,例如交通、配送、健身课程或现场活动。
  • App 属于 Apple 政策规定的特定类别,并取得了相应 entitlement。
  • App 在允许替代支付或外部购买链接的地区发行。
  • 截图来自旧版本、特定地区或其他销售渠道。

所以,其他 App 可以使用信用卡,不代表所有 iOS App 都能照搬这种支付流程。能否使用取决于商品性质、发行地区、App 类型,以及当时有效的 App Store Review Guidelines。

数字订阅的标准实现方式

1. 在 App Store Connect 创建订阅

先在 App Store Connect 中创建订阅组和自动续期订阅,例如:

Subscription Group: Premium
Product ID: com.example.app.premium.monthly
Duration: 1 Month
Introductory Offer: Free Trial
Trial Duration: 7 Days

免费试用应配置为订阅的 introductory offer,不要由 App 自行记录试用开始时间。用户是否有资格再次试用,由 App Store 判断。

2. 使用 StoreKit 2 加载并购买订阅

import StoreKit

enum SubscriptionError: Error {
    case productNotFound
    case failedVerification
}

func purchaseMonthlySubscription() async throws {
    let productID = "com.example.app.premium.monthly"
    let products = try await Product.products(for: [productID])

    guard let product = products.first else {
        throw SubscriptionError.productNotFound
    }

    let result = try await product.purchase()

    switch result {
    case .success(let verification):
        guard case .verified(let transaction) = verification else {
            throw SubscriptionError.failedVerification
        }

        // 在此刷新用户的订阅权限。
        await transaction.finish()

    case .pending:
        // 交易等待批准,例如“购买前询问”。
        break

    case .userCancelled:
        break

    @unknown default:
        break
    }
}

购买时,系统会显示 Apple 的付款界面。App 不会接触用户的信用卡号码、有效期或安全码。

3. 检查免费试用资格

import StoreKit

func isEligibleForIntroOffer(
    product: Product
) async -> Bool {
    guard let subscription = product.subscription else {
        return false
    }

    return await subscription.isEligibleForIntroOffer
}

可以根据这个结果决定是否展示“免费试用”文案,但最终价格和试用资格仍以系统购买确认界面为准。

4. 根据有效交易解锁权限

import StoreKit

func hasActiveSubscription(productID: String) async -> Bool {
    for await result in Transaction.currentEntitlements {
        guard case .verified(let transaction) = result else {
            continue
        }

        if transaction.productID == productID,
           transaction.revocationDate == nil {
            return true
        }
    }

    return false
}

在生产环境中,还需要持续监听 Transaction.updates,并考虑在服务器端验证交易状态,以便处理续订、退款、撤销、宽限期和账单重试等情况。

什么时候可以接入信用卡支付

如果收费对象明确是实体商品或现实世界服务,可以使用自己的支付服务。一般由服务器创建支付订单,App 只接收可公开使用的支付参数。

下面是一个简化的服务器端示意:

import Stripe from "stripe";

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);

const paymentIntent = await stripe.paymentIntents.create({
  amount: 1999,
  currency: "usd",
  automatic_payment_methods: {
    enabled: true
  },
  metadata: {
    orderId: "order_123"
  }
});

不要把支付服务的 secret key 放进 iOS App,也不要自行保存完整信用卡号或 CVC。客户端应使用支付服务商提供的官方 SDK,将敏感信息直接提交给支付服务商。

能否使用这种方式,取决于所售内容的性质。如果付款后解锁的是 App 内数字会员、云端数字功能、视频、音乐、课程、虚拟货币或其他数字内容,通常不能用这种方式替代 In-App Purchase。

如果用户已在网站订阅

用户可以在 iOS App 中登录,App 再根据服务器保存的订阅状态开放相应权限。但 App 内能否放置“前往网站订阅”的按钮、链接、价格说明或购买引导,可能受到 App 类别、店面地区和 entitlement 的限制。

较稳妥的做法是:

  1. 网站负责符合政策的信用卡购买流程。
  2. 服务器保存订阅及到期状态。
  3. 用户登录 iOS App 后,App 从服务器读取用户权益。
  4. App 内不展示未经允许的外部购买入口或规避性文案。
  5. 提交审核前,按照目标发行地区核对最新的 App Store Review Guidelines。

注意事项

  • App 无法获取 Apple Account 中保存的信用卡资料。
  • 不要通过自己的输入框收集信用卡信息,再用这些信息为 App 内数字订阅收费。
  • 不要自行实现“7 天后自动扣款”。自动续费、取消和试用资格应交给 StoreKit 管理。
  • 必须提供恢复购买入口,并正确处理用户更换设备或重新安装 App 的情况。
  • 订阅页面应明确说明价格、订阅周期、试用期限,以及试用结束后的收费方式。
  • App Store 的外部支付政策会随地区和时间变化。如果计划使用外部购买链接或替代支付,应以提交审核时的官方规则和 entitlement 资格为准。

备注:内容仅供参考。