如何在 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 的限制。
较稳妥的做法是:
- 网站负责符合政策的信用卡购买流程。
- 服务器保存订阅及到期状态。
- 用户登录 iOS App 后,App 从服务器读取用户权益。
- App 内不展示未经允许的外部购买入口或规避性文案。
- 提交审核前,按照目标发行地区核对最新的 App Store Review Guidelines。
注意事项
- App 无法获取 Apple Account 中保存的信用卡资料。
- 不要通过自己的输入框收集信用卡信息,再用这些信息为 App 内数字订阅收费。
- 不要自行实现“7 天后自动扣款”。自动续费、取消和试用资格应交给 StoreKit 管理。
- 必须提供恢复购买入口,并正确处理用户更换设备或重新安装 App 的情况。
- 订阅页面应明确说明价格、订阅周期、试用期限,以及试用结束后的收费方式。
- App Store 的外部支付政策会随地区和时间变化。如果计划使用外部购买链接或替代支付,应以提交审核时的官方规则和 entitlement 资格为准。
备注:内容仅供参考。