5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

AppStoreサブスクリプションのバックエンドを構築する ①レシート検証編

Posted at

とあるwebサービスでApp Storeの自動更新サブスクリプションを実装したのですが、
公式ドキュメントがわりと雑で、backend側を作るのがすごく大変だったのでまとめます

(Node.jsとfirebase Functionsを使用)

前提

この記事はiOSアプリ側の話ではなく、バックエンド側の話なので
レシートデータを取得できてからのお話になります

Sandboxのアカウントの作成、アプリ側の購入処理などについては

@Masataka-n様のどこよりもわかりやすいiOS最強課金まとめで分かりやすくまとめられているので参考にしてみてください

App Storeのサブスクリプションについて

サブスクリプションを実装する上で一番大事なものってなんでしょうか

それは、**『ユーザーが現在サブスクリプション購読中か否か』**を知ることです

そのためには、verifyReceiptApp Store Server Notificationsの2つを理解する必要があります

また、重要なパラメータとしてApp Storeのレシート情報に含まれるoriginal transaction idがあります

これはApple IDと1対1で紐づいているIDで、
これを使用することでサービス内のユーザーを特定することができます

今回はverifyReceiptを、次回でApp Store Server Notificationsを説明していきます

レシート検証API verifyReceipt

とりあえず持っているbase64エンコードされたくっっっっそ長いレシートデータをPostmanなどを使って
以下の通りjson形式で
https://sandbox.itunes.apple.com/verifyReceiptへPOSTしてみましょう

{
  "password": ***, // iOSアプリ側で設定済みのはず
  "exclude-old-transactions": true,
  "receipt-data": ******** //base64エンコードされたくっっっっそ長い文字列
}

返ってくる値はこんな感じです

{
    "environment": "Sandbox",
    "receipt": {
        "receipt_type": "ProductionSandbox",
        "adam_id": 0,
        "app_item_id": 0,
        "bundle_id": "***",
        "application_version": "45",
        "download_id": 0,
        "version_external_identifier": 0,
        "receipt_creation_date": "2020-10-13 01:48:13 Etc/GMT",
        "receipt_creation_date_ms": "1602553693000",
        "receipt_creation_date_pst": "2020-10-12 18:48:13 America/Los_Angeles",
        "request_date": "2020-11-10 01:59:34 Etc/GMT",
        "request_date_ms": "1604973574730",
        "request_date_pst": "2020-11-09 17:59:34 America/Los_Angeles",
        "original_purchase_date": "2013-08-01 07:00:00 Etc/GMT",
        "original_purchase_date_ms": "1375340400000",
        "original_purchase_date_pst": "2013-08-01 00:00:00 America/Los_Angeles",
        "original_application_version": "1.0",
        "in_app": [
            {
                "quantity": "1",
                "product_id": "***",
                "transaction_id": "1000000724743083",
                "original_transaction_id": "1000000724743083",
                "purchase_date": "2020-09-30 10:30:56 Etc/GMT",
                "purchase_date_ms": "1601461856000",
                "purchase_date_pst": "2020-09-30 03:30:56 America/Los_Angeles",
                "original_purchase_date": "2020-09-30 10:30:58 Etc/GMT",
                "original_purchase_date_ms": "1601461858000",
                "original_purchase_date_pst": "2020-09-30 03:30:58 America/Los_Angeles",
                "expires_date": "2020-09-30 10:35:56 Etc/GMT",
                "expires_date_ms": "1601462156000",
                "expires_date_pst": "2020-09-30 03:35:56 America/Los_Angeles",
                "web_order_line_item_id": "1000000056115800",
                "is_trial_period": "false",
                "is_in_intro_offer_period": "false"
            },
            {
                "quantity": "1",
                "product_id": "***",
                "transaction_id": "1000000724745717",
                "original_transaction_id": "1000000724743083",
                "purchase_date": "2020-09-30 10:35:56 Etc/GMT",
                "purchase_date_ms": "1601462156000",
                "purchase_date_pst": "2020-09-30 03:35:56 America/Los_Angeles",
                "original_purchase_date": "2020-09-30 10:30:58 Etc/GMT",
                "original_purchase_date_ms": "1601461858000",
                "original_purchase_date_pst": "2020-09-30 03:30:58 America/Los_Angeles",
                "expires_date": "2020-09-30 10:40:56 Etc/GMT",
                "expires_date_ms": "1601462456000",
                "expires_date_pst": "2020-09-30 03:40:56 America/Los_Angeles",
                "web_order_line_item_id": "1000000056115801",
                "is_trial_period": "false",
                "is_in_intro_offer_period": "false"
            },
            ..
        ]
    },
    "latest_receipt_info": [
        {
            "quantity": "1",
            "product_id": "***",
            "transaction_id": "1000000737669781",
            "original_transaction_id": "1000000724743083",
            "purchase_date": "2020-11-04 08:38:37 Etc/GMT",
            "purchase_date_ms": "1604479117000",
            "purchase_date_pst": "2020-11-04 00:38:37 America/Los_Angeles",
            "original_purchase_date": "2020-11-04 08:37:58 Etc/GMT",
            "original_purchase_date_ms": "1604479078000",
            "original_purchase_date_pst": "2020-11-04 00:37:58 America/Los_Angeles",
            "expires_date": "2020-11-04 08:43:37 Etc/GMT",
            "expires_date_ms": "1604479417000",
            "expires_date_pst": "2020-11-04 00:43:37 America/Los_Angeles",
            "web_order_line_item_id": "1000000057023410",
            "is_trial_period": "false",
            "is_in_intro_offer_period": "false",
            "subscription_group_identifier": "20664761"
        }
    ],
    "latest_receipt": "***", // めっちゃ長い
    "pending_renewal_info": [
        {
            "expiration_intent": "1",
            "auto_renew_product_id": "***",
            "original_transaction_id": "1000000724743083",
            "is_in_billing_retry_period": "0",
            "product_id": "***",
            "auto_renew_status": "0"
        }
    ],
    "status": 0
}

購読中かどうかを知りたいとき

項目多すぎてワケワカラン。

でも大丈夫。**『ユーザーが現在サブスクリプション購読中か否か』**を知りたい場合

latest_receipt_info内のexpired_dateを見るだけです

既に期限切れであれば、latest_receipt_info等についているoriginal_transaction_idから該当のユーザーと照合し、
フラグを折る感じの実装になると思います

過去の購入履歴を知りたい

他にも、過去の購入履歴を見たいときはin_app内からpurchase_dateexpires_dateでフィルターをかけると取得できます

レシートデータの更新

リクエスト時に投げたreceipt-dataが古い場合は、
latest_receiptで渡される値に更新しておきましょう

現在自動更新が有効かどうかを知りたい

ときは、pending_renewal_infoに含まれるauto_renew_statusを見ればOKです

1ならば自動更新が有効で、0ならば次回自動更新されないユーザーです

自動更新されない理由を知りたい

ときは、pending_renewal_infoに含まれるexpiration_intentを見てください

月に一回cronでレシートデータをチェックして、1のユーザーには訴求用のプッシュ通知を送る、
などの実装例が想像できますね

expiration_intent.png

詳しくは公式リファレンスへ

次回 Server Notifications

レシート検証APIはいかがだったでしょうか?

次回はApp Store Server Notificationsについて説明していきます!

5
5
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?