TECH

Play Store in-app purchase verification on the server | Subscription product

In this guide, we’ll walk you through how to verify a subscription on your server after an in-app purchase is completed in your Android app. We'll be

Hitesh Verma Jun 6, 2024 · 5 min. read

In this guide, we’ll walk you through how to verify a subscription on your server after an in-app purchase is completed in your Android app. We'll be using TypeScript on a Node.js server, but the logic can be adapted to any backend framework.

Photo by <a href="https://unsplash.com/@austindistel" target="_blank">Austin Distel</a> on <a href="https://unsplash.com" target="_blank">Unsplash</a>
Photo by Austin Distel on Unsplash

Step 1: Setup Google Cloud Project & Play Console

First, create a Pub/Sub topic in your Google Cloud Project and register it in the Play Console to receive real-time notifications for subscription purchases or status changes. If you're unfamiliar with this process, refer to this article first.
Save the service account key in your project folder.

Step 2: Store Product Information

Ensure that all product IDs from the Play Store, along with their associated prices, are saved in your database. This will help in verifying and processing the subscription details accurately.

Step 3: Setup the APIs

Create two POST API endpoints:
/play_store/subscription/verify: For verifying the initial subscription purchase.
/play_store/subscription/notifications: To handle subscription status change notifications like renewals and cancellations from the Play Store.

Step 4: Install googleapis Package

npm install googleapis

Step 5: Logic to Verify First-Time Subscription

Ensure the request body contains the following parameters:
a. packageName: The application ID of your Android app.
b. subscriptionId: The ID of the susbcription (created in the play store) for which the payment was made.
c. purchaseToken: The token received after successful payment.

import { google } from "googleapis";

export class PaymentsController {
       /**
       * Verify play store in-app purchase subscription first purchase
       */
       async playStoreSubscriptionVerify(req: any, res: any): Promise<void> {
              const {packageName, subscriptionId, purchaseToken} = req.body;
              const data = await this.verifyPurchaseToken(packageName, subscriptionId, purchaseToken);
              if(data) {
                     // TODO: Handle the verification response
                     res.send({message: "Subscription successful"});
              } else {
                     res.status(400).send({message: "Subscription failed"});
              }
       }

       private async verifyPurchaseToken(
              packageName: string, 
              subscriptionId: string, 
              purchaseToken: string
       ) {
              const auth = new google.auth.GoogleAuth({
                     keyFile: "path/to/service/account/key/json",
                     scopes: ["https://www.googleapis.com/auth/androidpublisher"],
              });

              const androidPublisher = google.androidpublisher({
                     auth,
                     version: "v3",
                     params: { sandbox: true },
              });

              const res = await androidPublisher.purchases.subscriptions.get({
                     token: purchaseToken,
                     packageName: packageName,
                     subscriptionId: subscriptionId,
               });

              return res.status === 200 ? res.data : undefined;
       }
}

You will receive a response like this after verification.

{
    startTimeMillis: '1715966748689',
    expiryTimeMillis: '1715968836900',
    autoRenewing: false,
    priceCurrencyCode: 'INR',
    priceAmountMicros: '490000000',
    countryCode: 'IN',
    developerPayload: '',
    cancelReason: 1,
    orderId: 'GPA.3340-0954-1746-39862..0’,
    purchaseType: 0,
    acknowledgementState: 1,
    kind: 'androidpublisher#subscriptionPurchase'
 }

Upon Receiving a Successful Response from Google Play:
1. Check for Duplicate Orders:
a. Verify if the order ID already exists in your database.
b. If it does, respond with an error message: "This subscription was already captured".
c. If not, proceed further.
2. Update User Subscription and save transaction details:
Update the user's subscription details with the start time and expiry time in your database.
Store userId, startTimeMillis, expiryTimeMillis, orderId, subscriptionId, amount, fees, etc., in your
transactions table.

*Note: The order ID will look like GPA.3340-0954-1746-39862..0 where the prefix (before ..) is constant for all renewals of this subscription. The suffix (after ..) represents the renewal number (0 means subscription start, 1 means first renewal, 2 means second renewal etc.).

Step 6: Handling Subscription Notifications

You will receive base64 encoded data from the Play Store.
1. Decode the Data
2.Verify the Purchase Token
3. Check for Existing Order ID
If the order ID already exists in the database, stop processing (this usually occurs for the first-time subscription purchase).
4. Process the Order ID
a. Split the orderId with “..” and append “..0” to the first segment to get the subscription_order_id (representing the first-time purchase of this subscription).
b. Find the user associated with this subscription_order_id in your database and update according to the notificationType. For example, if notificationType is 4 (subscription renewal), update the subscription expiry time with the new data.

The decoded data will look like this.

{
   version: '1.0',
   packageName: 'com.example.demo',
   eventTimeMillis: '1715966749255',
   subscriptionNotification: {
      version: '1.0',
      notificationType: 4,
      purchaseToken: 'pnppabchkjpihkldbkkopjni.AO-J1Oy-P76JVQqc0N8w-sexdfDfbkNT5NAEdv2h',
      subscriptionId: 'subs_1'
    }
 }
async onPlayStoreSubscriptionNotification(req: any): Promise<void>{
     const dataBuffer = Buffer.from(req.body.data, "base64");
     const dataStr = dataBuffer.toString("utf-8");
     const data = JSON.parse(dataJson);

      let notificationDesc: string;
      const notificationType: number = data["subscriptionNotification"]["notificationType"];

      switch (notificationType) {
        case 1:
          notificationDesc = "Subscription was recovered.";
          break;
        case 2:
          notificationDesc = "Subscription was renewed.";
          const res = await this.verifyPurchaseToken(
            data["packageName"], 
            data["subscriptionNotification"]["subscriptionId"], 
            data["subscriptionNotification"]["purchaseToken"]
          );
          // TODO: Handle the verification response
          break;
        case 3:
          notificationDesc = "Subscription was canceled.";
          break;
        case 4:
          notificationDesc = "Subscription was purchased.";
          break;
        case 5:
          notificationDesc = "Subscription on hold.";
          break;
        case 6:
          notificationDesc = "Subscription in grace period.";
          break;
        case 7:
          notificationDesc = "Subscription restarted.";
          break;
        case 8:
          notificationDesc = "Subscription price change confirmed.";
          break;
        case 9:
          notificationDesc = "Subscription deferred.";
          break;
        case 10:
          notificationDesc = "Subscription paused.";
          break;
        case 11:
          notificationDesc = "Subscription pause schedule changed.";
          break;
        case 12:
          notificationDesc = "Subscription revoked.";
          break;
        case 13:
          notificationDesc = "Subscription expired.";
          break;
        default:
          throw new Error(
            `Unknown notification type received: ${notificationType}`
          );
      }
}

Step 7: Create a Push Type Subscription

Create a push-type subscription for the Pub/Sub topic you created in Step 1, and set the notification endpoint to /play_store/subscription/notifications

This guide simplifies the process of verifying and handling Google Play Store subscriptions on your server. By following these steps, you can ensure that your users’ subscription statuses are accurately tracked and updated in real-time.

Read next

Customized Calendar in Flutter

Hey, have you ever wanted a date-picker in your app but not in a dialog box? And you tried all the dart packages out there but cannot change their UIs

Hitesh Verma May 28, 2023 · 9 min read

Flutter: Custom Cupertino Date Picker

Hitesh Verma in TECH
May 28, 2023 · 6 min read

Simplifying Google Play Console & Cloud Project Setup for In-App Purchases

Hitesh Verma in TECH
May 21, 2024 · 5 min read

Flutter | Highlight searched text in results

Hitesh Verma in TECH
May 12, 2023 · 4 min read