1. 程式人生 > >iOS應用程式內購/內付費(一)

iOS應用程式內購/內付費(一)

很久之前就想出一篇IOS內付費的教程,但是一查網上的教程實在太多了,有的寫得真的蠻不錯的,就心想算了,於是就儲存在草稿箱了。至於為什麼寫完它呢!真是說來話長,最近公司有個專案經理跑來問我有關蘋果內付費相關的細節,跟他聊了半天,從專案對接蘋果官方支付介面聊到了如何檢視App收益,最後終於使他有了一些眉目,但是悲催的是還要我繼續去跟他們專案的程式設計師講解(真是瘋了),所以我就決定給他們專案寫一個內購的文件,所以我順便把這篇部落格完成吧!

首先進入蘋果的ItunesConnection(https://itunesconnect.apple.com)點選左上角的加號新建一個App應用,點選後該網站會彈出一個資訊編輯框,大家只要將上面的資訊填充完畢點選save即可在蘋果的app平臺上擁有一個屬於自己的App。


在套裝ID的上,需要提前為該App申請一個AppID以及BundleID,只要是申請成功了就會在選擇列表中顯示出來。如果有人有疑問如何申請,請看我之前那一篇推送的部落格,裡面有詳細的步驟。附上鍊接點選開啟連結

這裡順便多說一句這個ItunesConnect是用來幹嘛的,它是蘋果公司給個人或企業提供管理自己App的一個平臺。在這個平臺上開發者可以新建,刪除和管理自己的App應用,開發者可以根據需求對App應用進行上架與下架,編輯App資訊,生成測試app所需的資訊,例如賬號,邀請碼等,還有就是我們今天要講的內付費功能。當然啦,他的功能可不止我講的這些,我大致說一下這個平臺的作用,如果你經常跟它打交道的話就會慢慢熟悉了。

接下來,我就來為大家演示一下如何新增付費道具,首先開啟iTunesConnect,顯示如下頁面:


選擇紅圈所圈起來的選項,然後將裡面的相關資訊補充完畢,如果缺少這一步,內購功能是不會成功的。

假如你已經完成了上述相關銀行賬戶的設定,就點選你的App,選擇上面標題欄中的"App 內購買專案"


隨後點選左上角的 "create new"選項,如下圖所示,進入到下一個介面:


這個介面是讓你選擇消費道具的種類,現在改版的網站是有簡體中文翻譯的,所以不像以前開啟一看都不知道選哪一個,甚至都不知道每個代表的什麼意思(比如我第一次遇到的時候,在領導面前真是囧)。它的種類分為如下幾種:


一般對專案來說大多數都是選擇“消耗型專案

”這個種類,比如遊戲中購買金幣,寶石balabala~之類的,選中之後就會到這個介面中來:


在上圖所示的編輯框中輸入,商品名稱,產品ID以及價格等級,在這邊說明一下:

1.商品名稱根據你的消費道具的實際意義來說明,比如“100顆寶石”,“100金幣”等。

2.產品ID是比較重要的,由專案自定義,只要唯一即可,像我一般都是用App的bundleID加一個字尾來表示,這樣既跟專案關聯又具有唯一性。

3.價格等級的話“檢視價格表”中有對應的說明,可以對照著表中每個國家的貨幣價格與等級來選擇。

我們繼續,在這個網頁的接下來部分如圖所示:

選擇新增語言選項,彈出一個編輯頁面:


點選save儲存,則會在介面上顯示成如下:


最後一步就是點選“選取檔案”提交一張蘋果它指定畫素(640*920)的商品圖片,當他上傳完畢後點擊“save”按鈕,我們這第二部分就大工告成了。提交的商品最後會在內購的頁面上顯示為如圖:


這個圖是我在已經發布的app上面擷取的,添加了3個商品,已經是通過的的狀態了(顯示綠色),當您剛提交的時候,因為通過蘋果的審查需要一段時間所以會顯示黃色的等待狀態,所以不必擔心是不是商品編輯錯了。如圖:


這部分,我主要給大家演示一下,如何申請測試賬號,利用蘋果的沙盒測試環境來模擬AppStore的購買流程。

在ItunesConnect中選擇“使用者和職能”選項~


隨後在左上角的選項中選擇沙盒測試者,點選左上角的加號圖示增加一位測試者,如圖:


編輯好相應的內容,點選儲存,就建立了一個測試賬號,是不是很簡單啊!當然這個賬號如果你忘記了密碼可以重新生成一個,無關緊要。

順帶多句嘴,不要在正式的appstore上面用沙盒測試的賬號來登入,千萬要牢記在心,此賬號只用於測試環境下~

接下來就是程式碼部分啦~

1.首先在專案工程中加入“storekit.framework”,加入標頭檔案#import <StoreKit/StoreKit.h>

2.在.h檔案中加入“SKPaymentTransactionObserver,SKProductsRequestDelegate”監聽機制

下面貼上內購的核心程式碼,就幾個函式,我在這邊就不在做更多詳細的解釋了,各位看官可以執行跑一下就一目瞭然了。

.h檔案

//
//  PaymentViewController.h
//  IAPPayTest
//
//  Created by silicon on 14-10-28.
//  Copyright (c) 2014年 silicon. All rights reserved.
//

#import <UIKit/UIKit.h>

#import <StoreKit/StoreKit.h>

@interface PaymentViewController : UIViewController<SKPaymentTransactionObserver,SKProductsRequestDelegate>

@property (strong, nonatomic) IBOutlet UITextField *productID;

@property (strong, nonatomic) IBOutlet UIButton *purchase;

- (IBAction)purchaseFunc:(id)sender;

@end

.m檔案

//
//  PaymentViewController.m
//  IAPPayTest
//
//  Created by silicon on 14-10-28.
//  Copyright (c) 2014年 silicon. All rights reserved.
//

#import "PaymentViewController.h"

@interface PaymentViewController ()

@end

@implementation PaymentViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    
    [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
    self.productID.text = @"com.games.ztyxs.product_point.1";
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (IBAction)purchaseFunc:(id)sender {
    NSString *product = self.productID.text;
    if([SKPaymentQueue canMakePayments]){
        [self requestProductData:product];
    }else{
        NSLog(@"不允許程式內付費");
    }
}

//請求商品
- (void)requestProductData:(NSString *)type{
    NSLog(@"-------------請求對應的產品資訊----------------");
    NSArray *product = [[NSArray alloc] initWithObjects:type, nil];

    NSSet *nsset = [NSSet setWithArray:product];
    SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsset];
    request.delegate = self;
    [request start];
    
}

//收到產品返回資訊
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{

    NSLog(@"--------------收到產品反饋訊息---------------------");
    NSArray *product = response.products;
    if([product count] == 0){
        NSLog(@"--------------沒有商品------------------");
        return;
    }
    
    NSLog(@"productID:%@", response.invalidProductIdentifiers);
    NSLog(@"產品付費數量:%d",[product count]);
    
    SKProduct *p = nil;
    for (SKProduct *pro in product) {
        NSLog(@"%@", [pro description]);
        NSLog(@"%@", [pro localizedTitle]);
        NSLog(@"%@", [pro localizedDescription]);
        NSLog(@"%@", [pro price]);
        NSLog(@"%@", [pro productIdentifier]);
        
        if([pro.productIdentifier isEqualToString:self.productID.text]){
            p = pro;
        }
    }
    
    SKPayment *payment = [SKPayment paymentWithProduct:p];
    
    NSLog(@"傳送購買請求");
    [[SKPaymentQueue defaultQueue] addPayment:payment];
}

//請求失敗
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error{
    NSLog(@"------------------錯誤-----------------:%@", error);
}

- (void)requestDidFinish:(SKRequest *)request{
    NSLog(@"------------反饋資訊結束-----------------");
}


//監聽購買結果
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transaction{
    for(SKPaymentTransaction *tran in transaction){
        
        switch (tran.transactionState) {
            case SKPaymentTransactionStatePurchased:
                NSLog(@"交易完成");
                
                break;
            case SKPaymentTransactionStatePurchasing:
                NSLog(@"商品新增進列表");
                
                break;
            case SKPaymentTransactionStateRestored:
                NSLog(@"已經購買過商品");
            
                break;
            case SKPaymentTransactionStateFailed:
                NSLog(@"交易失敗");
                
                break;
            default:
                break;
        }
    }
}

//交易結束
- (void)completeTransaction:(SKPaymentTransaction *)transaction{
    NSLog(@"交易結束");
    
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}


- (void)dealloc{
    [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
    [super dealloc];
}

@end

程式碼就這麼多,到這邊我們的IOS內購教程就接近尾聲了,在測試的時候還有幾點因素要注意一下:

1.沙盒環境測試appStore內購流程的時候,請使用沒越獄的裝置

2.請務必使用真機來測試,一切以真機為準。

3.專案的Bundle identifier需要與您申請AppID時填寫的bundleID一致,不然會無法請求到商品資訊。

講了這麼多,附上幾張測試截圖給大家展示一下:

請求商品時的列印日誌:


交易成功後:


手機截圖:

要求輸入AppStore帳密,使用測試生成的即可:


確定購買:


交易完成:


當我們的交易完成後還要去appstore 上面去驗證票據資訊是否正確,這樣我們才可以給玩家發放道具,apple官方文件

//交易結束
- (void)completeTransaction:(SKPaymentTransaction *)transaction{
    NSLog(@"交易結束");
    //交易驗證
    NSURL *recepitURL = [[NSBundle mainBundle] appStoreReceiptURL];
    NSData *receipt = [NSData dataWithContentsOfURL:recepitURL];
    
    if(!receipt){
        
    }
    
    NSError *error;
    NSDictionary *requestContents = @{
                                      @"receipt-data": [receipt base64EncodedStringWithOptions:0]
                                      };
    NSData *requestData = [NSJSONSerialization dataWithJSONObject:requestContents
                                                          options:0
                                                            error:&error];
    
    if (!requestData) { /* ... Handle error ... */ }
    
    //In the test environment, use https://sandbox.itunes.apple.com/verifyReceipt
    //In the real environment, use https://buy.itunes.apple.com/verifyReceipt
    // Create a POST request with the receipt data.
    NSURL *storeURL = [NSURL URLWithString:@"https://buy.itunes.apple.com/verifyReceipt"];
    NSMutableURLRequest *storeRequest = [NSMutableURLRequest requestWithURL:storeURL];
    [storeRequest setHTTPMethod:@"POST"];
    [storeRequest setHTTPBody:requestData];
    
    // Make a connection to the iTunes Store on a background queue.
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [NSURLConnection sendAsynchronousRequest:storeRequest queue:queue
                           completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
                               if (connectionError) {
                                   /* ... Handle error ... */
                               } else {
                                   NSError *error;
                                   NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
                                   if (!jsonResponse) { /* ... Handle error ...*/ }
                                   /* ... Send a response back to the device ... */
                                   //Parse the Response
                               }
                           }];

    
    
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}



好了,所有的內購流程基本上講完了,原諒我在圖片上的塗抹,因為關係到產品的敏感詞彙所以希望大家能夠不介意。趕這篇部落格的時間比較匆忙,如果有童鞋還有什麼疑問或者我寫的那個地方不對歡迎私信我或者評論,我會在第一時間回覆。謝謝~

大家可以關注我的微信公眾號與我互動,相關問題也可以直接用公眾號聯絡我: