山东省首届翻译大赛优秀微课程公示在什么网站

iOS(257)
消耗型项目:对于消耗型App内购买项目,用户每次下载时都必须进行购买。一次性服务通常属于消耗型项目,例如钓鱼App 中的鱼饵。
非消耗型项目:对于非消耗型App内购买项目,用户仅需要购买一次。不会过期或随使用而减少的服务通常为非消耗型项目,例如游戏App 的新跑道。
自动续订订阅:通过自动续订订阅,用户可以购买指定时间期限内的更新和动态内容。除非用户取消选择,否则订阅(例如杂志订阅等)会自动续订。
免费订阅:通过免费订阅,开发者可以将免费订阅内容放入“报刊杂志”。用户注册免费订阅后,该订阅内容将会出现在与该用户Apple ID 关联的所有设备上。请注意,免费订阅不会过期,并且仅在支持报刊杂志功能的 App 中提供。
非续订订阅:非续订订阅允许有时限性的营销服务。对于 App 内购买项目中的限时访问内容,就需使用非续订订阅。例如,导航App 中语音导航功能的一周订阅,或者年度订阅已存档的视频或音频的在线目录。
在sandbox中验证receipt:
在生产环境中验证receipt:
那么如何自动的识别收据是否是sandbox receipt呢?
识别沙盒环境下收据的方法有两种:
根据收据字段 environment = sandbox。根据收据验证接口返回的状态码。
如果status=21007,则表示当前的收据为沙盒环境下收据, t进行验证。
苹果反馈的状态码:
21000 App Store无法读取你提供的JSON数据21002 收据数据不符合格式21003 收据无法被验证21004 你提供的共享密钥和账户的共享密钥不一致21005 收据服务器当前不可用21006 收据是有效的,但订阅服务已经过期。当收到这个信息时,解码后的收据信息也包含在返回内容中21007 收据信息是测试用(sandbox),但却被发送到产品环境中验证21008 收据信息是产品环境中使用,但却被发送到测试环境中验证
在verifyWithRetry方法中,首先向向真实环境验证票据,如果是21007则向沙盒环境验证;==但是在消耗品类型的测试中,使用沙盒票据在真实环境中验证票据得到返回码:21002.所以下面代码在真实环境运行时,沙盒测试消耗型商品得不到正确的验证结果==。
IAPVerifier.verifyWithRetry = function(receipt, isBase64, cb) {
var encoded = null, receiptData = {};
if (isBase64) {
encoded = receipt;
encoded = new Buffer(receipt).toString('base64');
receiptData['receipt-data'] = encoded;
var options = this.requestOptions();
return this.verify(receiptData, options, (function(_this) {
return function(error, data) {
if (error) return cb(error);
if ((21007 === (data != null ? data.status : void 0)) && (_this.productionHost == _this.host)) {
var options_this.requestOptions();
options.host = 'sandbox./verifyReceipt';
return _this.verify(receiptData, options, function(err, data) {
return cb(err, data);
return cb(err, data);
})(this));
IAPVerifier.verify = function(data, options, cb) {
var post_data, request;
post_data = JSON.stringify(data);
options.headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': post_data.length
var request = https.request(options, (function(_this) {
return function(response) {
var response_chunk = [];
response.on('data', function(data) {
if (response.statusCode !== 200) {
return cb(new Error(&response.statusCode != 200&));
response_chunk.push(data);
return response.on('end', function() {
var responseData, totalData;
totalData = response_chunk.join('');
responseData = JSON.parse(totalData);
} catch (_error) {
return cb(_error);
return cb(null, responseData);
})(this));
request.write(post_data);
request.end();
request.on('error', function (exp) {
console.log('problem with request: ' + exp.message);
IAPVerifier.requestOptions = function() {
return options = {
host: 'buy.',
port: 443,
path: '/verifyReceipt',
method: &POST&,
rejectUnauthorized: false
为保证审核的通过,需要在客户端或server进行双重验证,即,先以线上交易验证地址进行验证,如果苹果正式验证服务器的返回验证码code为21007,则再一次连接沙盒测试服务器进行验证即可。在应用提审时,苹果IAP提审验证时是在沙盒环境的进行的,即:苹果在审核App时,只会在sandbox环境购买,其产生的购买凭证,也只能连接苹果的测试验证服务器,如果没有做双验证,需要特别注意此问题,否则会被拒。
PS:上面代码是服务器的验证方式,客户端的验证方式的代码请参考上面一篇博文
原文地址:/Buggo/p/5503644.html
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:79313次
积分:1668
积分:1668
排名:第19642名
原创:26篇
转载:281篇
译文:21篇
(9)(28)(31)(22)(13)(6)(38)(16)(10)(12)(6)(2)(12)(11)(5)(29)(4)(6)(5)(11)(2)(3)(9)(16)(22)(6)iOS开发内购教程In App Purchase 需要了解的
iOS开发内购教程In App Purchase 需要了解的
这一节里面主要介绍一些IAP的常见问题。
-我要在app里添加IAP,必须要注册自己的产品标识符(product identifiers)。产品标识符是什么?
产品标识符(Product Identifiers)是一串字符串,它用来识别你在应用内贩卖的每件商品。App Store用产品标识符来检索产品信息,标识符只能包含大小写字母(A-Z)、数字(0-9)、下划线(-)、以及圆点(.)。你可以任意排列这些元素,但我们建议你创建标识符时使用反向域名,比如 panyname.application.productid
备注:产品标识符和Apple ID以及Bundle ID没有关系,它们看起来可能比较像Bundle ID,但是两者是不一样的。所以,在代码里你不能用Apple ID或者Bundle ID代替产品标识符。
-怎么创建产品标识符?
IAP表单(TheIn-App Purchases form)是用来生成IAP产品的,它包含了产品ID的字段,这个字段必须要填到表里。这个字段可以为你的产品指定产品标识符。你可以按下面的步骤来创建产品标识符:
1.登入iTunes Connect, 点击主页面上的Manage Your Applications模块。
2.进入Manage Your Apps 页面,你可以看到你所有的应用列表。选择你想要创建IAP的产品的app,在下个页面中点击Manage In-App Purchases按键,然后在点击创建。
3.选择IAP产品的类型。iTunes Connect会带你进入IAP表单,在这个表单里有&保存&按键。完整填写Product ID以及表单里的其他字段。
注意事项:产品标识符一旦创建无法修改,此外,如果应用审核没有通过,这个产品标识符也无法再次使用。
-如何在app中如何使用产品标识符?
首先创建SKProductsRequest,然后把产品标识符列表传至initWithProductIdentifiers 来读取产品信息。
-当在Sandbox 环境测试In-App Purchase 时,为什么我得到的是&Payment requests are restricted to products returned as valid via Store Kit&s didReceiveResponse method&错误信息?
执行In-App Purchase的app的用户界面必须有App Store允许的可供购买的产品。在你决定在用户界面展示用于购买的产品之前,你的app必须先向App Store发送一个产品请求。
-StoreKit提供了两种支付支付请求的解决办法:
[SKPayment paymentWithProductIdentifier:PRODUCT_ID] [SKPayment paymentWithProduct:YOUR_SKPRODUCT_OBJECT] 苹果建议您使用 [SKPayment paymentWithProduct:YOUR_SKPRODUCT_OBJECT] 使用这个方法可以确保你一直处于有效的产品支付请求状态,同时也确保用户可以购买你的产品。 所以,务必只展现App Store返回的产品信息。了解更多有关于app内产品展现信息,请查看In-App Purchase 编程指南的&部分。
-In-App Purchase有几种类型?
In-App Purchase共有3中类型,Consumable:一次性购买,最简单的类型,不用保存历史记录。 Nonconsumable:每个产品,用户只买一次,此后就可以在自己的所有设备上看到该产品。 Subscriptions:订阅模式允许多次购买末一个产品,但是购买后,用户可以在自己的所有设备上看到该产品。
-iOS 中使用 IAP 方式在程序内购买的内容在重装系统后还需要再次购买吗? 比如Camera+内的滤镜。
不必。以 Camera+ 为例,Menu 里最下方的选项 Restore purchases 即可帮你解锁已经购买的滤镜。任何实现了 IAP 功能的 App 基本都会提供这一选项,或者在第二次购买时提示已购。
消耗类(如游戏金币)需要购买。 功能类不需要购买,有些应用提供&Restore purchases&,没有的可以再次尝试购买,会提示已付费。
-IAP 能做限时免费吗? 想做一个免费下载、部分内容免费,但是阅读更多内容需要付费解锁的阅读应用。这样可以把IAP的价格限时免费为0吗?
可以的,你可以设定价格为0时就不走IAP了。
创建产品ID的时候,选择多少钱的时候选错了,程序还未提交,还能修改吗?
可以的,在应用程序的信息中。
-升级iOS5 GM的过程中恢复备份出了问题,导致In-App Purchase也丢失了。有没有办法恢复? 在iPad上曾经进行过一些In-App Purchase,但是在升级iOS5 GM的过程中恢复备份出了问题,导致除了恢复升级前的所有应用程序之外,其余个人信息一概丢失。iTunes上可以查询到购买记录。具体来说,买的是Splashtop的把iPad作为第二显示器的软件,Xdisplay。
In-App Purchase 分好几种,最常见的是 Non-Consumable,就是只需要用户购买一次的。如果是这种类型的,再购买一次同一个商品就能够恢复了,不会收取额外费用。如果 app 做得好的话,应该提供一个 Restore 功能,以专门恢复以前购买过的 Non-Consumable 的商品。
-在iTunes Connect中每个应用可以创建多少个IAP产品ID?
请参阅的注册IAP部分
-在iTunes Connect里找不到Manage In-App Purchase (管理IAP)按键怎么办?
以下是可能导致&Manage In-App Purchase&按键无法使用的原因
a.不是iTunes Connnect的管理员(Admin)或开发者(Technical)账号。
b.没有同意最新的iOS或Mac开发者许可协议。
c.最新的付费应用协议(Paid Applications contract)没有生效
-必须上传程序的二进制码才能测试IAP吗?
没有这个必要。
重要事项:除非你的应用已经做好了接受苹果审核的准备,否则请不要上传Development Binary至iTunes Connect。如果iTunes Connect里的二进制编码不完整,二进制编码被拒的可能性很大。一旦二进制编码通过审核,就可以测试In-App Purchase功能了。
-出现了&您的账号信息已变&错误怎么办?
&您的账号信息已变&错误出现的原因是在测试IAP的时候你使用的是设备的测试账号。解决方法是退出账号,然后在iTunes Connect创建一个新的测试账号,使用新的测试账号来测试IAP。
-为什么我的产品标识符在invalidProductIdentifiers中被退返?
有可能是以下原因:
a.没有填完财政需求表(请参阅本文档的&合同、税务以及银行信息&部分)
b.没有使用正确的App ID。
c.没有使用正确的与App ID紧密关联的Provisioning Profile。
d.代码中没有使用正确的产品标识符。更多产品标识符的信息请参阅技术问答,第1329条&IAP产品标识符。
e.你没有清除iTunes Connect中促销的IAP产品。
f.也许你已经修改了你的产品,但它没有在App Store的服务器中生效。
g.苹果拒绝了你最新向iTunes Connect提交的二进制码。
-出现了&你已购买本商品,但商品没有被下载&错误怎么办?
你的应用没有调用SKPaymentQueue &s finishTransaction。调用finishTransaction:允许你从支付队列中移除交易。
-出现&你已成功购买,点击&确认&再次进行免费下载&错误怎么办?
这个信息是个提醒,不是错误。这表明你试图购买一个你已经购买了的非消耗性产品。购买一个非消耗性的产品时不会被扣费。
-调用 payment queue的 restoreCompletedTransactions: 方法不能恢复app的任何产品。
可能由于以下原因:
你没有任何先前购买过的非消耗类产品。
你试图恢复的订阅产品或者非消耗类产品是不可恢复的类型。
restoreCompletedTransactions: 方法只能恢复非消耗类产品。
注意: 在没有可恢复产品的情况下,Store Kit不会调用paymentQueue:updatedTransactions:方法。
-应该什么时候恢复In-App Purchase产品?
在以下两种情况下你可以恢复自动更新的订阅产品和非消耗类产品:
a.在客户其他设备上安装
b.在删除了关联应用的其他设备上重新安装
-如何解决&这不是测试用的用户账号,请在Sandbox环境下创建一个新的账号&的问题?
出现这个错误的原因是在确认购买信息时你使用的是iTunes用户账号。解决的办法是退出账号,然后使用你的IAP测试账号。
-回单(receipt )核实失败,并且出现字符串 (iOS)。
可能有以下几个原因:
a.在你的iOS app里,你没有使用64位编码对回单数据进行编码,
b.没有有效的回单,你的回单大概使用了等号隔开键和值,用分号隔开了关键字。
c.有效的回单使用冒号来隔开键和值,用逗号隔开关键字,
下表是有效的回单样本
receipt: { &signature& : &&&, &purchase-info& : &&&, &pod& : &&&, &signing-status& : &&& }
-我更新了使用In-App Purchase的iOS app,如何对它进行测试呢(iOS)?
不管更新后的app是否正确执行地了In-App Purchase,如果你要测试的话就按照以下步骤:
通过Ad Hoc Distribution方式安装原始app。
通过Ad Hoc Distribution方式安装更新后的app来验证它是否完全覆盖了原始的app。
试着从更新后的app中执行In-App Purchase。
-我应该使用哪个url核实回单(receipt)(iOS)?
在sandbox环境测试app时使用 sandbox URL ,当应用处于审核状态时也可以使用这个URL:https://sandbox./verifyReceipt 使用产品 URL 一旦你的app上架App Store,你就要用产品URL: http://buy./verifyReceipt
-如何核实receipt(iOS)?
第一次一般使用产品的URL核实收据。如果你收到一个21007状态代码,那么接下来要用sandbox URL。当app处于测试状态或者在sandbox环境下进行检测,或者上架App Store,你可以使用这种方法,从而避免在URL之间进行切换,
注明:21007 状态码表明receipt是一个sandbox receipt。
温馨提示:&&&&&&&本站网站原创文章,版权归本站所有。转载请注明出处:懒人ios代码库-
亲!懒人ios代码库常来看看哦...
[相关文章]每天三分钟,知晓天下事,视频、语音、文字综合版任您挑!微信搜索fgzadmin关注或点击标题下方可以快速关注。
原创不易,认可价值,动手指点并转发,就是最好的支持与肯定。淘宝特约店址:http://goldengame.
深夜十点,陪你读书。
慢工出细活
由于中、美、俄三国自2008年后基本上长期上演“三国杀”(昨天文章《原创丨中美俄世纪三国杀,谁是百年长跑冠军
其实这是个有奖活动贴。n其实这是个有奖活动贴。n其实这是个有奖活动贴。
思考者正在阅读原创丨三次世界大战亚洲开打,美国推演靠谱吗?原创丨央行连出两大招,有何深意?微历史丨张学良为啥
美国总统奥巴马日在接受媒体采访时表示,2011年对利比亚局势的干涉,是其总统生涯中做出的最
我们都知道,美国软实力很厉害,在过去很多年都一直掌控者国际话语权,他们可以提着民主、自由、人权的大棒满世界乱
思考者正在阅读原创丨重大变革,我们的世界都将逃不过被TA重塑!原创丨中美黄岩岛较量,谁是最后赢家?原创丨你射阅读:19973次
[caption id="attachment_2798" align="alignright" width="250"] Now you don\'t have to rage against in-app purchases any more![/caption]
成为ios开发者最大的好处就是,你编写的应用程序会有很多方式可以赚钱。比如,收费版,免费挂广告版,还有就是程序内置购买。
程序内置购买会让你爱不释手,主要有以下原因:
相比程序本身的下载收费以,你还可以赚更多的钱。一些用户愿意为那些额外的功能花费更多的金钱!
你可以免费发布你的程序(这样的话,用户就可以任意下载了),如果他们喜欢这个程序的话,那么就会有人愿意购买额外功能。
在你做完一个程序的时候,你可以在以后的发布版中添加更多的功能,然后这些功能可以用内置购买(这样的话,你就不用为获取更多的利益,再重新制作另一个程序了!)。
我最近正在制作的一个程序里面(Wild Fables,) ,我就决定先把程序免费(其中只包含一个故事),然后把更多的故事放在in-app purchase里面。
我最近正在制作的一个程序里面(Wild Fables,),我决定发布免费的应用,包括一个故事,更多的故事需要付费获取。
在这篇教程里面,你将会学到如何使用程序内置付费来解琐程序里面的本地内容。我将向您展示如何处理应用内购买棘手的异步问题。请谨慎采纳这些建议,因为我的程序也还在开发之中 —— 但是,随着我的知识的积累,我用我获得经验教训来更新教程内容,以确保不误人子弟!:]
这篇教程的前提条件你需要熟悉基本的ios编程概念。如果你还是一个ios开发新手,可以先参考 。In App Rage那么,本教程将制作一个怎样的程序呢?好吧,在揭晓答案之前,我先介绍一些背景情况。。。
最近,我对 这玩意儿非常着迷,或者叫做 "F7U12"。如果你以前从没听说过它,它们实际上就是一些非常有趣的漫画,里面有些人非常搞笑和搞怪的人和事。
因此,这篇教程,我们想要叫做“In App Rage”的一个非常小巧的应用,在这个程序里面,用户可以使用内置购买来获得一些漫画。但是,在我们开始编码之前,我们需要先用ios Developer Center和iTunes Connect来为本程序创建一个入口点(a placeholder app entry)。
第一步,就是为这个程序创建一个App ID。所以,登录 ,选择“App IDs”标签而,然后点击“New App ID”,如下图所示:
你可以按照下面的截图,根据提示输入描述和bundle identifier:
注意,你应该使用自己独特的前缀来修改bundle identifier,使用你自己的域名(如果你有的话),或者如果都不可用的话,根据自己的名字或则其他的独特字符 你需要定义你自己的独一无二的identifier,通常的做法是把你的域名反过来写就行了,然后你也可以基于其它规则来制作啦。
当你完成的时候,点击Submit。好,恭喜你 —— 你现在有一个新的App ID了!现在,你将使用这个ID在iTunes Connect里面来创建一个新的应用了。
首先登录 iTunes Connect,点击“Manage Your Applications”,然后选择“Add New App”,并输入依次App Name,SKU number,同时选择你之前刚刚创建好的Bundle ID。
首先登录 , 点击“Manage Your Applications”,然后选择 "Add New App"。并输入依次 an App Name, SKU number, 同时选择你之前刚刚创建好的Bundle ID,如下图所示:
你可能必须调整应用程序名称,因为,app名字必须是唯一的,而且我们之前为它添加了一个入口点(entry)。
接下来的两页将要求你输入你的应用程序的一些信息。现在,可以随便填一些内容 —— 之后可以更改内容。但不幸的是,每个带*号的文本框你都必须要填好(包括程序截图,甚至你现在还没有截图,呵呵,造一个吧)。
好吧,让你们看看我对于这个过程的感觉吧,请看下图:
如果你像上面一样出错了,只需要随便填写一些数据就可以了(你可以使用任何图标或者截屏,只要大小合适就行了)。一旦你把所有的错误都解决完以后,你就大功告成啦,oh yeah!管理 In App Purchases在你开始编写in app purchase代码之前,你为此创建的placeholder应用程序,同时,你必须在iTunes Connet里面设置好。所以,现在你拥有placeholder应用程序,你现在只需要点击“Manage In App Purchases”按钮就行了,如下图所示:
然后,点击左上角的“Create New”,然后按照下图所示,填写相应的信息:
让我们来解释下这几个文本域的含义吧:
Reference Name: 这个名字就是在iTunes Connect里面为相应的in-app purchase显示。这个名字你可以随便命名,因为在你的程序里面是看不到它滴。
Product ID: 在苹果的开发文档里面,这个也叫做“product identifier”,这是一个唯一的字符串,用来标识你的in-app purchase。通常的做法是,使用你的bundle id,然后在最后加一个唯一的字符串表示相应的purchase。
Type: 你可以选择non-consumable(购买一次,永久使用),comsumable(购买一次,使用一次),或者subscription(自动续款)。本教程中,我们采用non-consumables。
Cleared for Sale: 当应用程序可用时,这些in-app purchase就可以使用了。
Price Tier: 设置程序内置购买的价钱。
在你完成上面的设置以后,往下滚动鼠标,然后在Display Detail section部分添加一个English language entry,如下图所示:
稍后,当the in-app purchases可用的时候,当你查询App Store时,会向你返回一些信息。
您可能想知道为什么这一步是必要的(毕竟,你可以在你的应用程序中嵌入这些信息!)好吧,很明显Apple想知道你定的价钱嘛。同时,在App Store里面会根据你填写的这些东西来显示一些信息(比如,内置付费应用排行榜)。最后,如果你这一步设置了,你之后会变得很轻松。因为,它让你不用硬编码这些信息在你的代码之中。而且可以让你动态改变是允许内置购买还是禁止内置购买。
一旦你完成之后,保存entry,然后创建更多,和下面的截图效果类似。不要担心描述信息 —— 在本教程中我们并不会使用它们。
你可能会注意到,这个过程需要花费一段时间,我能够想象,当你的程序有很多内置购买“商品”时,这个创建过程会有多么的烦人!幸运的是,本教程我们体会不到,但是,如果你应用程序真的遇到了这种情况的话, :]提取产品列表(Retrieving Product List)在你能让用户从你的程序里面购买任何东西之前,你必须向iTunes Connect发送一个查询请求,从服务器上查询可用的产品列表。
我们可以直接在view controller里面添加代码来实现之,但是那样扩展性太不好了,不利于重用。取而代之,我们将创建一个辅助类来管理所有与in-app purchase相关的内容,然后你就可以在你的其它程序里面重用了。
在从服务器上获得产品列表的同时,这个辅助类还会跟踪是否购买了产品。它会将每个已经购买的product identifier保存在NSUserDefaults。
好了,让我们动手实验一下吧!打开XCode,然后选择File\New Project,再选择 iOS\Application\Navigation-based Application,点击Choose。把工程命名为InAppRage,然后点击Save。
接下来,创建IAPHelper类来管理内置付费代码。首先,右击Classes分组,选择File\New File,选择iOS\Cocoa Touch Class\Objective-C class,确保Subclass of NSObject被选中,然后点击Next。把这个文件命名为IAPHelper.m,通过确保“Also create IAPHelper.h” 被选中,然后点击Finish。
我们首先往IAPHelper.m里面添加从iTunes Connect检索产品列表的方法,代码如下:- (void)requestProducts {
self.request = [[[SKProductsRequest alloc] initWithProductIdentifiers:_productIdentifiers] autorelease];
_request.delegate =
[_request start];
}这个方法假设我们已经定义了一个实例变量,叫做 _productIdentifiers ,它包含了product identifiers列表,用来在iTunes Connect中查找(比如,com.raywenderlich.inapprage.drummerrage)。
它然后创建了一个SKProductsRequest实例,这个类是苹果公司定义的,它实现了从iTunes Connect里面提取信息的功能。使用此类灰常easy,你设置这个类的delegate(delegate 实现 SKProductsRequestDelegate中的协议方法),然后就可以调用start方法了。
我们设置IAPHelper类本身作为delegate,那就意味着,产品列表查询完毕时(productsRequest:didReceiveResponse),它会收到一个回调消息。我感到奇怪的是,由于一些原因购买没有成功,并获得错误的回调消息是,我不知道具体怎么处理,所以我稍后用超时处理这些错误 。Update:erry 在论坛里面指出,SKProductsRequestDelegate协议是从SKRequestDelegate派生而来滴,而SKRequestDelegate协议有一个方法,叫做 request:didFailWithError:,当delegate实现这个方法的时候,你就获得购买失败的信息。如果你想的话,你可以用这个方法替代下面描述的超时方法,感谢Jerry!
好吧,接下来让我们来实现productsRequest:didReceiveResponse 方法吧,具体如下所示:- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
NSLog(@"Received products results...");
self.products = response.
self.request =
[[NSNotificationCenter defaultCenter] postNotificationName:kProductsLoadedNotification object:_products];
}这个非常简单 —— 它贮存产品列表并返回(是一个SKProducts的数组),然后把request设置为nil(为了释放内存),然后发出一个通知,任何侦听这个通知的对象都会收到这个消息。
接下来添加初始化代码:- (id)initWithProductIdentifiers:(NSSet *)productIdentifiers {
if ((self = [super init])) {
// Store product identifiers
_productIdentifiers = [productIdentifiers retain];
// Check for previously purchased products
NSMutableSet * purchasedProducts = [NSMutableSet set];
for (NSString * productIdentifier in _productIdentifiers) {
BOOL productPurchased = [[NSUserDefaults standardUserDefaults] boolForKey:productIdentifier];
if (productPurchased) {
[purchasedProducts addObject:productIdentifier];
NSLog(@"Previously purchased: %@", productIdentifier);
NSLog(@"Not purchased: %@", productIdentifier);
self.purchasedProducts = purchasedP
}这个初始化代码将检测哪些产品已经被购买,哪些还没有(根据NSUserDefaults保存的信息判断),并设置适当的数据结构。
好了,现在,我们已经见过最重要的代码了,接下来,我们在头文件中添加一些声明,synthesize声明语句,和其他的一些声明。首先,打开 IAPHelper.h,并作如下修改:#import
#import "StoreKit/StoreKit.h"
#define kProductsLoadedNotification
@"ProductsLoaded"
@interface IAPHelper : NSObject
NSSet * _productI
NSArray * _
NSMutableSet * _purchasedP
SKProductsRequest * _
@property (retain) NSSet *productI
@property (retain) NSArray *
@property (retain) NSMutableSet *purchasedP
@property (retain) SKProductsRequest *
- (void)requestP
- (id)initWithProductIdentifiers:(NSSet *)productI
@end这个简单地导入StoreKit 头文件,然后定义一些实例变量、函数和通知的名字。
接下来,在IAPHelper.m里面添加synthesize代码,以后内存释放代码,如下所示:// Under @implementation
@synthesize productIdentifiers = _productI
@synthesize products = _
@synthesize purchasedProducts = _purchasedP
@synthesize request = _
// In dealloc
- (void)dealloc
[_productIdentifiers release];
_productIdentifiers =
[_products release];
_products =
[_purchasedProducts release];
_purchasedProducts =
[_request release];
_request =
[super dealloc];
}最后一步 —— 你需要添加StoreKit框架。右键点击Frameworks文件夹,然后点Add\Existing Frameworks ,然后选择 StoreKit.framework。然后选择Build\Build 编译一下,编译完之后,你的代码应该是没有错误的。(译者注:此方法在Xcode4.0以上不适用。4.0需要点击工程文件名,然后右键target,然后在build phase里面添加框架)。Subclassing for Your App这里将创建一个IAPHelper类,这样以后你在你的程序里面只需要继承一下它,然后指定你的产品标识符(product identifier)就可以啦。许多人给我提建议,说可以从WEB服务器上获取产品标识符列表,以及其它相关信息,这样,你就可以动态的加载新的内置购买的商品(in-app purchases),而不是更新应用程序。
这个提议非常好,但是,为了保持本教程的简单性,我这里就采用了硬编码的方式。
右键选中Classes 分组,然后选择File\New File,再选择 iOS\Cocoa Touch Class\Objective-C class,确保Subclass of NSObject 被复选中,然后点击Next。把这个文件命名为InAppRageIAPHelper.M,同时确保 “Also create InAppRageIAPHelper.h” 被复选中,然后点击Finish。
然后,把InAppRageIAPHelper.h 替换成下列代码:#import
#import "IAPHelper.h"
@interface InAppRageIAPHelper : IAPHelper {
+ (InAppRageIAPHelper *) sharedH
@end这里把InAppRageIAPHelper类定义为IAPHelper类的子类,然后创建了一个静态方法用来创建些帮助类的单例。
接下来,把InAppRageIAPHelper.m替换成下面的代码。#import "InAppRageIAPHelper.h"
@implementation InAppRageIAPHelper
static InAppRageIAPHelper * _sharedH
+ (InAppRageIAPHelper *) sharedHelper {
if (_sharedHelper != nil) {
return _sharedH
_sharedHelper = [[InAppRageIAPHelper alloc] init];
return _sharedH
- (id)init {
NSSet *productIdentifiers = [NSSet setWithObjects:
@"com.raywenderlich.inapprage.drummerrage",
@"com.raywenderlich.inapprage.itunesconnectrage",
@"com.raywenderlich.inapprage.nightlyrage",
@"com.raywenderlich.inapprage.studylikeaboss",
@"com.raywenderlich.inapprage.updogsadness",
if ((self = [super initWithProductIdentifiers:productIdentifiers])) {
@end首先,实现sharedHelper方法,来实现InAppRageIAPHelper类的单例。注意,这种实现单例的方式并不是线程安全的,但是,因为 对于本应用来说完全足够了,因为我们只有一个主线程。
接下来,我们硬编码了产品标识符的字符串数组,然后调用了基类的初始化方式。注意,我们在这里的字符串名字必须和之前在iTunes Connect里面定义的名称保持一致。
然后选择Build\Build,保证没有错误再继续哦。Adding Helper Code我们差不多完成了我们的帮助类了,但是,在调用这个类的时候会有两个问题,我们接下来会讨论解决办法。
第一个问题就是,这段代码在没有网络连接的情况下是跑不起来滴。所以,我们在使用之前,需要检查是否有网络。
第二个问题,加载产品列表可以会耗费一定的时间,所以,我们需要让用户知道我们在加载产品列表,我们需要显示一个activity indicator就可以啦。
关于这两个问题,我们都可以自己动手来解决,但是,你为什么要重新发明轮子呢?(译者注:工作中,遇到任何“问题”的时候,这里的“问题”,我指的是有点难度的问题,或者自己一时想不清楚的问题,不要急着动手编码,你还没想清楚呢!瞎编码什么呀!不妨google一下,你会有意想不到的收获。当然,这里我并不是鼓励大家不动脑筋,而是,有时候,我们程序员需要一种“懒”。)苹果已经为我们写好了一个检测网络是否可用的代码,叫做
则为我们写了一个非常好用的指示器类 !
所以,尽管去下载这些源代码吧,当然,你也可以直接从本教程的 获得上面提到的源码。
一旦你下载完了这些文件,直接把MBProgressHUD.h/m 和 Reachability.h/m拖到你的项目的Classes分组下面就可以啦。同时确保 “Copy items into destination group’s folder”被复选中,然后点击Add。
最后一步 —— 你需要添加SystemConfiguration框架,因为Reachability类依赖此类库。右键点击Frameworks文件夹,然后选择Add\Existing Frameworks,然后再从列表中选择SystemConfiguration.framework就可以啦。然后,编译,确保没有错误后再继续。
好了,现在我们得到所有的产品列表和价格了,现在让我们把它们整合起来。显示产品列表打开RootViewController.h ,然后做如下修改:// Before @interface
#import "MBProgressHUD.h"
// Inside @interface
MBProgressHUD *_
// After @interface
@property (retain) MBProgressHUD *上面只是简单的声明MBProgressHUD的实例变量和定义属性(我们将使用可重用的进度)。
然后,打开RootViewController.m,并做如下修改:// At top of file
#import "InAppRageIAPHelper.h"
#import "Reachability.h"
// Under @implementation
@synthesize hud = _
// Uncomment viewDidLoad and add the following
self.title = @"In App Rage";
// Uncomment viewWillAppear and add the following
self.tableView.hidden = TRUE;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(productsLoaded:) name:kProductsLoadedNotification object:nil];
Reachability *reach = [Reachability reachabilityForInternetConnection];
NetworkStatus netStatus = [reach currentReachabilityStatus];
if (netStatus == NotReachable) {
NSLog(@"No internet connection!");
if ([InAppRageIAPHelper sharedHelper].products == nil) {
[[InAppRageIAPHelper sharedHelper] requestProducts];
self.hud = [MBProgressHUD showHUDAddedTo:self.navigationController.view animated:YES];
_hud.labelText = @"Loading comics...";
[self performSelector:@selector(timeout:) withObject:nil afterDelay:30.0];
}viewWillAppear里面的代码比较重要。它首先设置table view默认情况下隐藏(table view在产品列表加载完之后会再重新显示滴)。然后,设置了一个通告,因为此类需要知道什么时候产品列表加载完了。
然后再使用Reachability来检测网络是否可用。如果可用的话,它就调用IAPHelper的requestProducts方法来下载之前填好的产品列表。
当产品列表在加载过程中的时候,我们用MBProgressHUD显示一个“loading”界面。同时,我们还设置一个超时检测函数,当30秒过后,如果还没有加载完产品列表的话,我们就提示用户错误。
所以,接下来,让我们添加一些代码来处理通告消息,和超时处理函数。- (void)dismissHUD:(id)arg {
[MBProgressHUD hideHUDForView:self.navigationController.view animated:YES];
self.hud =
- (void)productsLoaded:(NSNotification *)notification {
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[MBProgressHUD hideHUDForView:self.navigationController.view animated:YES];
self.tableView.hidden = FALSE;
[self.tableView reloadData];
- (void)timeout:(id)arg {
_hud.labelText = @"Timeout!";
_hud.detailsLabelText = @"Please try again later.";
_hud.customView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:@"37x-Checkmark.jpg"]] autorelease];
_hud.mode = MBProgressHUDModeCustomV
[self performSelector:@selector(dismissHUD:) withObject:nil afterDelay:3.0];
}第一个方法(dismissHUD)只是一个辅助函数,用来隐藏加载面板的。
第二个方法(productsLoaded)是在kProductsLoadedNotification被触发时,被调用。它隐藏了加载面板,同时重新加载table view中的数据,这样就可以显示新添加的商品。
最后一个方法(timeout),更新HUD并显示一个超时的消息,然后让这个HUD过一段时间再消失。
最后 —— 我们需要在 RootViewController.m里面再添加一些代码来完成table view的填补,代码如下:// Replare return 0 in numberOfRowsInSection with the following
return [[InAppRageIAPHelper sharedHelper].products count];
// In cellForRowAtIndexPath, change cell style to "subtitle":
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
// In cellForRowAtIndexPath, under "Configure the cell"
SKProduct *product = [[InAppRageIAPHelper sharedHelper].products objectAtIndex:indexPath.row];
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[numberFormatter setLocale:product.priceLocale];
NSString *formattedString = [numberFormatter stringFromNumber:product.price];
cell.textLabel.text = product.localizedT
cell.detailTextLabel.text = formattedS
if ([[InAppRageIAPHelper sharedHelper].purchasedProducts containsObject:product.productIdentifier]) {
cell.accessoryType = UITableViewCellAccessoryC
cell.accessoryView =
UIButton *buyButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
buyButton.frame = CGRectMake(0, 0, 72, 37);
[buyButton setTitle:@"Buy" forState:UIControlStateNormal];
buyButton.tag = indexPath.
[buyButton addTarget:self action:@selector(buyButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
cell.accessoryType = UITableViewCellAccessoryN
cell.accessoryView = buyB
// In viewDidUnload
self.hud =
// In dealloc
[_hud release];
_hud =在这里,table view只是简单的显示IAPHelper单例里面的产品列表 —— 这个列表我们是通过SKProductsRequest来获取的。
products数组里面的对象都是SKProduct的实例。它们包含了你在iTunes Connect里面设置的信息,比如title,description,price,等。本教程中,table view只是简单的显示价格和标题。同时,我们还添加了一个“购买”按钮,现在这个“购买”还不起作用,因为我们还没有为购买编写任何代码。
你现在差不多可以测试一下了,但是,还差最后一步(而且是非常重要的一步!)。你需要设置bundle identifier。点击你的InAppRage-Info.plist并修改Bundle identifier和你在iOS Developer Center里面的那个一致,如下图所示:
好了,差不多了!编译并运行你的程序(你需要编译到设备上面,模拟器上是不行的),然后你会看到一个loading indicator,之后,就会显示一系列产品列表,如下图所示:
Show Me The Money这是篇超级无敌又臭又长的教程,而且最重要的部分还是没有讲到 —— 如何处理支付,如何赚钱,接下来,马上为您揭晓!
做支付基本的几个基本的要点:
你创建一个SKPayment对象,然后指定用户想要购买的产品的标识符。然后把它加到支付队列(payment queue)里面去。
StoreKit将会提醒用户“are you sure?”, 然后要求用户输入用户名和密码,然后支付,然后就会告知你支付成功还是失败。你也可以处理这种情况:用户已经为此付过费了,然后可以重新再下载,同时给出一个恰当的提示就可以了。
你指定特殊的对象接收购买notifications。这个对象需要处理支付内容下载(在我们这个教程没必要,因为我们是硬编码的),同时解琐程序里面的相关内容(我们可以通过设置NSUserDefaults中保存的标记,然后把值存储到purchasedProducts数组中)。
不要担心 —— 当你看到代码的时候,就会发现这个过程其实很easy滴。再强调一次,IAPHelper尽可能实现重用,我们将在 IAPHelper.h里面做如下修改:// Add two new notifications
#define kProductPurchasedNotification
@"ProductPurchased"
#define kProductPurchaseFailedNotification
@"ProductPurchaseFailed"
// Modify @interface to add the SKPaymentTransactionObserver protocol
@interface IAPHelper : NSObject
// After @interface, add new method decl
- (void)buyProductIdentifier:(NSString *)productI然后打开IAPHelper.m 文件并作如下修改:
Then switch to IAPHelper.m and add the following methods:- (void)recordTransaction:(SKPaymentTransaction *)transaction {
// Optional: Record the transaction on the server side...
- (void)provideContent:(NSString *)productIdentifier {
NSLog(@"Toggling flag for: %@", productIdentifier);
[[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:productIdentifier];
[[NSUserDefaults standardUserDefaults] synchronize];
[_purchasedProducts addObject:productIdentifier];
[[NSNotificationCenter defaultCenter] postNotificationName:kProductPurchasedNotification object:productIdentifier];
- (void)completeTransaction:(SKPaymentTransaction *)transaction {
NSLog(@"completeTransaction...");
[self recordTransaction: transaction];
[self provideContent: transaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
- (void)restoreTransaction:(SKPaymentTransaction *)transaction {
NSLog(@"restoreTransaction...");
[self recordTransaction: transaction];
[self provideContent: transaction.originalTransaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
- (void)failedTransaction:(SKPaymentTransaction *)transaction {
if (transaction.error.code != SKErrorPaymentCancelled)
NSLog(@"Transaction error: %@", transaction.error.localizedDescription);
[[NSNotificationCenter defaultCenter] postNotificationName:kProductPurchaseFailedNotification object:transaction];
[[SKPaymentQueue defaultQueue] finishTransaction: transaction];
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
for (SKPaymentTransaction *transaction in transactions)
switch (transaction.transactionState)
case SKPaymentTransactionStatePurchased:
[self completeTransaction:transaction];
case SKPaymentTransactionStateFailed:
[self failedTransaction:transaction];
case SKPaymentTransactionStateRestored:
[self restoreTransaction:transaction];
- (void)buyProductIdentifier:(NSString *)productIdentifier {
NSLog(@"Buying %@...", productIdentifier);
SKPayment *payment = [SKPayment paymentWithProductIdentifier:productIdentifier];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}啊!好多代码啊,但是,其实都不难,我会一个个向大家解释每个方法的作用。
当点击table view里面的buy按钮时,它将会调用buyProductIdentifier函数。然后会创建一个新的SKPayment对象,并且把这个对象加载到队列中去。我们将把此类当作delegate来接收支付事务的更新消息,所以,当支付完成或者失败的时候,paymentQueue:updatedTransactions 这个函数将会被调用。
如果支付成功了(或者取消了),最终provideContent函数会被调用。重点来了 —— 它设置NSUserDefaults里面的标记,然后把这个事务加到队列中去。剩下的代码就是用来检测用户是访问相应的内容了。
无论成功失败,会发出消息,这样任何观察者可以更新 UI accordingly,等等操作。
注意,这里并没有实现记录事务(record Transaction)。如果你想,你可以去实现此方法,给WEB服务器发送一个消息,让服务器来记录事务。个人来讲,如果没有任何下载,那么实现这个方法没什么实际的用处 —— 但是如果你的应用程序需要它,这是一种选择。
同时,也请注意,整个这种解决方案被黑的(译者注:你需要加密保存)—— 但是,我并不是很关心这个东东,因为,任何想要破解程序的人,他们肯定是不愿意付钱的。
在我们使用这些代码之前,我们还需要在App Delegate里面添加一些东西,这样的话,产品采购交易信息“进来”时候,IAPHelper类就会得到相应通知。所以,打开InAppRageAppDelegate.m并作如下修改:// At top of file
#import "InAppRageIAPHelper.h"
// In application:didFinishLaunchingWithOptions
[[SKPaymentQueue defaultQueue] addTransactionObserver:[InAppRageIAPHelper sharedHelper]];如果没有这句代码的话,那么 paymentQueue:updatedTransactions 这个函数将不会被调用,所以,一定要记得要加上去!
最后一步,让我们回到table view上面来。打开RootViewController.m ,然后作如下修改:// Add new method
- (IBAction)buyButtonTapped:(id)sender {
UIButton *buyButton = (UIButton *)
SKProduct *product = [[InAppRageIAPHelper sharedHelper].products objectAtIndex:buyButton.tag];
NSLog(@"Buying %@...", product.productIdentifier);
[[InAppRageIAPHelper sharedHelper] buyProductIdentifier:product.productIdentifier];
self.hud = [MBProgressHUD showHUDAddedTo:self.navigationController.view animated:YES];
_hud.labelText = @"Buying fable...";
[self performSelector:@selector(timeout:) withObject:nil afterDelay:60*5];
// Add inside viewWillAppear
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(productPurchased:) name:kProductPurchasedNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector: @selector(productPurchaseFailed:) name:kProductPurchaseFailedNotification object: nil];
// Add new methods
- (void)productPurchased:(NSNotification *)notification {
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[MBProgressHUD hideHUDForView:self.navigationController.view animated:YES];
NSString *productIdentifier = (NSString *) notification.
NSLog(@"Purchased: %@", productIdentifier);
[self.tableView reloadData];
- (void)productPurchaseFailed:(NSNotification *)notification {
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[MBProgressHUD hideHUDForView:self.navigationController.view animated:YES];
SKPaymentTransaction * transaction = (SKPaymentTransaction *) notification.
if (transaction.error.code != SKErrorPaymentCancelled) {
UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:@"Error!"
message:transaction.error.localizedDescription
delegate:nil
cancelButtonTitle:nil
otherButtonTitles:@"OK", nil] autorelease];
[alert show];
}你就要成功啦,再坚持一小会儿!In App Purchases, Accounts, and the Sandbox当你在XCODE里面运行你的程序的时候,你并不是在运行真正的In-App Purchase服务器 —— 你实际上是跑在沙盒服务器上面。
这意味着,你可以购买任何东西而不用担心会被扣钱。但是,你需要先创建一个测试帐号,同时确保你的设备登出了apple store,这样的话,你就可以看到这个处理过程了。
要创建测试帐号,登陆iTunes Connect ,然后点击“Manage Users”。点击“Test User”, 然后就可以创建一个测试帐号了,这样你就可以在“沙盒服务器”购买“假”的商品。
然后,打开你的iphone,确保你退出当前的帐号了。你可以通过打开Settings程序,然后点击"Store",然后点"Sign out”。(大家千万注意啊!)
最后,运行你的程序吧。然后点击购买,输入测试帐号信息,如果一切顺利的话,你会得到如下截屏的输出!
但是,等一下 —— 哪有里漫画啊!!!!你没值钱当然就没有啦。。。
好吧,这篇教程已经足够长了,用户购买以后可以得到漫画的任务就交由读者来完成吧。 :]
本教程使用的资源压缩包 。包括漫画图片,如果喜欢的话你可以继续完成这个项目:当点击一个已经购买的商品(也就漫画),就会进入展示相应漫画新的view controller!如果你像这样实现,在用户阅读漫画之前,你可以查询InAppRageIAPHelper的purchasedProducts数组中的productIdentifier。何去何从?这里本教程中提到的所有的代码 ,包括可重用的in-app purchase的帮助类。
就像上文建议的那样,如果你喜欢,为什么不将漫画融入到程序里?如果没有,至少你应该
如果你想了解更多程序内置购买,首先查看苹果的官方文档 .
其他还有, Noel Llopis程序内置购买的优秀系列文章 。同时,他还在
做了很好的演示,关于,你可以点击查看
我很愿意听取你关于本篇文章主体的想法和建议,特别是因为我只是刚刚开始做这些,! 很感激您对在技术或业务方面任何评价或建议。Update:LOL - 这是另一个伟大的iOS应用愤怒漫画由Jayant C Varma!
相关文章推荐

我要回帖

更多关于 山东省首届翻译大赛 的文章

 

随机推荐