gosyuincho-case.jpg
総桐仕上げの豪華御朱印帳ケース! 千糸繍院 御朱印帳ケース / 御朱印帳保管用 総桐箱 12冊用

『御朱印マップ』に関するお問い合わせは、下記の記事のコメントでお寄せ下さい。

お問い合わせ用記事

秋の京都、青蓮院の将軍塚です✨ - Spherical Image - RICOH THETA
ワンショットで360°全天球写真・動画が撮れる! RICOH THETA S 360°全天球カメラ 910720

2016年07月16日

App内課金の問題対応(購入したApp内課金が動作しない場合)

Xcode「このApp内課金はすでに購入済みです。 無料で復元できます。」
と表示されても、App内課金(In-App purchase)がダウンロードできず、復旧(restore)もできない場合があります。
その問題を発生させないためのソースコードメモです。

iPhoneアプリ『御朱印マップ』を提供していて、ユーザーの方から「App内課金でお金を払ったのに使用できない」というお問い合わせを数件受けました。

課金はされたが購入したものは使えず、再購入もできず、復旧もできないということで、お金を払ったのにむしろ使えなくなるという、とても申し訳ない状況となります。

Appleにも問い合わせを行った結果、ユーザー側でAppleに問い合わせ、課金処理を取り消してもらうことで解消することがわかっています。

  ◆購入したマップやマップセットが使えない場合の対処方法

上記記事は「問題が発生してしまった後の対処方法」ですので、この記事では「問題を発生させない対応方法」を記載していきます。
なお以下の記載は、Objective-C、iOS 9.3.2、Xcode 7.3 の環境で、App内課金の種類は非消耗型(Non-consumable)です。


(1) 問題の現象

この問題は、以下のメッセージが表示される場合が該当します。

このApp内課金はすでに購入済みです。
無料で復元できます。

20160708_01.jpg

※英語版メッセージ
This In-App Purchase has already been bought.
it will be restored for free.

このメッセージが表示されると、この画面で「OK」をタップしても、SKPaymentのTransactionObserverが何もレスポンスを返しません。結果として、以降の処理のハンドリングができなくなります。

この現象は、Apple側で課金処理が完了した後に発生します。
従って、ユーザーはお金を支払っている状態です。

アプリ内では、購入処理が完了していません。そのため再購入処理が可能であり、本来は無料で再購入が完了するところ、上記メッセージが表示されてそれ以上先に進めません。
従って、ユーザーは購入した機能を使用することができません。

Apple側に購入履歴が存在するので、本来ならばrestoreできるはずです(非消耗型なので)。しかし上記メッセージが表示される商品は、restoreもできません。

このメッセージが表示される場合でも、他のApp内課金は正常に処理できることもわかっています。restoreもできます。
このメッセージが表示されるコンテンツだけが、ユーザーは使用できない状態となってしまいます。

ちなみにこの問題は、アプリを削除して再インストールしても、解消しないようです。


(2) 問題発生の原因

この現象は、iOSの処理の仕組み上発生してしまう問題です。

App内課金を、アプリの外側で購入してしまうと発生します。

20160708_02_08.jpg

実際に報告された問題は、以下の場合に発生しました。

購入しようとしたらクレジットカードの有効期限が切れていて、カード情報を更新して購入した。
購入しようとしたらiTunesカードの残高が不足していて、残高を追加して購入した。

このような場合、アプリはバックグラウンドに移動するのですが、iOSではユーザーの利便性の為か、購入処理を継続します。
そして、アプリがバックグラウンドのまま購入してしまった商品が、アプリ内で使用できなくなってしまうのです。

iOSの仕組みで発生してしまう問題なので、Appleに課金処理をキャンセルしてもらったら解消できるかと思って問い合わせたところ、解消しました。

  ◆購入したマップやマップセットが使えない場合の対処方法

プログラムで未然に防止できない場合、ユーザー様に上記の対応をとって頂ければ、お手間をかけてはしまいますが不満の残る課金はなくなります。

この問題は、SKPaymentのQueueに、未完了のtransactionが残ってしまうために発生します。

SKPaymentのdefaultQueueはアプリの外側にあるため、アプリを削除しても、未完了のtransactionはQueueの中に残ったままになってしまいます。
そのため、アプリを再インストールしても解消しません。

ですので対応方法としては、未完了のtransactionをQueueから消してあげる、ということになります。


(3) Sandbox での問題再現方法

この問題を、Sandbox環境で再現する方法は以下の通りです。
シミュレータではなく、実機を使用します。

@Macと実機を接続し、Xcodeから実機上でアプリを起動します。

20160716_01.jpg

私のアプリでは、この画面右上の「地図購入」ボタンから、非消耗型のApp内課金が購入できます。

Aアプリ上で購入処理を開始します。

20160716_02.jpg

iTunes storeへのログイン画面が表示されたら、テスト用IDでログインしてください。

B購入確認画面が表示されたら、Xcodeでアプリの実行を停止します。

Aの手順の後、Apple側の処理で下記のような購入確認画面が表示されます。

20160716_03.jpg


この画面が表示されたら、Xcodeでアプリの実行を停止してください。
下の画面のように、アプリは停止しますが、購入確認画面が残ります。

20160716_04.jpg

C残っている購入確認画面で、購入を完了させます。

20160716_05.jpg

これで、この問題が発生する原因の作成が完了しました。

Dもう一度Xcodeからアプリを起動し、同じ商品の購入処理を開始します。

20160716_01.jpg

20160716_03.jpg

購入処理を進めると、問題のメッセージが表示されます。

20160716_07.jpg

この後、Observerからは何もレスポンスが返って来ません。
私のアプリの場合は、Observerから返答がくるまでインジケータを表示しているのですが、それがいつまでたっても消えません。

20160716_08.jpg

この状態が、問題の状態です。
購入したものが使えず、再購入もできない状態。復旧もできません。

この状態で、SKPaymentのQueueを確認すると、transactionが残っていることがわかります。

    // 未完了トランザクションの個数確認。
NSLog(@"%lu", [SKPaymentQueue defaultQueue].transactions.count);
  2016-07-16 13:07:38.229 GOSYUIN Map[779:60b] 1

Queueの中に、トランザクションが1件残っている状態です。

この状態を解消すると、App内課金の再購入や復旧ができるようになります。


(4) 対応方法

Queueの中に残っている未完了トランザクションを完了させると、この問題を解消することができます。

未完了トランザクションの完了には、finishTransaction:を使用します。

この処理をどのタイミングで実施するかですが、AppDelegateの中で実施するとQueueの中の未完了トランザクション数が常に0で検知されるという噂もあるため、私の場合は購入画面のviewWillAppear:で実施しています。

- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];

// 他のviewWillAppear:の処理。

// 未完了トランザクションがないか確認する。
if ([SKPaymentQueue defaultQueue].transactions.count > 0) {
// 未完了トランザクションが存在する場合は、個別に処理を行う。
for (SKPaymentTransaction *tempTransaction in [SKPaymentQueue defaultQueue].transactions) {
// 未完了トランザクションのステータスを確認する。
if (tempTransaction.transactionState != SKPaymentTransactionStatePurchasing) {
// 未完了トランザクションが「購入中」以外の場合は終了させる。
[[SKPaymentQueue defaultQueue] finishTransaction:tempTransaction];
}
}
}
}

14行目で未完了トランザクションを完了させています。

この処理は、未完了トランザクションが無い場合は不要な処理です。
そのため、8行目で未完了トランザクション数の確認をしています。

未完了トランザクションが存在する場合、複数件の配列形式なので、10行目でforを回して個別に処理を行います。

トランザクションのステータスが
 「購入中(=SKPaymentTransactionStatePurchasing)」
のものをfinishTransaction:させると、エラーが出てしまうことがあるそうです。
ですので、12行目で「購入中」以外のものを処理するようにしています。

そして、14行目でトランザクションを完了させます。
これで、この問題は解消されます。


(5) 問題の解消確認

先ほど(3)の手順で再現したApp内課金を使用して、問題が解消していることを確かめましょう。

もう一度購入処理を開始します。

20160716_01.jpg

20160716_09.jpg

20160716_10.jpg

非消耗型を購入済みなので、この画面が表示されます。
ここで「OK」をタップすると、無料で取得できます。

20160716_11.jpg

無事解消できました。

ちなみに消耗型のApp内課金の場合は、再購入できるようにはなりますが、無料で復旧できず、再課金されるようです。
消耗型でこの問題が発生した場合は、ご紹介している別の記事に記載している通り、ユーザー様からAppleに返金処理を依頼して頂いた方が良いかもしれません。

以上です。

この記事が参考になったら下記バナーをクリック!
ランキングUPにご協力をお願いします。m(_ _)m

ico_GOSYUIN-Map.png

iPhoneで御朱印集めを楽しくするアプリ『御朱印マップ』を公開しています。ダウンロードはこちらからどうぞ。

ラベル:SKPaymentQueue
posted by o(^0^)o ほくほく at 10:51| Comment(0) | Xcodeメモ | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前: [必須入力]

メールアドレス:

ホームページアドレス:

コメント:

※ブログオーナーが承認したコメントのみ表示されます。