In-App Purchase 实战

最近公司的APP需要新增苹果内购产品,需要重构一下苹果内购功能。顺便写篇文章总结一下遇到的所有内购的坑。

一、嵌入流程介绍

  • 1.1 简介
  • 1.2 如何开放内购功能
  • 1.3 商品的创建
  • 1.4 商品类型
  • 1.5 商品定价
  • 1.6 产品ID

二、编程指南

  • 2.1 常用类说明
  • 2.2 流程代码
  • 2.2.1 获取产品信息列表
  • 2.2.2 购买商品
  • 2.2.3 沙盒测试账号
  • 2.2.4 校验支付凭证
  • 2.3 漏单处理

一、嵌入流程介绍

1.1 简介

IAP(In-App Purchase) 苹果应用内购买。通过在应用程序内部的购买为用户提供额外的内容和服务。属于StoreKit下的功能。

这里就不直译官方文档的内容了,简单总结一下就是购买应用程序内的虚拟产品,例如游戏金币、软件服务、订阅等,凡是苹果App内售卖的虚拟产品都可以走苹果内购买渠道。如果使用苹果内购购买的商品,苹果公司是会分成的(会抽取商品总价的30%)。

本文只介绍在选择使用苹果内购的情况下如果去嵌入内购功能,其他方式本文暂不讨论。

详细原理见官方文档,这里就不过多阐述了。

1.2 如何开放内购功能

一个APP如果想要加入苹果内购,是需要在创建 AppId 的时候勾选 In-App Purchase 功能的(后期也可以修改)。

1.3 商品的创建

需要购买的商品需要在App Store Connect后台注册后方可被程序获取。

流程如下:

使用具有App管理功能的开发者账号登录App Store Connect --> 我的App --> 选择需要添加内购功能的App --> 功能 --> App内购项目 --> 点击右侧“加号“ 即可添加app内购项目了。

首先需要选择商品类型,然后参考名称、产品ID、价格、本地描述 、截图和审核备注等信息。具体每一步都有说明。

1.4 商品类型

对于苹果内购的来说,用户每次购买的都是一个商品,商品和商品之间是有区别的。苹果提供了4中不同种类的商品模式,供开发者选择,也已经足够应付大部分应用的需求了。

  • 消耗型项目

只可使用一次的产品,使用之后即失效,必须再次购买。

示例:钓鱼App中的鱼食

  • 非消耗型项目

只需购买一次,不会过期或随着使用而减少的产品。

示例:游戏 App 的赛道。

  • 自动续期订阅

允许用户在固定时间段内购买动态内容的产品。除非用户选择取消,否则此类订阅会自动续期。

示例:每月订阅提供流媒体服务的 App。

  • 非续期订阅

允许用户购买有时限性服务的产品。此 App 内购买项目的内容可以是静态的。此类订阅不会自动续期。

示例:为期一年的已归档文章目录订阅。

当用户购买一个 自动续期订阅非续期订阅 时,应用程序负责使它在所有用户的设备都可用,并使用户能够恢复过去的购买。

1.5 商品定价

商品价格并不是随意定制的,是以美元为单位计算的,最低0.99美元,对应6.00元人民币(以前最低6.00人民币,现在也推出了1.00人民币的商品)。

有一个价格列表,开发者可根据公司产品定价选择最接近的产品价格。

美国(USD) 中国大陆(CNY)
$0.99 ¥6.00
$1.99 ¥12.00
$3.99 ¥25.00

只有表中出现的价格可以选择,例如6元 12元,表中没有出现的价格是无法选择的(因为是以美元为单位的)。

1.6 产品ID

每个产品都有一个产品ID号,用来在程序中对某个商品进行定位。一般建议使用 AppBundle Identifier 后面再加一个产品名称。

例如建议 Bundle Identifier 为 com.XXX.XXX ,则产品ID为 com.XXX.XXX.productName(商品描述),
productName 可以是任意商品描述或缩写


恭喜你 到这里就成功创建了一个可供使用的内购商品了。通过创建的 产品ID 即可在程序中获取指定商品信息,和购买该商品了。

二、编程指南

2.1 常用类说明

获取产品信息
  • SKProduct

用来描述一个在 App Store Connect 里注册的内购商品的信息。

  • SKProductsRequest

可以检索 App Store Connect 上注册的产品列表的对象。

  • SKProductsResponse

对一个产品信息列表请求的响应对象。

  • SKProductSubscriptionPeriod

包含订阅持续时间的对象。

  • SKProductDiscount

订阅商品的折扣信息。

请求付款
  • SKPayment

对应用程序内购买附加功能商品的一次支付行为的描述对象。

  • SKMutablePayment

对应用程序内购买附加功能商品的一次支付行为的描述的可变对象。

  • SKPaymentQueue

一个处理对App Store购买行对象的队列(购买队列)

2.2 流程代码

工程内加入 StoreKit 库,同时在需要使用支付的文件内加入头文件

#import <StoreKit/StoreKit.h>

2.2.1 获取产品信息列表

 //商品ID数组NSArray *productIdArray = @[IAP_PRODUCT_ID_1,IAP_PRODUCT_ID_2,IAP_PRODUCT_ID_3,IAP_PRODUCT_ID_4,IAP_PRODUCT_ID_5];NSSet *productIdSet = [NSSet setWithArray:productIdArray];//创建商品信息获取请求SKProductsRequest *productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdSet];//设置代理 <SKProductsRequestDelegate>productsRequest.delegate = self;  //开始请求[productsRequest start];

此处的 IAP_PRODUCT_ID_1 就是上文中提到的 产品ID

实现代理

@protocol SKProductsRequestDelegate <SKRequestDelegate>@required
// Sent immediately before -requestDidFinish:
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response NS_AVAILABLE(10_7, 3_0);@end

通过协议方法返回的 SKProductsResponse 获取 SKProduct 的数组

//SKProductsResponse 的属性// Array of SKProduct instances.
@property(nonatomic, readonly) NSArray<SKProduct *> *products NS_AVAILABLE(10_7, 3_0);

SKProduct 包含了可购买商品的详细信息(包含商品的本地描述,价格,商品ID等详细信息)
,这些信息可用于展示给用户,供用户选择购买。

2.2.2 购买商品,首先监听支付状态

 //self 实现<SKPaymentTransactionObserver>协议[[SKPaymentQueue defaultQueue] addTransactionObserver:self];

实现协议 SKPaymentTransactionObserver 的必要方法

@protocol SKPaymentTransactionObserver <NSObject>
@required
// Sent when the transaction array has changed (additions or state changes).  Client should check state of transactions and finish as appropriate.
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions NS_AVAILABLE(10_7, 3_0);@optional
...
...@end

根据用户选择的商品 SKProduct 创建支付对象 SKPayment

 //创建支付对象 product为用户选择的商品的SKProduct对象SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product];//设置购买数量payment.quantity = quantity;//可记录一个字符串,用于帮助苹果检测不规则支付活动//payment.applicationUsername = [self encryptionString:userName];//将支付加入支付队列[[SKPaymentQueue defaultQueue]addPayment:payment];

注意: 每个商品的单次购买数量不能超过10个,所以请结合公司业务设计每个商品,(以前就被购买个数不足的问题坑过,由于文档说明的地方很隐蔽,所以第一次都没有发现)

//SKPayment
@property(nonatomic, readonly) NSInteger quantity;
The default value is 1, the minimum value is 1, and the maximum value is 10.

当将支付加入支付队列后,会出现提示用户输入 Apple ID 密码以完成支付的弹窗,待用户进一步操作
SKPaymentTransactionObserver 协议的回调会被触发

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions
{for (SKPaymentTransaction *transaction in transactions) {switch (transaction.transactionState) {// Call the appropriate custom method for the transaction state.//支付中case SKPaymentTransactionStatePurchasing:break;//支付失败case SKPaymentTransactionStateFailed:break;//支付成功case SKPaymentTransactionStatePurchased://结束本次交易//[[SKPaymentQueue defaultQueue]finishTransaction:transaction]; //支付完成后调用(建议验证支付凭证有效后再调用)break;  //支付被恢复case SKPaymentTransactionStateRestored:break;//支付延迟(这种情况我还没有碰到过)case SKPaymentTransactionStateDeferred:break;default:break;}}
}

支付完成后需要调用 finishTransaction:

[[SKPaymentQueue defaultQueue]finishTransaction:transaction];

如果不调用,则每次启动app的时候都会有支付完成的回调上来。

这里建议验证交易凭证成功后再调用支付完成方法


虽然支付过程到这里就结束了,但是为了安全起见建议将支付凭证发送给服务器校验,获取校验结果后再结束支付并下发商品。下文中将介绍如何校验支付凭证。

2.2.3 沙盒测试账号

支付的代码加好之后,要开始测试一下了,但总不可能用真钱去购买吧。 别担心, 苹果提供了一种沙盒测试账号,可以随意购买商品而不用真正消费。
沙盒账号的创建方式:

登录 App Store Connect --> 用户和访问 --> 测试员

这里就可以创建用于测试的沙盒账号了。
沙盒账号使用起来和一个正常的 AppleID 账号几乎没有区别。
可以直接登录在设备上,也可以在苹果内购支付的时候再填写账号密码。

2.2.4 校验支付凭证

获取支付凭证

//获取支付凭证NSURL *receiptUrl = [[NSBundle mainBundle] appStoreReceiptURL];NSData *receiptData = [NSData dataWithContentsOfURL:receiptUrl];NSString *receiptStr = [receiptData base64EncodedStringWithOptions:0];

此处拿到的receiptStr可以传给公司自己的服务器进行校验。

也可以自己做校验
将凭证做成json格式 key = @“receipt-data”
然后转成NSData

    NSString *key = @"receipt-data";NSDictionary *dic = [[NSDictionary alloc]initWithObjects:@[receiptString] forKeys:@[key]];NSError *error;NSData *postData = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:&error];

将拿到的 postData 通过 POST 请求传给苹果服务器进行验证,这样即可获取凭证的校验结果。

下面是正式校验地址 和 沙盒测试校验地址

#if 1   //正式校验地址static NSString *productionURL = @"https://buy.itunes.apple.com/verifyReceipt";
#else   //沙盒测试校验地址static NSString *productionURL = @"https://sandbox.itunes.apple.com/verifyReceipt";
#endif

由于服务器不知道凭证是否由沙盒账号购买生成的,可先进行正式地址校验,如果校验失败再进行沙盒地址校验。

凭证返回结果内也会有是否是沙盒的提示信息。

2.3 漏单处理

在App实际上线后,我们发现经常会有漏单,总结后大致分为两种原因

  • 1.使用虚假交易凭证验证

建议使用https请求,并且加入带有时间戳的验证字符串,交易凭证本身也要一同加密并拼接在验证字符串后面。虽然会使上传数据明显增加,但能提高安全性 所以还是很有必要的。

  • 2.未收到来自客户端交易凭证验证请求。

猜测可能由于网络等问题造成,或者app本身收到的支付完成回调比较滞后。
如果是网络原因造成的交易凭证验证请求失败,可在验证请求发送失败时将交易凭证存在本地,待稍后或者下次App启动再行验证。

以上就是大致的支付流程,希望对刚入坑的同学有些帮助。:)

作者:张文宇 向日葵远程控制软件/花生壳/蒲公英路由器 iOS高级软件工程师

In-App Purchase 实战相关推荐

  1. App项目实战之路(二):API篇

    原创文章,转载请注明:转载自Keegan小钢 并标明原文链接:http://keeganlee.me/post/practice/20160812 微信订阅号:keeganlee_me 写于2016- ...

  2. Store Kit Guide(In App Purchase)翻译

    一.In App Purchase概览 Store Kit代表App和App Store之间进行通信.程序将从App Store接收那些你想要提供的产品的信息,并将它们显示出来供用户购买. 当用户需要 ...

  3. 《移动App测试实战》——2.2 App UI层面的自动化

    本节书摘来自华章出版社<移动App测试实战>一 书中的第2章,第2.2节,作者:邱鹏 陈吉 潘晓明,更多章节内容可以访问云栖社区"华章计算机"公众号查看. 2.2 Ap ...

  4. iOS应用内购买(In App Purchase)总结

    先附上几篇文章: 1.In App Purchases: A Full Walkthrough 这篇文章里说的都很详尽了,代码什么的基本可以照搬. 2.Store Kit Guide(In App P ...

  5. AppStore苹果应用支付开发(In App Purchase)翻译

    http://yarin.blog.51cto.com/1130898/549141 一.In App Purchase概览 Store Kit代表App和App Store之间进行通信.程序将从Ap ...

  6. 2022你不容错过的软件测试项目实战(APP项目实战)免费版

    前言 最近很多的人都在问我有没有什么项目可以用来练手,正好我这里有一个比较适合练手的项目,那就给大家安利一下吧,废话就不多说了. 项目名称: APP项目实战 项目说明: 本项目里面包括了功能测试.性能 ...

  7. php app接口开发,「PHP开发APP接口实战005」基础示例接口的实现一

    前一章,我们对接口参数基本定义做了一个简要说明.里面提到了几个示例接口,接下来,我们就来讲解这个几点个示例接口的具体实现. 「PHP开发APP接口实战004」基础响应参数说明 前言 由于我们的接口返回 ...

  8. Hybrid App开发实战

    Hybrid App开发实战 [引言]近年来随着移动设备类型的变多,操作系统的变多,用户需求的增加,对于每个项目启动前,大家都会考虑到的成本,团队成员,技术成熟度,时间,项目需求等一堆的因素.因此,开 ...

  9. 精通移动App测试实战:技术、工具和案例

    本文是根据书籍<精通移动App测试实战:技术.工具和案例>进行学习记录,方便后期查阅,感谢书籍作者提供的学习机会. 目录 第1章 Android系统基础内容介绍 1.6创建模拟器 第2章J ...

  10. Android开发高级进阶内涵段子APP项目实战视频教程

    Android开发高级进阶内涵段子APP项目实战课程视频教程下载.本课程带你从框架入手,开启我们的Android进阶之旅,开始写一步一步完善整个项目. 项目目录: 01.Android进阶之旅与你同行 ...

最新文章

  1. SQL Server开发者Oracle快速入门
  2. NTU 课程笔记:Nonparametric statistics
  3. 《高性能JavaScript》第二章 数据存取
  4. Rx2.0后台开发分享
  5. (图文详细)如何使用Code::Blocks运行c/cpp文件?
  6. Android Handler的使用方法
  7. Django学习目录
  8. MySQL 5.7: Page Cleaner的刷脏问题
  9. wpe手机中文汉化版下载_wpe中文3.0专业版
  10. 组态王与三菱PLC编程软件GXWorks2通过OPC数据库进行动态仿真,只要写三菱程序即可实现组态王动态画面
  11. 线性规划问题(excel和python)
  12. 【C++】类和对象(中) —— 构造函数 | 析构函数 | 拷贝构造 | 赋值运算符重载
  13. gmail邮箱注册成功流程
  14. 1:0 本立而道生!
  15. video全局事件属性
  16. 烟台有线电视频率与节目表
  17. python之简单的文件处理
  18. 下载SAPUI5 SDK
  19. 2016 清华 计算机 考研 经验 总结
  20. 和自己赛跑的人 —不要怕、不后悔!

热门文章

  1. 推荐几个不错的苹果电脑截图工具
  2. 一 c语言程序设计 张玉生版
  3. Axure RP 9 安装、汉化
  4. python点云可视化
  5. 一款好用的取色工具TakeColor.exe
  6. Pycharm安装中文语言插件
  7. 无法连接到目标服务器,如何解决IDM连接错误?
  8. 俄罗斯方块C++代码(转载他人代码)
  9. 深入理解Nginx:模块开发与架构解析
  10. APP测试—专项测试概念