iOS應用內置付費In-App Purchase 個人使用總結
上一個項目用到了In App Purchase,發現現在大家對這個挺關注的,把上次寫的總結貼出來給大家看一下,希望對大家有點幫助!
一、有關如何在程序中加入In APP Purchase 的內容參考下面的連接
二、我的程序具體步驟
1.添加Storekit.Framework,編寫自己的storeObsever,用于處理交易,代碼如下,其中completeTransaction 和failedTransaction兩個函數是自定義的用來處理交易成功與失敗其它的就都是SKPaymentTransactionObserver 這個代理要求的。
- #import <Foundation/Foundation.h>
- #import <StoreKit/StoreKit.h>
- #import <StoreKit/SKPaymentTransaction.h>
- @interface MyStoreObserver : NSObject < SKPaymentTransactionObserver > {
- }
- - (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;
- @end
- - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions函數用來更新transactions的狀態
- - (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:
- break;
- default:
- break;
- }
- }
- }
2.在程序中添加storeObsever,最好在applicationDidFinishLaunching中添加
- MyStoreObserver *tempObserver = [[MyStoreObserver alloc] init];
- self.observer = tempObserver;
- [tempObserver release];
- [[SKPaymentQueue defaultQueue] addTransactionObserver:self.observer];
3.發送付費請求,在相應的類中實現SKProductsRequestDelegate,別忘記定義自己產品的identifier
- //請求產品信息
- #define kMyFeatureIdentifier yourProductIdentifiers (你自己的產品identifiers)
- SKProductsRequest *request= [[SKProductsRequest alloc] initWithProductIdentifiers:
- [NSSet setWithObject: kMyFeatureIdentifier]];
- request.delegate = self;
- [request start];
代理方法相關代碼,如果請求成功的話就可以發送付費請求
- #pragma mark request delegate
- //!收到產品的消息
- - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
- SKPayment *payment = [SKPayment paymentWithProductIdentifier:kMyFeatureIdentifier];
- [[SKPaymentQueue defaultQueue] addPayment:payment];
- [request autorelease];
- }
- - (void)request:(SKRequest *)request didFailWithError:(NSError *)error{
- UIAlertView *alerView = [[UIAlertView alloc] initWithTitle:@"Alert" message:[error localizedDescription] delegate:nil c
- ancelButtonTitle:NSLocalizedString(@"Close",nil) otherButtonTitles:nil]; [alerView show]; [alerView release];
- }
4.接下來就是Apple自己的付費相關處理了,需要做是就是點擊按鈕來確定付費
5.交易完成后需要向Apple驗證這次交易是否成功,要不然沒成功就把產品給別人的話,那就虧了,在1中可以看到當交易成功時會調用自定義的 completeTransaction函數,在該函數中我們需要驗證transactionReceipt 關于驗證SKPaymentTransaction的transactionReceipt transactionReceipt是只有當 SKPaymentTransaction完成時,即transactionState 被設置為SKPaymentTransactionStatePurchased 或 SKPaymentTransactionStateRestored時才被創建,因此只有這兩種狀態下能去驗證transactionReceipt
原始思路及具體步驟如下:
a、從SKPaymentTranscation的實例中將transactionReceipt轉化為NSString
- NSString *temptransactionReceipt = [[NSString alloc] initWithData:[mytransaction transactionReceipt] encoding:NSUTF8StringEncoding];
如果用NSLog的方法將其寫出來顯示如下,里邊有很多的‘+’
- { "signature" =
- "AZNZdoggtjbU/wMqZ4SSd3lgkxbWr+/zcV7Oez4io7f5oPMliKlQzWW4vj+FLsVyhjyyuPyTSugJ6m4Hrp+CjdAptGZg4iWExoyE6stltg0EfD8Ezggjg5q04ws74pMZ/0aRgjedua8dCMMqR7C8ZjojfOYU6LrFiK7qbUUiV+inMIIDUzCCAjugAwIBAgIIZRSRTdlYBLUwDQYJKoZIhvcNAQEFBQAwfzELMAkGA1UEBhMCVVMxEzARBgNVBAoMCkFwcGxlIEluYy4xJjAkBgNVBAsMHUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MTMwMQYDVQQDDCpBcHBsZSBpVHVuZXMgU3RvcmUgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDkwNjE1MjIwNTU2WhcNMTQwNjE0MjIwNTU2WjBkMSMwIQYDVQQDDBpQdXJjaGFzZVJlY2VpcHRDZXJ0aWZpY2F0ZTEbMBkGA1UECwwSQXBwbGUgaVR1bmVzIFN0b3JlMRMwEQYDVQQKDApBcHBsZSBJbmMuMQswCQYDVQQGEwJVUzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAytGMXZy3gitJ2JMKFojSDynC/9yYezyn9HBX+u3/3VcpWE2XhcgGKYqNBA1+AewOzrKO774OsokTu4qymEx10ph8UTmsZewB0ESMHBEjF7FN6/HccsQUYC3WagrHnT12HG2Ih0OAm/ZhpWzj0HS4m813LpIyo00sewMvMNL2hkcCAwEAAaNyMHAwDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBQ2HejinYLSARi1MmsO10MLkVhDOjAOBgNVHQ8BAf8EBAMCB4AwHQYDVR0OBBYEFKmDg/IZSMU+ElcIFMzNo36ZXyT1MBAGCiqGSIb3Y2QGBQEEAgUAMA0GCSqGSIb3DQEBBQUAA4IBAQARpJs+O2Y3gL8gHdASkrfZHFpwINd1VcB5VF5LkVpnFz63zylA/3cGIDG91b/d5NIwZjkVt4Bgvd62o/mCbzCsWiNfSKTJVFK1D78BDQoSO2oHTuQuz1BR7xzNHxQZ90zUS6ZX9SC8N3g3A1jEtAyDhZNB+CRBBXLwZdnBUeBsT9QLpjvTnekZcGTnU08zfCjGF3eBJEu9eP6WgexK1xMSp72kEOmYbn6yTi3D4YrcYx4Q3n/57VBP2en8qXWeP5oHDsLTGzLRsWdoB3VxJLrF2ivL8JS8zqC0qyac452pN6xunRuzyyfpaqzQL12BzFEe44xna2byektSbtquA5LNAAAAAA=="; "purchase-info" = "ewoJIml0ZW0taWQiID0gIjMzMDU5OTg4MCI7Cgkib3JpZ2luYWwtdHJhbnNhY3Rpb24taWQiID0gIjEwMDAwMDAwMDAwOTEyNTgiOwoJInB1cmNoYXNlLWRhdGUiID0gIjIwMDktMTAtMTQgMDY6MDY6NTQgRXRjL0dNVCI7CgkicHJvZHVjdC1pZCIgPSAiY29tLnNlbnNreS5jbmFtZXNpZ3B1cmNoYXNlY29uc3VtYWJsZSI7CgkidHJhbnNhY3Rpb24taWQiID0gIjEwMDAwMDAwMDAwOTEyNTgiOwoJInF1YW50aXR5IiA9ICIxIjsKCSJvcmlnaW5hbC1wdXJjaGFzZS1kYXRlIiA9ICIyMDA5LTEwLTE0IDA2OjA2OjU0IEV0Yy9HTVQiOwoJImJpZCIgPSAiY29tLnNlbnNreS5jc2lnbmF0dXJlYXBwIjsKCSJidnJzIiA9ICIxLjAiOwp9"; "pod" = "100"; "signing-status" = "0";}
b、然后通過Post的方法將其提交給服務器,在這里只是將transactionReceipt傳給服務器,驗證由服務器完成。代碼如下,
- NSString *requestStirng =[NSString stringWithFormat: @"receipt_data=%@",temptransactioReceipt];
- requestStirng = [requestStirng stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
- NSData *postData = [NSData dataWithBytes:[requestStirng UTF8String] length:[requestStirng length]];
- NSMutableURLRequest *connectionRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[kURL stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
- [connectionRequest setHTTPMethod:@"POST"];
- [connectionRequest setTimeoutInterval:120.0];
- [connectionRequest setCachePolicy:NSURLRequestUseProtocolCachePolicy];
- [connectionRequest setHTTPBody:postData];
出現的錯誤,服務器用傳過去的receipt_data向Apple驗證,通不過,但將用NSLog的方法在后臺寫出的 temptransactionreceipt向Apple驗證能通過 錯誤原因 對比發現通過URL傳給服務器的字符串不能很好的完成urlEncode,在里邊出現的+不能自動進行編碼轉化,因為在Objective-C 中不能將NSString 真正的實現URL encode ,
解決方法可參考下面的地址
http://simonwoodside.com/weblog/2009/4/22/how_to_really_url_encode/
但當我用上面地址給出的方法進行編碼后仍不能解決該問題,原因不明
解決辦法:
在將transactionReceipt轉化成的NSString傳給服務器之前先進行轉化,將里邊的+轉換為%2B , 將步驟a中的代碼改為
- NSString *temptransactionReceipt = [[NSString alloc] initWithData:[mytransaction transactionReceipt] encoding:NSUTF8StringEncoding];
- temptransactionReceipt = [temptransactionReceipt stringByReplacingOccurrencesOfString:@"+" withString:@"%2B"];
6.服務器端驗證相關代碼 其中XML為自定義的一個類,getHeader所做的工作就是把receipt數據Post給Apple然后得到返回的結果返回
- $url = "https://buy.itunes.apple.com/verifyReceipt";/ $receipt = json_encode(array("receipt-data" => base64_encode($receipt_data)));
- $response_json = $Xml->getHeader($url, $receipt);
- $response = json_decode($response_json['content'], true);
- getHeader的代碼如下 public function getHeader($url, $data) {
- $ch = curl_init();
- $timeout = 300; // set to zero for no timeout
- curl_setopt($ch, CURLOPT_URL, $url);
- // curl_setopt($ch, CURLOPT_ENCODING, 'gzip');
- curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 1); //post到https
- curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
- curl_setopt($ch, CURLOPT_POST, true);
- curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
- curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);//跟隨頁面的跳轉
- // curl_setopt($ch, CURLOPT_HEADER, true);
- curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
- $handles = curl_exec($ch);
- $header = curl_getinfo($ch);
- curl_close($ch);
- $header['content'] = $handles;
- return $header;
- }
把自己寫的測試的例子傳上來供大家參考下
下載Demo例子源碼下載地址:http://down.51cto.com/data/611901




















