iOSのアプリ内課金(In App Purchase)での注意点

先日、着信メロディーズをリリースした訳ですが、このアプリはアプリ内課金(In App Purchase)で着信音をダウンロードできます。
実はアプリ内課金を実装するのは初めてだったのですが、その際にAppleから度重なる洗礼(リジェクト)を受けました。
そこで、アプリ内課金でのハマりどころを共有したいと思います。

アプリ内課金といっても幾つかの種類がありますが、着信メロディーズは下記の様な感じでした。

  • 着信音は購入後に自サーバーからアプリ内にダウンロードしている。(サーバープロダクトモデル)
  • 着信音は一度購入すれば同一のiTunesアカウントでデバイス間で共有できる。(Non-consumable(非消耗型))
  • ダウンロードした着信音はPCのiTunesとファイル共有するため、アプリ内のDocumentディレクトリに保存。

基本的にはAppleが提供しているIn-App Purchaseプログラミングガイドを読みながら開発をしていきます。

テスト用のiTunesアカウントで事前にサインインしない

開発も終盤に差し掛かると、本当にアプリ内課金が正しく動作するのかを確認したくなりますよね。
実はテスト用のiTunesアカウントを作ってアプリ内課金を試す(課金なしで)ことができます。
「iOS Developer Center > iTunes Connect > Manage Users > Test User」で新しいテスト用アカウントを作成します。
実機の方は「設定 > iTunes & App Stores」に普段使用しているiTunesアカウントが登録されていると思うのでサインアウトしておきます。

っでサインアウトした後にこの画面で先ほど作ったテスト用アカウントでサインインをしてはいけません。

2012121801

ここでサインインはせずにアプリを起動してアプリ内課金のテストを行います。
すると、購入処理時にiTunesアカウントのユーザーID、パスワードをきいてくるのでその時に先ほど作ったテスト用アカウントを入力します。

もし、事前にテスト用アカウントでサインインをしてしまうと、アプリ内課金でコンテンツを購入する際に「テスト用アカウントのパスワードの入力が求められる > パスワード入力してOK > App Storeアプリが起動 > エラーメッセージ」というのを永遠に繰り返すことになりますので気をつけましょう。

Appleサーバーに対してレシートの有効性を確認する場合には本番用URL -> サンドボックスURLの順で検証する

アプリ内課金で配布するコンテンツの情報はAppleサーバーに事前に「iTunes Connect > 該当App > Manage In-App Purchase」で登録しておく必要があります。
購入時には下記の様な処理を行います。 

  1. アプリでコンテンツの購入ボタン押下
  2. コンテンツのProduct IDをAppleサーバーに問い合わせ
  3. 〜課金処理〜
  4. 課金処理が正常終了した場合には、Appleから通知。その際にレシートデータが返される。
  5. レシートデータを持って、自サーバーにコンテンツのダウンロード処理を依頼
  6. 自サーバーではレシートデータを持ってAppleサーバーにレシートデータの有効性を確認
  7. 有効性が確認できたらクライアントへのコンテンツのダウンロードを許可

ここで「6」のレシートデータをAppleサーバーに問い合わせる際に、サンドボックス用のURL(https://sandbox.itunes.apple.com/verifyReceipt)と本番用のURL(https://buy.itunes.apple.com/verifyReceipt)の2種類が存在します。

自分の勝手な思い込みでサンドボックス用のURLは開発者が開発時に使用するもので、Appleの審査時と本番リリース時には本番用のURLを使用するのかと思っていましたが、違いました。

アプリを審査に出す際に本番用のURLに切り替えて提出すると、Appleから「アプリ内課金が動作しないぜ!」っとリジェクトされました。 
つまりAppleの審査もサンドボックス用のURLで行われる様です。
しかも、アプリ側では現在本番環境なのかテスト&審査環境なのかを取得する術がありません。ではどうするのか?

「6」の処理で、アプリから受け取ったレシートデータをまずは、本番用のURLに問い合わせます。戻り値にステータスコードがあるのでステータスコードが「21007」の場合にはそのままサンドボックス用のURLに問い合わせを行うという。つまり、本番もテストも審査も関係なくて本番URLに問い合わせして失敗したらサンドボックスURLに問い合わせしろと。
例えばサーバー側のレシートデータの問い合わせをRailsで書いたらこんな感じ。

復元可能なデータはiCloudのバックアップ対象から除外する

Documentディレクトリ配下は初期設定でiCloudのバックアップ対象ディレクトリになります。 しかしiCloudのバックアップ対象とするのはユーザーが入力した設定値など、アプリだけでは復元不可能なデータとしなければいけないようです。 着信メロディーズではダウンロードした着信音をiTunesとファイル共有するために、Documentディレクトリ配下に保存していました。 しかし着信音はアプリ側で復元可能なデータであり、しかも音楽ファイルとなるとそれなりの容量となります。 そういった物をiCloudのバックアップ対象にしてはならないということでリジェクトされました。

そこでDocumentディレクトに配置したファイルに「iCloudのバックアップ対象から除外する」という属性を付けます。

最後のiCloudはアプリ内課金とは関係ありませんが、その他にも着信音が著作権に関連するコンテンツなのでJASRACとの契約書を送付しろなど5回程度、リジェクトを貰いました。
ただここまでの内容で一つ疑問が残ります。

Appleはコンテンツの内容の審査まではおこなっていない?(予測)

レシートの有効性確認の所で、サーバー側は本番用 > サンドボックス用のサーバーにそれぞれ問い合わせを行います。
しかし、アプリ側では現在の状態(本番か審査中かテストか)を取得することが出来なさそう。(多分)
そうすると今回の様にアプリの初回リリース時には良いのですが、今後コンテンツだけを追加する場合に問題が発生します。

  1. アプリのコンテンツを追加する為に、Appleサーバーにコンテンツを追加(Produdct IDを取得)
  2. アプリ側の一覧で追加コンテンツが表示されるようにサーバー側に追加

しかし、「2」を登録した時点で既に配布されているアプリにはコンテンツが見えてしまいます。
追加コンテンツはApple審査が通過してないのでユーザーがダウンロードしようとするとエラーとなってしまいます。
でも、Appleがコンテンツの中身を審査するためには追加コンテンツをアプリに表示する必要があります。。。ん?

そこでアプリリリース後に試しにコンテンツを追加してApple審査に提出しました。
しかし、アプリ側には表示されないように。。。するとコンテンツの審査が普通に通過しました!

っという事でAppleはコンテンツの内容までは審査していないと予測されます。(予測なので、あくまでも自己責任で)

今回のアプリ開発でアプリ内課金についてはかなり勉強になりました。