サブスクリプションの取り扱い

サブスクリプションを提供するAppについては、追加の動作と考慮事項があります。サブスクリプションには時間の要素がからむので、Appは、サブスクリプションが現在アクティブか、過去のある日のサブスクリプションの状態がどうだったか、を判断できる必要があります。Appは、新規のサブスクリプション、サブスクリプションの更新や中断にも対応し、また、期限切れのサブスクリプションも適切に処理する必要があります。図 6-1に、Appで処理する必要のある複雑な部分も含めた、サブスクリプションのタイムラインの例を示します。

図 6-1 サブスクリプションのタイムラインの例

サブスクリプションのアクティブ期間の計算

Appでは、サブスクリプションのアクティブ期間に基づいて、ユーザーがアクセスできるコンテンツを決定する必要があります。たとえば、毎月初日に最新号が出る月刊誌を定期購読する、という状況で、表 6-1に示すタイムラインを考えてみましょう。

表 6-1 月刊誌を定期購読する例のタイムライン

日付

イベント

サブスクリプションの状態

2月1日

2月号発売。ユーザーがまだ登録していないので提供しない。

未登録

2月20日

月刊誌のプランを登録。最新である2月号をすぐに提供する。

アクティブ

3月1日

3月号発売。サブスクリプションがアクティブなのですぐに提供する。

アクティブ

3月20日

サブスクリプションの自動更新(1か月)。

アクティブ

4月1日

4月号発売。サブスクリプションがアクティブなのですぐに提供する。

アクティブ

4月20日

ユーザーがサブスクリプションをキャンセルし、サブスクリプションの期間が終了。

中断

5月1日

5月号発売。サブスクリプションは中断しているのでユーザーに提供しない。

中断

6月1日

6月号発売。サブスクリプションは中断しているのでユーザーに提供しない。

中断

6月17日

ユーザーが再登録。6月号をすぐに提供する。

アクティブ

7月1日

7月号発売。サブスクリプションがアクティブなのですぐに提供する。

アクティブ

顧客が利用できることになっているコンテンツすべてに、実際にアクセスできるよう、各コンテンツが発行された日付を記録しておきます。各レシートエントリの「オリジナル購入日」、「購入日」、「サブスクリプションの有効期限」フィールドを確認し、各サブスクリプションの期間の開始日と終了日を特定します(レシートについては『Receipt Validation Programming Guide』を参照)。ユーザーは、サブスクリプションを購入した時点で最初にアンロックされたコンテンツに加えて、各サブスクリプションの期間の開始日と各終了日の間に発行されたすべてのコンテンツにアクセスすることができます。サブスクリプションが中断した場合、サブスクリプションのアクティブな期間が複数あることになり、サブスクリプションの期間の開始時にコンテンツがアンロックされます。

サブスクリプションの中断期間は、レシートエントリすべてについて、「サブスクリプションの有効期限」フィールドと、その前のレシートの「PurchaseDate」フィールドを比較することにより特定できます。

表 6-1に示した例を続けましょう。レシートには開始日と終了日が次のように記載されます。

ユーザーは、サブスクリプションを開始/再開した時点でロックが解除される、2月号と6月号を読めます。

さらに、3月号、4月号、7月号も読めます。それぞれの発売日時点でサブスクリプションがアクティブだからです。

アップグレードとプラン変更

ユーザーはサブスクリプションの管理を、App Storeのアカウント設定で、あるいはAppの画面内で行えます。App Storeは各サブスクリプションについて、サブスクリプショングループが提供する更新オプションをすべて表示します。ユーザーは容易にサービスレベルを変更できるほか、アップグレード、ダウングレード、クロスグレードの選択は、必要に応じて何度でも可能です。ある期間中のダウングレードや、異なる期間とのクロスグレードは、次の更新日に有効になります。

レシートの「サブスクリプションの自動更新に関する設定」フィールドで、ユーザーが選択したプラン変更(次の更新日に有効になるもの)について調べることができます(レシートについては『Receipt Validation Programming Guide』を参照)。

期限切れと更新

サブスクリプションの更新プロセスは、期限切れの10日前に始まります。この10日の間に、たとえば、

問題がある場合はApp Storeがユーザーに通知して、更新が必要になる前に問題を解決し、サブスクリプションサービスの中断を回避できるようにします。

サブスクリプションが期限切れになる前の24時間の期間中に、App Storeはサブスクリプションの自動更新を開始します。App Storeは一定期間、数回にわたってサブスクリプションの自動更新を試みますが、失敗する回数が多いと最終的に更新を停止します。

App Storeはサブスクリプションの期間に隙間が生じないように、期限切れになる少し前に更新を行います。ただし、これでも隙間が生じる可能性があります。たとえば、ユーザーの支払情報が有効でなかった場合、最初の更新は失敗します。サブスクリプションが期限切れになった後でユーザーが支払情報を更新した場合は、サブスクリプションが期限切れになった日付と、これに続いて自動更新が成功した日付との間に短い隙間が生じます。ユーザーは自動更新を無効にして、サブスクリプションを意図的に期限切れにしてから、後日、サブスクリプションを更新して、サブスクリプションの期間に長い間隔を空けることができます。Appのサブスクリプションロジックでは、いろいろな長さのサブスクリプションの期間の間隔を正しく扱えるようにしてください。「サブスクリプションの自動更新ステータス」フィールドで、サブスクリプションの更新状態を判断できます。

サブスクリプションの更新に成功すると、StoreKitはトランザクションキューに更新用のトランザクションを追加します。Appは起動時にトランザクションキューをチェックして、他のトランザクションと同じ方法で更新の処理を行います。サブスクリプションの更新時にAppが既に実行中であった場合は、トランザクションのオブザーバは呼び出されません。Appは次回起動されたときに更新を認識します。

たとえば以下のタイムラインは、月次ベースでサービスが提供されるAppのサブスクリプションのユーザーアカウントを表します。この例では請求の問題のため、サブスクリプションが短期間中断します。ユーザーが問題を解決した結果、サブスクリプションは更新され、月次の更新日付が新しくなります。

表 6-2 月次のサブスクリプションのタイムラインの例

日付

イベント

サブスクリプションの状態

2月20日

月次のサブスクリプションを登録。サービスはすぐに有効になる。

アクティブ

3月20日

サブスクリプションの自動更新(1か月)。サービスはなお有効。

アクティブ

4月19日

ユーザーの支払い方法が期限切れになる。

アクティブ

4月20日

サブスクリプションの更新に失敗(トランザクションの失敗のため)。サブスクリプションは中断。

中断

5月5日

ユーザーが支払い方法を更新。App Storeはサブスクリプションの更新に成功する。サービスはすぐに有効になる。

アクティブ

6月5日

サブスクリプションの自動更新(1か月)。サービスはなお有効。

アクティブ

キャンセル

サブスクリプションの購入時には全額が支払われます。Appleカスタマーサービスに連絡することによってのみ払い戻されます。たとえば、ユーザーが間違って違うプロダクトを購入した場合、カスタマーサポートはこのサブスクリプションをキャンセルして一部または全部の払い戻しを行うことができます。サブスクリプション期間の途中でキャンセルしても構いませんが、残りの期間は支払い済みの状態で残ります。

Appleカスタマーサポートが購入をキャンセルしたかどうかをチェックするには、レシートの「キャンセル日」フィールドを確認してください。フィールドに日付が入力されていた場合は、サブスクリプションが期限切れになる日付にかかわらず、購入はキャンセルされています。コンテンツやサービスの提供に関する限り、キャンセルされたトランザクションは、購入が行われなかったものとして扱われます。

Appが提供するプロダクトのタイプに応じて、現在アクティブなサブスクリプションの期間のみをチェックすればよい場合や、過去のすべてのサブスクリプションの期間をチェックするべき場合があります。たとえば、雑誌のAppでは、ユーザーがアクセスできたのがどの号であったのかを判断するために、過去のすべてのサブスクリプションの期間についてチェックする必要があります。一方、ストリーミングサービスを提供するAppは、現在アクティブなサブスクリプションを調べるだけで、ユーザーがサービスにアクセスできるかどうか判断できます。

状態更新通知

statusUpdateNotificationはサーバからサーバへの通知サービスで、自動更新サブスクリプションを対象とします。通知を送信する時点における、サブスクリプションの状態を通知します。

イベントを処理する際、最新の情報を取得するためには、App Storeに問い合わせて、最新のレシートを調べる必要があります。状態更新通知サービスとレシート検証を組み合わせて、ユーザーの現在のサブスクリプションの状態を調べ、サービスに渡すとよいでしょう。レシートの検証については、『Receipt Validation Programming Guide』を参照してください。

状態更新通知を受け取るためには、Appのサブスクリプションの状態を表すURLを、App Store Connectで設定する必要があります。App Storeは、表 6-3に示す主なサブスクリプションイベントが発生したとき、HTTP POSTを介してJSONオブジェクトをサーバに配送します。サーバ側では、受け取ったstatusUpdateNotificationをすべてパースし、解釈した上で、適切に応答しなければなりません。

statusUpdateNotificationはHTTP POSTです。POSTの本体には、表 6-3に示すデータ要素が含まれます。

表 6-3 状態更新通知のキー

キー

説明

environment

通知がサンドボックス環境と実稼働環境のどちらに対するものかを指定:

Sandbox

PROD

notification_type

通知のトリガとなったイベントの種類。値については表 6-4を参照。

password

この値はレシートの検証時にPOSTする共有シークレットと同じ。「App Storeを使用してレシートを検証する」を参照。

original_transaction_id

この値はレシートの「オリジナルのトランザクションID」と同じ。この値を使って、個々の顧客のサブスクリプションに関する、iOS 6型の複数のトランザクションレシートを関連付けることができます。

cancellation_date

Appleカスタマーサポートがトランザクションをキャンセルした日時。notification_typeCANCELである場合のみ。値については表 6-4を参照。

web_order_line_item_id

サブスクリプションの購入を識別するための主キー。notification_typeCANCELである場合のみ。値については表 6-4を参照。

latest_receipt

直近の更新トランザクションに対応するレシートにbase-64エンコードを施したもの。notification_typeRENEWALまたはINTERACTIVE_RENEWALであり、かつ更新に成功した場合のみ。

latest_receipt_info

直近の更新に対応するトランザクションレシートをJSON形式で表したもの。更新に成功した場合のみ。notification_typeCANCELである場合は除外。値については表 6-4を参照。

latest_expired_receipt

直近の更新トランザクションに対応するレシートにbase-64エンコードを施したもの。サブスクリプションが期限切れになった場合のみ。

latest_expired_receipt_info

直近の更新トランザクションに対応するレシートをJSON形式で表したもの。notification_typeRENEWALまたはCANCEL、あるいは更新に失敗し、サブスクリプションが期限切れになった場合のみ。

auto_renew_status

Booleanを表す”true"か"false"の文字列。レシートの自動更新状態と同じ。「レシートのフィールド」も参照。

auto_renew_adam_id

自動更新サブスクリプションについて、更新に関する現在の設定。実際の値はプロダクトのApple ID。

auto_renew_product_id

レシートの「サブスクリプションの自動更新設定」と同じ。「レシートのフィールド」も参照。

expiration_intent

レシートの「サブスクリプションの期限切れの理由」と同じ。notification_typeRENEWALまたはINTERACTIVE_RENEWALの場合のみ。「レシートのフィールド」も参照。

App Storeは表 6-4に示す状況で、通知をポストすることがあります。この表は、通知タイプと対応するサブスクリプションイベントの、完全なリストでもあります。

表 6-4 状態更新通知のタイプ

notification_type

説明

INITIAL_BUY

初めてのサブスクリプションの購入。latest_receiptをトークンとしてサーバに格納し、これをApp Storeで検証することにより、いつでもユーザーのサブスクリプションの状態を調べられるようにします。

CANCEL

Appleカスタマーサポートがサブスクリプションをキャンセル。「キャンセル日」フィールドで、サブスクリプションがキャンセルされた日時を判断。

RENEWAL

期限切れになったサブスクリプションについて、自動更新に成功。「サブスクリプションの有効期限」フィールドで次の更新日時を判断。

INTERACTIVE_RENEWAL

中断していたサブスクリプションを顧客が対話的に更新(Appの画面で、またはApp Storeのアカウント設定で)。サービスはすぐに有効になる。

DID_CHANGE_RENEWAL_PREF

顧客がプランを変更(次のサブスクリプションの更新時から有効)。現在アクティブなプランには影響しない。

statusUpdateNotificationが正常に届いた旨を示すため、サーバは200というHTTP状態コードを返します。データ値を返す必要はありません。サーバが50xまたは40xというHTTPコードを送信した場合、App Storeは通知を再試行します。App Storeは一定期間、数回にわたって通知を再試行しますが、失敗する回数が多いと最終的に更新を停止します。

セキュリティ要件

通知を送信する前に、App Storeはサーバとの間に、App Transport Security(ATS)プロトコルによる安全なネットワーク接続を確立しようと試みます。ATSの要件について詳しくは、『Requirements for Connecting Using ATS』を参照してください。安全な接続を確立できなければ、サーバに通知が届きません。セキュリティについて詳しくは、https://developer.apple.com/security/を参照してください。

テスト環境における状態更新通知

トランザクションのstatusUpdateNotificationsは、テスト環境で充分にテストした上で、このロジックを実稼働環境に実装するとよいでしょう。

サブスクリプションイベントの状態更新通知がテスト環境にある旨は、JSONオブジェクトstatusUpdateNotificationenvironmentキーの値がSANDBOXであるか否かで判断します。

プラットフォームをまたがる場合の考慮事項

プロダクトIDは、1つのAppに対して関連付けられています。AppにiOSバージョンとmacOSバージョンの両方がある場合は、各プラットフォームに対して、独立したプロダクトに一意のプロダクトIDを与えます。iOSバージョンのAppでサブスクリプションを購入しているユーザーがmacOSバージョンのAppから(またはこの逆から)コンテンツにアクセスできるようにする機能の実装は、Appの開発者に委ねられています。これには、非更新サブスクリプションを使用するAppに実装されるものと同様な、ユーザーを識別してユーザーが登録したコンテンツの記録を取るための何らかのシステムが必要になります。

ユーザーによるサブスクリプションの管理

Appにサブスクリプションの管理用のUIを実装せず、次のURLを開く形にしても構いません。

https://buy.itunes.apple.com/WebObjects/MZFinance.woa/wa/manageSubscriptions

このURLを開くとiTunesまたはiTunes Storeが起動し、「Manage Subscription」ページが現れます。

テスト環境

自動更新サブスクリプションの挙動は、テスト環境か実稼働環境かによって異なります。

テスト環境では、更新が発生する頻度が速くなり、自動更新サブスクリプションが1日で最大6回更新されます。これにより、サブスクリプションの更新、サブスクリプションの失効、サブスクリプションの期間に間隔があるサブスクリプションの履歴がAppでどのように処理されるかをテストできます。

更新と期限切れの頻度が増しているために、サブスクリプションの期間に短い間隔を残したまま、システムがサブスクリプションの更新を実行しようとする前に、サブスクリプションが期限切れになる場合があります。本番環境でも、このような間隔が生じるのには多くの理由があります。Appでこれらを正しく処理できることを確認してください。