docs: shorten the store receipt rules in the badge service protocol

This commit is contained in:
spaced4ndy
2026-09-30 18:38:13 +04:00
parent f6c31cb8be
commit e3e4866103
+1 -1
View File
@@ -38,7 +38,7 @@ The client never states what is signed. The tier is that of whatever funded the
- **Which class an answer belongs to.** The two mistakes do not cost the same, so when it is not certain, answer unreachable. `receipt_invalid` tells the client the purchase is dead: it drops the keys it signed with and finishes the store transaction — consumed on Play, finished on StoreKit — and nothing can present it again, so a buyer answered this by mistake has paid for a badge they can never receive. Answering unreachable by mistake costs one more verification request each time the app presents the purchase again, and a Play purchase left unacknowledged for three days is refunded to the buyer.
- **Terminal**, `receipt_invalid` — the store answered about this purchase and cannot change its mind: an Apple signature or certificate chain that does not verify, another app's bundle id, a test purchase (Apple Sandbox, Play `purchaseType` test), a refunded or revoked purchase, Play `purchaseState` canceled.
- **Pending**, `payment_pending` — Play `purchaseState` pending.
- **Unreachable**, `provider_unavailable` — the store was not asked or did not answer: network, timeout, 5xx, quota, this service's own auth or permission failures, and every Play error response, a 404 from `purchases.products.get` or `purchases.subscriptionsv2.get` and a 400 or 410 calling the token invalid included. Only a purchase record Play returns is a verdict: Play may not yet know a token it has just issued, and nothing tells that apart from one it never issued. A token outside the characters this service will send is not sent, and lands here too, since Play documents no token grammar and our check is not Play's verdict. A product id outside the characters Play does document is terminal, since no product of this app can have one.
- **Unreachable**, `provider_unavailable` — the store was not asked or did not answer: network, timeout, 5xx, quota, this service's own auth or permission failures, and every Play error response, a 404 from `purchases.products.get` or `purchases.subscriptionsv2.get` and a 400 or 410 calling the token invalid included. A 404 decides nothing: Play answers it both for a token it has just issued and not yet recorded, and for one that was never a purchase. Only a purchase record is a verdict. A token whose characters this service will not send is never sent, so Play never answers about it — and Play documents no token grammar, so that check is our guess, not its verdict. A product id outside the characters Play does document is terminal, since no product of this app can have one.
- Apple is verified offline, so it has no "not yet" and a negative answer about the signed evidence is terminal. Two things are not the store's answer and leave the client holding the purchase: the verifier's own failure, such as a root certificate it could not load, which is `internal`; and a product this service does not price, which is `product_unavailable` — the verifier vouched for the transaction either way.
- Funding by `receipt` is a transfer (post-MVP): the unissued months of the purchase that receipt belongs to move to the signing key, recorded as `debit(transferOut)` on the source and `credit(transferIn)` on the new purchase, and the presented receipt is retired for a fresh one. The transferred period's issuance debits a month like any other. Lifetime badges hold no receipt, so support handles them.
- `upgradeBadgeSubscription` → `badgeCredential` — the app-led store subscription change, on the same key: verifies the store evidence of the replaced subscription and records the new plan; an immediate upgrade returns the new credential, a deferred change returns none. Its `badgeRequest` is to be dropped (see above).