Help us understand the problem. What is going on with this article?

Apple月額課金について【Productionレシート】

More than 3 years have passed since last update.

先日のAppleの発表により、月額課金を適用できるアプリの範囲拡大や利益配分の変更などが発表されました。
これから月額課金を実装するアプリが増えていくと予想されますが、Appleの課金の仕様はかなり複雑であり、ドキュメントを読んだだけで理解できない部分もあります。

そこで、今回は本番(Production)環境で取得できるレシートをまとめて、今後課金を実装する人の助けになればと思い書かせていただきます。
今回の記事ではアプリ内で取得したレシートデータをAppleのサーバに送信し、返答のjsonを一部改変(アプリ固有の情報を削除)した物をまとめていきます。

課金の実装にあたってはCyberAgentEngineerBlogを参考にさせていただきました。
レシートの有効期限やSandBox環境等の情報は今回の記事では扱いませんので、上記のブログを参考にしてください。

記事対象

今回記載するデータはiOS7以降、具体的に以下のコードで取得できるデータを取り扱います。

receipt.swift
NSBundle.mainBundle().appStoreReceiptURL

未課金時

AppStoreにリリースされているアプリでは、課金アイテムがない場合も appStoreReceiptURL にレシートが存在します。

no-purchase.json
{
  "status": 0,
  "environment": "Production",
  "receipt": {
    "receipt_type": "Production",
    "adam_id": 0,
    "app_item_id": 0,
    "bundle_id": "bundle_id",
    "application_version": "1.0",
    "download_id": 0,
    "version_external_identifier": 0,
    "receipt_creation_date": "2016-02-18 12:15:48 Etc/GMT",
    "receipt_creation_date_ms": "1455797748000",
    "receipt_creation_date_pst": "2016-02-18 04:15:48 America/Los_Angeles",
    "request_date": "2016-02-18 13:05:37 Etc/GMT",
    "request_date_ms": "1455800737074",
    "request_date_pst": "2016-02-18 05:05:37 America/Los_Angeles",
    "original_purchase_date": "2016-02-18 09:12:43 Etc/GMT",
    "original_purchase_date_ms": "1455786763000",
    "original_purchase_date_pst": "2016-02-18 01:12:43 America/Los_Angeles",
    "original_application_version": "1.0",
    "in_app": []
  }
}

未課金状態では、レシートの大元データのみが存在し、初回にレシートが作成された日付やbundle_id、アプリケーションバージョンなどが格納されています。

課金開始時

初めて月額課金を開始した後のレシートは以下のような形となります。

purchase.json
{
  "status": 0,
  "environment": "Production",
  "receipt": {
    "receipt_type": "Production",
    "adam_id": 0,
    "app_item_id": 0,
    "bundle_id": "bundle_id",
    "application_version": "1.0",
    "download_id": 0,
    "version_external_identifier": 0,
    "receipt_creation_date": "2016-02-18 13:08:26 Etc/GMT",
    "receipt_creation_date_ms": "1455800906000",
    "receipt_creation_date_pst": "2016-02-18 05:08:26 America/Los_Angeles",
    "request_date": "2016-02-18 13:08:26 Etc/GMT",
    "request_date_ms": "1455800906898",
    "request_date_pst": "2016-02-18 05:08:26 America/Los_Angeles",
    "original_purchase_date": "2016-02-18 09:12:43 Etc/GMT",
    "original_purchase_date_ms": "1455786763000",
    "original_purchase_date_pst": "2016-02-18 01:12:43 America/Los_Angeles",
    "original_application_version": "1.0",
    "in_app": [
      {
        "quantity": "1",
        "product_id": "product_id",
        "transaction_id": "0",
        "original_transaction_id": "0",
        "purchase_date": "2016-02-18 13:08:25 Etc/GMT",
        "purchase_date_ms": "1455800905000",
        "purchase_date_pst": "2016-02-18 05:08:25 America/Los_Angeles",
        "original_purchase_date": "2016-02-18 13:08:25 Etc/GMT",
        "original_purchase_date_ms": "1455800905000",
        "original_purchase_date_pst": "2016-02-18 05:08:25 America/Los_Angeles",
        "expires_date": "2016-03-18 12:08:25 Etc/GMT",
        "expires_date_ms": "1458302905000",
        "expires_date_pst": "2016-03-18 05:08:25 America/Los_Angeles",
        "web_order_line_item_id": "0",
        "is_trial_period": "false"
      }
    ]
  },
  "latest_receipt_info": [
    {
      "quantity": "1",
      "product_id": "product_id",
      "transaction_id": "0",
      "original_transaction_id": "0",
      "purchase_date": "2016-02-18 13:08:25 Etc/GMT",
      "purchase_date_ms": "1455800905489",
      "purchase_date_pst": "2016-02-18 05:08:25 America/Los_Angeles",
      "original_purchase_date": "2016-02-18 13:08:25 Etc/GMT",
      "original_purchase_date_ms": "1455800905000",
      "original_purchase_date_pst": "2016-02-18 05:08:25 America/Los_Angeles",
      "expires_date": "2016-03-18 12:08:25 Etc/GMT",
      "expires_date_ms": "1458302905489",
      "expires_date_pst": "2016-03-18 05:08:25 America/Los_Angeles",
      "web_order_line_item_id": "0",
      "is_trial_period": "false"
    }
  ],
  "latest_receipt": "base_64_latest_receipt"
}

課金状態になるとin_applatest_receipt_infoにレシート情報が記載されます。
また、latest_receiptには最新のレシートがbase64文字列で格納されています。

ここで、注意しなければいけないのは、latest_receipt_infoin_appは同階層にいないことです。

課金後にアプリを削除し、リストア処理

一度アプリを削除し、リストアを行いレシートを復元させた場合は以下のような形となります。

restore.json
{
  "status": 0,
  "environment": "Production",
  "receipt": {
  "receipt_type": "Production",
    "adam_id": 0,
    "app_item_id": 0,
    "bundle_id": "bundle_id",
    "application_version": "1.0",
    "download_id": 0,
    "version_external_identifier": 0,
    "receipt_creation_date": "2016-02-18 13:29:12 Etc/GMT",
    "receipt_creation_date_ms": "1455802152000",
    "receipt_creation_date_pst": "2016-02-18 05:29:12 America/Los_Angeles",
    "request_date": "2016-02-18 13:29:13 Etc/GMT",
    "request_date_ms": "1455802153032",
    "request_date_pst": "2016-02-18 05:29:13 America/Los_Angeles",
    "original_purchase_date": "2016-02-18 09:12:43 Etc/GMT",
    "original_purchase_date_ms": "1455786763000",
    "original_purchase_date_pst": "2016-02-18 01:12:43 America/Los_Angeles",
    "original_application_version": "1.0",
    "in_app": [
      {
        "quantity": "1",
        "product_id": "product_id",
        "transaction_id": "0",
        "original_transaction_id": "0",
        "purchase_date": "2016-02-18 13:08:25 Etc/GMT",
        "purchase_date_ms": "1455800905000",
        "purchase_date_pst": "2016-02-18 05:08:25 America/Los_Angeles",
        "original_purchase_date": "2016-02-18 13:08:25 Etc/GMT",
        "original_purchase_date_ms": "1455800905000",
        "original_purchase_date_pst": "2016-02-18 05:08:25 America/Los_Angeles",
        "expires_date": "2016-03-18 12:08:25 Etc/GMT",
        "expires_date_ms": "1458302905000",
        "expires_date_pst": "2016-03-18 05:08:25 America/Los_Angeles",
        "web_order_line_item_id": "0",
        "is_trial_period": "false"
      }
    ]
  },
  "latest_receipt_info": [
    {
      "quantity": "1",
      "product_id": "product_id",
      "transaction_id": "0",
      "original_transaction_id": "0",
      "purchase_date": "2016-02-18 13:08:25 Etc/GMT",
      "purchase_date_ms": "1455800905489",
      "purchase_date_pst": "2016-02-18 05:08:25 America/Los_Angeles",
      "original_purchase_date": "2016-02-18 13:08:25 Etc/GMT",
      "original_purchase_date_ms": "1455800905000",
      "original_purchase_date_pst": "2016-02-18 05:08:25 America/Los_Angeles",
      "expires_date": "2016-03-18 12:08:25 Etc/GMT",
      "expires_date_ms": "1458302905489",
      "expires_date_pst": "2016-03-18 05:08:25 America/Los_Angeles",
      "web_order_line_item_id": "0",
      "is_trial_period": "false"
    }
  ],
  "latest_receipt": "base_64_latest_receipt"
}

データの違いはレシートの作成日やlatest_receiptの値が変更されたのみで課金情報等はかわりありませんでした。

自動更新があった後に古いレシートをAppleに送信

今までの情報はweb上で検索すればいくつか見ることができましたが、継続課金が一度でも行われた後のレシートの情報はあまりありませんでした。
今回は初回課金のレシートをサーバ側に保持しておき、そのデータをそのまま継続課金後(1ヶ月後)にAppleに送信して得られたレシート情報を記載します。

この時、継続課金が行われたアプリは初回の課金状態から一度も起動を行わずレシートの更新処理などもまったく行っていません。

renewal-purchase.json
{
  "status": 0,
  "environment": "Production",
  "receipt": {
    "receipt_type": "Production",
    "adam_id": 0,
    "app_item_id": 0,
    "bundle_id": "bundle_id",
    "application_version": "1.0",
    "download_id": 0,
    "version_external_identifier": 0,
    "receipt_creation_date": "2016-02-18 13:29:12 Etc/GMT",
    "receipt_creation_date_ms": "1455802152000",
    "receipt_creation_date_pst": "2016-02-18 05:29:12 America/Los_Angeles",
    "request_date": "2016-03-22 08:35:07 Etc/GMT",
    "request_date_ms": "1458635707217",
    "request_date_pst": "2016-03-22 01:35:07 America/Los_Angeles",
    "original_purchase_date": "2016-02-18 09:12:43 Etc/GMT",
    "original_purchase_date_ms": "1455786763000",
    "original_purchase_date_pst": "2016-02-18 01:12:43 America/Los_Angeles",
    "original_application_version": "1.0",
    "in_app": [
      {
        "quantity": "1",
        "product_id": "product_id",
        "transaction_id": "0",
        "original_transaction_id": "0",
        "purchase_date": "2016-02-18 13:08:25 Etc/GMT",
        "purchase_date_ms": "1455800905000",
        "purchase_date_pst": "2016-02-18 05:08:25 America/Los_Angeles",
        "original_purchase_date": "2016-02-18 13:08:25 Etc/GMT",
        "original_purchase_date_ms": "1455800905000",
        "original_purchase_date_pst": "2016-02-18 05:08:25 America/Los_Angeles",
        "expires_date": "2016-03-18 12:08:25 Etc/GMT",
        "expires_date_ms": "1458302905000",
        "expires_date_pst": "2016-03-18 05:08:25 America/Los_Angeles",
        "web_order_line_item_id": "0",
        "is_trial_period": "false"
      }
    ]
  },
  "latest_receipt_info": [
    {
      "quantity": "1",
      "product_id": "product_id",
      "transaction_id": "0",
      "original_transaction_id": "0",
      "purchase_date": "2016-02-18 13:08:25 Etc/GMT",
      "purchase_date_ms": "1455800905489",
      "purchase_date_pst": "2016-02-18 05:08:25 America/Los_Angeles",
      "original_purchase_date": "2016-02-18 13:08:25 Etc/GMT ",
      "original_purchase_date_ms": "1455800905000",
      "original_purchase_date_pst": "2016-02-18 05:08:25 America/Los_Angeles",
      "expires_date": "2016-03-18 12:08:25 Etc/GMT",
      "expires_date_ms": "1458302905489",
      "expires_date_pst": "2016-03-18 05:08:25 America/Los_Angeles",
      "web_order_line_item_id": "0",
      "is_trial_period": "false"
    },
    {
      "quantity": "1",
      "product_id": "product_id",
      "transaction_id": "0",
      "original_transaction_id": "0",
      "purchase_date": "2016-03-18 12:08:25 Etc/GMT",
      "purchase_date_ms": "1458302905000",
      "purchase_date_pst": "2016-03-18 05:08:25 America/Los_Angeles",
      "original_purchase_date": "2016-03-18 06:08:31 Etc/GMT",
      "original_purchase_date_ms": "1458281311702",
      "original_purchase_date_pst": "2016-03-17 23:08:31 America/Los_Angeles",
      "expires_date": "2016-04-18 12:08:25 Etc/GMT",
      "expires_date_ms": "1460981305000",
      "expires_date_pst": "2016-04-18 05:08:25 America/Los_Angeles",
      "web_order_line_item_id": "0",
      "is_trial_period": "false"
    }
  ],
  "latest_receipt": "base_64_latest_receipt"
}

レシートデータの中は、latest_receipt_infoの中に更新後のレシートが入っていることがわかります。
この情報を元に継続課金が行われているかどうかの判定を行えばよさそうです。

in_appの中はレシートはそのままなのがわかります。
これは、base64が作成された時点でアプリ内に入っているレシートが格納されているため、初回のレシートを投げると初回の状態のままのin_appが返答されます。

また、課金開始時のjson内のlatest_receiptのbase64文字列をこのタイミングでAppleに送信しても同様の結果を得られます。

アプリを起動し、最新のレシートをAppleに送信

継続課金後にアプリを起動し、取得できた最新のレシートを送信すると以下のjsonが得られます。

latest_purchase.json
{
  "status": 0,
  "environment": "Production",
  "receipt": {
    "receipt_type": "Production",
    "adam_id": 0,
    "app_item_id": 0,
    "bundle_id": "bundle_id",
    "application_version": "1.0",
    "download_id": 0,
    "version_external_identifier": 0,
    "receipt_creation_date": "2016-03-22 08:45:28 Etc/GMT",
    "receipt_creation_date_ms": "1458636328000",
    "receipt_creation_date_pst": "2016-03-22 01:45:28 America/Los_Angeles",
    "request_date": "2016-03-22 08:46:44 Etc/GMT",
    "request_date_ms": "1458636404704",
    "request_date_pst": "2016-03-22 01:46:44 America/Los_Angeles",
    "original_purchase_date": "2016-02-18 09:12:43 Etc/GMT",
    "original_purchase_date_ms": "1455786763000",
    "original_purchase_date_pst": "2016-02-18 01:12:43 America/Los_Angeles",
    "original_application_version": "1.0",
    "in_app": [
      {
        "quantity": "1",
        "product_id": "product_id",
        "transaction_id": "0",
        "original_transaction_id": "0",
        "purchase_date": "2016-02-18 13:08:25 Etc/GMT",
        "purchase_date_ms": "1455800905000",
        "purchase_date_pst": "2016-02-18 05:08:25 America/Los_Angeles",
        "original_purchase_date": "2016-02-18 13:08:25 Etc/GMT",
        "original_purchase_date_ms": "1455800905000",
        "original_purchase_date_pst": "2016-02-18 05:08:25 America/Los_Angeles",
        "expires_date": "2016-03-18 12:08:25 Etc/GMT",
        "expires_date_ms": "1458302905000",
        "expires_date_pst": "2016-03-18 05:08:25 America/Los_Angeles",
        "web_order_line_item_id": "0",
        "is_trial_period": "false"
      },
      {
        "quantity": "1",
        "product_id": "product_id",
        "transaction_id": "0",
        "original_transaction_id": "0",
        "purchase_date": "2016-03-18 12:08:25 Etc/GMT",
        "purchase_date_ms": "1458302905000",
        "purchase_date_pst": "2016-03-18 05:08:25 America/Los_Angeles",
        "original_purchase_date": "2016-03-18 06:08:31 Etc/GMT",
        "original_purchase_date_ms": "1458281311000",
        "original_purchase_date_pst": "2016-03-17 23:08:31 America/Los_Angeles",
        "expires_date": "2016-04-18 12:08:25 Etc/GMT",
        "expires_date_ms": "1460981305000",
        "expires_date_pst": "2016-04-18 05:08:25 America/Los_Angeles",
        "web_order_line_item_id": "0",
        "is_trial_period": "false"
      }
    ]
  },
  "latest_receipt_info": [
    {
      "quantity": "1",
      "product_id": "product_id",
      "transaction_id": "0",
      "original_transaction_id": "0",
      "purchase_date": "2016-02-18 13:08:25 Etc/GMT",
      "purchase_date_ms": "1455800905489",
      "purchase_date_pst": "2016-02-18 05:08:25 America/Los_Angeles",
      "original_purchase_date": "2016-02-18 13:08:25 Etc/GMT",
      "original_purchase_date_ms": "1455800905000",
      "original_purchase_date_pst": "2016-02-18 05:08:25 America/Los_Angeles",
      "expires_date": "2016-03-18 12:08:25 Etc/GMT",
      "expires_date_ms": "1458302905489",
      "expires_date_pst": "2016-03-18 05:08:25 America/Los_Angeles",
      "web_order_line_item_id": "0",
      "is_trial_period": "false"
    },
    {
      "quantity": "1",
      "product_id": "product_id",
      "transaction_id": "0",
      "original_transaction_id": "0",
      "purchase_date": "2016-03-18 12:08:25 Etc/GMT",
      "purchase_date_ms": "1458302905000",
      "purchase_date_pst": "2016-03-18 05:08:25 America/Los_Angeles",
      "original_purchase_date": "2016-03-18 06:08:31 Etc/GMT",
      "original_purchase_date_ms": "1458281311702",
      "original_purchase_date_pst": "2016-03-17 23:08:31 America/Los_Angeles",
      "expires_date": "2016-04-18 12:08:25 Etc/GMT",
      "expires_date_ms": "1460981305000",
      "expires_date_pst": "2016-04-18 05:08:25 America/Los_Angeles",
      "web_order_line_item_id": "0",
      "is_trial_period": "false"
    }
  ],
  "latest_receipt": "base_64_latest_receipt"
}

アプリ内のデータも更新されているレシートのため、in_appの中のレシートも2個になっています。

まとめ

今回はただ、appleから返答されているデータをまとめただけですが、実際に運用されているProduction環境のレシートの例は少なく、継続課金時の判定に参考になるかと思います。

願わくばこの検証を行うためにApple様に捧げた実費が今後の課金機能実装者の助けになれば幸いです。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away