詳解iOS應(yīng)用程序內(nèi)購/內(nèi)付費(fèi)(一)
很久之前就想出一篇iOS內(nèi)付費(fèi)的教程,但是一查網(wǎng)上的教程實(shí)在太多了,有的寫得真的蠻不錯(cuò)的,就心想算了,于是就保存在草稿箱了。至于為什么寫完它呢!真是說來話長,最近公司有個(gè)項(xiàng)目經(jīng)理跑來問我有關(guān)蘋果內(nèi)付費(fèi)相關(guān)的細(xì)節(jié),跟他聊了半天,從項(xiàng)目對(duì)接蘋果官方支付接口聊到了如何查看App收益,最后終于使他有了一些眉目,但是悲催的是還要我繼續(xù)去跟他們項(xiàng)目的程序員講解(真是瘋了),所以我就決定給他們項(xiàng)目寫一個(gè)內(nèi)購的文檔,所以我順便把這篇博客完成吧!
首先進(jìn)入蘋果的ItunesConnection(https://itunesconnect.apple.com)點(diǎn)擊左上角的加號(hào)新建一個(gè)App應(yīng)用,點(diǎn)擊后該網(wǎng)站會(huì)彈出一個(gè)信息編輯框,大家只要將上面的信息填充完畢點(diǎn)擊save即可在蘋果的app平臺(tái)上擁有一個(gè)屬于自己的App。

在套裝ID的上,需要提前為該App申請(qǐng)一個(gè)AppID以及BundleID,只要是申請(qǐng)成功了就會(huì)在選擇列表中顯示出來。
這里順便多說一句這個(gè)ItunesConnect是用來干嘛的,它是蘋果公司給個(gè)人或企業(yè)提供管理自己App的一個(gè)平臺(tái)。在這個(gè)平臺(tái)上開發(fā)者可以新建,刪除和管理自己的App應(yīng)用,開發(fā)者可以根據(jù)需求對(duì)App應(yīng)用進(jìn)行上架與下架,編輯App信息,生成測(cè)試app所需的信息,例如賬號(hào),邀請(qǐng)碼等,還有就是我們今天要講的內(nèi)付費(fèi)功能。當(dāng)然啦,他的功能可不止我講的這些,我大致說一下這個(gè)平臺(tái)的作用,如果你經(jīng)常跟它打交道的話就會(huì)慢慢熟悉了。
接下來,我就來為大家演示一下如何添加付費(fèi)道具,首先打開iTunesConnect,顯示如下頁面:

選擇紅圈所圈起來的選項(xiàng),然后將里面的相關(guān)信息補(bǔ)充完畢,如果缺少這一步,內(nèi)購功能是不會(huì)成功的。
假如你已經(jīng)完成了上述相關(guān)銀行賬戶的設(shè)置,就點(diǎn)擊你的App,選擇上面標(biāo)題欄中的"App 內(nèi)購買項(xiàng)目"

隨后點(diǎn)擊左上角的 "create new"選項(xiàng),如下圖所示,進(jìn)入到下一個(gè)界面:
這個(gè)界面是讓你選擇消費(fèi)道具的種類,現(xiàn)在改版的網(wǎng)站是有簡體中文翻譯的,所以不像以前打開一看都不知道選哪一個(gè),甚至都不知道每個(gè)代表的什么意思(比如我第一次遇到的時(shí)候,在領(lǐng)導(dǎo)面前真是囧)。它的種類分為如下幾種:

一般對(duì)項(xiàng)目來說大多數(shù)都是選擇“消耗型項(xiàng)目”這個(gè)種類,比如游戲中購買金幣,寶石balabala~之類的,選中之后就會(huì)到這個(gè)界面中來:

在上圖所示的編輯框中輸入,商品名稱,產(chǎn)品ID以及價(jià)格等級(jí),在這邊說明一下:
1.商品名稱根據(jù)你的消費(fèi)道具的實(shí)際意義來說明,比如“100顆寶石”,“100金幣”等。
2.產(chǎn)品ID是比較重要的,由項(xiàng)目自定義,只要唯一即可,像我一般都是用App的bundleID加一個(gè)后綴來表示,這樣既跟項(xiàng)目關(guān)聯(lián)又具有唯一性。
3.價(jià)格等級(jí)的話“查看價(jià)格表”中有對(duì)應(yīng)的說明,可以對(duì)照著表中每個(gè)國家的貨幣價(jià)格與等級(jí)來選擇。
我們繼續(xù),在這個(gè)網(wǎng)頁的接下來部分如圖所示:

選擇添加語言選項(xiàng),彈出一個(gè)編輯頁面:

點(diǎn)擊save保存,則會(huì)在界面上顯示成如下:

最后一步就是點(diǎn)擊“選取文件”提交一張?zhí)O果它指定像素(640*920)的商品圖片,當(dāng)他上傳完畢后點(diǎn)擊“save”按鈕,我們這第二部分就大工告成了。提交的商品最后會(huì)在內(nèi)購的頁面上顯示為如圖:

這個(gè)圖是我在已經(jīng)發(fā)布的app上面截取的,添加了3個(gè)商品,已經(jīng)是通過的的狀態(tài)了(顯示綠色),當(dāng)您剛提交的時(shí)候,因?yàn)橥ㄟ^蘋果的審查需要一段時(shí)間所以會(huì)顯示黃色的等待狀態(tài),所以不必?fù)?dān)心是不是商品編輯錯(cuò)了。如圖:

這部分,我主要給大家演示一下,如何申請(qǐng)測(cè)試賬號(hào),利用蘋果的沙盒測(cè)試環(huán)境來模擬AppStore的購買流程。
在ItunesConnect中選擇“用戶和職能”選項(xiàng)~

隨后在左上角的選項(xiàng)中選擇沙盒測(cè)試者,點(diǎn)擊左上角的加號(hào)圖標(biāo)增加一位測(cè)試者,如圖:

編輯好相應(yīng)的內(nèi)容,點(diǎn)擊保存,就創(chuàng)建了一個(gè)測(cè)試賬號(hào),是不是很簡單?。‘?dāng)然這個(gè)賬號(hào)如果你忘記了密碼可以重新生成一個(gè),無關(guān)緊要。
順帶多句嘴,不要在正式的appstore上面用沙盒測(cè)試的賬號(hào)來登錄,千萬要牢記在心,此賬號(hào)只用于測(cè)試環(huán)境下~
接下來就是代碼部分啦~
1.首先在項(xiàng)目工程中加入“storekit.framework”,加入頭文件#import <StoreKit/StoreKit.h>
2.在.h文件中加入“SKPaymentTransactionObserver,SKProductsRequestDelegate”監(jiān)聽機(jī)制
下面貼上內(nèi)購的核心代碼,就幾個(gè)函數(shù),我在這邊就不在做更多詳細(xì)的解釋了,各位看官可以運(yùn)行跑一下就一目了然了。
.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(@"不允許程序內(nèi)付費(fèi)");
}
}
//請(qǐng)求商品
- (void)requestProductData:(NSString *)type{
NSLog(@"-------------請(qǐng)求對(duì)應(yīng)的產(chǎn)品信息----------------");
NSArray *product = [[NSArray alloc] initWithObjects:type, nil nil];
NSSet *nsset = [NSSet setWithArray:product];
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:nsset];
request.delegate = self;
[request start];
}
//收到產(chǎn)品返回信息
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
NSLog(@"--------------收到產(chǎn)品反饋消息---------------------");
NSArray *product = response.products;
if([product count] == 0){
NSLog(@"--------------沒有商品------------------");
return;
}
NSLog(@"productID:%@", response.invalidProductIdentifiers);
NSLog(@"產(chǎn)品付費(fèi)數(shù)量:%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(@"發(fā)送購買請(qǐng)求");
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
//請(qǐng)求失敗
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error{
NSLog(@"------------------錯(cuò)誤-----------------:%@", error);
}
- (void)requestDidFinish:(SKRequest *)request{
NSLog(@"------------反饋信息結(jié)束-----------------");
}
//監(jiān)聽購買結(jié)果
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transaction{
for(SKPaymentTransaction *tran in transaction){
switch (tran.transactionState) {
case SKPaymentTransactionStatePurchased:
NSLog(@"交易完成");
break;
case SKPaymentTransactionStatePurchasing:
NSLog(@"商品添加進(jìn)列表");
break;
case SKPaymentTransactionStateRestored:
NSLog(@"已經(jīng)購買過商品");
break;
case SKPaymentTransactionStateFailed:
NSLog(@"交易失敗");
break;
default:
break;
}
}
}
//交易結(jié)束
- (void)completeTransaction:(SKPaymentTransaction *)transaction{
NSLog(@"交易結(jié)束");
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
- (void)dealloc{
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
[super dealloc];
}
@end
代碼就這么多,到這邊我們的IOS內(nèi)購教程就接近尾聲了,在測(cè)試的時(shí)候還有幾點(diǎn)因素要注意一下:
1.沙盒環(huán)境測(cè)試appStore內(nèi)購流程的時(shí)候,請(qǐng)使用沒越獄的設(shè)備。
2.請(qǐng)務(wù)必使用真機(jī)來測(cè)試,一切以真機(jī)為準(zhǔn)。
3.項(xiàng)目的Bundle identifier需要與您申請(qǐng)AppID時(shí)填寫的bundleID一致,不然會(huì)無法請(qǐng)求到商品信息。
講了這么多,附上幾張測(cè)試截屏給大家展示一下:
請(qǐng)求商品時(shí)的打印日志:

交易成功后:

手機(jī)截屏:
要求輸入AppStore帳密,使用測(cè)試生成的即可:

確定購買:

交易完成:

當(dāng)我們的交易完成后還要去appstore 上面去驗(yàn)證票據(jù)信息是否正確,這樣我們才可以給玩家發(fā)放道具,apple官方文檔:
//交易結(jié)束
- (void)completeTransaction:(SKPaymentTransaction *)transaction{
NSLog(@"交易結(jié)束");
//交易驗(yàn)證
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];
}
好了,所有的內(nèi)購流程基本上講完了,原諒我在圖片上的涂抹,因?yàn)殛P(guān)系到產(chǎn)品的敏感詞匯所以希望大家能夠不介意。以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
iOS應(yīng)用開發(fā)中使用設(shè)計(jì)模式中的觀察者模式的實(shí)例
這篇文章主要介紹了iOS應(yīng)用開發(fā)中使用設(shè)計(jì)模式中的觀察者模式的實(shí)例,包括Cocoa框架使用中的KVO機(jī)制的相關(guān)配合運(yùn)用,代碼為傳統(tǒng)的Objective-C,需要的朋友可以參考下2016-03-03
iOS8調(diào)用相機(jī)報(bào)警告Snapshotting a view的解決方法
這篇文章主要介紹了iOS8調(diào)用相機(jī)報(bào)警告Snapshotting a view……的解決方法 ,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11
iOS中讀取照片庫及保存圖片或視頻到照片庫的要點(diǎn)解析
iOS中保存到本地的圖片視頻都會(huì)被匯總到系統(tǒng)的PhotoLibrary中,這里我們就來看一下iOS中讀取照片庫及保存圖片或視頻到照片庫的要點(diǎn)解析2016-06-06
iOS自動(dòng)生成表格效果的實(shí)現(xiàn)代碼
這篇文章主要為大家詳細(xì)介紹了iOS自動(dòng)生成表格效果的實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03
IOS開發(fā)之tableView點(diǎn)擊行跳轉(zhuǎn)并帶有“顯示”更多功能
這篇文章給大家介紹通過點(diǎn)擊城市中的tableView跳轉(zhuǎn)到旅游景點(diǎn)的tableView,下面會(huì)有“顯示”更多的功能,代碼簡單易懂,對(duì)ios點(diǎn)擊tableview跳轉(zhuǎn)相關(guān)知識(shí)感興趣的朋友一起學(xué)習(xí)吧2016-03-03
iOS NSNotificationCenter通知中心使用小結(jié)
IOS中經(jīng)常會(huì)使用到NSNotification和delegate來進(jìn)行一些類之間的消息傳遞,這篇文章主要介紹了iOS NSNotificationCenter使用小結(jié),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-11-11

