iOS 应用内购买的推介优惠、促销优惠、优惠代码的处理

iOS 应用内购买的推介优惠、促销优惠、优惠代码的处理

苹果应用内购买的自动续订订阅,主要有三种优惠方式,三种可以同时提供。

推介优惠(Introductory Offers)

促销优惠(Promotional Offers)

优惠代码(Offer Codes)

其中促销优惠(Promotional Offer)是苹果在 2019 年出的一种促销优惠方案。

最近开发的时候发现,网上几乎没有相关的文章,所以记录一下,方便其他人更快地实现。

文章中提到的一些文档和链接都可以在最后一章“附录”中找到。

关于应用内购买,可以查看我另一篇文章 iOS In-App Purchase(IAP) 流程与实现。

一、三种优惠的区别

苹果在文档中介绍了几种优惠的区别:

英文 Auto-renewable subscriptions - providing-subscription-offers

中文 自动续期订阅 - 在 App 中实现推介促销优惠

优惠对比中英.jpg

跟产品沟通时,需要了解产品想要的是哪种优惠。网上对三种优惠的翻译都各不相同。

二、实现

下面主要讲下客户端的实现,服务端可以参考其他文档。

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 内购买项目)- 密钥 ID

nonce

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(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

官方 - 自动续订订阅的优惠设置说明(说明文档)

为自动续期订阅设置推介促销优惠

为自动续期订阅设置促销优惠

为自动续期订阅设置优惠代码

官方 - 订阅优惠的最佳实践 Subscription Offers Best Practices

官方 - 推广你的 App 内购买项目

官方 - app_store_receipt_data_types

官方 - ReceiptFields

聊聊应用内购买

苹果内购之推介促销优惠和订阅优惠

优惠对比.jpg

相关推荐

人口老龄化及其衡量标准是什么
365防伪码查询系统

人口老龄化及其衡量标准是什么

📅 08-03 👁️ 9323
腾讯全民闯关答题怎么通关 腾讯全民闯关答题通全关攻略
拖地水更換.居家拖地.地板清潔習慣.拖布使用.拖地臭味.家庭清潔順序.正確拖地方法.拖地技巧.家事清潔