◉ANTIGRAVITY LABJP
Articles/App Development
▣ App Development/2026-05-17Advanced

Where Is the Source of Truth for Billing State? Designing the ad-free Pattern with Antigravity

A billing-state design pattern proven in a long-running indie Android app: centralizing state with AdFreeManager, BillingManager, and ModalGate — designed and implemented using Antigravity IDE.

android30billing3admob17architecture20indie-dev20kotlin6

✦ Premium Article

"I purchased the ad removal but ads are still showing."

In April 2026, this review arrived for the wallpaper app I have been running as an indie developer for years (Android). The purchase flow was working correctly. The transaction was recorded in Google Play Billing. Yet on certain devices, for certain users, ads were still appearing after a confirmed purchase.

Tracing through the code revealed the root cause: billing state was being evaluated in five separate places across the codebase. Each location updated independently, and during app restarts or session transitions, temporary inconsistencies were causing the bug.

This was one crystallization of technical debt that had accumulated quietly over years of continuous operation. And it was exactly the kind of problem that gets harder to fix the longer you wait.

Working through this with Antigravity led me to a design pattern built around three components — AdFreeManager, BillingManager, and ModalGate — unified by the concept of a single Source of Truth. This article covers the design reasoning and the implementation details.

Why Billing State Gets Scattered

When you run a personal app for years, billing state checks tend to grow organically. The pattern is predictable. You start with a simple boolean flag in SharedPreferences, read from an Activity. Then you introduce rewarded ads, and now there's a temporary session state that lives only in memory. Later you add purchase restoration logic, creating another code path. Each new Fragment gets its own copy of the billing check, written slightly differently.

By the time the bug appears, you have a collection of "distributed truths" — each one individually correct, but inconsistent with each other during window edges like app restarts or asynchronous Billing Library connection delays.

When I described this situation to Antigravity, its first response wasn't to generate code. It asked a design question: "Let's identify a single reliable location for billing state across the entire app. That's the Source of Truth. Which data source do you think is most authoritative?"

That question became the starting point.

The Source of Truth Principle

In software design, a "Source of Truth" means having one canonical place for a given piece of data — a location you can trust unconditionally to hold the correct value. When multiple locations hold the same data and update independently, inconsistencies are inevitable. The design challenge isn't preventing bugs — it's making them impossible by construction.

For billing state, the candidates are clear. Google Play Billing API responses are authoritative because they reflect what Google's servers know about the user's purchase history. SharedPreferences are a local cache — fast but potentially stale. In-memory variables work for transient state like rewarded ad sessions but disappear on restart.

Antigravity's recommendation was to use Google Play Billing as the Source of Truth and route all state evaluation through an AdFreeManager class. The reasoning is straightforward: the purchase happened on Google's infrastructure. Every other representation is a derived copy. When copies disagree, the original wins.

By centralizing evaluation in one class, the design guarantees that wherever you check billing state in the app, you get the same answer. Logic changes live in one place. Bug investigations start in one place.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦Solve the scattered billing state problem once and for all with the Source of Truth pattern — no more ad-display bugs after purchases
✦Implement the AdFreeManager + BillingManager + ModalGate three-layer architecture to centralize ad and paywall control
✦Learn the architectural thinking process behind Antigravity-assisted design and apply it to your own app's structural challenges
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

▣ App Dev2026-05-24
Running iOS Push Notification A/B Tests Weekly With Antigravity Agent — A Self-Improving Loop For Copy, Timing, And Segments
Drawing on years of indie iOS development across wallpaper and wellness apps, this article walks through a weekly Push Notification A/B loop powered by Antigravity Agent — the FCM bridge, BigQuery measurement, segment design, a D7 retention recovery story, and a dependency-free simulation that made me cut the number of arms and add a significance rule before declaring any winner.
▣ App Dev2026-08-31
Why I took the show-or-not decision away from every dialog
A paywall prompt, a review request and a rewarded-ad confirmation landed on the same frame. Here is how I moved the show-or-not decision out of each dialog into a single gate, how I rewrote the agent instructions as invariants, and how a CI check keeps it that way.
▣ App Dev2026-08-18
Ordering Six App Updates for the API Level 36 Deadline, and Sizing Each Staged Rollout
Google Play's API level 36 requirement lands on every app you own with the same date. When the deadline is fixed, the only thing left to decide is the order. Here is how I ranked six apps by fragility and worked each rollout step backwards from the observations I actually needed.