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

2024-03-31  本文已影响0人  zackzheng

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

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

最近开发的时候发现,网上几乎没有相关的文章,所以记录一下,方便其他人更快地实现。
文章中提到的一些文档和链接都可以在最后一章“附录”中找到。
关于应用内购买,可以查看我另一篇文章 iOS In-App Purchase(IAP) 流程与实现

一、三种优惠的区别

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

优惠对比中英.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)

初始化方法中需要几个字段:

  1. identifier
    A string used to uniquely identify a discount offer for a product.
    优惠 ID,苹果后台新建的优惠最后的字段
    在 APP - 分发 - 营利(订阅)- 点击订阅组 - 点击某个订阅 - 订阅价格(有效的订阅优惠)- 点击优惠

  2. keyIdentifier
    A string that identifies the key used to generate the signature.
    密钥ID,苹果后台新建的密钥的ID
    在 用户和访问 - 集成 - 密钥(APP 内购买项目)- 密钥 ID

  3. nonce
    A universally unique ID (UUID) value that you define.
    UUID,服务器生成
    这种格式 58780c93-31e0-4a21-af9c-a34fec006c73。python 里是调用 uuid.uuid4()。

  4. signature
    A string representing the properties of a specific promotional offer, cryptographically signed.
    签名,服务器生成

  5. 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))

先将上一章提到的“私有密钥”(SubscriptionKey_XXXXXXXXXX.p8)从 .p8 格式转成 .der 格式:

openssl pkcs8 -nocrypt -in SubscriptionKey_xxxxxxxx.p8 -out cert.der -outform der

再将以下 python 脚本保存在同个目录中,修改其中的 bundle_idkey_idproductofferapplication_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 优惠代码

优惠代码比较简单,就不赘述,附录中有官方的相关文档可以查阅。

三、附录

上一篇下一篇

猜你喜欢

热点阅读