iOS内购一:基础
内容例子教程:In-App Purchases
参考文章:
- In-App Purchase Tutorial: Getting Started
- In-App Purchase
- App内购通关:(一)非代码准备篇
- App内购通关:(二)代码篇
- App Store Connect 帮助
内购项目类型
可参考通过 App 内购买和订阅购买额外的应用功能
-
Consumable - 消耗型项目
如游戏币、游戏提示、额外生命值、额外经验值、导出为新文件格式的软件包,每次要用这些项目时,您都需要进行购买;而且,您无法免费重新下载已购买的项目。如果您移除并重新安装应用,或在新设备上安装应用,这些消耗型购买项目可能会丢失。例如,如果您之前曾在 iPhone 上开始玩一款游戏,而后又在 iPod touch 上安装了这款游戏,则游戏级别会同步,但您在 iPhone 上购买的额外生命值或经验值不会同步。
-
Non-Consumable - 非消耗型项目
如升级到专业版本、去除广告、完整版游戏解锁、无限提示、额外角色、额外配件、奖励游戏级别、城市导览图,只需购买一次,这些项目即可传输到与您的 Apple ID 相关联的其他设备上。如果您丢失了非消耗型购买项目,或许可以免费重新下载。
-
订阅
您可以针对特定的订阅时段来购买这些服务或这类内容。应用可能会提供不同长度的订阅续订时段,例如一周、一个月、一个季度或一年,或者应用可能提供在一段时间内有效的订阅。某些应用还可能会提供折扣或免费试用,或为多个应用提供一个订阅。
自动续订型订阅会持续进行,直到您将其取消。如果订阅在一段时间内有效,您也许可以再次购买,使其在另一段时间内有效。- Auto-Renewable Subscriptions - 自动续期订阅
- Non-Renewing Subscriptions - 非续期订阅
大概的步骤
1.define purchase in iTunes Connect
2.load indenties
3.fetch products
4.user buy product
5.process response
6.valdiate the receipt
7.unlock the content
8.finish the transaction
准备
1.设置Identifiers支持内购
创建一个新的Identifiers,默认是勾选中内购的,如下所示
2.在iTunes Connect中设置
在iTunes Connect
的协议、税务和银行业务
需要先签署Paid Applications Agreement
,即付费App协议,且要设置税务、银行业务和联系信息
可参考:协议、税务和银行业务概述
点击设置税务、银行业务和联系信息后
添加银行帐户
报税表
可参考:
按要求设置后,等待一段时间后,状态会变成有效
创建一个App
创建内购项目
如下,选择App内购项目
,选择添加
如下所示,创建一个非续期订阅
最后创建的有如下的内购项目
沙盒测试用户
测试IAP,可以创建沙盒用户,在App Store Connect的用户与访问中创建沙盒测试用户
需要注意的是,email地址,不能是已经与Apple ID账户绑定的。Apple会发邮件给你,收到邮件后,要确认
另外,据Adding Sandbox Tester in itunes connect with an existing Apple account的说法,可以在现有的apple id的邮箱右面加别名,例如,如果standardappleid@apple.com
已被注册为账户,可以加上standardappleid+alias@apple.com
作为别名,但貌似QQ邮箱并不支持这种形式
If standardappleid@apple.com is already registered as a regular account, you can not add standardappleid+alias@apple.com as a sandbox tester.
貌似现在的gmail
是支持这种形式的,即如果xxxx@gmail.com
被用来注册为apple id,你可以使用xxxx+alias@gmail.com
来注册沙盒账户,发送的验证邮件会被发送到xxxx@gmail.com
地址,所以就不用新注册一个账号了
需要注意的是,要在真机上测试,先退出登录原来的App Store account
当点击购买的时候,会弹出如下的提示框:
选择Use Existing Apple ID,使用沙盒测试账户登录
项目的一些设置
1.设置Bundle Id 为你自己创建的identity
2.启用in-app purchase服务
集成StoreKit
获取内购项目列表,核心的代码
import Foundation
import StoreKit
//表示产品的标识符
public typealias ProductIdentifier = String
public typealias ProductsRequestCompletionHandler = (_ succ: Bool, _ products: [SKProduct]?) -> Void
class IAPHelper: NSObject {
private let productIdentifiers: Set<String>
private var productsRequest: SKProductsRequest?
private var productsRequestCompletionHandler: ProductsRequestCompletionHandler?
init(productIDs: Set<String>) {
productIdentifiers = productIDs
super.init()
}
}
extension IAPHelper {
func requestProducts(completionHandler: @escaping ProductsRequestCompletionHandler) {
//取消当期的请求,创建一个新的请求
productsRequest?.cancel()
productsRequestCompletionHandler = completionHandler
productsRequest = SKProductsRequest.init(productIdentifiers: productIdentifiers)
productsRequest?.delegate = self
productsRequest?.start()
}
}
extension IAPHelper: SKProductsRequestDelegate {
//成功
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
productsRequestCompletionHandler?(true, response.products)
productsRequestCompletionHandler = .none //不再适用,置为none
productsRequest = .none
}
//失败
func request(_ request: SKRequest, didFailWithError error: Error) {
productsRequestCompletionHandler?(false, nil)
productsRequestCompletionHandler = .none
productsRequest = .none
}
}
收据验证
官方文档有2片文章关于Receipt Validation
2种方式可以验证收据的真实性:
- 本地 - 设备内收据验证,建议验证应用程序内购买的收据签名
- 服务器 - 建议在应用程序内保存购买以维护和管理购买记录
Consumable in-app purchases remain in the receipt until you call
finishTransaction:
. Maintain and manage records of consumables on a server if needed. Non-consumables, auto-renewing subscription items, and non-renewing subscription items remain in the receipt indefinitely. For auto-renewable subscription management, server-side receipt validation gives key advantages over on-device receipt validation.
Consumable类型会保存收据,直到你调用finishTransaction:
。必要时,在服务器上维护和管理记录。对应Non-consumables,auto-renewing subscription,non-renewing subscription项目,会无限期的保留在收据中。
一个App Store收据是一个用苹果证书签名的二进制加密文件。为了读取加密文件的内容,你需要通过苹果的/verifyReceipt
节点传递它,节点返回的是JSON
获取Receipt数据
使用 NSBundle
的appStoreReceiptURL
方法获取收据的位置,将数据编码为base64,将base64数据发送给服务器
/* Load the receipt from the app bundle. */
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];
if (!receipt) {
NSLog(@"no receipt");
/* No local receipt -- handle the error. */
} else {
/* Get the receipt in encoded format */
NSString *encodedReceipt = [receipt base64EncodedStringWithOptions:0];
}
/* ... Send the receipt data to your server ... */
在服务端,使用receipt-data
、 password
(如果收据包含auto-renewable订阅)、exclude-old-transactions
键,创建JSON对象
具体可参考官方文档Receipt Validation Programming Guide
Key | Key |
---|---|
receipt-data | The base64 encoded receipt data. |
password | Only used for receipts that contain auto-renewable subscriptions. Your app’s shared secret (a hexadecimal string). |
exclude-old-transactions | Only used for iOS7 style app receipts that contain auto-renewable or non-renewing subscriptions. If value is true, response includes only the latest renewal transaction for any subscriptions. |
使用POST提交对象,在沙盒测试app,app在审核时,使用测试环境https://sandbox.itunes.apple.com/verifyReceipt
,在App Store生产环境中使用https://buy.itunes.apple.com/verifyReceipt
处理响应
The App Store’s response payload is a JSON object that contains the keys and values detailed in Response Body.
The in_app array contains the non-consumable, non-renewing subscription, and auto-renewable subscription items previously purchased by the user. Check the values in the response for these in-app purchase types to verify transactions as needed.
For auto-renewable subscription items, parse the response to get information about the currently-active subscription period. When you validate the receipt for the latest renewal, the value for latest_receipt is the same as receipt-data (in the request) and the value for latest_receipt_info is the same as receipt.
You can use these values to check whether an auto-renewable subscription has expired. Use these values along with the expiration_intent subscription field to get the reason for expiration.
如果status=21007,则表示当前的收据为沙盒环境下收据
其它文档: