CLAUDE LABJP
VERSION — v2.1.227 landed on August 10 with no new features, just fixes around plan detection and CI behaviorAUTO — Two days remain until August 14, when auto mode becomes the default in Claude Code for Pro, Max, and TeamCI — Bash commands no longer fail across the board under claude-code-action with allowed_non_write_users on GitHub-hosted runnersBILLING — Sessions started with an expired token could misread your plan and nudge Max users toward usage credits; that is now fixedSUNSET — The legacy Workbench and the experimental prompt tool APIs retire on August 17, five days outPRICE — Sonnet 5 promo pricing at $2/$10 per Mtok runs through August 31, moving to $3/$15 on September 1VERSION — v2.1.227 landed on August 10 with no new features, just fixes around plan detection and CI behaviorAUTO — Two days remain until August 14, when auto mode becomes the default in Claude Code for Pro, Max, and TeamCI — Bash commands no longer fail across the board under claude-code-action with allowed_non_write_users on GitHub-hosted runnersBILLING — Sessions started with an expired token could misread your plan and nudge Max users toward usage credits; that is now fixedSUNSET — The legacy Workbench and the experimental prompt tool APIs retire on August 17, five days outPRICE — Sonnet 5 promo pricing at $2/$10 per Mtok runs through August 31, moving to $3/$15 on September 1
Articles/Claude Code
Claude Code/2026-08-12Intermediate

When Paying Users Still See Ads: Collapsing the Ad-Free Check Into One Place

A user who paid to remove ads still sees them. The cause is rarely a missing flag — it is a check that lives in too many places. Here is how I inventoried every ad call with Claude Code and folded the decision into a single gate.

Claude Code216Android8In-App PurchaseAdsState Management

Premium Article

There is no message I dread more than one from someone who paid to remove ads and still sees them.

A crash I can fix and move on from. But when someone pays and the thing they paid for does not happen, refunding the money does not undo the impression it left. As an indie developer running wallpaper apps for years, this is the single failure mode I have spent the most care on avoiding.

And almost every time I have chased one of these reports, the cause was not a missing flag. The flag existed. One place in the app simply never asked about it.

Ads disappear through more than one door

My apps never had a single path to an ad-free state.

PathOwner of the stateLifetimeHow it ends
Ad-removal purchaseBillingManager (Google Play Billing)PermanentRefund, account change
Rewarded videoAdFreeManager (stored locally)Fixed windowTime passes
Purchase restoreBillingManager (queried at launch)PermanentUnknown until the query returns

These three differ in lifetime and, more importantly, in when they become knowable. A purchase is not known during the first moments after launch. A reward is a clock problem. A restore turns true asynchronously, after the screen has already drawn.

Yet at the call site, all three collapse into a condition that looks the same.

// The early version. Each screen grew its own slightly different condition.
if (!billingManager.isAdFree) {
    interstitialAd.show(this)
}

Then rewarded video arrives, and the line grows:

if (!billingManager.isAdFree && !adFreeManager.isRewardAdFree) {
    interstitialAd.show(this)
}

Adding one clause looks like a correct, local fix. The real problem is that nobody knows how many copies of that line exist. Gallery, detail, category, the return from settings, the exit flow behind the back button. Miss one, and that screen keeps the old rule forever.

Search for the ad calls, not for the flag

When I started the inventory, I grepped for isAdFree. That finds only half of what matters, because the places you forgot do not contain isAdFree at all. The right starting point is the other side: everywhere the app can show an ad.

Claude Code handles this inventory well, as long as you pin the search to the SDK side rather than to the condition.

claude -p 'List every call path that can result in an ad being displayed.
Start from the call sites below, not from any condition:
 
- InterstitialAd.show / RewardedAd.show / RewardedInterstitialAd.show
- AdView.loadAd / AdLoader.loadAd
- AdView declared directly in layout XML
 
For each hit, give me three columns:
1) file and line number
2) the ad-suppression check immediately guarding it (write "none" if absent)
3) which state that check reads (billing / reward / both / none)
 
Do not infer intent. Report only what exists in the code.'

The third column is what makes this useful. If you stop at "guarded / unguarded", the sites that read only one of the two states pass inspection. In my case the gaps were not unguarded calls — they were calls that checked the purchase but not the reward, plus a banner placed directly in a layout XML. No amount of grepping Kotlin will ever surface that XML banner.

Splitting the results into "none" and "partial" also orders the work for you. I worked through mine in this order:

  1. Close the "none" rows first — these are the ones showing ads to people who paid
  2. Fix the "partial" rows next — these show ads to people who watched a rewarded video
  3. Sweep the layout XML banners by eye, since no Kotlin grep will ever list them

My apps run on AdMob, where banners, interstitials and rewarded units coexist on the same screens. With an SDK that separates loading from display, leaving loadAd out of the gate produces a quiet failure mode in production: no ad is shown, but requests keep going out. I recommend routing both the load and the show through the same decision.

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
You will be able to fold ad-free state that lives in two systems — purchases and rewarded video — into a single decision point
You will be able to prevent the one bug that damages trust the most, paying users seeing ads, before it ships rather than after a refund request
You will be able to systematically find every place your codebase can still show an ad without asking whether it should
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

Claude 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

Claude Code2026-05-24
Retiring Dormant SDKs with Claude Code — A 12-Year Indie Developer's Pipeline for Safely Auditing Inactive Dependencies
Twelve years of indie iOS/Android development leaves a quiet sediment of SDKs that nobody calls anymore — but the dependencies remain. Here is the Claude Code pipeline I built to audit and safely retire them across four wallpaper apps, with the actual code and four weeks of operational metrics.
Claude Code2026-05-23
Three Months of Letting Claude Code Handle the Monthly Content Refresh for My Four Wallpaper Apps
Starting in February 2026 I began handing off the monthly content refresh of my wallpaper apps — Beautiful HD Wallpapers and three sibling titles — to Claude Code. After three months I want to share what kinds of judgement I felt comfortable delegating, and where I deliberately kept my own hands on the work.
Claude Code2026-05-18
Automated Play Store Staged Rollout Monitoring with Claude Code — Lessons from 50+ Crashes in v2.0.0
A hands-on record of building a Claude Code-powered monitoring system for Android staged rollouts (5%→100%). Covers crash-free rate thresholds, Wilson confidence intervals, and automatic Go/No-Go decisions — based on real experience shipping Beautiful HD Wallpapers to 50M+ users.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →