Refund Clawback
How store refunds reverse coin credits -RTDN, sweeps, Apple notifications.
Last updated Thu Jul 16 2026 00:00:00 GMT+0000 (Coordinated Universal Time)
TL;DR -If a viewer refunds a coin purchase at the store, the platform automatically takes those coins back -even if it pushes the wallet negative — so a refund can never become free money.
Who this is for -Business & Product: why clawback exists and the policy choices (the intro and the policy section). Backend & Operations engineers: the three notification mechanisms and the reversal transaction below.
Here's the loophole this page closes. A viewer could pay for coins, receive them, spend them all on gifts, and then ask the store for a refund. Without a response, they'd keep the gifts and get their money back -free money at the platform's expense. Clawback is how the platform reverses the coins the moment a refund lands. Three separate mechanisms make sure no refund slips through.
In plain terms: why it takes three mechanisms
Refund notices arrive by different routes and can be delayed or dropped, so the platform listens in three overlapping ways: an instant webhook from Google, a periodic sweep that catches anything the webhook missed, and Apple's own notification stream. Belt, braces, and a second belt. The technical detail of each follows.
How it works: 1. Google real-time -RTDN webhook
POST /api/wallet/google/notifications receives Real-Time Developer
Notifications via Google Cloud Pub/Sub push.
Authentication is strict: the push subscription attaches a
Google-signed OIDC token. The backend verifies its RS256 signature against
Google's published certs (cached, refreshed on unknown key), the audience
(GOOGLE_RTDN_AUDIENCE -the endpoint URL), the issuer, and the sender
service account (GOOGLE_RTDN_SERVICE_ACCOUNT_EMAIL). Unconfigured → all
pushes rejected. No signature → 401.
voidedPurchaseNotification for one-time products triggers the clawback.
Notifications that can't be acted on (unknown order) are acked -a retry
cannot fix them; real errors 5xx so Pub/Sub redelivers.
How it works: 2. Google backstop -voided purchases sweep
VoidedPurchaseSweepService polls the Voided Purchases API every
GOOGLE_IAP_VOIDED_SWEEP_HOURS (default 6h, 30-day lookback) and reverses
anything RTDN missed -endpoint downtime, misconfiguration, or refunds
issued before RTDN existed. Google recommends running both; overlap is safe
because reversals are idempotent.
How it works: 3. Apple -Server Notifications V2
POST /api/wallet/apple/notifications receives App Store Server
Notifications. Zero-trust: the signed payload is decoded unverified
only to find the transaction id for REFUND/REVOKE types; the backend
then re-fetches the transaction from Apple's API and reverses only if
Apple confirms a revocation. A forged POST therefore cannot move money,
which is why the endpoint needs no shared secret.
How it works: the reversal itself
Once a refund is confirmed, the actual clawback is a small, idempotent
transaction. For a matched COIN_PURCHASE ledger row (found by store
order/transaction id):
- Idempotency check on
coin_refund:<channel>:<externalRef>-replays return the existing reversal. - In one transaction: wallet
coinsdecremented by the package amount, and aREFUNDledger row written (negative coins, negative gross/net cents, metadata: refund reason, source, original transaction id).
Deliberate policy choices
These three choices are where the business rules live -read them even if you skip the mechanics above.
- Wallets may go negative. If the coins were already spent, the user
now owes the balance -and the gift-spend check (
coins ≥ paidSpent) blocks further paid gifting until it recovers. This removes the refund-abuse economics entirely. - Host diamonds are not clawed back. The host did nothing wrong; the platform absorbs that cost and the refunder carries the debt. (The 72h settlement hold limits exposure for fast refunds.)
- Refunded orders can't re-credit: the order id's purchase key is already consumed, so re-presenting the receipt replays idempotently.
Not yet built
Automatic moderation flags for repeat refunders. The ledger metadata
(voidedReason, refundType, source) is recorded specifically so this can
be added as a query or hook later.
Related pages
- In-app purchases -the purchase these mechanisms reverse.
- Economy -the no-loss rules refunds enforce, including negative wallets.
- Gifting -why the 72h settlement hold limits refund exposure.
- Environments -where the RTDN and sweep env vars are set.