Open3

AndroidのGoogle Play's Billing system(In-App Purchases)学習ログ

matsuchiyomatsuchiyo

サーバーサイドで買い切りのアイテムを検証する

  • 買い切り: Integrate the Google Play Billing Library into your appでいうところのNon-consumable products。

  • 何を検証するか

    • 以下の通り検証する

      Your app should process a purchase in the following way:

      1. Verify the purchase.
      2. Give content to the user, and acknowledge delivery of the content. ...
        To verify a purchase, first check that the purchase state is PURCHASED. If the purchase is PENDING, then you should process the purchase as described in Handling pending transactions. For purchases received from onPurchasesUpdated() or queryPurchasesAsync(), you should further verify the purchase to ensure legitimacy before your app grants entitlement. To learn how to properly verify a purchase, see Verify purchases before granting entitlements.

      Once you've verified the purchase, your app is ready to grant entitlement to the user. The user account associated with the purchase can be identified with the ProductPurchase.obfuscatedExternalAccountId returned by Purchases.products:get for in app product purchases and the SubscriptionPurchase.obfuscatedExternalAccountId returned by Purchases.subscriptions:get for subscriptions on the server side, or the obfuscatedAccountId from Purchase.getAccountIdentifiers() on the client side, if one was set with setObfuscatedAccountId when the purchase was made.

      After granting entitlement, your app must then acknowledge the purchase. This acknowledgement communicates to Google Play that you have granted entitlement for the purchase.
      Processing Purchases

      • 上の「For purchases received from onPurchasesUpdated() or queryPurchasesAsync(), you should further verify the purchase to ensure legitimacy before your app grants entitlement. To learn how to properly verify a purchase, see Verify purchases before granting entitlements.」について
        • purchaseTokenが過去のpurchaseTokenと同じでないか確認。他のユーザーのpurchaseTokenと同じでないか確認するのかな。
          1. Send the corresponding purchaseToken to your backend. This means that you should maintain a record of all purchaseToken values for all purchases.
          2. Verify that the purchaseToken value for the current purchase does not match any previous purchaseToken values. purchaseToken is globally unique, so you can safely use this value as a primary key in your database.
        • 以下、具体的にどうチェックする?
          1. Use the Purchases.products:get or Purchases.subscriptionsv2:get endpoints in the Google Play Developer API to verify with Google that the purchase is legitimate.
          2. If the purchase is legitimate and has not been used in the past, you can then safely grant entitlement to the in-app item or subscription.
          • subscriptionなら、statusがpurchasedになっているか。expiryすぎてないか。
          • non-consumable productなら、statusがpurchasedになっているか。
        • subscriptionの場合、linkedPurchaseTokenのpurchaseTokenを無効にする必要がある。
          1. For subscriptions, when linkedPurchaseToken is set in Purchases.subscriptionsv2:get, you should also remove the linkedPurchaseToken from your database and revoke the entitlement that is granted to the linkedPurchaseToken to ensure that multiple users are not entitled for the same purchase.
        • PURCHASEDになっているかどうか検証。
          1. You should grant entitlement only when the purchase state is PURCHASED and make sure to handle the PENDING purchases correctly.
        • acknowledgeする。サーバーでやった方がいいらしい。

          After granting entitlement,... call the relevant Play Developer API endpoint, either Purchases.products:acknowledge or Purchases.subscriptions:acknowledge on your secure backend server.
          ...
          Note that while you can acknowledge or consume the purchase on the client side through your app, server side APIs provide additional protection against issues like poor network connectivity and malicious activity.

      • 買い切りはPurchases.products:getで取得できそう。
  • どうやって: Firebase Functions

matsuchiyomatsuchiyo
  • アプリ内購入を実装しようとしているアプリが以下のようなアプリなので、サーバーサイドでの検証は不要ではないかと思った。
    • 購入により提供するのは、広告の非表示。これは、サーバーからコンテンツを提供するなどではなく、アプリ側の制御。
    • このアプリは、ツールアプリでログインはない。
  • しかし、purchases.subscriptionsv2.getで取得できるpurchases.subscriptionv2の、SubscriptionState(今回ハンドリングしたいIN_GRACE_PERIODやACCOUNT_HOLDがある)と同じものがアプリ側では取得できなかった。アプリ側で取得できるPurchase.getPurchaseState()で取得できる、PurchaseStateには、PURCHASED, PENDING, UNSPECIFIED_STATEしかない。
    • このあたりのステータスがいらなかったら、アプリ側で、PurchaseのgetPurchaseState()等を使って検証ぽいことをしてもいいのかもしれない。
  • purchases.subscriptionv2.getをアプリから直接呼び出すことはできなそう。→ 以下の通りできなくはないみたい。アプリ側でGoogleログインさせて認可画面を表示するような感じになりそう。 ← やはりできなそう。下記参照。

    OAuth とサービス アカウントを設定する
    OAuth クライアントまたはサービス アカウントで Google Play Developer API へのアクセスを設定する必要があります。通常、API にアクセスするには、サービス アカウントを使用する必要があります。

    サービス アカウントは、サーバーなどの安全な環境で使用する必要があります。サービス アカウントの認証情報は、API の使用を許可されていない人に知られることのないように、安全に管理する必要があります。

    各ユーザーの代わりに API にアクセスする必要がある場合は、OAuth クライアント ID を使用してください。たとえば、ウェブサイトがユーザーの代わりにウェブ クライアントから Google Play Developer API にアクセスする必要がある場合に、クライアント ID を使用できます。そのユーザーは、サービス アカウントではなく Google アカウントで認証されることになります。ウェブサイトはこれにより、サービス アカウントの認証情報を損なうことなく、ユーザーの代わりに API を呼び出せます。

    サービス アカウント: 安全なソフトウェア サービスが API にアクセスする場合(最も一般的)
    OAuth クライアント: ユーザーが API にアクセスする場合
    https://developers.google.com/android-publisher/getting_started?hl=ja#configure

    • やはり、OAuthは使えないかも。以下の記載の2文目を見ると、上記の「各ユーザーの代わりにAPIにアクセスする必要がある場合」の「各ユーザー」は、Google Developerの契約をしているユーザーということだと思う。

      ユーザーが OAuth クライアントを使用して、API を通じて自分の認証情報で操作を実行できるようにします。ユーザーの操作は、Google Play Console の [ユーザーと権限] ページで許可されているものに限定されます。

  • 以下の理由でサーバーサイドで検証することにした。
    • IN_GRACE_PERIODかどうかは知りたい。その場合、purchases.subscriptionv2.getを呼び出す必要がある。アプリからも呼び出せるが、呼び出すにはOAuthの認可フローが必要でUX的に必要なステップが増える。ステップを増やしたくないためアプリからではなくサーバーから呼び出したい。サーバーから呼び出すならその検証も一緒にサーバーでやってしまう方がいいため。