苹果应用内购买的自动续订订阅,主要有三种优惠方式,三种可以同时提供。
- 推介优惠(Introductory Offers)
- 促销优惠(Promotional Offers)
- 优惠代码(Offer Codes)
其中促销优惠(Promotional Offer)是苹果在 2019 年出的一种促销优惠方案。
最近开发的时候发现,网上几乎没有相关的文章,所以记录一下,方便其他人更快地实现。
文章中提到的一些文档和链接都可以在最后一章“附录”中找到。
关于应用内购买,可以查看我另一篇文章 iOS In-App Purchase(IAP) 流程与实现。
一、三种优惠的区别
苹果在文档中介绍了几种优惠的区别:
跟产品沟通时,需要了解产品想要的是哪种优惠。网上对三种优惠的翻译都各不相同。
二、实现
下面主要讲下客户端的实现,服务端可以参考其他文档。
2.1 配置
- 自动续订订阅的优惠设置说明
为自动续期订阅设置推介促销优惠
为自动续期订阅设置促销优惠
为自动续期订阅设置优惠代码
苹果官方文档已经详细说明了如何配置和各种配置的注意点,这里就不再赘述和截图说明了。
对于促销优惠,需要生成购买项目密钥(文档中有说明),然后下载私有密钥,后续需要用到。只能下载一次,请妥善保管已下载的密钥。
2.2 客户端开发
2.2.1 推介优惠
是否享有推介优惠,是由苹果根据 Apple 账号决定的。
不同产品希望的享有逻辑会不同,根据产品策略不同,会有不一样的开发流程。
我们是最简单的场景,新用户都可以享受到推介优惠,不处理用户切换 Apple 账号的场景。
客户端不用开发,配置后,点击购买,系统的购买弹窗上就会显示出优惠信息。复杂的业务逻辑在后端。
2.2.2 促销优惠
购买商品时可以传入商品支持的订阅优惠,在支付弹窗中就会显示相关信息。
2.2.2.1 生成优惠 SKPaymentDiscount
购买商品时,需要先生成一个优惠 SKPaymentDiscount。我们看下 SKPaymentDiscount 的初始化方法:
public init(identifier: String, keyIdentifier: String, nonce: UUID, signature: String, timestamp: NSNumber)
初始化方法中需要几个字段:
identifier
A string used to uniquely identify a discount offer for a product.
优惠 ID,苹果后台新建的优惠最后的字段
在 APP - 分发 - 营利(订阅)- 点击订阅组 - 点击某个订阅 - 订阅价格(有效的订阅优惠)- 点击优惠keyIdentifier
A string that identifies the key used to generate the signature.
密钥ID,苹果后台新建的密钥的ID
在 用户和访问 - 集成 - 密钥(APP 内购买项目)- 密钥 IDnonce
A universally unique ID (UUID) value that you define.
UUID,服务器生成
这种格式 58780c93-31e0-4a21-af9c-a34fec006c73。python 里是调用 uuid.uuid4()。signature
A string representing the properties of a specific promotional offer, cryptographically signed.
签名,服务器生成timestamp
The date and time of the signature's creation in milliseconds, formatted in Unix epoch time.
服务器时间戳,单位毫秒
let discount = SKPaymentDiscount(identifier: xxx, keyIdentifier: xxx, nonce: xxx, signature: xxx, timestamp: NSNumber(integerLiteral: xxx))
- 使用 python 生成签名进行自测
先将上一章提到的“私有密钥”(SubscriptionKey_XXXXXXXXXX.p8)从 .p8 格式转成 .der 格式:
openssl pkcs8 -nocrypt -in SubscriptionKey_xxxxxxxx.p8 -out cert.der -outform der
再将以下 python 脚本保存在同个目录中,修改其中的 bundle_id、key_id、product、offer、application_username 并执行。
脚本来自参考的文章,新增和修改了部分注释
# pip3 install ecdsa
import json
import uuid
import time
import hashlib
import base64
from ecdsa import SigningKey
from ecdsa.util import sigencode_der
bundle_id = 'com.xxx.xxx' # bundle ID
key_id = 'XXXXXXXXXX' # 私钥 ID
product = 'sp_3' # 订阅商品 ID
offer = '3day_test' # 优惠 ID
application_username = '' # Should be the same you use when making purchases
nonce = uuid.uuid4()
timestamp = int(round(time.time() * 1000))
payload = '\u2063'.join([bundle_id,
key_id,
product,
offer,
application_username,
str(nonce), # Should be lower case
str(timestamp)])
# Read the key file
with open('cert.der', 'rb') as myfile:
der = myfile.read()
signing_key = SigningKey.from_der(der)
signature = signing_key.sign(payload.encode('utf-8'),
hashfunc=hashlib.sha256,
sigencode=sigencode_der)
encoded_signature = base64.b64encode(signature)
print(str(encoded_signature, 'utf-8'), str(nonce), str(timestamp), key_id)
控制台打印结果中,第一个为签名,第二个为 nonce,第三个为 timestamp,第四个为 私钥 ID。此时将所有信息写死在代码中就可以自测购买流程了。
2.2.2.2 使用优惠购买
/// StoreKit
let payment = SKMutablePayment(product: product)
payment.applicationUsername = usernameHash
payment.paymentDiscount = discountOffer
SKPaymentQueue.default().add(payment)
/// SwiftyStoreKit
SwiftyStoreKit.purchaseProduct(product, quantity: 1, paymentDiscount: paymentDiscount, completion: { [weak self] result in
})
其中 product 需要自行去获取,下面是使用 StoreKit 和 SwiftyStoreKit 去获取的代码:
/// StoreKit
let productRequest = SKProductsRequest(productIdentifiers: Set<String>(arrayLiteral: productId))
productRequest.delegate = self
productRequest.start()
extension XXX: SKProductsRequestDelegate {
func productsRequest(request: SKProductsRequest, didReceiveResponse response: SKProductsResponse) {
if let product = response.products.first { /// 获取返回的商品
}
}
}
/// SwiftyStoreKit
SwiftyStoreKit.retrieveProductsInfo([productId], completion: { result in
if let product = result.retrievedProducts.first {
}
})
2.2.3 优惠代码
优惠代码比较简单,就不赘述,附录中有官方的相关文档可以查阅。
三、附录
官方 - 自动续期订阅 - 在 App 中实现推介优惠(介绍文档)
英文 Auto-renewable subscriptions - providing-subscription-offers
中文 自动续期订阅 - 在 App 中实现推介促销优惠官方 - 推介优惠(开发文档)
中文 在 App 中实现推介促销优惠
英文 implementing_introductory_offers_in_your_app官方 - 促销优惠(开发文档)
setting_up_promotional_offers
implementing_promotional_offers_in_your_app
generating_a_signature_for_promotional_offers
generating_a_promotional_offer_signature_on_the_server官方 - 优惠代码(开发文档)
implementing_offer_codes_in_your_app官方 - 自动续订订阅的优惠设置说明(说明文档)
为自动续期订阅设置推介促销优惠
为自动续期订阅设置促销优惠
为自动续期订阅设置优惠代码官方 - 推广你的 App 内购买项目
官方 - ReceiptFields
-