iOS 內購講解

一.總說內購的內容算法

 

1.協議、稅務和銀行業務 信息填寫數據庫

2.內購商品的添加json

3.添加沙盒測試帳號數組

4.內購代碼的具體實現安全

5.內購的注意事項服務器

 

二.協議、稅務和銀行業務 信息填寫網絡

 

  • 2.一、協議、稅務和銀行業務 信息填寫 的入口數據結構

 

  • 2.二、選擇申請合同類型app

 

  • 進入協議、稅務和銀行業務頁面後,會有3種合同類型,若是你以前沒有主動申請過去合同,那麼通常你如今激活的合同只有iOS Free Application一種。dom

     

  • 頁面內容分爲兩塊:

     

    Request Contracts(申請合同)

    Contracts In Effect(已生效合同)。

     

  • 合同類型分爲3種:

     

    iOS Free Application(免費應用合同)

    iOS Paid Application(付費應用合同)

    iAd App NetNetwork(廣告合同)

 

這裏咱們主要主要講一下付費應用合同的申請流程。

 

 

    • 2.三、申請iOS Paid Application合同(協議、稅務和銀行業務3個都要填寫)

       


2.四、Contact Info(填寫聯繫方式)

若是你沒有添加過聯繫人,你須要經過Add New Contact按鈕來添加一個新的聯繫人。而後指定聯繫人的職務,

職務以下:

 

Senior Management:高管

Financial:財務

Technical:技術支持

Legal:法務

Marketing:市場推廣

 

若是你是獨立開發者,能夠所有填你本身一我的。


2.五、填寫銀行信息

選擇你的銀行帳戶,若是你沒有,點擊旁邊的Add Bank Account添加一個帳戶。下面是添加一個帳戶的流程。

 

  • 2.5.一、選擇銀行所在的國家

 

  • 2.5.二、填寫銀行標識CNAPS Code

若是你不知道CNAPS Code是多少,能夠百度搜CNAPS Code來查詢,查詢時會根據3個關鍵信息來查詢,以下:

 

Bank Name:銀行的英文名稱(不能是拼音)

City:銀行所在的城市英文名稱(中國的城市用拼音)

Postal Code:郵編

 

而後在下面就會出來備選的銀行,選擇正確的銀行後,點擊next,進入下一步。


2.5.三、確認銀行信息

 

  • 2.5.四、填寫銀行帳號信息

 

Bank Account Number:銀行帳號

Confirm Bank Account Number:再次輸入銀行帳號

Account Holder Name:持卡人姓名,中文名用拼寫,名在前,姓在後

Bank Account Currency:貨幣類型,通常國內的開發者選擇CNY


2.5.五、確認全部信息

 

  • 2.6.填寫稅務信息(這個內容比較多)

 

    • 2.6.1.稅務信息這一塊瞭解不是不少,不過由於是國內開發者,能夠不用太費心,稅務信息分3種:

       

      U.S Tax Forms:美國稅務

      Australia Tax Forms:澳大利亞稅務

      Canada Tax Forms:加拿大稅務

 

  • 2.6.2.一堆條約

  我選擇的是U.S Tax Forms,選擇後會問你兩個問題:

 

    • 第1個問題以下:詢問你是不是美國居民,有沒有美國夥伴關係或者美國公司,若是沒有直接選擇No。


接下來第二個問題以下:詢問你有沒有在美國的商業性活動,沒有也直接選No

 

2.6.3.而後填寫你的稅務信息,包括如下幾點:

Individual or Organization Name:我的或者組織名稱

Country of incorporation: 所在國家

Type of Beneficial Owner:受益方式,獨立開發者選我的

Permanent Residence:居住地址

Mailing address:郵寄地址

Name of Person Making this Declaration:聲明人

Title:頭銜


2.6.4.打鉤


2.6.5.澳大利亞的不要管了


2.6.6.加拿大的也不用管了


2.7.填寫完成

 

 

  • 2.8.待審覈

 

你填寫完全部資料後,合同狀態就會變成Processing, 大概24小時內就會有結果。

 

三.內購商品的添加


3.1.建立內購商品

 

  • 3.2.選擇內購類型

 

    • 3.2.1.消耗型商品:相似遊戲中的鑽石,還有如今某些APP中的貨幣,好比鬥魚裏的魚丸、映客裏的映票。會被消耗的,要選擇消耗型商品

      注意:大多數的消耗型商品都是須要登陸的,由於須要在數據庫存餘額。

      須要注意的是:在登陸以前,你最好不要讓用戶看到商品,有可能會由於這個緣由被拒(你們都說看運氣)

    • 3.2.2.非消耗型商品:沒法被消耗的商品,好比上文提到的視頻課程,一次購買,就應該永久能夠觀看

      注意:當你使用非消耗型商品時,你須要添加一個恢復購買的按鈕

      這個常見於各類遊戲中,其實知道這個規定之後仍是挺好理解的,非消耗型商品是不可被消耗的,一次購買終身使用的,非消耗型的商品是跟appleId綁定的,就是你平時下載APP讓你輸入帳號密碼的內個。

      你須要一個恢復購買的按鈕,來讓用戶恢復他購買的內容

    • 3.2.3.訂閱類型商品:若是你的公司是外包公司,有訂閱類型商品的APP必定要用客戶的帳號提交審覈,由於當APP中有過訂閱類型商品,注意是有過,建立過再刪除也算,這個APP沒法被轉移帳號

      注意:使用或曾經使用過訂閱型商品的APP沒法轉移


3.3.建立好的產品


3.4.在上線的時候記得添加內購的商品

四.添加沙盒測試帳號

 

  • 4.1.添加沙盒測試的入口


4.2.添加沙盒測試帳號


4.3.具體的測試帳號信息填寫

五.內購代碼的具體實現(這纔是你們所指望看到的)

 

  • 5.1.我建立了一個購買金幣的內購控制器ApplePayCIOViewController

    在此,我僅僅向你們貼出.m的詳細代碼

 

  • 5.2.內購的流程詳細講解

 

  • 5.2.1. 用戶先拿到購買產品的單子,

  • 5.2.2.拿着單子去蘋果那裏交錢,交完錢讓蘋果在單子上蓋個章

  • 5.2.3.拿着蓋了章的單子傳給本身的服務器來驗證是否真的支付成功

  • 5.2.4.根據服務器返回的信息作具體的處理

 

 

  • 5.3.上代碼

 

    • 5.3.1..先導入StoreKit.framework庫;

       

    • 5.3.2.建立ApplePayCIOViewController,遵照協議<SKPaymentTransactionObserver,SKProductsRequestDelegate>

       

    • 5.3.3.ApplePayCIOViewController.m代碼

#import "ApplePayCIOViewController.h"
#import <StoreKit/StoreKit.h>
// 產品的ID
#define ProductID1 @"CIOCourses1"
@interface ApplePayCIOViewController ()<SKProductsRequestDelegate,SKPaymentTransactionObserver>
{
    NSString *selectProductID;
}
@end
@implementation ApplePayCIOViewController
-(void)viewWillAppear:(BOOL)animated{
   [super viewWillAppear:animated];
   // 添加觀察者
   [[SKPaymentQueue defaultQueue] addTransactionObserver:self];   
}
-(void)viewWillDisappear:(BOOL)animated{
   [super viewWillDisappear:animated];
   // 移除觀察者
  [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
}

- (void)viewDidLoad {
  [super viewDidLoad];
   self.title = @"內購";

   self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"測試" style:UIBarButtonItemStylePlain target:self action:@selector(test)];

    // 恢復購買的按鈕
    UIButton * revert = [[UIButton alloc]initWithFrame:CGRectMake(20, 100, 100, 80)];
    [revert setBackgroundColor:JKRandomColor];
    [revert addTarget:self action:@selector(replyToBuy) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview: revert];
    self.view.backgroundColor =[UIColor redColor];
}

#pragma mark 恢復購買(主要是針對非消耗產品)
-(void)replyToBuy{

   [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
#pragma mark 測試內購
-(void)test{

  if([SKPaymentQueue canMakePayments]){

    // productID就是你在建立購買項目時所填寫的產品ID
    selectProductID = [NSString stringWithFormat:@"%@",ProductID1];
    [self requestProductID:selectProductID];

   }else{
              
   // NSLog(@"不容許程序內付費");
   UIAlertView *alertError = [[UIAlertView alloc] initWithTitle:@"舒適提示"
                                                                                                                    message:@"請先開啓應用內付費購買功能。"
                                                                                                    delegate:nil
                                                                                                              cancelButtonTitle:@"肯定"
                                                                                                        otherButtonTitles: nil];
   [alertError show];
   }
}
#pragma mark 1.請求全部的商品ID
-(void)requestProductID:(NSString *)productID{

   // 1.拿到全部可賣商品的ID數組
   NSArray *productIDArray = [[NSArray alloc]initWithObjects:productID, nil];
   NSSet *sets = [[NSSet alloc]initWithArray:productIDArray];

   // 2.向蘋果發送請求,請求全部可買的商品
   // 2.1.建立請求對象
   SKProductsRequest *sKProductsRequest = [[SKProductsRequest alloc]initWithProductIdentifiers:sets];
   // 2.2.設置代理(在代理方法裏面獲取全部的可賣的商品)
   sKProductsRequest.delegate = self;
   // 2.3.開始請求
   [sKProductsRequest start];

}
#pragma mark 2.蘋果那邊的內購監聽
-(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{

   NSLog(@"可賣商品的數量=%ld",response.products.count);

   NSArray *product = response.products;
   if([product count] == 0){

     NSLog(@"沒有商品");
     return;
   }

 for (SKProduct *sKProduct in product) {

      NSLog(@"pro info");
      NSLog(@"SKProduct 描述信息:%@", sKProduct.description);
      NSLog(@"localizedTitle 產品標題:%@", sKProduct.localizedTitle);
      NSLog(@"localizedDescription 產品描述信息:%@",sKProduct.localizedDescription);
      NSLog(@"price 價格:%@",sKProduct.price);
      NSLog(@"productIdentifier Product id:%@",sKProduct.productIdentifier);

     if([sKProduct.productIdentifier isEqualToString: selectProductID]){
  
        [self buyProduct:sKProduct];
  
        break;
  
     }else{
  
      //NSLog(@"不不不相同");
     }
  }

}

#pragma mark 內購的代碼調用
-(void)buyProduct:(SKProduct *)product{

   // 1.建立票據
  SKPayment *skpayment = [SKPayment paymentWithProduct:product];

  // 2.將票據加入到交易隊列
  [[SKPaymentQueue defaultQueue] addPayment:skpayment];

  // 3.添加觀察者,監聽用戶是否付錢成功(不在此處添加觀察者)
  //[[SKPaymentQueue defaultQueue] addTransactionObserver:self];

}

#pragma mark 4.實現觀察者監聽付錢的代理方法,只要交易發生變化就會走下面的方法
-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions{

  /*
    SKPaymentTransactionStatePurchasing,    正在購買
    SKPaymentTransactionStatePurchased,     已經購買
    SKPaymentTransactionStateFailed,        購買失敗
    SKPaymentTransactionStateRestored,      回覆購買中
    SKPaymentTransactionStateDeferred       交易還在隊列裏面,但最終狀態尚未決定
  */

  for (SKPaymentTransaction *transaction in transactions) {
     switch (transaction.transactionState) {
           case SKPaymentTransactionStatePurchasing:{
      
                NSLog(@"正在購買");
           }break;
           case SKPaymentTransactionStatePurchased:{
      
              NSLog(@"購買成功");
              // 購買後告訴交易隊列,把這個成功的交易移除掉
              [queue finishTransaction:transaction];
              [self buyAppleStoreProductSucceedWithPaymentTransactionp:transaction];
           }break;
           case SKPaymentTransactionStateFailed:{
      
               NSLog(@"購買失敗");
               // 購買失敗也要把這個交易移除掉
               [queue finishTransaction:transaction];
           }break;
           case SKPaymentTransactionStateRestored:{
               NSLog(@"回覆購買中,也叫作已經購買");
               // 回覆購買中也要把這個交易移除掉
               [queue finishTransaction:transaction];
           }break;
  case SKPaymentTransactionStateDeferred:{
      
              NSLog(@"交易還在隊列裏面,但最終狀態尚未決定");
           }break;
           default:
           break;
         }
    }
 }

// 蘋果內購支付成功
- (void)buyAppleStoreProductSucceedWithPaymentTransactionp:(SKPaymentTransaction *)paymentTransactionp {

  NSString * productIdentifier = paymentTransactionp.payment.productIdentifier;
  // NSLog(@"productIdentifier Product id:%@", productIdentifier);
  NSString *transactionReceiptString= nil;

  //系統IOS7.0以上獲取支付驗證憑證的方式應該改變,切驗證返回的數據結構也不同了。
   NSString *version = [UIDevice currentDevice].systemVersion;
   if([version intValue] >= 7.0){
       // 驗證憑據,獲取到蘋果返回的交易憑據
       // appStoreReceiptURL iOS7.0增長的,購買交易完成後,會將憑據存放在該地址
       NSURLRequest * appstoreRequest = [NSURLRequest requestWithURL:[[NSBundle mainBundle]appStoreReceiptURL]];
       NSError *error = nil;
       NSData * receiptData = [NSURLConnection sendSynchronousRequest:appstoreRequest returningResponse:nil error:&error];
       transactionReceiptString = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
   }else{

      NSData * receiptData = paymentTransactionp.transactionReceipt;
        //  transactionReceiptString = [receiptData base64EncodedString];
      transactionReceiptString = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
    }
   // 去驗證是否真正的支付成功了
   [self checkAppStorePayResultWithBase64String:transactionReceiptString];

}

- (void)checkAppStorePayResultWithBase64String:(NSString *)base64String {

   /* 生成訂單參數,注意沙盒測試帳號與線上正式蘋果帳號的驗證途徑不同,要給後臺標明 */
  /*
   注意:
   本身測試的時候使用的是沙盒購買(測試環境)
   App Store審覈的時候也使用的是沙盒購買(測試環境)
   上線之後就不是用的沙盒購買了(正式環境)
   因此此時應該先驗證正式環境,在驗證測試環境

  正式環境驗證成功,說明是線上用戶在使用
  正式環境驗證不成功返回21007,說明是本身測試或者審覈人員在測試
   */
   /*
     蘋果AppStore線上的購買憑證地址是: https://buy.itunes.apple.com/verifyReceipt
     測試地址是:https://sandbox.itunes.apple.com/verifyReceipt
    */
   //    NSNumber *sandbox;
   NSString *sandbox;
   #if (defined(APPSTORE_ASK_TO_BUY_IN_SANDBOX) && defined(DEBUG))
  //sandbox = @(0);
  sandbox = @"0";
  #else
  //sandbox = @(1);
  sandbox = @"1";
  #endif

  NSMutableDictionary *prgam = [[NSMutableDictionary alloc] init];;
  [prgam setValue:sandbox forKey:@"sandbox"];
  [prgam setValue:base64String forKey:@"reciept"];

  /*
     請求後臺接口,服務器處驗證是否支付成功,依據返回結果作相應邏輯處理
     0 表明沙盒  1表明 正式的內購
     最後最驗證後的
   */
    /*
      內購驗證憑據返回結果狀態碼說明
      21000 App Store沒法讀取你提供的JSON數據  
      21002 收據數據不符合格式  
      21003 收據沒法被驗證  
      21004 你提供的共享密鑰和帳戶的共享密鑰不一致  
      21005 收據服務器當前不可用  
      21006 收據是有效的,但訂閱服務已通過期。當收到這個信息時,解碼後的收據信息也包含在返回內容中  
      21007 收據信息是測試用(sandbox),但卻被髮送到產品環境中驗證  
      21008 收據信息是產品環境中使用,但卻被髮送到測試環境中驗證
      */

   NSLog(@"字典==%@",prgam);

}

#pragma mark 客戶端驗證購買憑據
- (void)verifyTransactionResult
{
   // 驗證憑據,獲取到蘋果返回的交易憑據
   // appStoreReceiptURL iOS7.0增長的,購買交易完成後,會將憑據存放在該地址
   NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
   // 從沙盒中獲取到購買憑據
   NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];
   // 傳輸的是BASE64編碼的字符串
   /**
      BASE64 經常使用的編碼方案,一般用於數據傳輸,以及加密算法的基礎算法,傳輸過程當中可以保證數據傳輸的穩定性
      BASE64是能夠編碼和解碼的
    */
   NSDictionary *requestContents = @{
                            @"receipt-data": [receipt base64EncodedStringWithOptions:0]
                            };
   NSError *error;
   // 轉換爲 JSON 格式
   NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents
                                                options:0
                                                  error:&error];
   // 不存在
   if (!requestData) { /* ... Handle error ... */ }

   // 發送網絡POST請求,對購買憑據進行驗證
   NSString *verifyUrlString;
   #if (defined(APPSTORE_ASK_TO_BUY_IN_SANDBOX) && defined(DEBUG))
   verifyUrlString = @"https://sandbox.itunes.apple.com/verifyReceipt";
   #else
    verifyUrlString = @"https://buy.itunes.apple.com/verifyReceipt";
   #endif
   // 國內訪問蘋果服務器比較慢,timeoutInterval 須要長一點
   NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:[[NSURL alloc] initWithString:verifyUrlString] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0f];

   [storeRequest setHTTPMethod:@"POST"];
   [storeRequest setHTTPBody:requestData];

   // 在後臺對列中提交驗證請求,並得到官方的驗證JSON結果
   NSOperationQueue *queue = [[NSOperationQueue alloc] init];
   [NSURLConnection sendAsynchronousRequest:storeRequest queue:queue
                 completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
                     if (connectionError) {
                         NSLog(@"連接失敗");
                     } else {
                         NSError *error;
                         NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
                         if (!jsonResponse) {
                             NSLog(@"驗證失敗");
                         }
                         
                         // 比對 jsonResponse 中如下信息基本上能夠保證數據安全
                         /*
                          bundle_id
                          application_version
                          product_id
                          transaction_id
                          */
                         
                         NSLog(@"驗證成功");
                     }
                 }];

}
@end

六.內購的注意事項

 

  • 6.1.通常發生於首次提交app或添加新商品,當你的app經過審覈之後,你發如今生產環境下獲取不到商品,這是由於app雖然過審覈了,可是內購商品尚未正式添加到蘋果的服務器裏,耐心等待一段時間就能夠啦~

     

  • 6.2. 代碼中的_currentProId所填寫的是你的購買項目的的ID,這個和第二步建立的內購的productID要一致;本例中是 123。

     

  • 6.3. 在監聽購買結果後,必定要調用[[SKPaymentQueue defaultQueue] finishTransaction:tran];來容許你從支付隊列中移除交易。

     

  • 6.4. 沙盒環境測試appStore內購流程的時候,請使用沒越獄的設備。

     

  • 6.5. 請務必使用真機來測試,一切以真機爲準。

     

  • 6.6. 項目的Bundle identifier須要與您申請AppID時填寫的bundleID一致,否則會沒法請求到商品信息。

     

  • 6.7. 真機測試的時候,必定要退出原來的帳號,才能用沙盒測試帳號

     

  • 6.8. 二次驗證,請注意區分宏, 測試用沙盒驗證,App Store審覈的時候也使用的是沙盒購買,因此驗證購買憑證的時候須要判斷返回Status Code決定是否去沙盒進行二次驗證,爲了線上用戶的使用,驗證的順序確定是先驗證正式環境,此時若返回值爲21007,就須要去沙盒二次驗證,由於此購買的是在沙盒進行的。

     

  • 6.9.您的應用是否處於等待開發者發佈(Pending Developer Release)狀態?等待發布狀態的IAP是沒法測試的。

     

  • 6.10.您的內購項目是不是最近才新建的,或者進行了更改?內購項目須要一段時間才能反應到全部服務器上,這個過程通常是一兩小時,也可能再長一些達到若干小時。

     

  • 6.11.您在iTC中Contracts, Tax, and Banking Information項目中是否有尚未設置或者過時了的項目?不完整的財務信息沒法進行內購測試。

     

  • 6.12.您是在越獄設備上進行內購測試麼?越獄設備不能用於正常內購,您須要重裝或者尋找一臺沒有越獄的設備。

     

  • 6.13.您的應用是不是被拒狀態(Rejected)或本身拒絕(Developer Rejected)了?被拒絕狀態的應用的話對應還未經過的內購項目也會一塊兒被拒,所以您須要從新將IAP項目設爲Cleared for Sale。

     

  • 6.14.您使用的測試帳號是不是美國區帳號?雖然不是必定須要,可是鑑於其餘地區的測試帳號常常抽風,加上美國區帳號一直很穩定,所以強烈建議使用美國區帳號。正常狀況下IAP不須要進行信用卡綁定和其餘信息填寫,若是你遇到了這種狀況,能夠試試刪除這個測試帳號再新建一個其餘地區的。

     

  • 6.15.您是否將設備上原來的app刪除了,並從新進行了安裝?記得在安裝前作一下Clean和Clean Build Folder。

     

  • 6.16.您的plist中的Bundle identifier的內容是否和您的AppID一致?

相關文章
相關標籤/搜索