1. 程式人生 > >In App Purchase總結

In App Purchase總結

In App Purchase屬於iPhone SDK3.0的新特性,用於在應用程式中購買付費道具,增加新功能,訂閱雜誌。是應用程式除了植入廣告外的另一種取得收益的方式。 雖然Apple的官方文件已經對In App Purhcase這一特性做了比較詳盡的解釋,但就某些細節方面還是需要程式設計人員進行嘗試和推敲,今天我就對之前專案中實現In App Purchase功能做下簡單的總結。

一.In App Purchasep註冊流程

1.登陸Apple開發者帳號

2.建立一個新的Apple ID或是選用一個已存在的Apple ID,確定Apple ID的In App Purchase功能可使用:

3.建立develop(用於沙盒測試)和distribution(用於釋出)的profile,建立時選擇剛才建立的Apple ID。

4.登陸itunes connect,建立一個新的App或選用一個已存在的App,App的Bundle ID要使用步驟2中選用的App Id(注:Bundle ID只能在App建立時指定,且App建立後不能不能被修改);

5. 進入App Information頁面,點選“Manage In-App Purchases”按鈕,進入 In-App Purchases管理頁面:

5.點選“Create New”按鈕開始建立一個新的Iap產品,目前蘋果提供的iap產品有5類:

  • Consumable:消耗類,可重複購買,每次使用都需要收費
  • Non-Consumable:非消耗類,購買一次,永久使用,重複購買不收費;
  • Auto-Renewable Subscriptions:自動更新訂閱,購買後有使用期限,App Store會自動在使用期限到了後提示使用者重新購買,我們的程式需要有一套機制驗證subscription的有效性來決定使用者是否能繼續使用;
  • Free Subscription:
  • Non-Renewing Subscription:

6. 以Non-Consumable為例,建立一個iap產品:

在iap建立頁面有若干配置需要我們填寫:

  • Reference Name:在使用in-app purchase的時候會顯示在iTunes Connect和銷售報表裡面,而不會顯示在程式裡;
  • Product ID:也叫做“product identifier”,這是一個唯一的字串,用來標識當前的in-app purchase產品,通常的做法是,使用應用的bundle id,然後在最後加一個唯一的字串;
  • Add Language:使用者購買時彈出視窗顯示的內容,可以設定多個國家的語言實現本地化顯示;
  • Cleared for Sale:當應用程式上架的時候,程式內建購買功能購買記錄清空。
  • Price Tier:設定程式內建購買的價錢。

7. 建立了幾個Iap產品:

二.在程式中增加 In-App Purchases功能ECPurchase

通過上面的配置,我們已經完成了In-App Purchases服務端的的註冊,在程式中,我們可以使用IOS內建庫StoreKit.framework裡提供的Api實現In-App Purchases產品的購買功能。但如果你不想根據文件再自己寫purchase功能,那麼有一個第三方的庫ECPurchase會適合你。 ECPurchase庫封裝了purchase的內在邏輯,並且提供了幾種驗證方式(用於防止iap破解),呼叫簡單方便。ECPurchase庫可在文章後面我提供的例子裡獲得。ECPurchase提供了下面的介面需要開發者自己完成:

1.在App Delegate中新增Observer
  1. [[ECPurchase shared] addTransactionObserver];  
2.設定ECPurchase的product delegate(產品列表代理),transaction delegate(購買結果代理),驗證方式
  1. [[ECPurchase shared] setProductDelegate:self];   
  2. [[ECPurchase shared] setTransactionDelegate:self];   
  3. [[ECPurchase shared] setVerifyRecepitMode:ECVerifyRecepitModeiPhone];  
3.請求商品列表
  1. [[ECPurchase shared] requestProductData:identifiers];  
  實現代理函式繪製UI
  1. [[ECPurchase shared] requestProductData:identifiers];  
4.購買商品
  1. [[ECPurchase shared] addPayment:proIdentifier];  
5.確認結果 如果不需要收據認證實現代理函式:
  1. -(void)didFailedTransaction:(NSString *)proIdentifier;   
  2. -(void)didRestoreTransaction:(NSString *)proIdentifier;   
  3. -(void)didCompleteTransaction:(NSString *)proIdentifier;  
否則實現代理函式:
  1. -(void)didCompleteTransactionAndVerifySucceed:(NSString *)proIdentifier;   
  2. -(void)didCompleteTransactionAndVerifyFailed:(NSString *)proIdentifier withError:(NSString *)error;  

三.使用ECPurchase實現IAP的例子

下面我會以一個簡單的例子演示程式端如何使用ECPurchase完成IAP功能,該例子實現了以下功能:從App Store上獲取In App Purchase產品列表,並顯示在一個表格(UITableView)上,點選表格上某一個產品名右邊的“buy”按鈕能購買對應的產品。這個例子主要包含兩個類:ProductsViewController 和IAPHandler。

ProductsViewController為UI類,用於顯示Iap產品列表和彈出購買結果;

IAPHandler實現ECPurchase與的兩個代理介面:ECPurchaseTransactionDelegate和ECPurchaseProductDelegate,ECPurchase的執行結果會被IAPHandler接收處理。 IAPHandler被設計為單例類,目的是防止在iap請求過程中IAPHandler的例項被銷燬,導致iap購買結果返回後找不到處理的例項物件。

我們可以在IAPHandler里加入自己的業務邏輯,如購買某個金幣產品成功後給玩家帳號增加對應的金幣數、購買解鎖道具後給遊戲解鎖等邏輯等。IAPHandler處理了玩家的購買請求後會使用訊息中心(NSNotificationCenter)對外發送一條訊息,我們可以在相關的視圖裡接收該訊息,更新檢視。

首先我們需要建立一個專案,主要專案裡設定的Bundle identifier要與itunes connect裡指定的一樣;

匯入以下庫:
  • StoreKit.framework
  • CFNetwork.framework
  • SystemConfiguration.framework
  • libz.1.2.5.dylib
把ECPurchase庫拷貝到專案裡(可從下載的例子裡獲得)

編寫下面的程式碼: ProductsViewController.h
  1. //
  2. //  ProductsViewController.h
  3. //  IAPDemo
  4. //
  5. //  Created by luoyl on 12-3-24.
  6. //  Copyright (c) 2012年 http://luoyl.info. All rights reserved.
  7. //
  8. #import <UIKit/UIKit.h>
  9. #import "ECPurchase.h"
  10. @interface ProductsViewController : UITableViewController   
  11. {   
  12.     NSArray *products_;   
  13. }   
  14. @end  
ProductsViewController.m
  1. //
  2. //  ProductsViewController.m
  3. //  IAPDemo
  4. //
  5. //  Created by luoyl on 12-3-24.
  6. //  Copyright (c) 2012年 http://luoyl.info. All rights reserved.
  7. //
  8. #import "ProductsViewController.h"
  9. #import "IAPHandler.h"
  10. @interface ProductsViewController ()   
  11. - (void)registIapObservers;   
  12. @end   
  13. @implementation ProductsViewController   
  14. - (void)viewDidLoad   
  15. {   
  16.     [super viewDidLoad];     
  17.     self.title = @"IAP Demo";   
  18.     [self registIapObservers];   
  19.     [IAPHandler initECPurchaseWithHandler];   
  20. //iap產品編號集合,這裡你需要替換為你自己的iap列表
  21.     NSArray *productIds = [NSArray arrayWithObjects:@"info.luoyl.iap.item1",   
  22.                                                     @"info.luoyl.iap.item2",   
  23.                                                     @"info.luoyl.iap.item3", nil];   
  24. //從AppStore上獲取產品資訊
  25.     [[ECPurchase shared]requestProductData:productIds];   
  26. }   
  27. - (void)viewWillUnload   
  28. {   
  29.     [[NSNotificationCenter defaultCenter]removeObserver:self];   
  30. }   
  31. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section   
  32. {   
  33. return products_ ? [products_ count] : 0;   
  34. }   
  35. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath   
  36. {   
  37. static NSString *CellIdentifier = @"Cell";   
  38.     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];   
  39. if (cell == nil) {   
  40.         cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];   
  41.         cell.selectionStyle = UITableViewCellSelectionStyleNone;   
  42.         cell.accessoryType =  UITableViewCellAccessoryNone;   
  43.     } else {   
  44. for (UIView *view in [cell.contentView subviews]) {   
  45.             [view removeFromSuperview];   
  46.         }   
  47.     }   
  48.     SKProduct *product = [products_ objectAtIndex:indexPath.row];   
  49. //產品名稱
  50.     UILabel *localizedTitle = [[UILabel alloc]initWithFrame:CGRectMake(10, 10, 130, 20)];   
  51.     localizedTitle.text = product.localizedTitle;   
  52. //產品價格
  53.     UILabel *localizedPrice = [[UILabel alloc]initWithFrame:CGRectMake(150, 10, 100, 20)];   
  54.     localizedPrice.text = product.localizedPrice;   
  55. //購買按鈕
  56.     UIButton *buyButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];   
  57.     buyButton.tag = indexPath.row;   
  58.     buyButton.frame = CGRectMake(250, 10, 50, 20);   
  59.     [buyButton setTitle:@"Buy" forState:UIControlStateNormal];   
  60.     [buyButton addTarget:self action:@selector(buy:) forControlEvents:UIControlEventTouchUpInside];   
  61.     [cell.contentView addSubview:localizedTitle];   
  62.     [cell.contentView addSubview:localizedPrice];   
  63.     [cell.contentView addSubview:buyButton];   
  64. return cell;   
  65. }   
  66. - (void)getedProds:(NSNotification*)notification   
  67. {   
  68.     NSLog(@"通過NSNotificationCenter收到資訊:%@,", [notification object]);   
  69. }   
  70. - (void)buy:(UIButton*)sender   
  71. {   
  72.     SKProduct *product = [products_ objectAtIndex:sender.tag];   
  73.     NSLog(@"購買商品:%@", product.productIdentifier);   
  74.     [[ECPurchase shared]addPaymentWithProduct:product];   
  75. }   
  76. - (void)viewDidUnload   
  77. {   
  78.     [super viewDidUnload];   
  79. }   
  80. - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation   
  81. {   
  82. return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);   
  83. }   
  84. //接收從app store抓取回來的產品,顯示在表格上
  85. -(void) receivedProducts:(NSNotification*)notification   
  86. {   
  87. if (products_) {   
  88.         [products_ release];   
  89.         products_ = nil;   
  90.     }   
  91.     products_ = [[NSArray alloc]initWithArray:[notification object]];   
  92.     [self.tableView reloadData];    
  93. }   
  94. // 註冊IapHander的監聽器,並不是所有監聽器都需要註冊,
  95. // 這裡可以根據業務需求和收據認證模式有選擇的註冊需要
  96. - (void)registIapObservers   
  97. {   
  98.     [[NSNotificationCenter defaultCenter]addObserver:self    
  99.                                             selector:@selector(receivedProducts:)    
  100.                                                 name:IAPDidReceivedProducts    
  101.                                               object:nil];   
  102.     [[NSNotificationCenter defaultCenter]addObserver:self   
  103.                                             selector:@selector(failedTransaction:)   
  104.                                                 name:IAPDidFailedTransaction    
  105.                                               object:nil];   
  106.     [[NSNotificationCenter defaultCenter]addObserver:self   
  107.                                             selector:@selector(restoreTransaction:)    
  108.                                                 name:IAPDidRestoreTransaction    
  109.                                               object:nil];   
  110.     [[NSNotificationCenter defaultCenter]addObserver:self    
  111.                                             selector:@selector(completeTransaction:)   
  112.                                                 name:IAPDidCompleteTransaction object:nil];   
  113.     [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(completeTransactionAndVerifySucceed:)    
  114.                                                 name:IAPDidCompleteTransactionAndVerifySucceed    
  115.                                               object:nil];   
  116.     [[NSNotificationCenter defaultCenter]addObserver:self    
  117.                                             selector:@selector(completeTransactionAndVerifyFailed:)    
  118.                                                 name:IAPDidCompleteTransactionAndVerifyFailed    
  119.                                               object:nil];   
  120. }   
  121. -(void)showAlertWithMsg:(NSString*)message   
  122. {   
  123.     UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"IAP反饋"
  124.                                                    message:message    
  125.                                                   delegate:nil    
  126.                                          cancelButtonTitle:@"OK"
  127.                                          otherButtonTitles:nil, nil];   
  128.     [alert show];   
  129.     [alert release];   
  130. }   
  131. -(void) failedTransaction:(NSNotification*)notification   
  132. {   
  133.     [self showAlertWithMsg:[NSString stringWithFormat:@"交易取消(%@)",[notification name]]];    
  134. }   
  135. -(void) restoreTransaction:(NSNotification*)notification   
  136. {   
  137.     [self showAlertWithMsg:[NSString stringWithFormat:@"交易恢復(%@)",[notification name]]];    
  138. }   
  139. -(void )completeTransaction:(NSNotification*)notification   
  140. {   
  141.     [self showAlertWithMsg:[NSString stringWithFormat:@"交易成功(%@)",[notification name]]];    
  142. }   
  143. -(void) completeTransactionAndVerifySucceed:(NSNotification*)notification   
  144. {   
  145.     NSString *proIdentifier = [notification object];   
  146.     [self showAlertWithMsg:[NSString stringWithFormat:@"交易成功,產品編號:%@",proIdentifier]];   
  147. }   
  148. -(void) completeTransactionAndVerifyFailed:(NSNotification*)notification   
  149. {   
  150.     NSString *proIdentifier = [notification object];   
  151.     [self showAlertWithMsg:[NSString stringWithFormat:@"產品%@交易失敗",proIdentifier]];   
  152. }   
  153. @end   
IAPHandler.h
  1. //
  2. //  IAPHandler.h
  3. //  IAPDemo
  4. //
  5. //  Created by luoyl on 12-3-24.
  6. //  Copyright (c) 2012年 http://luoyl.info. All rights reserved.
  7. //
  8. #define IAPDidReceivedProducts                      @"IAPDidReceivedProducts"
  9. #define IAPDidFailedTransaction                     @"IAPDidFailedTransaction"
  10. #define IAPDidRestoreTransaction                    @"IAPDidRestoreTransaction"
  11. #define IAPDidCompleteTransaction                   @"IAPDidCompleteTransaction"
  12. #define IAPDidCompleteTransactionAndVerifySucceed   @"IAPDidCompleteTransactionAndVerifySucceed"
  13. #define IAPDidCompleteTransactionAndVerifyFailed    @"IAPDidCompleteTransactionAndVerifyFailed"
  14. #import <Foundation/Foundation.h>
  15. #import "ECPurchase.h"
  16. @interface IAPHandler : NSObject<ECPurchaseTransactionDelegate, ECPurchaseProductDelegate>   
  17. + (void)initECPurchaseWithHandler;   
  18. @end  
IAPHandler.m
  1. //
  2. //  IAPHandler.m
  3. //  IAPDemo
  4. //
  5. //  Created by luoyl on 12-3-24.
  6. //  Copyright (c) 2012年 http://luoyl.info. All rights reserved.
  7. //
  8. #import "IAPHandler.h"
  9. @interface IAPHandler()   
  10. -(void)afterProductBuyed:(NSString*)proIdentifier;   
  11. @end   
  12. @implementation IAPHandler   
  13. static IAPHandler *DefaultHandle_ = nil;   
  14. + (void)initECPurchaseWithHandler   
  15. {   
  16.     @synchronized(self)     {   
  17. if (!DefaultHandle_) {   
  18.             DefaultHandle_ = [[IAPHandler alloc]init];   
  19.         }   
  20.     }       
  21. }   
  22. - (id)init   
  23. {   
  24. if (self = [super init]) {   
  25.         [[ECPurchase shared] addTransactionObserver];   
  26.         [[ECPurchase shared] setProductDelegate:self];   
  27.         [[ECPurchase shared] setTransactionDelegate:self];   
  28.         [[ECPurchase shared] setVerifyRecepitMode:ECVerifyRecepitModeiPhone];   
  29.     }   
  30. return self;   
  31. }   
  32. #pragma mark - ECPurchaseProductDelegate
  33. //從App Store請求產品列表後返回的產品列表
  34. - (void)didReceivedProducts:(NSArray *)products   
  35. {   
  36.     [[NSNotificationCenter defaultCenter] postNotificationName:IAPDidReceivedProducts object:products];   
  37. }   
  38. #pragma mark - ECPurchaseTransactionDelegate
  39. // 如果不需要收據認證, 則實現以下3個代理函式
  40. // 即 ECVerifyRecepitModeNone 模式下
  41. -(void)didFailedTransaction:(NSString *)proIdentifier   
  42. {   
  43. //   NSLog(@"交易取消");
  44.     [[NSNotificationCenter defaultCenter] postNotificationName:IAPDidFailedTransaction object:proIdentifier];   
  45. }   
  46. -(void)didRestoreTransaction:(NSString *)proIdentifier   
  47. {   
  48. //NSLog(@" 交易恢復 ");
  49.     [[NSNotificationCenter defaultCenter] postNotificationName:IAPDidRestoreTransaction object:proIdentifier];   
  50. }   
  51. -(void)didCompleteTransaction:(NSString *)proIdentifier   
  52. {   
  53. //  NSLog(@" 交易成功 ");
  54.     [self afterProductBuyed:proIdentifier];   
  55.     [[NSNotificationCenter defaultCenter] postNotificationName:IAPDidCompleteTransaction object:proIdentifier];   
  56. }   
  57. // 否則, 需要收據認證, 實現以下2個代理函式
  58. // ECVerifyRecepitModeiPhone 和 ECVerifyRecepitModeServer模式下
  59. -(void)didCompleteTransactionAndVerifySucceed:(NSString *)proIdentifier   
  60. {   
  61.     [self afterProductBuyed:proIdentifier];   
  62.     [[NSNotificationCenter defaultCenter] postNotificationName:IAPDidCompleteTransactionAndVerifySucceed object:proIdentifier];   
  63. }   
  64. -(void)didCompleteTransactionAndVerifyFailed:(NSString *)proIdentifier withError:(NSString *)error   
  65. {   
  66.     [[NSNotificationCenter defaultCenter] postNotificationName:IAPDidCompleteTransactionAndVerifyFailed object:proIdentifier];   
  67. }   
  68. //使用者購買某件商品成功後的處理邏輯
  69. -(void)afterProductBuyed:(NSString*)proIdentifier   
  70. {   
  71. //寫下你的邏輯, 加金幣 or 解鎖 or ...
  72. }   
  73. @end   

本文例子專案

DEMO