lasciva blog

開発して得た知見やwebビジネスのストック

iOSアプリの自動更新サブスクリプション課金を実装した

副業で、iOSの自動更新型サブスクリプションの課金を担当して、サーバサイドとクライアントの実装をしたので、まとめた。
公式のガイドを見るのが一番だが、とっつきにくかったり開発する前の調査や理解に時間がかかったので、開発上の注意事項を中心に記載した。

環境

概要

参考になるリンク集

進める上での落とし穴はいくつもあったが、基本的には公式のガイドを見るのが一番良いと思う。

非技術的な仕様

料金設定

Appleが提示する金額リストから指定する形式のため、細かく自由に設定できる訳ではないので、注意。

審査

審査のガイドラインに記載されているように、あくまでも 継続的な価値を提供する必要がある。
ガチャのチケットを割り引いて配布するなどは、通らない可能性が高い(似たような条件でリジェクトされた)ので、他に購読ユーザ限定の機能を提供するなどの見せ方をする必要がありそう。

アップグレードとダウングレード

アップグレードとダウングレードは切り替えのタイミングが異なるので、注意。

アップグレード ダウングレード
切り替えのタイミング 即座に行われる 次回の更新のタイミングで行われる
差額の料金 元々のサブスクリプションの比例配分された金額が返金 そもそも発生しない

特に、アップグレードの切り替えは月途中でも行われるため、グレードに応じて特典の数量を変えるような場合は、要件を詰める必要がある。
アップグレード前のプランの日割り計算の料金で、1月分の数量を使用できるなどの状況が発生する。

開発

購読状態の監視

購読状態を監視するには、2つの手段がある。

サブスクリプション型の課金の更新は、アプリ内のライフサイクルだけでは完結しない。例えば、クレジットカードの有効期限が切れた、アプリをアンインストールし設定画面等から解約されたなど。
そのため、要件にもよるが、両方併用するケースが多くなると思う。

サーバ側

主には以下の3種類ぐらいのエンドポイントが必要になる。

  • アプリ内での購入・キャンセル等のイベント
  • 自動更新登録のサーバー通知
  • 復元

レシートの中身がプロパティが多く、扱いがややこしいので、こちらのライブラリを使った。
メンテされてなさそう感はあるが、最低限の機能は揃っていたので便利だった。

github.com

ライブラリで加工して、以下のようなDBのテーブルに保存した。

+-------------------------+--------------+------+-----+---------+----------------+
| Field                   | Type         | Null | Key | Default | Extra          |
+-------------------------+--------------+------+-----+---------+----------------+
| id                      | bigint(20)   | NO   | PRI | NULL    | auto_increment |
| user_id                 | bigint(20)   | NO   | MUL | NULL    |                |
| transaction_id          | bigint(20)   | NO   | UNI | NULL    |                |
| original_transaction_id | bigint(20)   | NO   |     | NULL    |                |
| product_id              | varchar(255) | NO   |     | NULL    |                |
| quantity                | int(11)      | NO   |     | NULL    |                |
| web_order_line_item_id  | bigint(20)   | NO   |     | NULL    |                |
| first_purchased_at      | datetime     | NO   |     | NULL    |                |
| purchased_at            | datetime     | NO   |     | NULL    |                |
| expires_at              | datetime     | NO   |     | NULL    |                |
| cancelled_at            | datetime     | YES  |     | NULL    |                |
| created_at              | datetime     | NO   |     | NULL    |                |
| updated_at              | datetime     | NO   |     | NULL    |                |
+-------------------------+--------------+------+-----+---------+----------------+

自動更新登録のサーバー通知

AppStoreConnect上のApp情報から登録できるが、これは一つしか登録できない。 そのため、本番環境でも開発環境でも同じURLを使用することになる。

  • リリース後
    • 本番のAPIのエンドポイントを登録せざるを得ない。
    • 本番環境ではsandboxのリクエストはスルーするようにしておく。
  • リリース前
function doPost(e) {
  var spreadSheet = SpreadsheetApp.getActive()
  var sheet = spreadSheet.getActiveSheet()
  sheet.appendRow([new Date(), JSON.stringify(e.postData.getDataAsString())])
}
iOS

開発環境周りがとにかく色々辛い。特に辛かった点を記載する。

AppleIDアカウント

sandbox環境では、5分経過すると課金状態は1ヶ月進み、6ヶ月進むとキャンセルされるようになる。 そのため、非常にテストと開発がし辛い。 同じアカウントを使っていると時間がかかるので、捨てアド専用のサービスを使った。 もしくは、gmailエイリアス機能を使ってもよいかと思う。

復元

アンインストールしたユーザが再インストールした場合に、購入状態を復活させる必要がある。
復元するには、iOSのStoreKitの SKReceiptRefreshRequestを使用するが、開発環境だと別のアカウントの情報も取得できるような挙動が見られた。
これの解決策はイマイチわからず終わった。