この記事は、「モバイルのサブスクリプションのアーキテクチャ」の参考記事になります。
Google Play Storeの Realtime Developer Notificationを実装し運用しているので、その中で得た知見を記載していきます。
実現することは、「Androidアプリの最新の定期購読レシート情報を、サービスのサーバーに常に保持しておくということ」です。
Realtime Developer Notificationとは
Realtime Developer Notificationとは、Google Playで管理される定期購入の状態変化をCloud Pub/Subを通して受信することのできるサービスです。
このRealtime Developer Notificationを用いたレシート情報更新のプロセス図です。
AppleのApp Store Server Notificationと違って、Google Playから直接指定のエンドポイントに最新レシート情報を送信することは出来ません。必ずCloud Pub/Subを経由する必要があります。
GCPのプロジェクト作成とCloud Pub/Subの設定
Cloud Pub/Subとは
Pub/Sub は、イベントを処理するサービスとイベントを生成するサービスを切り離す非同期メッセージング サービスです。
Cloud Pub/Subの設定
Cloud Pub/Subに定期購読の状態変化があったpurchase_tokenの通知が来るので、それをトリガーに最新のレシート情報を取得し、サービスのDBに最新レシート情報を格納するという流れになります。
GCPプロジェクトを作成したら、Coud Pub/Subを開き、「トピック」と「サブスクリプション」を作成します。
公式サイトに詳細な手順が記載されているので、その記述に従って設定します。
このページは何度も読み返すことになると思います。
サブスクリプションの作成
サブスクリプションの作成には、以下の点に留意してください。
- どのトピックデータを配信するのかを指定する
- 配信タイプは「push」にする
- pushエンドポイントに、サービスのエンドポイントを指定する。
配信タイプをpushにしてpushエンドポイントを指定することで、Play Consoleから定期購読の状態変化通知を受け取るたびに指定のエンドポイントにjsonデータが配信されるようになります。
Play Console上での設定
Google Play Console上で、リアルタイムデベロッパー通知の欄に、先程作成した「トピック名」を入力します。これにより、定期購読の状態変化があるたびに、指定したトピックに通知されるようになります。
ここまで設定をした上で、Google Play Console上から「テスト送信」を行います。
Play Console -> Topic -> Subscription -> Service Server
と疎通を確認します。
Cloud Pub/Subから受領するデータ形式
Cloud Pub/Subからの「サブスクリプション」で連携されてくるデータはbase64でエンコードされていますが、デコードすると以下のデータが含まれています。
version | string |
---|---|
packageName | string |
eventTimeMillis | long |
oneTimeProductNotification or subscriptionNotification or testNotification | 消耗型のプロダクトの場合は、oneTimeProductNotification、 定期購読はsubscriptionNotification、 play consoleからの「テスト送信」の場合は、testNotificationがそれぞれ含まれます。 |
今回は定期購読の更新を想定していますので、subscriptionNotificationが含まれることになります。
subscriptionNotificationの中には、どのレシートに状態変化があったのかを特定する”purchaseToken”が含まれますので、このデータを元に、そのpurchaseTokenの最新情報を取得するAPIを用いて、サーバー内に保管しているレシート情報を更新することになります。
レシート検証API
定期購読状態に変化があったpurchase_tokenを受け取ったら、次はその情報をもとに、最新のレシート情報を取得し、DB内に保管しているレシート情報を更新します。
[Method: purchases.subscriptions.get | Google Play Developer API]
HTTP request
GET
https://androidpublisher.googleapis.com/androidpublisher/v3/applications/{packageName}/purchases/subscriptions/{subscriptionId}/tokens/{token}
packageNameとsubscriptionIdは、GCPやGoogle Play Console上ですでに設定しているもの、tokenは先程Pub/Subから受領したものになります。
問題は、query parameterにはaccess_tokenを含める必要があることです。
access_toekn と refresh_token
最新のレシート情報を取得するには、レシート検証のAPIを使用する必要がありますが、そのAPIを使用するには、access_tokenが必要になります。そして、やっかいなのが、このaccess_tokenは1時間程度の有効期限があり、さらにaccess_tokenを取得するために、refresh_tokenを先に生成しておかなければいけないことです。
[Authorization | Google Play Developer API | Google Developers]
POSTMANやブラウザでのアクセスを駆使してなんとかクリアします。。
これで定期購読の状態変化をCloud Pub/Subで受け取り、サービスのサーバーにrefresh_toeknのデータとともにPOST、その情報を用いてレシート検証APIで最新レシート情報を取得し、DB内に最新のレシート情報を保存できるようになります。
コードサンプル
公開できる箇所のみに絞っていますが、Pub/Subからの「サブスクリプション」でPush通知されるエンドポイントで動く箇所のコードサンプルを記載します。(Ruby on Rails)
def XXXX
decoded_data = Base64.decode64(params[:message][:data])
data = JSON.parse(decoded_data, symbolize_names: true)
purchase_token = data[:subscriptionNotification][:purchaseToken]
log = Log.find_by(purchase_token: purchase_token)
token_json = json_from_api("https://accounts.google.com/o/oauth2/token?grant_type=refresh_token&client_id=XXX&client_secret=XXX&refresh_token=XXX", 'POST', nil)
access_token = token_json['access_token']
receipt_json = json_from_api("https://www.googleapis.com/androidpublisher/v3/applications/packageName/purchases/subscriptions/subscriptionId/tokens/token?access_token=#{access_token}", 'GET', nil)
log.update!(よしなにアップデート)
head :ok
end
private
def json_from_api(url, method, params)
# Net::HTTPを用いてHTTPレスポンスを返却する処理
end
以上です。参考になりましたら幸いです。