Creating subscriptions in Google Play Console

Google Play gives you two ways to structure subscriptions. Pick one before you create products in the console, because changing later is painful.

Two ways to structure subscriptions

Flat products (one product per billing period)

Each billing period is its own subscription product with its own product ID.

com.yourapp.pro.monthly   (subscription, 1 base plan)
com.yourapp.pro.annual    (subscription, 1 base plan)

This is the simplest structure and mirrors how subscriptions work in App Store Connect. If you also ship on iOS, your Google product IDs can match your Apple product IDs exactly.

Umbrella subscription with base plans

One subscription product contains multiple base plans, one per billing period.

com.yourapp.pro           (subscription)
├── monthly                 (base plan)
└── annual                  (base plan)

This is Google's recommended pattern when you want users to upgrade or downgrade between billing periods within the same subscription. It enables Google's native upgrade/downgrade flow, where switching from monthly to annual is treated as a plan change instead of a new purchase.

Which to use

You want Use
The simplest setup, or matching Apple's structure Flat products
Users to switch between monthly and annual within one subscription Umbrella + base plans
Multiple subscription tiers (e.g., Vendor and Employer) that are independent of each other One umbrella per tier

Both patterns work with PurchaseKit. The difference is how you configure products in the dashboard and what comes through in the webhook payload.

Flat products

Create the subscription

  1. In Google Play Console, go to MonetizeSubscriptions
  2. Click Create subscription
  3. Enter a Product ID matching your Apple product ID if you ship on iOS (e.g., com.yourapp.pro.annual)
  4. Click Add base plan, enter a Base plan ID (e.g., annual), set the billing period and price, then Activate
  5. Repeat for each billing period (each gets its own subscription product)

Add the product to PurchaseKit

  1. In the PurchaseKit dashboard, click Add a product
  2. Enter the Google product ID (e.g., com.yourapp.pro.annual)
  3. Leave Base plan ID blank
  4. Save

PurchaseKit uses the first base plan automatically. The webhook payload sends store_product_id set to the full product ID and google_base_plan_id as null.

Umbrella subscription with base plans

Create the subscription

  1. In MonetizeSubscriptions, click Create subscription
  2. Enter a Product ID for the umbrella (e.g., com.yourapp.pro)
  3. Click Add base plan, enter monthly as the base plan ID, configure billing and price, then Activate
  4. Click Add base plan again and add annual, configure, then Activate

If you have multiple independent subscription tiers (for example, Vendor and Employer), create one umbrella subscription per tier. Each gets its own product ID and its own set of base plans.

Add the products to PurchaseKit

Create one PurchaseKit product per (umbrella, base plan) pair:

PurchaseKit name Google product ID Base plan ID
Pro Monthly com.yourapp.pro monthly
Pro Annual com.yourapp.pro annual

Both products share the same Google product ID; the base plan ID is what distinguishes them. Without the base plan ID, both products would show the same (first) price and you couldn't tell them apart in your webhook handler.

What comes through in the webhook

When a user buys the annual base plan of com.yourapp.pro:

{
  "store": "google",
  "store_product_id": "com.yourapp.pro",
  "google_base_plan_id": "annual",
  ...
}

store_product_id is the umbrella ID. google_base_plan_id is the base plan the user actually purchased. Together, they identify which PurchaseKit product (and therefore which plan in your Rails app) was bought.

For flat products, google_base_plan_id is null. For Apple subscriptions, it's always null.

Looking up your plan from the webhook

If you store plans in your Rails app and want to map the webhook back to a plan record, key your lookup on both fields when google_base_plan_id is present:

plan =
  if event.google_base_plan_id.present?
    Plan.find_by(
      google_product_id: event.store_product_id,
      google_base_plan_id: event.google_base_plan_id
    )
  else
    Plan.find_by(apple_product_id: event.store_product_id) ||
      Plan.find_by(google_product_id: event.store_product_id)
  end

Add a google_base_plan_id column to your plans table if you don't already have one.

When using the Pay gem integration, google_base_plan_id is also persisted on Pay::Subscription#data["google_base_plan_id"] so it's available later without re-fetching from the store.

Next step

Configure Google Play Console to send webhooks to PurchaseKit.