1. エッセンスだけ書き下すと、こんな感じです。
こんな感じと言いながら、これでリリースしているので、ちゃんと動くと思います。
- (void)viewDidLoad {
[super viewDidLoad];
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
switch (indexPath.section) {
case 0:
if (indexPath.row == 0) {
//In-App Purchases機能制限の確認
NSString *productID = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleDisplayName"];
if ([SKPaymentQueue canMakePayments]) {
SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:[NSSet setWithObjects:productID, nil]];
request.delegate = self;
[request start];
[self.indicator startAnimating];
}
}
break;
}
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
if (response == nil || [response.invalidProductIdentifiers count] > 0) {
[_indicator stopAnimating];
return;
}
_product = nil;
for (_product in response.products) {
break;
}
if (_product == nil) {
[_indicator stopAnimating];
return;
}
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"title", nil) message:msg delegate:self cancelButtonTitle:NSLocalizedString(@"Restore", nil) otherButtonTitles:NSLocalizedString(@"Purchase", nil), nil];
[alert show];
}
- (void)alertView:(UIAlertView *)alert clickedButtonAtIndex:(NSInteger)buttonIndex
{
SKPayment *payment;
switch (buttonIndex) {
case 1:
payment = [SKPayment paymentWithProduct:_product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
break;
case 0:
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
default:
break;
}
}
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
default:
case SKPaymentTransactionStatePurchasing:
break;
case SKPaymentTransactionStateFailed:
[queue finishTransaction:transaction];
[_indicator stopAnimating];
break;
case SKPaymentTransactionStatePurchased:
case SKPaymentTransactionStateRestored:
[queue finishTransaction:transaction];
[_indicator stopAnimating];
break;
}
break;
}
}
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
{
[_indicator stopAnimating];
}
- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error
{
[_indicator stopAnimating];
}
2. 問題は、各stopAnimatingのところに、何を埋め込むかです。
【失敗処理なら】
StoreKitが勝手に出すダイアログもあるので、整理が難しいのですが、ダイアログが足りないと感じた場合には、自前ダイアログを埋め込んでいます。
【成功処理なら】
成功した場合には、成功したことをDocumentに保存しておく必要があります。
次回、起動時の際にはこの情報を読み込んで、例えば購入したアイテムを表示するとか、購入した機能を生かすとか、処理をしなくてはいけません。
一番簡単なのはplistにBooleanで書き込むことですが、App Storeの審査は通りますが、簡単にハッキングされてしまうので、アプリ販売で稼ぎたいなら、暗号化は必須でしょう。
ちなみにstopAnimatingは6箇所ありますが、4箇所は失敗で、2箇所が成功です。
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];を忘れていてえらいことになりました。
返信削除これがないと、updatedTransactionsが呼び出されることがないので、常に課金が失敗するのですが、実はそれだけでは済みません。
そのまま知らずに何度もデバッグを続けると、Apple Storeから戻ってくるキューが、完結しないまま溜まってしまいます。
忘れていた[[SKPaymentQueue defaultQueue] addTransactionObserver:self];を追加しビルドしても手遅れで、溜まっていたキューを消費しない限り、Apple IDを求めるダイアログが出まくります。
Apple IDを求めるダイアログが何度も出るアプリとは、課金をキャンセルした時の処理がまずく、キューを消費できていない場合と思われます。
iTunes Connectのコンソールで、App内課金を登録する際、製品IDをバンドルIDと一致させないと、TestFlightで試験できなくなった。
返信削除去年まではできていたような気がしたのだが…
確かに今年になってから審査時に、Reviewerから課金処理が動作しないから、バンドルIDで登録し直せと指摘を受けたな〜。その時はあまり考えず言う通りにしたわけだが。