OneShot

Play Billing Machinery

Build Play Billing Machinery on android-kotlin-node

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.

Verification pendingPinned for android-kotlin-node; verification pending. No pass rate is published until the harness runs it.
Coming soon — $149

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.

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.

What you get

**Features**

  • Google Play Billing Library **9.1.0** client module (`android/billing`, Kotlin, an Android

library you drop into an existing app): product query on the v9 `QueryProductDetailsResult`

shape, purchase flow with a stable `obfuscatedAccountId`, `PurchasesUpdatedListener`,

`queryPurchasesAsync` on every foreground, subscription upgrade/downgrade via the current

replacement-mode API, and auto-reconnection

  • Server-authoritative entitlement service (Node 24.18.1, `node:sqlite`, **one** production

dependency): authenticated RTDN intake, Play Developer API verification, a nine-state

subscription entitlement matrix, the three-day acknowledgement window handled server-side,

and a reconciliation job for everything RTDN drops

  • Offline-provable Pub/Sub push authentication: OIDC RS256 token verification against a JWKS,

with `alg` pinning, issuer/audience/service-account/expiry checks, and a locally minted

attacker key so "signed by the wrong key" is a real rejection in the test suite

  • Delivery-pathology handling as code, not comments: duplicate deliveries, out-of-order

deliveries, and late deliveries that must not regress newer state

  • Play Console review preflight: the statically checkable submission blockers as a script, plus

a short human checklist for the ones that need a person

  • One-command acceptance: `verify.sh`, 12 checks, no Google account required

**Screens / surfaces**

  • Server endpoints: `POST /rtdn` (Pub/Sub push target), `POST /link` (client purchase relay),

`GET /entitlements/:appAccountId`, `GET /healthz`

  • Android: a library module, not a UI. The purchase flow is Google's own Play billing sheet;

your paywall stays yours. `EntitlementState` is what your UI observes — a cache of server

truth, never a source of it

  • shadcn/Tailwind is n/a for this pack: there is no web UI surface to design

**Build budget**

  • Expected: **2–3 sessions on a $20/mo plan** (Claude Pro or equivalent — Pro's binding

constraint is prompt volume inside its 5-hour rolling window and weekly cap, not raw tokens;

Anthropic doesn't publish an exact token-per-window figure, so treat the session count as an

expectation, not a guarantee). Session 1: `prompts/01-build` (three parallel lanes, tasks

routed cheap/standard per TASKS.md; two tasks need the `capable` tier — the push-token

verifier and the entitlement/acknowledgement state machine). Session 2: `prompts/02-verify`

fix loop. Optional session 3: `prompts/03-golive` once your Play Console side exists.

  • Plan on **~800k fresh tokens** (input + output) for one build pass. **That number is an

estimate, not a measurement** — this pack is `status: draft` and has no receipt yet. It is

anchored on `appstore-machinery`'s measured 730k for a comparably shaped pack and adjusted up

for one extra task and a second capable-tier task. When the harness runs this pack, the

receipt replaces it with a measured figure and a measured per-run dollar cost.

How it works

  1. 01

    Buy the pack

    Instant download: PRD, architecture decisions, task graph, acceptance tests, scaffold, and per-phase prompts.

  2. 02

    Feed it to your agent

    Claude Code or Cursor builds inside the pinned scaffold — no context needed beyond the pack itself.

  3. 03

    Verify

    Run the pack's acceptance script. It checks the same things our verification harness checked.

android-kotlin-node versions lock

com.android.billingclient:billing9.1.0
androidx.core:core1.19.0
kotlin2.4.10
android-gradle-plugin9.3.1
gradle9.6.1
jdk17.0.20
node24.18.1
jose6.2.7

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.