← All packs
Play Billing Machinery
verifiedSell subscriptions in your Android app on the Billing Library version Google requires from 31 August 2026.
You have an Android app that charges money — and a hard deadline.
How you actually use this
You don’t install a library or wire up an SDK. Your own coding agent builds the code in your project, and you keep it — no runtime dependency on us.
Step 1
Download and unzip
You get a folder: the docs that tell an agent what to build, a starting skeleton, and the test suite that decides when it's done.
Step 2
Open it in Claude Code or Cursor
Point your coding agent at the folder. Nothing to install, no account with us, no API key.
Step 3
Paste one prompt
The pack contains the exact prompt. Paste it as your first message and leave it alone — it works through the build itself, choosing a cheaper or stronger model per task.
Step 4
Run ./verify.sh
One command. It prints a pass or fail for every check. Green means the build is done — the same script we ran to produce the receipt on this page.
Typical build: about 33 minutes of your agent working, mostly unattended. Then you integrate the working module into your app the way you would any code you’d written yourself.
Technically: Google Play subscriptions and one-time products done server-authoritatively — Billing Library 9 (which clears the August 2026 v8 mandate), RTDN-driven entitlements verified against the Play Developer API, the three-day acknowledgement window, and a Play Console review preflight.
Execution receipt✓3 of 3 runs passed- Model
- claude-sonnet-5
- Run at
- 2026-08-03T15:27:21+00:00
- Pack version
- a9998ae6a7e1
- Tokens in / out
- 17,324,257 / 233,532 · per-run mean · billed input includes 16,418,904 cache reads, priced far below fresh input
- Wall time
- 32.8 min · per-run mean
- Run cost
- $8.06 · what this verification run cost us at API pricing, per build pass · your plan and model prices differ
- Deploy
- not deployed
Acceptance checks
- toolchain✓ PASS
- billing-module-compiles✓ PASS
- billing-entitlement-tests✓ PASS
- billing-api-surface✓ PASS
- server-setup✓ PASS
- pubsub-auth✓ PASS
- rtdn-delivery-semantics✓ PASS
- entitlement-state-machine✓ PASS
- acknowledgement-window✓ PASS
- server-http✓ PASS
- play-preflight✓ PASS
- docs-present✓ PASS
Versions lock
android-kotlin-node
| com.android.billingclient:billing | 9.1.0 |
| androidx.core:core | 1.19.0 |
| kotlin | 2.4.10 |
| android-gradle-plugin | 9.3.1 |
| gradle | 9.6.1 |
| jdk | 17.0.20 |
| node | 24.18.1 |
| jose | 6.2.7 |
Scaffold integrity
16 scaffold files modified or deleted by the buyer-agent during verification
- android/billing/build.gradle.kts
- android/billing/src/main/kotlin/com/oneshot/billing/Entitlement.kt
- android/billing/src/main/kotlin/com/oneshot/billing/PlayBilling.kt
- android/billing/src/main/kotlin/com/oneshot/billing/PurchaseRelay.kt
- android/billing/src/test/kotlin/com/oneshot/billing/EntitlementTest.kt
- android/build.gradle.kts
- android/gradle/libs.versions.toml
- android/settings.gradle.kts
- scripts/preflight.sh
- server/scripts/reconcile.js
- server/src/entitlements.js
- server/src/notifications.js
- server/src/play.js
- server/src/pubsub.js
- server/src/server.js
- server/src/store.js
Google Play gives you a purchase token and a doorbell. Everything that decides whether a user is actually entitled — reading real state from the Developer API, surviving duplicate and out-of-order notifications, acknowledging inside a three-day window before Google auto-refunds the purchase, and telling "cancelled" apart from "expired" — is yours to build, and every one of those is a bug you find in production rather than in a test. This pack builds the whole rig for an existing Android app: a Billing Library 9 client with no v7 ghosts in it, a server that is the actual source of truth for entitlement, and a Play Console review preflight. Its verification is the unusual part — a locally minted OIDC signing chain and a fake Play API prove the push-authentication path and the entitlement state machine without a Google account.
Status: draft. This pack has no receipt yet. Every verified pack in this catalog publishes a measured pass rate as M of N clean-room builds. This one does not have one, because the harness has not run it. When it does, the receipt page will publish the pass rate, the measured token volume and the measured per-run cost — and if it cannot pass, this pack does not get listed. Until then, treat the numbers below as what they say they are: estimates.
Example use cases
- An indie Android dev with a v7 integration and a deadline. Google Play Billing Library 7 cannot be used for new apps or updates after 2026-08-31. Existing installs keep transacting, so nothing looks broken — right up until you try to ship a bug fix and the upload is rejected. This pack lands on 9.1.0, which clears that gate and the 2027 one.
- A developer who shipped iOS first (often with this pack's sibling,
appstore-machinery) and now needs Android parity with one concept of "entitled" in their backend, not two subtly different ones. - A subscription app that keeps seeing unexplained churn in its first week of purchases — which is what an unacknowledged-purchase bug looks like from the outside, because Google refunds and revokes silently after three days.
- A team migrating off RevenueCat because per-revenue pricing stopped making sense at their scale, and who need the entitlement logic they were renting to now exist in their own repo, with tests.
- An agency shipping billing into several client Android apps — the same server, preflight and acceptance suite re-stamped per client instead of re-solving Play's delivery semantics per contract.
Why this pack
| Bare agent, no pack | RevenueCat-class SaaS | Hand-rolled from Google's docs | This pack |
|---|
| Cost | Agent tokens only | Free tier, then a recurring percentage of tracked revenue | Your time | $149 one-time |
| Time to correct entitlements | Unknown. The failure modes below don't surface as errors — they surface as refunds, churn, and support tickets months later | Fast. Their SDK and dashboard handle the same traps, and they maintain them as Google changes | Slowest — reading the Billing Library migration guide, the RTDN reference, the Pub/Sub delivery-semantics docs and the Play Developer API reference, then writing the state machine | 2–3 agent sessions on a $20/mo plan (PRD estimate) |
| Correctness risk | High and silent — see the five bug classes below | Low, and genuinely their core competence | High until you have independently rediscovered the same failure modes | Bounded by a 12-check acceptance suite that runs offline. Not yet receipt-verified — that is the honest gap versus the rest of this catalog |
| Lock-in | None, and no guardrails | Real: entitlement history and logic live in their system; migrating means re-deriving both | None | None — a Kotlin module and a plain Node service in your repo, one production dependency, no vendor SDK beyond Google's own Billing Library |
| Who keeps it current | You | Them | You | You, with MIGRATION.md and the acceptance suite as the safety net. We promise no timing — see the FAQ |
Be told plainly where RevenueCat wins: if you want cross-platform entitlements, paywall A/B testing, cohort analytics, or you'd simply rather someone else tracked Google's annual migrations, buy RevenueCat. This pack is for the developer who wants the logic in their own repo with tests around it, and who is fine owning the migration — which MIGRATION.md and the acceptance suite are designed to make routine rather than heroic.
The five bug classes this pack pre-solves. Not a claim that every agent hits every one — these are the specific, documented traps this integration has:
- Client-side entitlement unlocking. The purchase completes, the feature unlocks, every test the agent thought to write is green — and the first refund breaks it, because a refunded user's local
Purchase still says PURCHASED. It is invisible until money moves backwards. The pack derives entitlement server-side and ships a test (serverStateWinsOverLocalPurchase) whose only job is to fail if someone re-introduces it. - The acknowledgement window. A purchase not acknowledged within three days is automatically refunded by Google and its entitlement revoked. Google's own docs say so in one sentence that is easy to read past. Worse,
purchases.subscriptionsv2 — the current, correct resource for reading subscription state — has no acknowledge method; it lives on the deprecated v1 purchases.subscriptions. An agent writing a clean v2-only backend will not find an acknowledge call and will ship without one. - Treating RTDN as exactly-once and ordered. It is neither. Cloud Pub/Sub documents at-least-once delivery with no ordering guarantee, and exactly-once delivery is pull-only — push subscriptions, which is what RTDN uses, cannot have it. Handlers that apply state in arrival order corrupt themselves the first time Google retries.
- Reading state from the notification body. RTDN carries a purchase token and a type, not a state — Google says explicitly that you must call the Developer API afterwards. And the payload is not the shape most code assumes:
subscriptionNotification no longer has a subscriptionId field at all, and one-time products name the product sku, not productId. - The v6/v7 API surface that v8 deleted.
SkuDetails, querySkuDetailsAsync, SkuType, queryPurchaseHistoryAsync, the no-argument enablePendingPurchases(), the proration-mode setters — all removed, and all heavily represented in any model's training data because they were correct for years. The pack pins the current surface and greps for the ghosts.
Scale envelope
Derived from the pinned architecture and Google's published limits, not from a load test. Nothing here is measured by a receipt — this pack does not have one yet — so every number is labelled as the engineering estimate it is.
- Notification volume is per billing cycle, not per session. A monthly subscriber generates roughly 12 renewal notifications a year (annual: 1), plus one-off purchase, cancel, grace, hold, pause and refund events. At 25,000 monthly subscribers that is on the order of 900–1,500 notifications/day — Google sizes each at about 1 KB. Estimate.
- The entitlement store. A single Node process over
node:sqlite on one volume, writing a handful of rows per notification. At the volume above, the database is nowhere near the constraint; roughly 50,000–100,000 active subscribers fits on one small Railway instance before instance limits — not row counts — become the issue. Estimate. - The first ceiling is a burst, not a total. The Play Developer API allows 200,000 requests/day per application and 3,000 queries/minute per bucket. Steady-state renewal traffic never approaches either. What does is a fan-out event — a price change or a policy update that generates a notification for every subscriber at once — where one Play API read per notification can cross 50/second. The daily quota is generous; the per-minute bucket is the one that bites.
- The second ceiling is a single point of failure, and it is deliberate at this price: one process, one volume, no clustering. Pub/Sub will retry a nacked push with backoff and holds messages for 7 days by default (31 maximum) — so a short outage is survivable by design and a long one is why
reconcile.js exists. The permanent blind spot is refunds: the voided purchases API only looks back 30 days, hard limit. - Upgrade path, in the order you'd actually need it: (1) put a queue between
/rtdn and the Play API call so fan-out bursts are smoothed under the per-minute quota instead of racing it — the handler is already idempotent, which is what makes this safe; (2) swap store.js for Postgres, the only file that touches the database; (3) run multiple instances, which the first two changes make possible. Request a quota increase from Google support only after (1), since most quota pain is burst-shaped.
What it costs to skip the pack (modelled twice over — read the caveat). For the verified packs in this catalog, this figure starts from a measured build. This pack has no receipt, so its input is itself an estimate, and the result is an estimate built on an estimate. Taking appstore-machinery's measured shape as the anchor and repricing at Opus-class rates with no cheap-tier routing, no parallel subagents and no prompt-caching discipline, the naive path models out at roughly $100–390 in API tokens, likely around $195, against this pack's $149. Honest reading: do not buy this for the token savings. At the low end it does not clear the price, and the whole band rides on an exploration multiplier we are estimating. Buy it because four of the five bug classes above are the silent kind — they cost you refunds and churn, not an error message — and because the acceptance suite is what lets you migrate on Google's schedule without re-deriving any of it.
And the caveat that matters more than the arithmetic: most buyers are not metered. If you are on a $20/mo Claude plan, you pay $20 either way and your marginal token saving is roughly zero. What the pack actually buys you is the decision list and the evidence.
Verified against
- claude-opus-5
- claude-sonnet-5
- claude-haiku-4-5
Supported stacks
- android-kotlin-node
| com.android.billingclient:billing | 9.1.0 |
| androidx.core:core | 1.19.0 |
| kotlin | 2.4.10 |
| android-gradle-plugin | 9.3.1 |
| gradle | 9.6.1 |
| jdk | 17.0.20 |
| node | 24.18.1 |
| jose | 6.2.7 |
Estimated buyer token cost
1,139,000 tokens for one build pass (estimate, not a guarantee).
FAQ
What if the build fails?
verify.sh is the arbiter and it is deterministic: no Google account, no live network calls to Google, no credentialed sandbox — locally minted signing keys and a fake Play Developer API. Full refund within 14 days of purchase if it fails on a supported configuration (the pinned stack and a model from the pack's matrix) — reply to your Stripe receipt email with the verify output attached. If your agent modified the acceptance surface (verify.sh, the mandated tests, or scripts/preflight.sh) to turn a check green, that falls outside the guarantee — the fix is to fix the implementation, which is what the verify prompt tells your agent in as many words.
This pack says status: draft — what does that actually mean for me?
It means no clean-room build has been run and published yet, so there is no pass rate and no measured cost, only estimates that are labelled as estimates. Every other pack in this catalog leads with "N of N builds passed"; this one cannot, and pretending otherwise would be the exact dishonesty the receipts exist to prevent. The refund policy applies from day one regardless, and if the harness cannot get this pack to pass it gets fixed or delisted rather than sold.
Do I need a Mac?
No. This is the Android sibling of appstore-machinery, which does require one. Anywhere the Android SDK and Node run — macOS, Linux, Windows — is fine.
What models does this need, and what plan?
Written against claude-opus-5, claude-sonnet-5 and claude-haiku-4-5. Tasks are pre-tagged cheap/standard/capable in TASKS.md and routed accordingly; only two of the ten need the top tier — the Pub/Sub token verifier and the entitlement/acknowledgement state machine. The PRD's estimate is 2–3 sessions inside a $20/mo plan, whose binding constraint is prompt volume in Claude's 5-hour rolling window and weekly cap rather than raw tokens. Plan on ~1.1M fresh tokens per build pass — again, an estimate, not a measurement.
Will this get my app approved by Google Play review?
scripts/preflight.sh catches the statically checkable blockers — billing library floor, target API floor, removed-API ghosts, client-side unlock, debuggable builds, cleartext traffic, the manage-subscriptions deep link — and prints the ones that need a human. Review is Google's call and this pack never guarantees approval, only that you are not submitting with the avoidable mistakes still in. The single most common blocker for a subscription app is not in your code at all: App access, the Console declaration that lets a reviewer get past your paywall. The checklist puts it first among the human rows for that reason.
Do I own the code afterward, and can I extend it?
Yes to both. A Kotlin library module and a plain Node service in your own repo, with one production dependency (jose, for verifying Google's push tokens — the one security boundary we refuse to hand-roll) plus Google's own Billing Library in the app. No hosted dashboard, no runtime call back to OneShot, no per-MAU fee. ARCHITECTURE.md names the seams for extending it: consumables need a grants table and a purchases.products.consume call, and purchase tokens already flow through /link while the acknowledgement module already branches on product type; store.js is the only file that talks to the database. The US external-content-links and alternative-billing programs are deliberately out of scope — each needs Play Console enrollment, a choice screen, transaction reporting and service fees, with reporting obligations starting 2026-10-01 — but the service-account credential they'd hang off is already there.
Google changes Play Billing every year. Am I buying a subscription to updates?
No, and the pack is explicit about it. This is a one-time purchase with free updates and no timing promise — there is no SLA and no support queue. Packs listed for sale are re-verified against current platform rules on our schedule (quarterly, plus whenever an authority announces a mandate); every re-verified version is free to prior buyers; we do not promise a delivery date, and we delist rather than sell a pack we can no longer verify. What you actually own is MIGRATION.md: the upstream sources to read, and a copy-paste prompt that has your own agent read the release notes since your pinned version, apply the changes, and re-run verify.sh until green. The acceptance suite is what makes that safe to do yourself. And the clock is long — pinning 9.1.0 means your next forced move is 2028-08-31, not this year's deadline.
How do I test any of this without charging real cards?
Play Console → Settings → License testing (account-level). Testers buy without being charged, get test payment instruments including "always declines" and "approves then chargebacks", and run on accelerated timings — a monthly subscription renews in 5 minutes, grace period and account hold in 10. Crucially the acknowledgement window compresses from three days to three minutes, which turns the pack's scariest failure mode into a ten-minute experiment you run deliberately, once, on a throwaway product. License testers can even use sideloaded debug builds; you don't need a signed release on a track just to exercise billing.
Specific problems this pack pre-solves
Prescribed services
Disclosure: some links on this page are affiliate links. We may earn a commission if you sign up through them, at no extra cost to you. We only link to services the pack actually verified against.