日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

IAP-应用内购买流程

發(fā)布時(shí)間:2023/12/13 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 IAP-应用内购买流程 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

?成為ios開發(fā)者最大的好處就是,你編寫的應(yīng)用程序會(huì)有很多方式可以賺錢。比如,收費(fèi)版,免費(fèi)掛廣告版,還有就是程序內(nèi)置購買。

? ? 程序內(nèi)置購買會(huì)讓你愛不釋手,主要有以下原因:

  • 除了程序本身的下載收費(fèi)以外,你還可以賺更多的錢。一些用戶愿意為那些額外的功能花費(fèi)大量的金錢。
  • 你可以免費(fèi)發(fā)布你的程序(這樣的話,用戶就可以任意下載了),如果他們喜歡這個(gè)程序的話,那么就會(huì)有人愿意購買額外功能。
  • 在你做完一個(gè)程序的時(shí)候,你可以在以后的發(fā)布版中添加更多的功能,然后這些功能可以用內(nèi)置購買,這樣的話,你就不用再重新制作另一個(gè)程序了。

? ? 我最近正在制作的一個(gè)程序里面,我就決定先把程序免費(fèi)(其中只包含一個(gè)故事),然后把更多的故事放在in-app purchase里面。

? ? 在這篇教程里面,你將會(huì)學(xué)到如何使用程序內(nèi)置付費(fèi)來解瑣本地程序里面的內(nèi)容,我將向你展示一些技巧,用來應(yīng)付使用程序內(nèi)置購買功能時(shí)的一些異步特性。請(qǐng)謹(jǐn)慎采納這些建議,因?yàn)槲业某绦蛞策€在開發(fā)之中,但是,隨著我的知識(shí)的積累,我會(huì)逐步更新教程內(nèi)容以確保不誤人子弟。

? ? 這篇教程的前提條件你需要熟悉基本的ios編程概念,如果你還是一個(gè)ios開發(fā)新手,可以先參考這些教程。

In App Rage

? ?那么,本教程將制作一個(gè)怎樣的程序呢?好吧,在揭曉答案之前,我先介紹一些背景情況。。。

? ?最近,我對(duì)?rage comics這玩意兒非常著迷。如果你以前從沒聽說過它,讓我向你們介紹一下吧。它們實(shí)際上就是一些非常有趣的漫畫,里面有些人非常搞笑和搞怪的人和事。

? ?因此,這篇教程,我們想要做一個(gè)非常小巧的應(yīng)用,叫做“In App Rage”,在這個(gè)程序里面,用戶可以使用內(nèi)置購買來獲得一些漫畫。但是,在我們開始編碼之前,我們需要先用ios Developer Center和iTunes Connect來為本程序創(chuàng)建一個(gè)入口點(diǎn)。

? ? 第一步,就是為這個(gè)程序創(chuàng)建一個(gè)App ID。所以,首先登錄?iOS Developer Center,選擇“App IDs”標(biāo)簽而,然后點(diǎn)擊“New App ID”,如下圖所示:

? ? 你可以按照下面的截圖,根據(jù)提示 輸入描述和bundle identifier:

? ?注意,你不能直接使用上面這個(gè)bundle identifier,你需要定義你自己的獨(dú)一無二的identifier,通常的做法是把你的域名反過來寫就行了,然后你也可以基于其它規(guī)則來制作啦。

? ? 當(dāng)你完成的時(shí)候,點(diǎn)擊Submit。好,恭喜你,你現(xiàn)在有一個(gè)新的App ID了!現(xiàn)在,你將使用這個(gè)ID在iTunes Connect里面來創(chuàng)建一個(gè)新的應(yīng)用了。

? ?首先登錄?iTunes Connect,點(diǎn)擊“Manage Your Applications”,然后選擇“Add New App”,并輸入依次App Name,SKU number,同時(shí)選擇你之前剛剛創(chuàng)建好的Bundle ID。

? ? 你可能不得不在你的應(yīng)用程序名字上面下點(diǎn)功夫,因?yàn)?#xff0c;app名字必須是唯一的,而且我們之前為它添加了一個(gè)入口點(diǎn)(entry)。

? ? 接下來的兩頁將要求你輸入你的應(yīng)用程序的一些信息。現(xiàn)在,可以隨便填一些內(nèi)容,因?yàn)楹竺孢€有機(jī)會(huì)再更改。但是,每個(gè)帶×號(hào)的文本框你都必須要填好(包括程序截圖,甚至你現(xiàn)在還沒有截圖,呵呵,造一個(gè)吧)

? ?好吧,讓你們看看我對(duì)于這個(gè)過程的感覺吧,請(qǐng)看下圖:

? ? 如果你像上面一樣出錯(cuò)了,只需要隨便填寫一些數(shù)據(jù)就可以了。你可以使用任何圖標(biāo)或者截屏,只要大小合適就行了。一旦你把所有的錯(cuò)誤都解決完以后,你就大功告成啦,oh yeah!

管理 In App Purchases

? ? 在你開始編寫in app purchase代碼之前,你需要為此創(chuàng)建一個(gè)樁應(yīng)用(placeholder app),同時(shí),你必須在iTunes Connet里面設(shè)置好。所以,現(xiàn)在你擁有一個(gè)樁應(yīng)用了,你現(xiàn)在只需要點(diǎn)擊“Manage In App Purchases”按鈕就行了,如下圖所示:

? ? 然后,點(diǎn)擊左上角的“Create New”,然后按照下圖所示,填寫相應(yīng)的信息:

? ? 讓我們來解釋下這幾個(gè)文本域的含義吧:

  • Reference Name:?這個(gè)名字就是在使用in-app purchase的時(shí)候會(huì)顯示在iTunes Connect里面。這個(gè)名字你可以隨便取,因?yàn)樵谀愕某绦蚶锩媸强床坏剿巍?/li>
  • Product ID:?在蘋果的開發(fā)文檔里面,這個(gè)也叫做“product identifier”,這是一個(gè)唯一的字符串,用來標(biāo)識(shí)你的in-app purchase。通常的做法是,使用你的bundle id,然后在最后加一個(gè)唯一的字符串。
  • Type:?你可以選擇non-consumable(購買一次,永久使用),comsumable(購買一次,使用一次),或者subscription(自動(dòng)續(xù)款)。本教程中,我們采用non-consumables。
  • Cleared for Sale:?手續(xù)已經(jīng)齊全,可以出售。如果該復(fù)選框未選中,in app purchase將不管用。
  • Price Tier:?設(shè)置程序內(nèi)置購買的價(jià)錢。

??? 在你完成上面的設(shè)置以后,往下滾動(dòng)鼠標(biāo),然后在Display Detail section部分添加一個(gè)English entry,如下圖所示:

??? 當(dāng)你的程序的內(nèi)置購買功能弄好之后,你查詢App Store的時(shí)候會(huì)返回你剛剛設(shè)置的信息。

??? 你可能會(huì)奇怪,為什么我們要設(shè)置剛剛這一步(畢竟,你還是可以直接硬編碼在你的程序之中啊!)好吧,很明顯Apple想知道你定的價(jià)錢嘛。同時(shí),在App Store里面會(huì)根據(jù)你填寫的這些東西來顯示一些信息,比如,內(nèi)置付費(fèi)應(yīng)用排行榜。最后,如果你這一步設(shè)置了,你之后會(huì)變得很輕松。因?yàn)?#xff0c;它讓你不用硬編碼這些信息在你的代碼之中。而且可以讓你動(dòng)態(tài)改變是允許內(nèi)置購買還是禁止內(nèi)置購買。

??? 一旦你完成之后,保存entry,然后創(chuàng)建更多的實(shí)體(entry),和下面的截圖效果類似。不要擔(dān)心描述信息,我們并不會(huì)在本教程中使用它們。

??? 你可能會(huì)注意到,這個(gè)過程要花費(fèi)您不少時(shí)間,我能夠相像,當(dāng)你的程序有很多內(nèi)置購買功能的時(shí)候,這個(gè)創(chuàng)建過程會(huì)有多么的煩人!幸運(yùn)的是,本教程我們體會(huì)不到,但是,如果你的教程真的遇到了這種情況,呵呵,可以留言給我抱怨一下吧!:)

Retrieving Product List(提取產(chǎn)品列表)

??? 在你能讓用戶從你的程序里面購買任何東西之前,你必須向iTunes Connect發(fā)送一個(gè)查詢請(qǐng)求來從服務(wù)器上提取所有可用的產(chǎn)品列表。

??? 我們可以直接在view controller里面添加代碼來實(shí)現(xiàn)之,但是那樣擴(kuò)展性太不好了,不利于重用。所以,我們將創(chuàng)建一個(gè)輔助類來管理所有與in-app purchase相關(guān)的內(nèi)容,然后你就可以在你的其它程序里面重用了。

??? 在從服務(wù)器上獲得產(chǎn)品列表的同時(shí),這個(gè)輔助類還會(huì)記錄哪些產(chǎn)品被購買了,哪些還沒有。它會(huì)為每一個(gè)已經(jīng)購買過的產(chǎn)品創(chuàng)建一個(gè)identifier,然后把它存到NSUserDefaults里面去。

??? 好了,讓我們動(dòng)手實(shí)驗(yàn)一下吧!打開XCode,然后選擇File\New Project,再選擇?iOS\Application\Navigation-based Application,點(diǎn)擊Choose。把工程命名為InAppRage,然后點(diǎn)擊Save。

??? 接下來,創(chuàng)建一個(gè)新的類來管理內(nèi)置付費(fèi)代碼,命名為IAPHelper。首先,點(diǎn)擊Classes分組,選擇File\New File,然后是iOS\Cocoa Touch Class\Objective-C class,確保Subclass of NSObject被選中,然后點(diǎn)擊Next。把這個(gè)文件命名為IAPHelper.m,通過確保“Also create IAPHelper.h” 被選中,然后點(diǎn)擊Finish。

??? 我們首先往IAPHelper.m里面添加一個(gè)方法來從iTunes Connect里面提取產(chǎn)品列表,代碼如下:

- (void)requestProducts {self.request = [[[SKProductsRequest alloc] initWithProductIdentifiers:_productIdentifiers] autorelease];_request.delegate = self;[_request start];}

??? 這個(gè)方法假設(shè)我們已經(jīng)定義了一個(gè)實(shí)例變量,叫做?_productIdentifiers ,它包含了一串產(chǎn)品標(biāo)識(shí)符,之后用來從iTunes Connect里面查詢產(chǎn)品滴。(比如com.raywenderlich.inapprage.drummerrage)

??? 它然后創(chuàng)建了一個(gè)SKProductsRequest實(shí)例,那是蘋果公司寫的一個(gè)類,它里面包含從iTunes Connect里面提取信息的代碼。使用此類灰常easy,你只需要給它一個(gè)delegate(它必須符合SKProductsRequestDelegate 協(xié)議),然后就可以調(diào)用start方法了。

??? 我們?cè)O(shè)置IAPHelper類本身作為delegate,那就意味著此類會(huì)收到一個(gè)回調(diào)函數(shù),此函數(shù)(productsRequest:didReceiveResponse)會(huì)返回產(chǎn)品列表。

Update:?Jerry 在論壇里面指出,SKProductsRequestDelegate 協(xié)議是從SKRequestDelegate派生而來滴,而SKRequestDelegate協(xié)議有一個(gè)方法,叫做?request:didFailWithError:。此方法會(huì)在失敗的時(shí)候調(diào)用,如果你喜歡的話,你可以使用此方法來代碼后面的timeout方法。感謝Jerry!

??? 好吧,接下來讓我們來實(shí)現(xiàn)productsRequest:didReceiveResponse 方法吧,具體如下所示:

- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {NSLog(@"Received products results..."); self.products = response.products;self.request = nil; [[NSNotificationCenter defaultCenter] postNotificationName:kProductsLoadedNotification object:_products]; }

??? 這個(gè)非常簡單。它首先保存返回的產(chǎn)品列表(是一個(gè)SKProducts的數(shù)組),然后把request設(shè)置為nil(為了釋放內(nèi)存),然后發(fā)出一個(gè)通知,任何偵聽這個(gè)通知的對(duì)象都會(huì)收到這個(gè)消息。

??? 接下來添加初始化代碼:

- (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 = purchasedProducts;}return self; }

??? 這個(gè)初始化代碼將檢測(cè)哪些產(chǎn)品已經(jīng)被購買,哪些還沒有。通過查詢NSUserDefaults可以知道,然后再建立一個(gè)適當(dāng)?shù)臄?shù)據(jù)結(jié)構(gòu)。

??? 好了,現(xiàn)在,我們已經(jīng)見過最重要的代碼了。接下來,我們?cè)陬^文件中添加一些聲明。首先,打開?IAPHelper.h,并作如下修改:

#import <Foundation/Foundation.h> #import "StoreKit/StoreKit.h"#define kProductsLoadedNotification @"ProductsLoaded"@interface IAPHelper : NSObject <SKProductsRequestDelegate> {NSSet * _productIdentifiers; NSArray * _products;NSMutableSet * _purchasedProducts;SKProductsRequest * _request; }@property (retain) NSSet *productIdentifiers; @property (retain) NSArray * products; @property (retain) NSMutableSet *purchasedProducts; @property (retain) SKProductsRequest *request;- (void)requestProducts; - (id)initWithProductIdentifiers:(NSSet *)productIdentifiers;@end

?? 這個(gè)簡單地導(dǎo)入StoreKit 頭文件,然后定義一些實(shí)例變量、函數(shù)和通知的名字。

???接下來,在IAPHelper.m里面添加synthesize 代碼,以后內(nèi)存釋放代碼,如下所示:

// Under @implementation @synthesize productIdentifiers = _productIdentifiers; @synthesize products = _products; @synthesize purchasedProducts = _purchasedProducts; @synthesize request = _request;// In dealloc - (void)dealloc {[_productIdentifiers release];_productIdentifiers = nil;[_products release];_products = nil;[_purchasedProducts release];_purchasedProducts = nil;[_request release];_request = nil;[super dealloc]; }

??? 最后一步,你需要添加StoreKit框架。右鍵點(diǎn)擊Frameworks文件夾,然后點(diǎn)Add\Existing Frameworks ,然后選擇?StoreKit.framework。然后選擇Build\Build 編譯一下,編譯完之后,你的代碼應(yīng)該是沒有錯(cuò)誤的。(此方法在Xcode4.0以上不適用。4.0需要點(diǎn)擊工程文件名,然后右鍵target,然后在build phase里面添加框架)

Subclassing for Your App

??? 這里將創(chuàng)建一個(gè)IAPHelper類,這樣以后你在你的程序里面只需要繼承一下它,然后指定你的產(chǎn)品標(biāo)識(shí)符(product identifier)就可以啦。許多人給我提建議,說可以從WEB服務(wù)器上把產(chǎn)品標(biāo)識(shí)符以及其它相關(guān)信息全部弄下來,然后,當(dāng)你的應(yīng)用程序需要更新的時(shí)候,你就可以動(dòng)態(tài)添加新的in-app purchase了。

??? 這個(gè)提議非常好,但是,為了保持本教程的簡單性,我這里就采用了硬編碼的方式。

??? 右鍵選中Classes 分組,然后選擇File\New File,再選擇?iOS\Cocoa Touch Class\Objective-C class,確保Subclass of NSObject 被復(fù)選中,然后點(diǎn)擊Next。把這個(gè)文件命名為InAppRageIAPHelper.M,同時(shí)確保?“Also create InAppRageIAPHelper.h” 被復(fù)選中,然后點(diǎn)擊Finish。

?? 然后,把InAppRageIAPHelper.h 替換成下列代碼:

#import <Foundation/Foundation.h> #import "IAPHelper.h"@interface InAppRageIAPHelper : IAPHelper {}+ (InAppRageIAPHelper *) sharedHelper;@end

??? 這里把InAppRageIAPHelper類定義為IAPHelper類的子類,然后創(chuàng)建了一個(gè)靜態(tài)方法用來創(chuàng)建些幫助類的單例。

??? 接下來,把InAppRageIAPHelper.m替換成下面的代碼:

#import "InAppRageIAPHelper.h"@implementation InAppRageIAPHelperstatic InAppRageIAPHelper * _sharedHelper;+ (InAppRageIAPHelper *) sharedHelper {if (_sharedHelper != nil) {return _sharedHelper;}_sharedHelper = [[InAppRageIAPHelper alloc] init];return _sharedHelper;}- (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",nil];if ((self = [super initWithProductIdentifiers:productIdentifiers])) { }return self;}@end

??? 第一個(gè)sharedHelper方法是為了使InAppRageIAPHelper類變成一個(gè)單例類。注意,這種實(shí)現(xiàn)單例的方式并不是線程安全的,但是,對(duì)于本應(yīng)用來說完全足夠了,因?yàn)槲覀冎挥幸粋€(gè)主線程。

??? 接下來,我們硬編碼了一組產(chǎn)品標(biāo)識(shí)符的字符串?dāng)?shù)組,然后調(diào)用了基類的初始化方式。注意,我們?cè)谶@里的字符串名字必須保持和之前在iTunes Connect里面定義的名稱要一致。

??? 然后選擇Build\Build,保證沒有錯(cuò)誤再繼續(xù)哦。

添加幫助類代碼

??? 我們差不多完成了我們的幫助類了,但是,在調(diào)用這個(gè)類的時(shí)候會(huì)有兩個(gè)問題,我們接下來會(huì)討論解決辦法。

?? 第一個(gè)問題就是,這段代碼在沒有網(wǎng)絡(luò)連接的情況下是跑不起來滴。所以,我們?cè)谑褂弥?#xff0c;需要檢查是否有網(wǎng)絡(luò)。

???第二個(gè)問題,加載產(chǎn)品列表可以會(huì)耗費(fèi)一定的時(shí)間,所以,我們需要讓用戶知道我們?cè)诩虞d產(chǎn)品列表,而不是神馬都不顯示,那樣用戶會(huì)以為程序出問題了。我們只需要簡單的顯示一個(gè)activity indicator就可以啦。

?? 關(guān)于這兩個(gè)問題,我們都可以自己動(dòng)手來解決,但是,你為什么要重新發(fā)明輪子呢?(譯者:工作中,遇到任何“問題”的時(shí)候,這里的“問題”,我指的是有點(diǎn)難度的問題,或者自己一時(shí)想不清楚的問題,不要急著動(dòng)手編碼,你還沒想清楚呢!瞎編碼什么呀!不妨google一下,你會(huì)有意想不到的收獲。當(dāng)然,這里我并不是鼓勵(lì)大家不動(dòng)腦筋,而是,有時(shí)候,我們程序員需要一種“懶”。)蘋果已經(jīng)為我們寫好了一個(gè)檢測(cè)網(wǎng)絡(luò)是否可用的代碼,叫做?Reachability class,而?Matej Bukovinski則為我們寫了一個(gè)非常好用的指示器類?reusable progress indicator。你完全可以重用他們,而不要去重新發(fā)明輪子。

?? 所以,盡管去下載這些源代碼吧,當(dāng)然,你也可以直接從本教程的源碼中獲得上面提到的源碼。

???一旦你下載完了這些文件,直接把MBProgressHUD.h/m?和 Reachability.h/m拖到你的項(xiàng)目的Classes分組下面就可以啦。同時(shí)確保?“Copy items into destination group’s folder”被復(fù)選中,然后點(diǎn)擊Add。

?? 最后一步----你需要添加SystemConfiguration 類庫,因?yàn)镽eachability這個(gè)類依賴此類庫。右鍵點(diǎn)擊Frameworks文件夾,然后選擇Add\Existing Frameworks,然后再從列表中選擇SystemConfiguration.framework就可以啦。然后,編譯,確保沒有錯(cuò)誤后再繼續(xù)。

?? 好了,現(xiàn)在我們得到所有的產(chǎn)品列表和價(jià)格了,現(xiàn)在讓我們把它們整合起來。

顯示產(chǎn)品列表

??? 打開RootViewController.h ,然后做如下修改:

// Before @interface #import "MBProgressHUD.h"// Inside @interface MBProgressHUD *_hud;// After @interface @property (retain) MBProgressHUD *hud;

?? 上面只是簡單的聲明一些實(shí)例變量和定義MBProgressHUD 屬性。

?? 然后,打開RootViewController.m,并做如下修改:

// At top of file #import "InAppRageIAPHelper.h" #import "Reachability.h"// Under @implementation @synthesize hud = _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!"); } else { 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里面。它首先設(shè)置table view默認(rèn)情況下隱藏(table view在產(chǎn)品列表加載完之后會(huì)再重新顯示滴)。然后,設(shè)置了一個(gè)通告,因?yàn)榇祟愋枰朗裁磿r(shí)候產(chǎn)品列表加載完了。

?? 然后再使用Reachability 來檢測(cè)網(wǎng)絡(luò)是否可用。如果可用的話,它就調(diào)用IAPHelper的requestProducts 方法來下載之前填好的產(chǎn)品列表。

?? 當(dāng)產(chǎn)品列表在加載過程中的時(shí)候,我們用MBProgressHUD 顯示一個(gè)“l(fā)oading”界面。同時(shí),我們還設(shè)置一個(gè)超時(shí)檢測(cè)函數(shù),當(dāng)30秒過后,如果還沒有加載完產(chǎn)品列表的話,我們就提示用戶錯(cuò)誤。

?? 所以,接下來,讓我們添加一些代碼來處理通告消息,和超時(shí)處理函數(shù)。

- (void)dismissHUD:(id)arg {[MBProgressHUD hideHUDForView:self.navigationController.view animated:YES];self.hud = nil;}- (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 = MBProgressHUDModeCustomView;[self performSelector:@selector(dismissHUD:) withObject:nil afterDelay:3.0];}

?? 第一個(gè)函數(shù)(dismissHUD)只是一個(gè)輔助函數(shù),用來隱藏加載面板的。

?? 第二個(gè)方法(productsLoaded)是在kProductsLoadedNotification 通告消息到達(dá)的時(shí)候被觸發(fā)的。它隱藏了加載面板,同時(shí)重新加載table view里面的東西,用來顯示down下來的產(chǎn)品列表滴。

?? 最后一個(gè)方法(timeout),更新HUD并顯示一個(gè)超時(shí)的消息,然后讓這個(gè)HUD過一段時(shí)間再消失。

? 最后,我們需要在?RootViewController.m里面再添加一些代碼來完成table view的動(dòng)作,代碼如下:

// 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.localizedTitle; cell.detailTextLabel.text = formattedString;if ([[InAppRageIAPHelper sharedHelper].purchasedProducts containsObject:product.productIdentifier]) {cell.accessoryType = UITableViewCellAccessoryCheckmark;cell.accessoryView = nil; } else { UIButton *buyButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];buyButton.frame = CGRectMake(0, 0, 72, 37);[buyButton setTitle:@"Buy" forState:UIControlStateNormal];buyButton.tag = indexPath.row;[buyButton addTarget:self action:@selector(buyButtonTapped:) forControlEvents:UIControlEventTouchUpInside];cell.accessoryType = UITableViewCellAccessoryNone;cell.accessoryView = buyButton; }// In viewDidUnload self.hud = nil;// In dealloc [_hud release]; _hud = nil;

?? 在這里,table view只是簡單的顯示IAPHelper單例里面的產(chǎn)品列表---這個(gè)列表我們是通過SKProductsRequest來獲取的。

?? products數(shù)組里面的對(duì)象都是SKProduct的實(shí)例。它們包含了你在iTunes Connect里面設(shè)置的信息,比如title,description,price,etc.本教程中,table view只是簡單的顯示價(jià)格和標(biāo)題。同時(shí),我們還添加了一個(gè)“購買”按鈕,現(xiàn)在這個(gè)“購買”還不起作用,因?yàn)槲覀冞€沒有為它編碼任何代碼。

?? 你現(xiàn)在差不多可以測(cè)試一下了,但是,還有最后一件步(而且是非常重要的一步!)。你需要設(shè)置bundle identifier。點(diǎn)擊你的InAppRage-Info.plist并修改Bundle identifier來匹配你的ios Developer Center里面的那個(gè),如下圖所示:

?? 好了,差不多了!編譯并運(yùn)行你的程序(你需要編譯到設(shè)備上面,模擬器上是不行的),然后你會(huì)看到一個(gè)loading indicator,之后,就會(huì)顯示一系列產(chǎn)品列表,如下圖所示:

給我錢看看

??? 這是篇超級(jí)無敵又臭又長的教程,而且最重要的部分還是沒有講到---如何處理支付,如何賺錢,接下來,馬上為您揭曉!

??? 做支付基本的幾個(gè)要領(lǐng)如下:

  • 你創(chuàng)建一個(gè)SKPayment對(duì)象,然后指定用戶想要購買的產(chǎn)品的標(biāo)識(shí)符。然后把它加到支付隊(duì)列(payment queue)里面去。
  • StoreKit將會(huì)提醒用戶“are you sure?”, 然后要求用戶輸入用戶名和密碼,然后支付,然后就會(huì)返回給你,支付成功還是失敗。你也可以處理這種情況:用戶已經(jīng)為此付過費(fèi)了,然后可以重新再下載,同時(shí)給出一個(gè)恰當(dāng)?shù)奶崾揪涂梢粤恕?/li>
  • 你設(shè)計(jì)一個(gè)特殊的對(duì)象來處理支付通告回調(diào)消息。這個(gè)對(duì)象需要處理支付內(nèi)容下載(在我們這個(gè)教程沒必要,因?yàn)槲覀兪怯簿幋a的),同時(shí)解瑣程序里面的相關(guān)內(nèi)容(我們可以通過使用NSUserDefaults類來處理,然后把值設(shè)置到purchasedProducts 里面就行啦)

?? 不要擔(dān)心---當(dāng)你看到代碼的時(shí)候,就會(huì)發(fā)現(xiàn),其實(shí)這個(gè)過程是很easy滴。再強(qiáng)調(diào)一次,我們?yōu)榱耸笽APHelper盡可能可以重用,我們將在?IAPHelper.h里面做如下修改:

// Add two new notifications #define kProductPurchasedNotification @"ProductPurchased" #define kProductPurchaseFailedNotification @"ProductPurchaseFailed"// Modify @interface to add the SKPaymentTransactionObserver protocol @interface IAPHelper : NSObject <SKProductsRequestDelegate, SKPaymentTransactionObserver> {// After @interface, add new method decl - (void)buyProductIdentifier:(NSString *)productIdentifier;

然后打開IAPHelper.m 文件并作如下修改:

- (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];break;case SKPaymentTransactionStateFailed:[self failedTransaction:transaction];break;case SKPaymentTransactionStateRestored:[self restoreTransaction:transaction];default:break;}} }- (void)buyProductIdentifier:(NSString *)productIdentifier {NSLog(@"Buying %@...", productIdentifier);SKPayment *payment = [SKPayment paymentWithProductIdentifier:productIdentifier];[[SKPaymentQueue defaultQueue] addPayment:payment];}

?? 啊!好多代碼啊,但是,其實(shí)都不難,我會(huì)一個(gè)個(gè)向大家解釋清楚。

?? 當(dāng)table view里面的buy按照被按下去的時(shí)候,它將會(huì)調(diào)用buyProductIdentifier函數(shù)。然后會(huì)創(chuàng)建一個(gè)新的SKPayment 對(duì)象,并且把這個(gè)對(duì)象加載到隊(duì)伍中去。我們將把此類當(dāng)作delegate來接收支持事務(wù)的更新消息,所以,當(dāng)支付完成 的時(shí)候或者失敗的時(shí)候,paymentQueue:updatedTransactions 這個(gè)函數(shù)將會(huì)被調(diào)用。

?? 如果支付成功了(或者取消了),那么provideContent 函數(shù)都會(huì)被調(diào)用。然后,重點(diǎn)來了---它會(huì)在NSUserDefaults里面設(shè)置一個(gè)標(biāo)記,然后把這個(gè)事務(wù)加到隊(duì)列中去。剩下的代碼就是用來檢測(cè)用戶是否獲得了相應(yīng)的內(nèi)容了。

?? 假如支付失敗了,也會(huì)相應(yīng)的有一個(gè)失敗的通告消息會(huì)到達(dá)的。

?? 注意,這里recordTransaction 并沒有任何實(shí)現(xiàn)。如果你可以的話,你可以去實(shí)現(xiàn)此方法,然后給WEB服務(wù)器發(fā)送一個(gè)消息,讓服務(wù)器來做一些記錄。個(gè)人來講,我覺得實(shí)現(xiàn)這個(gè)方法沒什么實(shí)際的用處。

? 同時(shí),也請(qǐng)注意,這種方法保存支付信息是非常容易被黑的(你需要加密保存),但是,我并不是很關(guān)心這個(gè)東東,因?yàn)?#xff0c;任何想要破解我的程序的人,他們肯定是不愿意付錢的,in-app對(duì)他們來說沒什么意義。

? 在我們使用這些代碼之前,我們還需要在App Delegate里面添加一些東西,這樣的話,當(dāng)產(chǎn)品支付事務(wù)完成的時(shí)候,IAPHelper類就會(huì)得到相應(yīng)的通千。所以,打開InAppRageAppDelegate.m并作如下修改:

// At top of file #import "InAppRageIAPHelper.h"// In application:didFinishLaunchingWithOptions [[SKPaymentQueue defaultQueue] addTransactionObserver:[InAppRageIAPHelper sharedHelper]];

??? 如果沒有這句代碼的話,那么?paymentQueue:updatedTransactions 這個(gè)函數(shù)將不會(huì)被調(diào)用,所以,造成記得要加上去!

?? 最后一步,讓我們回到table view上面來。打開RootViewController.m ,然后作如下修改:

// Add new method - (IBAction)buyButtonTapped:(id)sender {UIButton *buyButton = (UIButton *)sender; 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.object;NSLog(@"Purchased: %@", productIdentifier);[self.tableView reloadData]; }- (void)productPurchaseFailed:(NSNotification *)notification {[NSObject cancelPreviousPerformRequestsWithTarget:self];[MBProgressHUD hideHUDForView:self.navigationController.view animated:YES];SKPaymentTransaction * transaction = (SKPaymentTransaction *) notification.object; 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];}}

?? 你就要成功啦,再堅(jiān)持一小會(huì)兒!

In App Purchases, Accounts, and the Sandbox

?? 當(dāng)你在XCODE里面運(yùn)行你的程序的時(shí)候,你并不是在運(yùn)行真正的In-App Purchase服務(wù)器---你實(shí)際上是跑在沙盒服務(wù)器上面。

?? 這意味著,你可以購買任何東西而不用擔(dān)心會(huì)被扣錢。但是,你需要先創(chuàng)建一個(gè)測(cè)試帳號(hào),同時(shí)確保你的設(shè)備登出了apple store,這樣的話,你就可以看到這個(gè)處理過程了。

? 要?jiǎng)?chuàng)建測(cè)試帳號(hào),你可以先登際?iTunes Connect ,然后點(diǎn)擊“Manage Users”.點(diǎn)擊“Test User”, 然后就可以創(chuàng)建一個(gè)測(cè)試帳號(hào)了。

? 然后,打開你的iphone,確保你退出當(dāng)前的帳號(hào)了。你可以通過打開Settings程序,然后點(diǎn)擊"Store",然后點(diǎn)"Sign out”。(大家千萬注意啊!)

? 最后,運(yùn)行你的程序吧。然后點(diǎn)擊購買,輸入測(cè)試帳號(hào)信息,如果一切順利的話,你會(huì)得到如下截屏的輸出!

?? 但是,等一分鐘---哪有里漫畫啊!!!!你沒值錢當(dāng)然就沒有啦。。。

?? 好吧,這篇教程已經(jīng)足夠長了,用戶購買以后可以得到漫畫的任務(wù)就交由讀者來完成吧。

???這里有相應(yīng)的漫畫圖片資源,你可以拿去用用。

何去何從?

?? 本項(xiàng)目完整源代碼:sample project

?? 如果你想學(xué)習(xí)更多關(guān)于程序內(nèi)置購買的內(nèi)容,請(qǐng)參考蘋果的文檔?In-App Purchase Programming Guide。

?? 同時(shí),也請(qǐng)留意Noel Llopis 寫的一些非常不錯(cuò)的文章。

?

?? 要論壇交流,請(qǐng)點(diǎn)擊傳送門!

?

?


總結(jié)

以上是生活随笔為你收集整理的IAP-应用内购买流程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。