IOS?
最近自己做了個iOS內置付費的功能,寫下心得。
工具/原料
xcode+iphone+apple開發者帳號
方法/步驟
創建APPID,就是唯一標識這個APP的identifer.
登錄開發中心,https://developer.apple.com/devcenter/ios/index.action網站好像改版了,跟網上的教程都不一致),依次點擊紅框裡的鏈接,選擇右上角的+號,創建一個唯一的ID,一般是域名的倒寫加上一些其他的東西,反正唯一就行了。這裡取com.baidu.test
創建測試用的空APP,(就是沒有實際的程序,只是用來測試內置付費的)
還是登錄開發中心,如上1圖,選擇iTunes connect,登入。框1進去後是你所有的APP,框2帳號,測試帳號管理,框3是銀行卡 稅率等信息,這個務必要寫完整。
先說框3,你的銀行信息必須是完整的,像下圖那樣。否則你在創建產品列表時,產品的種類就可能只有免費訂閱,消耗品,非消耗品等是看不見的。
框1 MYAPP。進去後頂上有個+號,就是新建APP,上面有提示,一步一步填寫就行了,有些信息是不必要的,你可以save一下,看看有沒有報錯什麼的。頂部會有一行鏈接,如圖,我只填了price和inAPP purchase。inAPP purchase就是設置你的產品列表的地方,點開後如圖,點擊create,創建一個假的商品,也可以很多個,產品的ID必須是唯一的,可以用你的APPID+產品名什麼的。這裡假設是com.baidu.test.product1
框2,用來創建沙盒測試帳號,選擇下圖紅框,填入一個郵箱地址,隨便編,還有密碼,郵箱不需要驗證的。這裡假設是[email protected] 這一步做完,準備工作就做好了,然後就是編碼了。
先說一下IAP的大致流程,如圖,首先你得先有一個在iTunes設置的產品列表,這樣才知道向iTunes請求什麼商品,這個列表可以硬編碼,或者放到服務器上動態獲取,然後拿著這個列表通過apple的API去請求產品,返回後顯示到界面,用戶操作發起一個購買請求,成功或失敗會有相應的回調方法,成功的時候applestore會產生一個收據,這個收據可以反饋給服務器用來向store驗證,然後就是服務器向玩家發放道具什麼的。這裡說的非常粗,詳見代碼。對於交易恢復restoreTransaction,我不是特別理解這個概念,網上找到一張圖2,有更明白的歡迎指教。
剛搜了一下,有一條:
恢復交易信息(Transactions)當transaction被處理並從隊列移除之後,正常情況下,程序就再也看不到它們了。 如果你的程序提供的是非消耗性的或是訂閱類的商品,就必須提供restore的功能,使用戶可以在其他設備上重新存儲購買信息。
是不是說,如果這個商品是非消耗品,購買完成之後,再次拿著它的ID去請求它就請求不到了??????所以才需要恢復????
代碼的編寫需要導入StoreKit.framework,需要用到它的SKPayment,SKPaymentQueue,SKPaymentTransaction,SKPaymentTransactionObserver類。
1.假設我們已經有了一個產品列表:
NSSet *productIdentifiers = [NSSet setWithObjects:@"com.baidu.test.product1", nil];
2.我們用這個列表去向商店請求商品的具體信息,這個請求通過類
SKProductsRequest完成:
創建SKProductsRequest對象,
SKProductsRequest * _request=
[[[SKProductsRequest alloc] initWithProductIdentifiers:_productIdentifiers] autorelease];
創建後還不能立即請求,因為我們要回收結果,所以要實現一些的回調方法。在網上找了找,說是設置代理delegate,自己強行理解了下,這個delegate和java C#裡的接口差不多,規定一套實現者必須執行的動作,我學objectC才第2天,理解的不好請指教。那麼這個代理該怎麼設置呢:
_request.delegate = self;咋一看的暈暈的,把自己設成代理,然後看了下所在類的聲明:黑體部分應該就是所謂的代理了,它裡面有一些方法必須實現,這些方法在合適的時機被回調。
@interface IAPHelper : NSObject
@protected
NSSet * _productIdentifiers;
NSArray * _products;
NSMutableSet * _purchasedProducts;
SKProductsRequest * _request;
}
然後是發送請求:[_request start];
整個方法是這樣的:
- (void)requestProducts {
self.request = [[[SKProductsRequest alloc] initWithProductIdentifiers:_productIdentifiers] autorelease];
_request.delegate = self;//設置回調代理對象
[_request start];//請求
}
回調方法:這裡只實現了成功時的回調,如果請求不成功的,可以實現request:didFailWithError:
//請求成功的回調
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse :(SKProductsResponse *)response {
NSLog(@"Received products results...");
self.products = response.products;//接收列表
self.request = nil; //Null,釋放內存
//發送消息給界面,界面要接收消息,必須得先監聽才可以。
[[NSNotificationCenter defaultCenter] postNotificationName:kProductsLoadedNotification object:_products];
}
假設我們成功取到了產品列表,那麼接下列就是要顯示到界面上了。顯示代碼就略過了。接下來要做的就是用戶操作後發出購買請求:
因為產品的信息都在self.products中,用戶點擊後我們取出對應的產品ID,創建一個購買對象,放入隊列中,關鍵代碼如下:
//SKPayment對象
SKPayment *payment = [SKPayment paymentWithProductIdentifier:productIdentifier];
//加載到隊列中
[[SKPaymentQueue defaultQueue] addPayment:payment];
剩下的事情由apple來完成,但是交易的狀態還是要獲取的,獲取狀態通過添加監視:監視最好在創建類實例,或者程序加載時就加上。
[[SKPaymentQueue defaultQueue] addTransactionObserver:參數];因為IAPHelper: NSObject
我只找到一個:
//當發生交易事務時回調該方法,該方法根據對應狀態調用合適的方法
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction *transaction in transactions)
{
switch (transaction.transactionState)
{
case SKPaymentTransactionStatePurchased://成功完成事物
[self completeTransaction:transaction];
break;
case SKPaymentTransactionStateFailed://事物失敗
[self failedTransaction:transaction];
break;
case SKPaymentTransactionStateRestored://
[self restoreTransaction:transaction];
NSLog(@"已經購買過該商品");
default:
break;
}
}
}
到這裡基本上就算完了。
還有一個收據問題,只有交易狀態是成功(SKPaymentTransactionStatePurchased或者恢復(SKPaymentTransactionStateRestored)時,才會產生收據
Receipt,他保存本次交易的詳細內容,是transaction對象的一個屬性。
怎麼獲取呢?網上找了半天,_iOS_6_1之前的版本,保存在
transaction.transactionReceipt.bytes,好像是2進制,得轉碼什麼的,新版本通過NSBundle的一個方法appStoreReceiptURL來獲取。
拿到2進制的收據經過base64編碼之後就是向Appstore驗證交易是不是真的生效了。沙盒的驗證地址是"https://sandbox.itunes.apple.com/verifyReceipt";正式的驗證地址是https://buy.itunes.apple.com/verifyReceipt,代碼大致像這樣:
NSString* receipt64 = [self encode64:(uint8_t *)transaction.transactionReceipt.bytes length:transaction.transactionReceipt.length];
NSLog(@"receipt64== %@",receipt64);
/*本地驗證
NSString *[email protected]"https://sandbox.itunes.apple.com/verifyReceipt";
//https://buy.itunes.apple.com/verifyReceipt
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];// autorelease];
[request setURL:[NSURL URLWithString:URL]];
[request setHTTPMethod:@"POST"];
//設置contentType
[request addValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
//設置Content-Length
[request setValue:[NSString stringWithFormat:@"%d", [receipt64 length]] forHTTPHeaderField:@"Content-Length"];
NSDictionary* body = [NSDictionary dictionaryWithObjectsAndKeys:receipt64, @"receipt-data", nil];
SBJsonWriter* w = [SBJsonWriter new];
[request setHTTPBody:[[w stringWithObject:body] dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES]];
NSHTTPURLResponse *urlResponse=nil;
NSError *errorr=nil;
NSData *receivedData = [NSURLConnection sendSynchronousRequest:request
returningResponse:&urlResponse
error:&errorr];
//解析
NSString *results=[[NSString alloc]initWithBytes:[receivedData bytes] length:[receivedData length] encoding:NSUTF8StringEncoding];
NSLog(@"-Himi- %@",results);
NSDictionary*dic = [results JSONValue];
if([[dic objectForKey:@"status"] intValue]==0){//注意,[email protected]"0" 是驗證收據成功
NSLog(@"valid ok");
}
網上還說發起購買時最好先判斷一些內置購買能不能用,通過
if([SKPaymentQueue canMakePayments])
{
...//Display a store to the user
}
else
{
...//Warn the user that purchases are disabled.
}
還有就是我對於恢復交易的理解:restoreTransaction,如果你有一個商品是一次性的,玩家已經購買過,因為某種原因玩家的設備丟失了這個商品,這時候可以通過apple提供的API幫助玩家找回丟失的商品。具體流程是:
通過
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];發起恢復,
商品成功恢復會回調 paymentQueueRestoreCompletedTransactionsFinished方法,失敗會回調paymentQueue:restoreCompletedTransactionsFailedWithError:方法。