Social LiveDocumentationPLATFORM DOCS

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):

  1. Idempotency check on coin_refund:<channel>:<externalRef> -replays return the existing reversal.
  2. In one transaction: wallet coins decremented by the package amount, and a REFUND ledger 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.

  • 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.