Sign in
Signing in on mobile is the flow, choosing a client credential is why it ends in a bearer token, selling on Google Play is the purchase half, and gaps in Play APIs is what Google does not expose.
Passwordless. A magic link or an emailed one-time code — six digits, five minutes, single-use.
Google Sign-In is available as a social provider. Enable it, then store the client id and secret as one atomic secret — both halves travel together so the pair is never split across config and store.
Hold two credentials
An access token for the Authorization header, and a refresh credential you rotate on use.
Rotate on 401, once. Do not loop — 401 means get a fresh token, 403 means you are not allowed, and a client that treats them the same refreshes a perfectly good token forever.
A replay inside 30 seconds is denied and nothing else. Past that, the whole family is revoked — so do not fire two rotations concurrently from two coroutines.
Register the device
x-pithy-device-id is the only required header; x-pithy-platform: android and the rest are best-effort.
Keep the id stable across launches.
Buying things
Submit the receipt to your Worker as soon as the purchase completes, and gate on the entitlement key rather than the Play product id.
One entitlement, many products, five rails — that indirection is what lets somebody buy on Play and be entitled on the web.
The Play requirement, and the capability for it
A personal developer account created after 13 November 2023 must run a closed test with 12 testers opted in for 14 continuous days.
The days must be consecutive — lose one tester on day nine and the clock effectively restarts.
Adding an address to a Play email list is a manual step in the Console, because the API cannot do it — which is why the tester journey is two emails rather than one.
Translate errors on the client
t.maybe(payload.code, payload.params) ?? payload.messageThe code is the translation key, and the English always arrives on the wire as the fallback.
Uploading
Presigned URL, PUT to R2, then complete. Multipart above 100 MiB, resumable by re-listing parts.