StoreKit アプリ内課金で正常に終了していないトランザクションに対する対処方法

More than 3 years have passed since last update.


追記

記載のコードだとクラッシュする致命的なバグがあるので以下を追記しました。

if (t.transactionState != SKPaymentTransactionStatePurchasing) {

[[SKPaymentQueue defaultQueue] finishTransaction:t];
}

SKPaymentTransactionStatePurchasing-finishTransaction:すると例外が発生します

ただこれだとSKPaymentTransactionStatePurchasingなtransactionがある場合はどうすればいいのだろう・・・やはりSKPaymentTransactionObserver Protocolで何とか処理する方法がいいんですかね・・・。


Non-Consumable(非消耗型)での正常に終了していないトランザクションに対する対処方法です。

すでにこのアイテムを購入されていますが、ダウンロードされていません。今すぐダウンロードするには「OK」をタップします。

購入処理が正常に終了しなかった場合に再度購入しようとすると上記のアラートが出る場合があります。

このアラートが出る原因は、finishTransaction:がされてないトランザクションに対して同一のアイテムで再度購入リクエストを送ると表示されます。ちなみにOKを押しても購入処理は完了されません。

ユーザーが何をすれば起きるかですが、


  • 購入処理中にアプリを強制終了

  • 購入処理中にバックグラウンドに遷移する(※詳しくは補足で説明)

などが発生の原因になりそうです。(ようはそのトランザクション処理がfinishTransaction:されなければ起きるということです)

※以前、LINEアプリで起動の度にAppleIDとパスワードのアラートが出てしまうバグが起こっていましたが、この正常に終了してないトランザクションが原因かと思われます。


対応

単純にdefaultQueueに未処理のトランザクションがないかを確認し、その後未処理の各トランザクションに対してfinishTransaction:すれば解決します。


StoreKitHelper.m

+ (BOOL)isUntreatedTransaction

{
return [SKPaymentQueue defaultQueue].transactions.count > 0 ? YES : NO;
}

確認と一緒にfinishTransaction:したければ以下のようにすれば良いかと思います。

(更に購入フラグを立てたければ、各トランザクションを個別で判断やRestore処理をする必要があります)


StoreKitHelper.m

+ (BOOL)isUntreatedTransaction

{
BOOL isUntreated = NO;
NSArray *transactions = [SKPaymentQueue defaultQueue].transactions;
for (SKPaymentTransaction *t in transactions) {
NSLog(@"%@ %@ %@ %@ %d", t, t.transactionIdentifier, t.transactionReceipt, t.transactionDate, t.transactionState);
if (t.transactionState != SKPaymentTransactionStatePurchasing) {
[[SKPaymentQueue defaultQueue] finishTransaction:t];
}
isUntreated = YES;
}
return isUntreated;
}


注意

- application:didFinishLaunchingWithOptions:

の時点では、未処理のトランザクションがあってもtransactionsは0で確認出来ませんでした。

タイミングがわからないので、起動時とかでなくリクエストを送る前に確認するようにしています。


補足

iOS5とiOS6では微妙に挙動の違いがあります。

iOS6でのほとんどの場合に購入処理中にバックグラウンドに遷移してもキャンセル処理が発生します。(iOS5ではキャンセル処理が発生しないタイミングがiOS6より多かったです。)

ただし、購入アラートが表示されている状態(or直前)でバックグラウンドに遷移するとホーム画面で購入処理画面が表示され、そのまま購入処理を進めることが出来てしまいます。ようはタイミングによってはキャンセル処理が呼ばれない場合があります。

もしもそこで正常に購入処理を最後まで進めてしまいかつアプリに戻らなかった場合、今回のfinishTransaction:されてないトランザクションが出来てしまいます。

別の対処法でUIApplicationDelegateでバックグラウンドに遷移した場合にトランザクションを終了させてしまえばいいとも考えられます。ただしその場合でも強制終了には対応出来ないと思われますので、やはりリクエストを送る時に確認する方法が良いかと思います。