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

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

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

課金の実装にあたっては[CyberAgentEngineerBlog](http://ameblo.jp/principia-ca/entry-12071724382.html)を参考にさせていただきました。
レシートの有効期限やSandBox環境等の情報は今回の記事では扱いませんので、上記のブログを参考にしてください。

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

```swift:receipt.swift
NSBundle.mainBundle().appStoreReceiptURL
```

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

```json: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`、アプリケーションバージョンなどが格納されています。

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

```json: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_app`と`latest_receipt_info`にレシート情報が記載されます。
また、`latest_receipt`には最新のレシートがbase64文字列で格納されています。

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

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

```json: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上で検索すればいくつか見ることができましたが、継続課金が一度でも行われた後のレシートの情報はあまりありませんでした。
今回は初回課金のレシートをサーバ側に保持しておき、そのデータをそのまま継続課金後（１ヶ月後）にAppleに送信して得られたレシート情報を記載します。

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

```json: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が得られます。

```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`の中のレシートも２個になっています。

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

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