持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第9天,点击查看活动详情
一、什么是内购
内购是我们对苹果支付的一种称呼,即In‑App Purchase,同微信支付、支付宝支付,IAP也是用来购买商品的一个方式
二、为什么要使用内购
苹果要求APP所有非实物商品全部接入内购
三、内购的主要内容
商品类型
内购包含4中类型:
-
消耗型商品
-
非消耗型商品
- 用户可以购买一次性购买且不会过期的非消耗性高级功能。示例包括照片应用程序中的附加过滤器或游戏中的化妆品。
-
自动续期订阅
- 用户可以购买对您应用中的内容、服务或高级功能的持续访问。用户需要定期付费,直到他们决定取消为止。提供自动更新订阅的常见类别包括提供对媒体或内容库(例如视频、音乐或文章)、软件即服务(例如云存储、生产力或图形和设计)、教育、和更多。
-
非续期订阅
- 用户可以在有限的时间内购买服务或内容的访问权限,例如游戏内内容的季票。此类订阅不会自动续订,因此用户如果希望保留访问权限,则需要在订阅结束后购买新订阅。
接入内购
协议
若要提供 App 内购买项目,必须在 App Store Connect 中签署《付费应用程序协议》。如需了解具体的步骤,请参阅协议、税务和银行业务概述。
配置内购项目
签署协议后,请在 App Store Connect 中为您的 App 配置 App 内购买项目产品。每个 App 内购买项目产品都必须与某个 App 相关联,已经与一个 App 相关联的 App 内购买项目产品不可供其他 App 使用。如果您已经在 App Store Connect 上为您的 iOS 和 Apple tvOS 二进制文件创建了单个 App 记录,App 内购买项目可以在这些 App 二进制文件中通用。
您可以设置 App 内购买项目的定价,并添加显示名称和描述等元数据。在开发 App 的过程中,您可以根据需要添加、移除、优化或重新配置 App 内购买项目产品。如需了解如何在 App Store Connect 中配置 App 内购买项目,请参见创建 App 内购买项目;如需了解 XML,请参见《App 元数据规范》。
配置证书
在开发后台证书中开启内购功能
测试 App 内购买项目
Apple 提供了一种名为“沙盒”的测试环境,以便您测试您的 App 内购买项目,且不会产生费用。在沙盒环境中测试 App 内购买项目时,使用的是特殊的测试帐户,而不是您常规的 App Store Connect 帐户。请参见创建沙盒测试员帐户。
请在沙盒环境中使用您的 App 内进行购买,以便测试您的代码,确保各部分功能可正常运行。有关测试您 App 内购买项目的详细信息,请参见“TestFlight 让 Beta 测试更简单”。
您可以通过 TestFlight 进一步测试您的 App 及其 App 内购买项目。
推荐阅读
苹果提供了详细的内购文档:App 内购买项目配置流程
代码接入内购,网上已经有很多完善的接入文档,这里就不重复介绍了
凭证
IAP支付完成后,我们会收到支付信息:SKPaymentTransaction
我们可以通过这个信息拿到凭证:
NSString *transactionIdentifier = transaction.transactionIdentifier;
// 向自己的服务器验证购买凭证
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];
NSString *receiptString = [receipt base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
内购信息一定要数据持久化,包括一些我们自定义的信息,防止因程序闪退等情况造成的凭证丢失导致的漏单情况
验证凭证
苹果提供了正式、沙箱两个接口验证凭证,只有在凭证验证通过后我们才能删除本地记录的内购信息
沙箱:sandbox.itunes.apple.com/verifyRecei…
正式:buy.itunes.apple.com/verifyRecei…
验证凭证必须在后端验证
后端验证流程
可参考:
developer.apple.com/documentati…
验证结果示例:
{
"status": 0,
"environment": "Production",
"receipt": {
"download_id": 75017873837267,
"adam_id": 1149703708,
"request_date": "2017-01-13 06:57:20 Etc/GMT",
"app_item_id": 1149703708,
"original_purchase_date_pst": "2016-11-17 18:57:09 America/Los_Angeles",
"version_external_identifier": 820252187,
"receipt_creation_date": "2017-01-13 05:04:52 Etc/GMT",
"in_app": [
{
"is_trial_period": "false",
"purchase_date_pst": "2017-01-12 21:04:52 America/Los_Angeles",
"original_purchase_date_pst": "2017-01-12 21:04:52 America/Los_Angeles",
"product_id": "com.lucky917.live.gold.1.555",
"original_transaction_id": "350000191094279",
"original_purchase_date": "2017-01-13 05:04:52 Etc/GMT",
"original_purchase_date_ms": "1484283892000",
"purchase_date": "2017-01-13 05:04:52 Etc/GMT",
"purchase_date_ms": "1484283892000",
"transaction_id": "350000191094279",
"quantity": "1"
}
],
"original_purchase_date_ms": "1479437829000",
"original_application_version": "26",
"original_purchase_date": "2016-11-18 02:57:09 Etc/GMT",
"request_date_ms": "1484290640800",
"bundle_id": "com.lucky917.ios.Live",
"receipt_creation_date_pst": "2017-01-12 21:04:52 America/Los_Angeles",
"application_version": "32",
"request_date_pst": "2017-01-12 22:57:20 America/Los_Angeles",
"receipt_creation_date_ms": "1484283892000",
"receipt_type": "Production"
}}
自动续期订阅
自动续期订阅是目前使用比较广泛的一个商品类型,对比消耗型商品有很多不同的地方,这里我们拿出来单独介绍
群组
设置自动续期订阅与设置其他 App 内购买项目类型不同。每个自动续期订阅产品需创建为订阅群组的一部分并为其分配等级。您对订阅群组的设置将决定顾客如何订阅您的内容或服务、如何在不同订阅间转换、何时计费,以及您的收益率。有关订阅业务模式的指导,请参见“自动续期订阅”。
根据您提供的订阅产品的数量、服务等级和时限,每个产品可以列入单个订阅群组,也可分别列入多个订阅群组。在您参照以下指导确定了适合您业务的设置后,请参见创建自动续期订阅来了解如何创建订阅产品、群组和服务等级。
群组内不同的商品可相互切换,商品存在等级概念,相同等级的商品切换,下次续订生效,不同等级商品切换立即生效同时补齐差价
详细文档: help.apple.com/app-store-c…****
创建商品
主要要维护好定价、本地化信息
定价与汇率
管理自动续期订阅的定价与管理其他 App 内购买项目类型的定价有所不同。
不同于其他产品类型(只能以单一、同等价格在全球销售),自动续期订阅可以针对地区设定价格。每种货币都有 200 个价格点可供选择,此外您还可以设置推介促销优惠和促销优惠。
设置了自动续期订阅的最初价格后,您可以每次为一个地区安排一次未来的价格调整。如果您在安排了价格调整之后,再次安排价格调整,则已计划好的价格调整将被最新的更改覆盖。
汇率变化和税务调整会如何影响自动续期订阅的价格?
Apple 不会对您的自动续期订阅产品进行价格调整。Apple 针对税务变化和重大汇率变动,而对零售价格的调整不包括自动续期订阅。对于增值税(VAT)率的任何变化,您的收入和 Apple 的佣金将在扣除 VAT 后计算,换言之您的收入将会变化。您可以选择调整您的订阅价格,以规避任何由税务或外汇变化对您所造成的影响。请您谨记,所有价格的上涨都需要顾客的同意。
设置促销优惠
共享秘钥
验证自动续期订阅时,为了提高您的服务器与 Apple 服务器之间的通信安全,请在发送的收据中嵌入共享密钥。
共享密钥是在 App Store Connect 中生成的十六进制字符串,由 32 个字符组成。您可以生成一个主共享密钥,用于您的所有 App;也可以为单个 App 生成 App 专用共享密钥。您还可以设置部分 App 使用主共享密钥,其他 App 使用 App 专用共享密钥。
有关在 App 收据中嵌入共享密钥的更多信息,请参见“通过 App Store 验证收据”。
服务端通知 Server To Server
App Store 服务器通知提供有关您 App 内购买项目的关键事件的信息,例如订阅状态变更或 App 内购买项目退款。若要接收 App Store 发送的服务器通知,您必须在 App Store Connect 中提供一个链接至您服务器的网址(URL)。您可以输入用于生产环境和沙盒环境的网址(URL)。
如果您尚未配置服务器以接收 App Store 服务器通知,请参见“Enabling App Store Server Notifications(启用 App Store 服务器通知)”。如需进一步了解通知及其含义,请参考“App Store Server Notifications(App Store 服务器通知)”。
- 从“我的 App”中,选择您的 App。
- 在侧边栏的“综合”下方,点按“App 信息”。
- 向下滚动至“综合信息”,然后前往“App Store 服务器通知”部分。
- 在“生产环境服务器 URL”下方,点按“设置 URL”。
- 输入您用于接收 App Store 服务器通知的网址(URL)。
- 选择通知版本(版本 1 或版本 2)。有关通知版本的信息,请参见“App Store Server Notifications Changelog(App Store 服务器通知变动记录)”。
- 点按“存储”。
恢复购买
当上次内购没有完成的时候需要检查恢复购买,完成后才能开始下次购买
- (BOOL)iapNeedsRecoverIap {
NSArray* transactions = [SKPaymentQueue defaultQueue].transactions;
if (transactions.count > 0 && _iapModel == nil) {
//检测是否有未完成的交易
SKPaymentTransaction* transaction = [transactions firstObject];
if (transaction.transactionState == SKPaymentTransactionStatePurchased) {
[self commitSeversSucceeWithTransaction:transaction];
return YES;
}
}
return NO;
}
四、防攻击
因存在越狱设备,和一些不怀好意的人存在,内购需要做一些防攻击的手段保证支付的安全
目前主要存在的攻击方式,引自:www.jianshu.com/p/6adb6c177…
-
劫持apple server攻击 => 通过dns污染,让客户端支付走到假的apple_server,并返回验证成功的response。
-
重复验证攻击 => 一个receipt重复使用多次
-
跨app攻击 => 别的app的receipt用到我们app中来
-
换价格攻击 => 低价商品代替高价商品
-
歧义攻击 => iap支付之前的status=0表示verify成功 而现在变为status=0只能表示receipt合法 具体支付详情需要通过in_app字段决定
- For iOS 6 style transaction receipts, the status code reflects the status of the specific transaction’s receipt.
- For iOS 7 style app receipts, the status code is reflects the status of the app receipt as a whole. For example, if you send a valid app receipt that contains an expired subscription, the response is 0 because the receipt as a whole is valid.
-
中间人攻击 => 伪造apple_server,如果用户支付就将receipt保存起来 然后使用用户上传的receipt来完成自己的支付
我们要做的对策:
- 服务端校验凭证
- 服务端凭证校验是否重复
- 服务端校验验证结果的bundle_id、transaction_id、product_id等
- 同3
- 服务端不只通过status,还要通过in_app中的内容校验凭证
- Native与服务端约定接口加密传输,提高接口安全性