【iOS開發必收藏】詳解iOS應用程序內使用IAP/StoreKit付費、沙盒(SandBox)測試、建立測試帳號流程!


 Himi  原創, 歡迎轉載,轉載請在明顯處註明! 謝謝。
css

             原文地址:http://blog.csdn.net/xiaominghimi/article/details/6937097node


       終於在11月公司的遊戲即將上線了,那麼對於iOS遊戲來講當今都是內置道具收費屬於主流,那麼咱們的遊戲也是內置收費,因此Himi這裏分享給你們關於內置應用收費以及申請測試帳號進行測試購買的經驗;網絡

      在應用內嵌入付費代碼這一快Himi能夠直接將代碼分享給你們,因此咱們來講一些主要流程,畢竟沒有接觸過這一塊的童鞋確定至關頭疼 =。  =app

     OK,步入總體,若是你想在iOS裏內嵌收費,那麼分爲如下幾步:框架

            

             【提示:如下建立App部份內容,你不用非要等項目能打包了纔開始作,能夠隨時而且隨便的建立個測試項目便可,由於嵌入付費並不要求上傳App的ipa包的!!】          ide

 

     第一步:你須要在iTunesConnect中建立個新的App,而後爲這個App設置一些產品(付費道具)等;測試

     OK,這裏Himi稍微解釋下,iTunesConnect是蘋果提供的一個平臺,主要提供AP發佈和管理App的,最重要的功能是建立管理項目信息,項目付費產品(道具)管理、付費的測試帳號、提交App等等,這裏就簡單介紹這麼多,關於產品一詞在此咱們能夠理解成遊戲道具便可;在蘋果看來全部付費都屬於產品 =。 =千萬不要糾結字眼哦~網站

    OK,打開iTunesConnect網站:https://itunesconnect.apple.com/WebObjects/iTunesConnect.woa (注意:企業級的用戶必須使用公司主開發者帳號登錄纔可!)this

    成功登錄後的頁面以下:spa


              

              這裏大概說下重要的一些項:

             Contracts, Tax, and Banking   : 管理銀行帳號、聯繫人以及稅等等;這裏要根據提示完成對應的信息填寫!必定要詳細填寫喔~

             Manage Users :管理用戶的,好比主帳號以及測試付費的(測試App)帳號;

             Manage Your Applictions:管理應用程序的,你全部發布的應用和每一個應用的狀態都在這裏面;


     下面咱們新建一個App項目,你們放心,咱們這裏建立的是不會直接提交給App審覈的,因此放心建立,只要控制好App的狀態不要是待審覈狀態便可,不過即便你不當心將項目提交了,也沒事,直接更改App狀態便可了;

     選擇Manage Your Applictions選項,而後新建一個項目:【Add New App】,根據提示來填寫吧,這裏就不細緻說明了~

     建立好一個App以後,在點擊Manage Your Applictions後的界面應該以下:

     

    這裏你將看到本身建立的App,點擊你建立的App項目,這裏Himi建立的項目名字叫」ProjectForBuyTest「,點擊你的App進入以下界面:



    

  (注意:這裏的Bundle ID必定要跟你的項目中的info.plist中的Bundle ID保證一致!!!!)

     這裏能夠管理你的項目的信息、狀態、是否嵌入GameCenter等等選項,那麼本章咱們重點介紹如何使用IAp沙盒測試程序內付費,因此這裏咱們點擊右上角的」Manage In-App Purchases「選項進入建立產品(遊戲道具)界面以下:



      上圖中的下方看到Himi建立過的四個產品(道具)了,你能夠點擊」Create New「選項新建一個產品(付費道具),點擊新建以下界面:


  

   上圖中Himi沒有截圖出全部的選項,這裏大概介紹下,這個界面是選擇你的消費道具的種類,種類說明以下:

   類型選擇有四種選擇:

   1.Consumable(消耗品): 每次下載都須要付費;

   2.Non-consumable(非消耗品): 僅需付費一次;

   3.Auto-Renewable Subscriptions:自動訂閱;

   4.Free Subscription:免費訂閱

   最下方是你沙盒測試的截圖,暫且無論便可;

   這裏Himi選擇Consumable選項,好比不少遊戲都是購買金幣啦這樣子就能夠選擇這個;而後出現以下界面:



   Reference Name: 付費產品(道具的)參考名稱

   Product ID(產品ID): 你產品的惟一id。一般格式是 com.xx.yy,但它能夠是任何形式,不要求以程序的App ID做爲前綴。

   Add Language: 添加產品名稱與描述語言;

   Price Tier:選擇價格,這裏你選擇價格後,會出現如上圖最下方的價格對照表

   Screenshot(截屏): 展現你產品的截屏。(這個直接無視,測試App務必要管這個的)


  Product ID(產品ID)能夠建立多個,好比我想遊戲中分爲0.99$ 、1.99$等道具那就建立對應多個產品ID

  咱們填寫好了」Reference Name「與」Product ID「以及」Price Tier「後,點擊」Add Language「選項而後出現以下界面:

            


  上圖中的選項:

      Language:語言

      Displayed Name(顯示名稱): 用戶看到的產品名稱。

      Description(描述): 對產品進行描述。

  

  Ok,一路 Save保存回到」Manage In-App Purchases「界面中會看到咱們新建的產品(道具)以下:

 


  你們能夠看到新建的產品(道具)ID:這裏Himi建立的產品ID是com.himi.wahaha ,這裏要記住這個產品ID哦~

 

第二步:申請測試帳號,利用沙盒測試模擬AppStore購買道具流程!

  回到itunesconnect主頁中,選擇「Manage Users」而後選擇「Test User」,而後出現的界面以下圖:


    這裏Himi已經建立了兩個測試帳號了,點擊界面中的 「Add New User」進行建立便可;記住帳號和密碼哈,記不住就刪掉從新建 娃哈哈~(切記:不能用於真正的AppStore中使用此帳號,不只不能用,並且一旦AppStore發現後果你懂得~) 


   第三步:在項目中申請購買產品代碼以及監聽;

         這裏關於購買的代碼部分呢,我都有備註的,Himi這裏就不詳細講解了,Himi只是在代碼後介紹幾點值得注意的地方:

 

 這裏Himi是新建的一個Cocos2d的項目,而後給出HelloWorldLayer.h以及HelloWorldLayer.m的所有代碼,全部購買代碼也全在裏面也對應有Himi的註釋!

          HelloWorldLayer.h

//
//  HelloWorldLayer.h
//  buytest
//
//  Created by 華明 李 on 11-10-29.
//  Copyright Himi 2011年. All rights reserved.
//


// When you import this file, you import all the cocos2d classes
#import "cocos2d.h"
#import <UIKit/UIKit.h>

#import <StoreKit/StoreKit.h>
enum{
     IAP0p99=10,
     IAP1p99,
     IAP4p99,
     IAP9p99, 
     IAP24p99,
}buyCoinsTag; 

@interface HelloWorldLayer : CCLayer<SKProductsRequestDelegate,SKPaymentTransactionObserver>
{
    int buyType;
}

+(CCScene *) scene;  
- (void) requestProUpgradeProductData;
-(void)RequestProductData;
-(bool)CanMakePay;                             
-(void)buy:(int)type; 
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions;
-(void) PurchasedTransaction: (SKPaymentTransaction *)transaction;
- (void) completeTransaction: (SKPaymentTransaction *)transaction;
- (void) failedTransaction: (SKPaymentTransaction *)transaction;
-(void) paymentQueueRestoreCompletedTransactionsFinished: (SKPaymentTransaction *)transaction;
-(void) paymentQueue:(SKPaymentQueue *) paymentQueue restoreCompletedTransactionsFailedWithError:(NSError *)error;
- (void) restoreTransaction: (SKPaymentTransaction *)transaction;
-(void)provideContent:(NSString *)product;
-(void)recordTransaction:(NSString *)product;
@end
     
              HelloWorldLayer.m

//  
//  IapLayer.m  
//  
//  Created by Himi on 11-5-25.  
//  Copyright 2011年 李華明 . All rights reserved.  
//  
  
#import "HelloWorldLayer.h"   
#define ProductID_IAP0p99 @"com.buytest.one"//$0.99    
#define ProductID_IAP1p99 @"com.buytest.two" //$1.99   
#define ProductID_IAP4p99 @"com.buytest.three" //$4.99    
#define ProductID_IAP9p99 @"com.buytest.four" //$19.99    
#define ProductID_IAP24p99 @"com.buytest.five" //$24.99    
  
@implementation HelloWorldLayer  
+(CCScene *) scene  
{  
    CCScene *scene = [CCScene node];  
    HelloWorldLayer *layer = [HelloWorldLayer node];  
    [scene addChild: layer];  
    return scene;  
}  
-(id)init  
{  
    if ((self = [super init])) {  
        CGSize size = [[CCDirector sharedDirector] winSize];  
        CCSprite *iap_bg  = [CCSprite spriteWithFile:@"Icon.png"];    
        [iap_bg setPosition:ccp(size.width/2,size.height/2)];  
        [self addChild:iap_bg z:0];  
        //---------------------  
        //----監聽購買結果  
        [[SKPaymentQueue defaultQueue] addTransactionObserver:self];  
        //申請購買  
        /* 
         enum{ 
         IAP0p99=10, 
         IAP1p99, 
         IAP4p99, 
         IAP9p99, 
         IAP24p99, 
         }buyCoinsTag; 
         */  
        [self buy:IAP24p99];  
    }  
    return self;  
}  
  
-(void)buy:(int)type  
{   
    buyType = type;    
    if ([SKPaymentQueue canMakePayments]) {  
        //[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];  
        [self RequestProductData];    
        CCLOG(@"容許程序內付費購買");  
    }  
    else  
    {  
        CCLOG(@"不容許程序內付費購買");   
        UIAlertView *alerView =  [[UIAlertView alloc] initWithTitle:@"Alert"   
                                                            message:@"You can‘t purchase in app store(Himi說你沒容許應用程序內購買)"                                                          
                                                           delegate:nil cancelButtonTitle:NSLocalizedString(@"Close(關閉)",nil) otherButtonTitles:nil];  
          
        [alerView show];  
        [alerView release];  
          
    }   
}  
   
-(bool)CanMakePay  
{  
    return [SKPaymentQueue canMakePayments];  
}  
  
-(void)RequestProductData  
{  
    CCLOG(@"---------請求對應的產品信息------------");  
    NSArray *product = nil;  
    switch (buyType) {  
        case IAP0p99:  
            product=[[NSArray alloc] initWithObjects:ProductID_IAP0p99,nil];  
            break;  
        case IAP1p99:  
            product=[[NSArray alloc] initWithObjects:ProductID_IAP1p99,nil];  
            break;  
        case IAP4p99:  
            product=[[NSArray alloc] initWithObjects:ProductID_IAP4p99,nil];  
            break;  
        case IAP9p99:  
            product=[[NSArray alloc] initWithObjects:ProductID_IAP9p99,nil];  
            break;  
        case IAP24p99:  
            product=[[NSArray alloc] initWithObjects:ProductID_IAP24p99,nil];  
            break;  
              
        default:  
            break;  
    }  
    NSSet *nsset = [NSSet setWithArray:product];  
    SKProductsRequest *request=[[SKProductsRequest alloc] initWithProductIdentifiers: nsset];  
    request.delegate=self;  
    [request start];  
    [product release];  
}  
//<SKProductsRequestDelegate> 請求協議  
//收到的產品信息  
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{  
      
    NSLog(@"-----------收到產品反饋信息--------------");  
    NSArray *myProduct = response.products;  
    NSLog(@"產品Product ID:%@",response.invalidProductIdentifiers);  
    NSLog(@"產品付費數量: %d", [myProduct count]);  
    // populate UI   
    for(SKProduct *product in myProduct){  
        NSLog(@"product info");  
        NSLog(@"SKProduct 描述信息%@", [product description]);     
        NSLog(@"產品標題 %@" , product.localizedTitle);  
        NSLog(@"產品描述信息: %@" , product.localizedDescription);  
        NSLog(@"價格: %@" , product.price);  
        NSLog(@"Product id: %@" , product.productIdentifier);   
    }   
    SKPayment *payment = nil;   
    switch (buyType) {  
        case IAP0p99:  
            payment  = [SKPayment paymentWithProductIdentifier:ProductID_IAP0p99];    //支付$0.99  
            break;  
        case IAP1p99:  
            payment  = [SKPayment paymentWithProductIdentifier:ProductID_IAP1p99];    //支付$1.99  
            break;  
        case IAP4p99:  
            payment  = [SKPayment paymentWithProductIdentifier:ProductID_IAP4p99];    //支付$9.99  
            break;  
        case IAP9p99:  
            payment  = [SKPayment paymentWithProductIdentifier:ProductID_IAP9p99];    //支付$19.99  
            break;  
        case IAP24p99:  
            payment  = [SKPayment paymentWithProductIdentifier:ProductID_IAP24p99];    //支付$29.99  
            break;  
        default:  
            break;  
    }  
    CCLOG(@"---------發送購買請求------------");  
    [[SKPaymentQueue defaultQueue] addPayment:payment];    
    [request autorelease];   
      
}  
- (void)requestProUpgradeProductData  
{  
    CCLOG(@"------請求升級數據---------");  
    NSSet *productIdentifiers = [NSSet setWithObject:@"com.productid"];  
    SKProductsRequest* productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];  
    productsRequest.delegate = self;  
    [productsRequest start];   
      
}  
//彈出錯誤信息  
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error{  
    CCLOG(@"-------彈出錯誤信息----------");  
    UIAlertView *alerView =  [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Alert",NULL) message:[error localizedDescription]  
                                                       delegate:nil cancelButtonTitle:NSLocalizedString(@"Close",nil) otherButtonTitles:nil];  
    [alerView show];  
    [alerView release];  
}  
  
-(void) requestDidFinish:(SKRequest *)request   
{  
    NSLog(@"----------反饋信息結束--------------");  
      
}  
   
-(void) PurchasedTransaction: (SKPaymentTransaction *)transaction{  
    CCLOG(@"-----PurchasedTransaction----");  
    NSArray *transactions =[[NSArray alloc] initWithObjects:transaction, nil];  
    [self paymentQueue:[SKPaymentQueue defaultQueue] updatedTransactions:transactions];  
    [transactions release];  
}   
  
//<SKPaymentTransactionObserver> 千萬不要忘記綁定,代碼以下:  
//----監聽購買結果  
//[[SKPaymentQueue defaultQueue] addTransactionObserver:self];  
  
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions//交易結果  
{  
    CCLOG(@"-----paymentQueue--------");  
    for (SKPaymentTransaction *transaction in transactions)  
    {  
        switch (transaction.transactionState)  
        {   
            case SKPaymentTransactionStatePurchased://交易完成   
                [self completeTransaction:transaction];  
                CCLOG(@"-----交易完成 --------");  
                CCLOG(@"不容許程序內付費購買");   
                UIAlertView *alerView =  [[UIAlertView alloc] initWithTitle:@"Alert"   
                                                                    message:@"Himi說你購買成功啦~娃哈哈"                                                        
                                                                   delegate:nil cancelButtonTitle:NSLocalizedString(@"Close(關閉)",nil) otherButtonTitles:nil];  
                  
                [alerView show];  
                [alerView release];   
                break;   
            case SKPaymentTransactionStateFailed://交易失敗   
                [self failedTransaction:transaction];  
                 CCLOG(@"-----交易失敗 --------");  
                UIAlertView *alerView2 =  [[UIAlertView alloc] initWithTitle:@"Alert"   
                                                                    message:@"Himi說你購買失敗,請從新嘗試購買~"                                                        
                                                                   delegate:nil cancelButtonTitle:NSLocalizedString(@"Close(關閉)",nil) otherButtonTitles:nil];  
                  
                [alerView2 show];  
                [alerView2 release];  
                break;   
            case SKPaymentTransactionStateRestored://已經購買過該商品   
                [self restoreTransaction:transaction];  
                 CCLOG(@"-----已經購買過該商品 --------");  
            case SKPaymentTransactionStatePurchasing:      //商品添加進列表  
                 CCLOG(@"-----商品添加進列表 --------");  
                break;  
            default:  
                break;  
        }  
    }  
}  
- (void) completeTransaction: (SKPaymentTransaction *)transaction  
  
{  
    CCLOG(@"-----completeTransaction--------");  
    // Your application should implement these two methods.  
    NSString *product = transaction.payment.productIdentifier;  
    if ([product length] > 0) {  
          
        NSArray *tt = [product componentsSeparatedByString:@"."];  
        NSString *bookid = [tt lastObject];  
        if ([bookid length] > 0) {  
            [self recordTransaction:bookid];  
            [self provideContent:bookid];  
        }  
    }  
      
    // Remove the transaction from the payment queue.  
      
    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];  
      
}  
  
//記錄交易  
-(void)recordTransaction:(NSString *)product{  
    CCLOG(@"-----記錄交易--------");  
}  
  
//處理下載內容  
-(void)provideContent:(NSString *)product{  
    CCLOG(@"-----下載--------");   
}  
  
- (void) failedTransaction: (SKPaymentTransaction *)transaction{  
    NSLog(@"失敗");  
    if (transaction.error.code != SKErrorPaymentCancelled)  
    {  
    }  
    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];  
      
      
}  
-(void) paymentQueueRestoreCompletedTransactionsFinished: (SKPaymentTransaction *)transaction{  
      
}  
  
- (void) restoreTransaction: (SKPaymentTransaction *)transaction  
  
{  
    NSLog(@" 交易恢復處理");  
      
}  
  
-(void) paymentQueue:(SKPaymentQueue *) paymentQueue restoreCompletedTransactionsFailedWithError:(NSError *)error{  
    CCLOG(@"-------paymentQueue----");  
}  
  
  
#pragma mark connection delegate  
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data  
{  
    NSLog(@"%@",  [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease]);   
}  
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{  
      
}  
  
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{  
    switch([(NSHTTPURLResponse *)response statusCode]) {  
        case 200:  
        case 206:  
            break;  
        case 304:   
            break;  
        case 400:   
            break;    
        case 404:  
            break;  
        case 416:  
            break;  
        case 403:  
            break;  
        case 401:  
        case 500:  
            break;  
        default:  
            break;  
    }          
}  
  
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {  
    NSLog(@"test");  
}  
  
-(void)dealloc  
{  
    [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];//解除監聽
    [super dealloc];  
}   
@end



    代碼註釋的至關清楚了,沒有什麼可解釋的,這裏說幾點值得注意的地方:

1.添加對應對應代碼時不要忘記,添加框架 StoreKit.framework,如何添加框架請看個人博文【iOS-Cocos2d遊戲開發之十四】音頻/音效/視頻播放(利用Cocos2D-iPhone-Extensions嵌入Cocos2d進行視頻播放!)

2. 越獄機器沒法沙盒測試!模擬器的話,Himi用4.3模擬器不能夠,由於提示沒有開啓程序內付費- -(我都沒看到模擬器有store的選項,so~);可是使用iOS5的模擬器能夠測試沙盒,可是執行的順序會有些問題,可是還沒真機的童鞋可使用,建議一切以真機實測爲準

3. 千萬不要忘記在iTunesConnect中建立App Bundle ID必定要跟你的項目中的info.plist中的Bundle ID保證一致!!!!

4. 以上代碼中你須要修改的就是我在HelloWorldLayer.m類中的宏定義的Product ID(產品ID),例如Himi剛纔新建了一個產品ID是「com.himi.wahaha"


而後我運行項目截圖以下以及運行控制檯打印的信息以下:


點擊Buy以後運行截圖以及打印信息:


輸入測試帳號密碼後以及打印信息:



                這裏Himi最後一張截圖是沒有購買成功,這裏Himi是故意截圖出來的,緣由就是想告訴童鞋們:

 若是你的產品信息可以正常獲得,可是始終沒法成功的話,不要着急,由於你的產品要進入iTunes Connect,而且Apple準備好沙箱環境須要一些時間。Himi以前遇到過,而後在過了段時間後我沒有修改任何一行代碼,但產品ID變爲有效並能成功購買。=。 =鬱悶ing~~ 其實要使產品發佈到Apple的網絡系統是須要一段時間的,so~這裏別太着急!

           越獄機器沒法正常測試沙盒的喔~

順便提示一下:Bundle ID 儘量與開發者證書的app ID 一致。

    好了,寫了這麼多了,咳咳、Himi繼續忙了,作iOS的童鞋們我想此篇將成爲你必須收藏的一篇哦~嘿嘿!

 



原文連接: http://blog.csdn.net/xiaominghimi/article/details/6937097
相關文章
相關標籤/搜索