StoreKit の覚書

iOS における `StoreKit` が意味わからなさすぎるのでひとまず覚書
なにに使えるかは知らない

後から内容を180度修正する可能性もある
それくらいわけがわからない
情報も少ない・・・つらい

※ちなみに自分は消耗型の課金のみ扱ってるので、他の場合は知らない


## SKErrorUnknown と SKErrorPaymentCancelled は同じようなもん
主に課金周りの処理で、キャンセルボタンを押したら `SKErrorPaymentCancelled`
ホームボタンを押しちゃったりしたら `SKErrorUnknown` が返ってくる
という認識だったが、
これらは **iOSのバージョン毎にしっちゃかめっちゃかである**
ということがわかった

iOS7,iOS8,iOS9 で検証したが、例えば A の操作を行ったとして

- iOS7 では SKErrorPaymentCancelled が返ったかと思ったら
- iOS8 では SKErrorUnknown が返り
- iOS9 では A の操作そのものができない

色んな状況での返りをテキストにしたためたが、
結局 SKErrorUnknown と SKErrorPaymentCancelled が返ったら
課金処理エラーです とか、どっちも同じアラートだせばいいじゃん？　って結論になった（現時点）


## ［購入する］を選んだ後の通信エラーの判断
SKErrorUnknown と SKErrorPaymentCancelled は同じやん？
って言っておきながら、同じじゃない特例もあった

購入するを選んだ後に、すぐさま`機内モード`にしたりしてネットを切断すると
`-paymentQueue:updatedTransactions:` にて、`SKPaymentTransactionStateFailed` がすぐ返ってくる
この時の`transaction.error`の中身がまた iOS 毎に違う

- iOS7 では `transaction.error.code` が `SKErrorUnknown`
- iOS8, iOS9 ではそもそも `transaction.error` が `null`

ちなみに、ネットの接続状態を確認できる `Reachability` を使用しても、
この時点で通信状態の変化は読み取れない
仕方ないね

※ 2015/12/02 追記
`NSURLConnection` でどっかのURLに同期接続して、404でもなんでもとにかくネット接続できてるかどうかで判断する方法をとりました
どっかのURLといってグーグル先生とかを選ぶと普通にやばいので自鯖のURLで
`NSData` にも同期通信するメソッドはあったけど、こっちはステータスコードがとれないのでNG
他に良い方法あると思うんだけど、考える頭がないので応急処置(´･ω･`)
※

トランザクションをフィニッシュして、エラーを確認して処理を変えてる場合
iOS8 と iOS9 ではエラーそのものが確認できないので
失敗してんのに成功したと思われんじゃない？ なんかやばいんじゃない？　っていうお話

※ 2015/12/02 ↓に通信エラー時のことについて追記


## 購入中に通信エラー起こしたら、すぐ復元処理を行ったほうがいい
※現状 iOS8 のみでテスト中

上の章の続き
ここでいう復元処理ってのは `-restoreCompletedTransactions` を呼び出す
という意味

復元処理は消耗型アイテムには関係ないで〜　ってどっかに書いてあった気がしますが
「［購入する］ボタン選択直後に通信エラーを起こした場合」という状況においてはリストアは必要だ」
と思いました

という内容を↓にずらずら記載
書いてるうちにどんどん長くなったので推敲してないけど
めんどくさいんで一旦うpしましょう(´･ω･`)
<br>
***
<br>
購入中通信エラーを起こした時のトランザクションの状態は前章に書いた通りです

そのトランザクションを `-finishTransaction:` すれば、`[SKPaymentQueue defaultQueue].transactions` の中身はゼロになる
この辺までは通常の失敗処理と同じ
なんだけど・・・

その後、**ネット接続有効にした状態で「ホームボタン１回押下でホーム->アプリに戻る」**
手順を踏むと、**［iTunes Storeにサインイン］ダイアログが表示**されちゃう
（このダイアログのタイトル長いし、正式名称がわからんので、以後とりあえず認証ダイアログとよびます
サインインダイアログだと他とかぶる(´･ω･`)）

認証ダイアログが開く条件は

>1. リクエストを投げる（SKProductsRequest、SKReceiptRefreshRequestとか）
- リストア（-restoreCompletedTransaction）した時（確か出た）
- フィニッシュしてないトランザクションが端末に残ってる時に、特定手順を踏む（通信切断状態ならなにも起きない）
 - `[SKPaymentQueue defaultQueue]` を初めて呼んだ時
 - `[SKPaymentQueue defaultQueue]` を呼んだ後・・・
     - ホームに行ってからアプリに戻ってくる
     - 通信状態の切り替え（機内モードをOFFにしてからONに戻した時とか）
     - なんか時間経過すると？

とりあえずこういう感じ
この認証ダイアログってのはレシートの同期が必要な時によばれてんのかなーと
でもこのダイアログって、それぞれ同じ認証してるわけじゃないっぽいし、レシート更新リクエストで認証しても、商品投げる時にまた認証求められるし、でも別の認証ダイアログだと商品購入の認証しなくて済んだり・・・
と、深く調べようとするとなんだこれ状態になるんだけど
（自分もそのあたりはまとめるの諦めた）
とにかく**３番目の条件だけは、未処理のトランザクションが残ってる限り何度でも出続ける**
(´･ω･`)やばくない？
<br>
***
<br>
じゃあトランザクションを finish!! すれば出てこなくなるんでしょう
と思ったんだけど、最初の方に言った通り
「購入中通信エラーした時の `[SKPaymentQueue defaultQueue].transactions` の中身はゼロ」なんだよね・・・
トランザクションの量を確認できる唯一のプロパティの中身を見ても、トランザクションが残ってるかどうかわかんないんだよね・・・
Key-Value Coding とかで、transactions の変化を逐一チェックしようと思ったけど効果なかったしね・・・
（あと細かいところだと、購入中通信エラー時は通信状態の切り替えを行ってもダイアログが表示されないです
アプリを再起動したら通信状態の切り替えでダイアログでたかなって思う）


(´･ω･`)もうやだ


トランザクションがゼロなのを復元する方法は、

- リストア処理する
- 手順を踏んで表示したダイアログで認証する
- 失敗したリクエストと同じリクエストを投げる

以上
リストアは言わずもがな
認証ダイアログで認証した時は、`transactionState` が購入完了状態になってて、リストア時とは状態が異なるけど、多分そこが違うだけ
`-paymentQueueRestoreCompletedTransactionsFinished:` とか専用のメソッドが呼ばれるかどうかの違いくらい
同じリクエストを投げた時は、購入するを選ぶことで[この App 内課金はすでに購入済みです。]ダイアログが表示される

正しく処理が書けていれば、復元処理を強制させなくても、同じリクエストを選ぶだけで購入処理は完了させられる

だけども、
１２０円商品のリクエストをミスった！
その後なんの処理もしないで今度は４８０円商品のリクエストを投げて、またミスった！
を繰り返すと、端末に復元待ちのトランザクションがどんどん溜まるわけで
例えば１万円弱の買い物をして、リクエスト失敗して
「それ多分復元待ちなんで、も一回同じ商品を選んでください」ってアラートだしたとして
ユーザーから見たら「え・・・また１万円課金するの？　失敗したらどうすんの・・・？」って思っちゃうと思うわけで
実際失敗したら目もあてられないわけで

名前忘れたけども、どこかのサイトに
「複数の商品を１度に購入しない！　トランザクションは１つずつ確実に処理する！　失敗したら成功するまで繰り替えす！」
と書いてありました
これはトランザクションが複数溜まってると予期せぬ動作が発生するから（だったと思うの）で
この予期せぬ動作ってのは、これまで StoreKit いじってきて嫌という程体験したので、
StoreKit をいじる上での真理だと思いました。

（デバッグ中は停止させるタイミングによっては、普通にトランザクションが複数流れたりもして
トランザクションが１個しか流れないのを前提に作ってるとめんどくさいことになるけども）
<br>
***
<br>
以上のことから、見出しの通り、「購入中に通信エラー起こしたら、すぐ復元処理を行ったほうがいい」という考えに至りました

でも `NSUserDefaults` に復元フラグとか再処理フラグだかを立てといても、アプリの再インストールとかされるとおじゃんじゃんじゃん
もっかい同じリクエストを投げてもらうとか、復元ボタンを用意するとか
トランザクションのカウントはサーバーで行うとか？
ていうか課金処理ミスっといてそのままアプリ削除されたらもうユーザーが１００％悪いよね！

(´･ω･`)もうやだ