みなさんこんにちは。こんばんわ。スマートフォン事業部のイッシーです。スマートフォンアプリを開発しているスマートフォン事業部のイッシーです。スマートフォンという単語を乱発してますが今回はスマートフォンの話だからです。ʕ→ᴥ←ʔ

今回はiPhoneにおけるアプリ内課金のお話です。なんか敷居が高いですね!でも実装は意外と簡単です。では早速。

■フレームワーク

StoreKitが必要になります。

[cc lang="c"]
#import
[/cc]

■クラスの定義

以下のようにしてSKProductsRequestDelegateとSKPaymentTransactionObserverを実装します。ビューコントローラでなくても大丈夫です。ちなみに今回はローディング中の処理も解説コードに入れてます。

[cc lang="c"]
@interface HogeViewController : UIViewController {
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);
[/cc]

■実装

購入ボタンが押されたときの処理

ここから購入処理の通信が始まります。UIActivityIndicatorViewを使うと簡単にロード中の表示ができるのですが、背景色によっては全く見えない場合がありますので、1枚UIViewを下に敷いて見やすくしましょう。また、1点注意があるのですがUIによっては購入ボタンの2度押しが可能ですので、フラグを立ててしっかり防ぐようにしましょう。

[cc lang="c"]
- (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にしてください”];
}
}
[/cc]

アプリ内課金は本体の設定で無効にできますので、必ず分岐してあげましょう。

appleのサーバにidentiferをお問い合わせした結果の処理

なんてことはありません。identiferをセットしてリクエストしたレスポンスに商品データがあれば、その商品の購入手続きに入ります。

[cc lang="c"]
#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];
}
}
[/cc]

ここの部分で何度か購入を繰り返しているとクラッシュしてしまう人もいるかもしれません。それは最後述の方法で回避できます。

購入のトランザクションの状態が変わったときに呼び出される処理

状態はswitch文で分岐しています。完了した時の部分にアプリに合わせた処理を呼び出してあげましょう。

[cc lang="c"]
#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];
}
[/cc]

サーバ側

ユーザから送信されてきたレシート情報を自社サーバ側でappleに確認するには、以下のようなコードで処理してください。

[cc language="php"]
$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);
[/cc]

テスト

xcodeからインストールしたアプリではサンドボックス経由という事が購入ダイアログで表示されます。storeからインストールしたアプリでは本番と同じダイアログになります。特にコードを変更する必要はなく自動で判定してくれるようです。

最後に

だいたいOKなんですが実は複数回購入手続きをするとクラッシュします。dealloc部分で課金処理キューからオブザーバーをしっかり削除するようにしましょう。

[cc lang="c"]
- (void)dealloc{
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];// これ!!!
[loaderBg release];
[loaderTitle release];
[skProductsRequest release];
[spinner release];
[super dealloc];
}
[/cc]

実はここの部分でクラッシュの原因が分からず凄く苦労しました。(,,Ծ‸Ծ,, )

Post Navigation