应用内购买IAP

一、IAP
In-App Purchase,应用内购买。注意,IAP与ApplePay不是同一回事,ApplePay纯粹是一种支付方式,同微信支付、支付宝类似;而IAP是一种App内部购买的方式,购买时付款的方式是需要提前在苹果帐户中设置的,可以选择微信支付、支付宝、ApplePay、银行卡等。

二、4种商品类型
(1)消耗性商品:使用后就被消耗掉,需要再购买的商品。比如,钓鱼App中的鱼食,App中的金币;
(2)非消耗性商品:购买后可永久使用的商品。比如,读书App中的书籍,购买后可永久阅读;游戏App中的赛道;
(3)自动续期订阅商品:允许在固定时间段内购买动态内容的商品,如果购买后不手动取消,将自动续期自动付费。比如,每月订阅提供流媒体服务的App;
(4)非自动续期订阅商品:有时限性服务的商品,不自动续期。比如,App提供的VIP特权服务;

三、开发步骤:
(1)在iTunesConnect中填写协议、税务、银行等信息,创建配置内购商品,将商品添加到App中;
(2)Xcode中配置内购
(3)编写代码
(4)沙箱测试
(5)提审上线

四、内购流程:
(1)从本地或服务器获取可购买的商品ID列表;
(2)从App Store获取商品ID对应的商品信息;
(3)用户选择某一商品点击购买按钮后,请求服务端生成订单
(4)应用获取订单号后,向App Store发起交易请求,并监听交易状态;
(5)App Store处理交易请求,从苹果账户扣款;
(6)App Store交易成功,应用监听到交易成功状态;交易失败,则移除交易并提示购买失败;
(7)应用获取到交易票据;
(8)应用将票据和订单号提交到服务器,由服务器向App Store请求验证票据。注意对于沙盒环境和正式环境,App Store验证票据的URL是不同的;
(9)服务器判断订单号不存在,或票据中的交易号已存在,则通知应用移除交易,购买失败;
(9)验证成功后,服务器保存用户购买记录(订单号、交易号、用户账号等),并向用户发放相应商品,即修改服务器上用户的数据,并通知应用购买成功更新界面;如果验证失败,也通知应用;
(10)应用收到票据验证成功的反馈后,标记该交易完成状态,提示用户购买成功,并更新界面,并删除本地票据;
(11)应用收到票据验证失败的反馈后,如果错误码为21007,说明当前票据为沙盒测试票据,但却连接App Store正式环境进行了票据验证,故需要连接App Store沙盒环境再次验证票据;
(12)应用收到票据验证失败的反馈,如果确定票据无效,可提示购买失败,并标记交易完成,以便将交易移出交易队列;对于需要重新验证的票据,可以隔一段时间或者App重启时重新请求验证,直到验证成功才标记交易完成。

10票据验证:
(1)两种票据内容。一种是付费下载的App的票据;另一种是App内部购买商品的票据;
(2)从App Store安装App后,会自动在App的沙盒的StoreKit目录下创建票据文件receipt;
(3)票据文件receipt中,内购项目可能为空,也可能同时存在多个内购项目,只要数据正确,验证票据都会成功;
(4)对于购买的订阅类商品、非消耗性商品,购买信息会永久保存在票据文件中;
(5)对于消耗性商品,购买信息在交易付款后保存到票据中,一旦交易被标识为完成,该信息会在请求刷新票据,或者下次交易更新票据时被清除掉;
(6)iOS7以后,票据数据可以从沙盒中读取;iOS7以前,票据数据可以从交易对象的transactionReceipt属性中获取;如果本地没有票据数据,可以向App Store请求票据数据;

App Store票据验证接口参数:
(1)"receipt-data":base64编码的票据数据data;
(2)"password": 共享密码,仅在购买自动续期订阅商品时需要;

App Store票据验证返回JSON格式:
(1)"status": 0为验证成功;
(2)"receipt": 提交的票据原数据;

Receipt票据数据格式:
{
"adam_id" = 0;
"app_item_id" = 0;
"application_version" = 1;
"bundle_id" = "com.test.xxx";
"download_id" = 0;
"in_app" = (
{
"is_trial_period" = false;
"original_purchase_date" = "2017-12-14 07:18:56 Etc/GMT";
"original_purchase_date_ms" = 1513235936000;
"original_purchase_date_pst" = "2017-12-13 23:18:56 America/Los_Angeles";
"original_transaction_id" = 1000000359369424;
"product_id" = "com.test.product1";
"purchase_date" = "2017-12-14 07:18:56 Etc/GMT";
"purchase_date_ms" = 1513235936000;
"purchase_date_pst" = "2017-12-13 23:18:56 America/Los_Angeles";
quantity = 1;
"transaction_id" = 1000000359369424;
},
...
);
"original_application_version" = "1.0";
"original_purchase_date" = "2013-08-01 07:00:00 Etc/GMT";
"original_purchase_date_ms" = 1375340400000;
"original_purchase_date_pst" = "2013-08-01 00:00:00 America/Los_Angeles";
"receipt_creation_date" = "2017-12-14 07:18:56 Etc/GMT";
"receipt_creation_date_ms" = 1513235936000;
"receipt_creation_date_pst" = "2017-12-13 23:18:56 America/Los_Angeles";
"receipt_type" = ProductionSandbox;
"request_date" = "2017-12-14 07:19:23 Etc/GMT";
"request_date_ms" = 1513235963829;
"request_date_pst" = "2017-12-13 23:19:23 America/Los_Angeles";
"version_external_identifier" = 0;
};

服务端验证接口参数:
(1)base64编码的票据数据字符串;
(2)服务端订单号;
(3)验证环境标识(沙盒环境或正式环境);
(4)用户ID;
(5)所有接口参数应该加密后传输;

服务端验证逻辑:
(1)解密接口参数,提取出票据数据、订单号、验证环境、用户ID;
(2)如果票据数据为空,或验证环境参数错误,返回参数错误的错误码;否则进行下一步;
(3)查询订单是否存在:
(3.1)如果订单存在且已完成(订单对应交易ID已存在),则无需再验证票据数据。如果用户ID与订单所属用户相同,则返回用户的所有商品信息和重复验证票据的返回码,否则只返回重复验证票据的返回码而不返回用户所有商品信息;
(3.2)如果订单存在且未完成(订单对应交易ID不存在),则进入下一步验证票据数据;
(3.3)如果订单不存在,则查询票据中交易ID是否已有对应订单。如果没有订单,则返回订单不存在的错误码;如果有订单,则进入下一步验证票据数据;
(4)验证票据数据。调用苹果提供的票据验证接口进行验证。沙盒环境验证接口:https://sandbox.itunes.apple.com/verifyReceipt,正式环境验证接口:https://buy.itunes.apple.com/verifyReceipt。如果验证失败,将苹果返回的状态码作为错误码返回给客户端,如果验证成功,则向订单对应用户发放商品;如果验证成功(状态码为0)且用户ID与订单所属用户相同,则同时返回用户所有商品信息;

服务端验证接口返回数据:
(1)用户当前已购买的所有商品信息;
(2)接口错误信息;
(3)错误码:一类是服务器自身的报错;另一类是App Store返回的错误码。

漏单处理:
漏单,即用户付款成功,但没有收到购买的商品的情况。如果交易在App Store中尚未完成,则应用定时或重启后会继续处理该交易。如果交易已完成,则用户只能向客服反馈,提供苹果账号和单号,确认漏单后补发商品。

注意点:
(1)内购必须使用真机测试,测试时先退出真机上的AppleID,在App内购买时填写沙箱测试账号;
(2)游客模式下,App中不要出现商品相关信息;
(3)等待请求交易的时候,如果App重启,则重启后App Store仍会向监听对象发送交易状态反馈;如果交易成功后没有被标识为finished,App Store仍会向监听对象发送交易状态;

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容