■フレームワーク
StoreKitを追加する。
#import <StoreKit/StoreKit.h>
■実装
以下のようにしてSKProductsRequestDelegateとSKPaymentTransactionObserverを実装する。/p>
HogeViewController.h
@interface HogeViewController : UIViewController<SKProductsRequestDelegate, SKPaymentTransactionObserver> { bool isLoading; SKProductsRequest *skProductsRequest; UIActivityIndicatorView *spinner; UIView *loaderBg; UILabel *loaderTitle; } @property bool isLoading; - (IBAction)purchaseButtonPushed:(id)sender; - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response; - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransaction:(NSArray *)transactions; - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0);
HogeViewController.m
購入処理の開始
- (IBAction)purchaseButtonPushed:(id)sender { if([[UIDevice currentDevice] networkAvailable] == NO){ return; } if(self.isLoading){ return; } if([SKPaymentQueue canMakePayments]){ self.isLoading = true; // loader loaderBg = [[UIView alloc] init]; [loaderBg setFrame:CGRectMake(100, 150, 120, 85)]; [loaderBg setBackgroundColor:[UIColor blackColor]]; [loaderBg.layer setCornerRadius:13.0f]; [loaderBg setAlpha:0.7]; [self.view addSubview:loaderBg]; loaderTitle = [[UILabel alloc] init]; [loaderTitle setTextColor:[UIColor whiteColor]]; [loaderTitle setFrame:CGRectMake(0, 57, 120, 18)]; [loaderTitle setText:@"Loading..."]; [loaderTitle setTextAlignment:UITextAlignmentCenter]; [loaderTitle setBackgroundColor:[UIColor clearColor]]; [loaderTitle setAlpha:1.0]; [loaderBg addSubview:loaderTitle]; // spinner spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; [spinner setCenter:CGPointMake(self.view.frame.size.width/2.0, self.view.frame.size.height/2.0)]; [self.view addSubview:spinner]; [spinner startAnimating]; [UIApplication sharedApplication].networkActivityIndicatorVisible = YES; // 課金部分 // identiferを元にappleサーバに問い合わせます [[SKPaymentQueue defaultQueue] addTransactionObserver:self]; skProductsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObject:@"identifer"]]; [skProductsRequest setDelegate:self]; [skProductsRequest start]; } else{// 本体の設定でアプリ内課金をOFFにしている人向けの表示 [self showAlert:@"cannot purchase" text:@"設定 > 一般 > 機能制限で[App内での購入]をONにしてください"]; } }
上述のようにアプリ内課金は本体の設定で無効にできる事を考慮する。
appleのサーバにidentiferをお問い合わせした結果の処理
identiferをセットしてリクエストしたレスポンスに商品データがあれば、その商品の購入手続きに入る。
#pragma mark - #pragma mark SKProductsRequestDelegate - (void) productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response { if (response == nil) { return; } for(SKProduct *product in response.products){// productを元にした購入オブジェクトをキューに入れて購入手続きに SKPayment *payment = [SKPayment paymentWithProduct:product]; [[SKPaymentQueue defaultQueue] addPayment:payment]; } }
後述のオブザーバーの管理を忘れないようにすること。
トランザクションの状態に変更があった時の処理
以下のように状態はswitch文で分岐する。
#pragma mark - #pragma mark SKPaymentTransactionObserver - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransaction:(NSArray *)transactions { BOOL isFinished = YES; for (SKPaymentTransaction *transaction in transactions) { switch (transaction.transactionState) { case SKPaymentTransactionStatePurchasing:// 何らかのOKを押す前の処理 break; case SKPaymentTransactionStatePurchased:// success : 決済手続き完了処理 [queue finishTransaction:transaction]; // もし自社サーバでユーザが購入を完了したかどうかappleサーバに確認する場合は、 // transaction.transactionReceiptの値をbase64に変換して自社サーバに送信 // [transaction.transactionReceipt base64EncodedString]; isFinished = NO; break; case SKPaymentTransactionStateFailed:// 途中でキャンセルした時 isFinished = NO; [queue finishTransaction:transaction]; break; case SKPaymentTransactionStateRestored:// 通常はコールされない isFinished = NO; [queue finishTransaction:transaction]; break; default: break; } } if (isFinished == NO) {// トランザクションが何らかの完了をした時=>ローディングを消す self.isLoading = false; [UIApplication sharedApplication].networkActivityIndicatorVisible = NO; [loaderTitle removeFromSuperview]; [loaderBg removeFromSuperview]; [spinner stopAnimating]; } } - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0){ [self paymentQueue:queue updatedTransaction:transactions]; }
オブザーバー
dealloc部分で課金処理キューからオブザーバーをしっかり削除するようにする。
- (void)dealloc{ [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];// これ!!! [loaderBg release]; [loaderTitle release]; [skProductsRequest release]; [spinner release]; [super dealloc]; }
サーバ側
以下のようにしてユーザから送信されてきたレシート情報を自社サーバ側でappleに確認する。
$ch = curl_init(); $url = "https://sandbox.itunes.apple.com/verifyReceipt";// サンドボックス(テスト用) curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_POST, TRUE); curl_setopt($ch, CURLOPT_POSTFIELDS, '{"receipt-data" : "' . $receipt . '"}'); curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json')); curl_setopt($ch, CURLOPT_HEADER, FALSE); $result = curl_exec($ch);
■テスト
xcodeからインストールしたアプリではサンドボックス経由という事が購入ダイアログで表示されます。storeからインストールしたアプリでは本番と同じダイアログになります。特にコードを変更する必要はなく自動で判定してくれるようです。