Google Play Billing Library 8: Reading Subscription Base Plans and Offers for Revenue Tracking
See why client data misses subscription prices, and how server verification maps offers and verified amounts to accurate Airbridge revenue events.

Key Takeaway
- Pass the client-side purchase token to your backend to verify the transaction directly through the server API.
- Server-side validation keeps test transactions and tampered receipts from inflating your revenue numbers.
- Send the verified amount and currency with a boolean revenue flag so subscription events sum accurately in your reports.
You just shipped an Android update with Google Play Billing Library 8, and your ad spend is running. A new user converts from a campaign, and your paywall offers a monthly base plan, an annual base plan, and a discounted introductory offer under a single subscription product ID. When the purchase finishes, your app only sees the parent subscription ID. If you fire a revenue event guessing the price from the client, an introductory discount or a monthly renewal gets logged as a full-price annual plan, and your ROAS numbers fall apart.
Single ID, Multiple Prices
Google's subscription hierarchy separates the parent product from how it is sold. Instead of creating a separate SKU for every billing period and promotional trial, you define a parent subscription product ID and attach multiple base plans (such as monthly auto-renewing or annual prepaid) and multiple conditional offers (such as a free trial or a first-month discount) underneath it.
During the checkout flow, your app queries product details, selects a specific offerToken tied to the user's chosen base plan and offer, and passes that token into the billing flow launch parameters. The trouble starts right after the user completes the payment. While your UI knew which offerToken the user tapped seconds ago, the asynchronous purchase callback and subsequent purchase restoration checks do not hand that same offer structure back to the client. Relying on local device state to cache which button the user tapped breaks as soon as a purchase stays pending, completes outside the app, or renews in the background.
Client Receipt Limits
Calling queryPurchasesAsync on the device returns a list of Purchase objects representing active subscriptions. If you inspect the Purchase object in your code, you will find the parent product ID via getProducts(), the purchaseToken, the order ID, and the purchase state. You will not find a basePlanId, an offerId, or the actual price paid by the user.
Because a single subscription ID now maps to several different price points and trial lengths, reading getProducts() on the client tells you what subscription family the user belongs to, not how much money changed hands. Hardcoding a price table in your Android app based on the parent product ID does not work unless every subscription in your catalog has only one base plan and zero offers. To know whether a user just started a free trial, claimed an intro offer, or paid for an annual base plan, you have to look up the purchase token outside the client SDK.
Server API Verification
In an Android implementation, the Google Play Billing Library manages purchases on the device and generates purchase tokens. For Google Play purchases, the app sends that purchase token to your backend, and your server validates it by calling the Google Play Developer API directly. This Receipt Validation: Secure In-App Purchases workflow uses the Google Play Developer API and requires proper authentication on your server.
When your server queries the purchases.subscriptionsv2.get endpoint using the package name and the purchase token, the API response returns a SubscriptionPurchaseV2 resource. Inside its lineItems array, you get the exact productId alongside an offerDetails object that explicitly states the basePlanId and, if a promotional offer or trial was applied, the offerId. For auto-renewing plans, autoRenewingPlan.recurringPrice gives the current recurring price, which does not account for discounts or include taxes, and prepaid plans have no such field. offerPhase shows whether the item is in a free trial or introductory phase. For the amount actually charged, call the orders.get API with the line item's latestSuccessfulOrderId.
Validating on the backend does more than resolve missing plan IDs. Server-side receipt validation prevents inflated purchase data from test transactions or tampered receipts, as covered in In-App Purchase (IAP): Types, Models & Measurement. Before you fire any attribution event back to your ad channels, your server has the exact plan tier and trial status from the store, plus the charged amount from orders.get.
Mapping Revenue Events
Once your backend knows the exact base plan, offer, and charged amount, the next step is translating that state into attribution events. Among the 25 standard events in Airbridge, the only subscription events are Start Trial, Subscribe, and Unsubscribe. As outlined in Connect trials and subscriptions to the click, the platform tracks subscriptions using those three standard events.
When the server response has offerPhase.freeTrial set, trigger Start Trial. When the user makes their first paid purchase, converts from a trial, or completes each subsequent renewal, fire Subscribe. For Subscribe revenue tracking, send the amount and currency with the event and flag it as revenue so the report sums the revenue. In your payload, use the isRevenue semantic attribute—described in Event Properties—which is a boolean where true indicates the event is revenue-related and false indicates it is not.
Subscription revenue aggregation is included in the Core Plan. Subscription revenue from tools like RevenueCat, Adapty, and Superwall connects through subscription revenue aggregation.
Billing 8 Migration Checks
Before pushing your Billing Library 8 update to production, walk through your purchase and event pipeline to make sure no revenue data drops along the way:
- Confirm your paywall passes the specific
offerTokenfor the selected base plan and offer into the billing flow, rather than defaulting to the first offer in the array. - Remove any client-side analytics code that assigns a hardcoded dollar value based solely on
purchase.getProducts()insideonPurchasesUpdatedorqueryPurchasesAsync. - Route the
purchaseTokenfrom the Android client to your backend or subscription management tool immediately after the purchase, so the server verifies it before you acknowledge it. - Check that your backend reads offerDetails (basePlanId and offerId) and offerPhase from the server API response before deciding whether to log a trial or a paid conversion, and takes the charged amount from orders.get.
- Verify that free trial starts do not carry a full base-plan price tag with
isRevenueset totrue.
FAQS
FAQ
How much does the Airbridge Core Plan cost?
The Core Plan starts with a 30-day free trial and then costs $40+/mo, which includes 500,000 data points per month and charges $0.0001 per additional data point, with no annual contract and the option to cancel anytime.
Does the Core Plan include Predictive LTV?
Yes, the Core Plan includes Predictive LTV by default, covering a calculation period of 1 to 180 days.
Track verified plans
Connect RevenueCat, Adapty, or Superwall to sum subscription revenue.


