RORK LABJP
APPLE-AI — Apple opens Foundation Models free to developers under 2M first-time downloads, slashing the cost of adding AI to indie appsSWIFT-API — Foundation Models server-side integration lets you call Claude and Gemini through the same Swift API, now with image inputKOTLIN-MIGRATION — Android Studio's migration agent converts React Native apps into native Kotlin automatically — a future path for Rork-built appsRORK-MAX — Rork Max generates native Swift code ($200/mo), covering iPhone, iPad, Watch, TV, Vision Pro, and iMessageSIMULATOR — A browser-based streaming iOS simulator lets you test on a real Apple environment without Xcode or Mac hardwareSWIFTUI — SwiftUI evolves at WWDC 2026 with reorderable containers, swipe actions for any container, and layouts up to 2x fasterAPPLE-AI — Apple opens Foundation Models free to developers under 2M first-time downloads, slashing the cost of adding AI to indie appsSWIFT-API — Foundation Models server-side integration lets you call Claude and Gemini through the same Swift API, now with image inputKOTLIN-MIGRATION — Android Studio's migration agent converts React Native apps into native Kotlin automatically — a future path for Rork-built appsRORK-MAX — Rork Max generates native Swift code ($200/mo), covering iPhone, iPad, Watch, TV, Vision Pro, and iMessageSIMULATOR — A browser-based streaming iOS simulator lets you test on a real Apple environment without Xcode or Mac hardwareSWIFTUI — SwiftUI evolves at WWDC 2026 with reorderable containers, swipe actions for any container, and layouts up to 2x faster
Articles/Dev Tools
Dev Tools/2026-06-12Advanced

Your App Won't Notice the Refund — Revoking Entitlements with REFUND Notifications and the Voided Purchases API

A refund doesn't reach your app on its own — a cached premium flag survives it. Implementation notes on revoking access via App Store Server Notifications V2, Google Play's Voided Purchases API, and RevenueCat.

RevenueCat20App Store66Google Play17subscriptions8in-app purchases2

Premium Article

I was going through the monthly report for one of my wallpaper apps when I noticed a few small negative rows in the sales list. App Store refunds. The amounts were trivial — what bothered me was something else entirely. I realized the refunded users' devices were almost certainly still running with the lifetime Premium unlock fully active.

I checked, and I was right. At the time, my app set a local "premium" flag on a successful purchase and simply read that flag on launch. When a refund goes through, neither Apple nor Google reaches out to your app on its own. Unless you receive the server notification and revoke the entitlement yourself, the refunded user keeps the feature indefinitely.

For an indie developer, refunds are a handful of cases per month at most, so this is easy to postpone. But the higher your one-time or subscription price, the harder it becomes to ignore. These are my notes from wiring refund detection into my own apps, including the parts where I stumbled.

Why refunds never reach your app

An App Store refund is settled entirely between the user and Apple. The user files a request through "Report a Problem," Apple approves it, and the refund is done — nothing is pushed to your app at that moment. StoreKit 2 does record a revocationDate on the affected transaction, but that's information your app only sees if it actively reconciles transactions.

Google Play behaves the same way. If a refund issued through the Play Console or the API includes "revoke entitlement," the purchase is voided — but the client only finds out the next time the Billing Library reconciles purchase state.

The trouble is that many apps, mine included at the time, treat purchase handling as "set a local flag on success and move on." Under that design, the flag stays up after the refund, and the user keeps Premium. Across my wallpaper apps, refunds sit around 0.5% of sales — small, but the highest-priced lifetime tiers are exactly where each case hurts most, and left alone it quietly becomes a known loophole: refund first, keep using it anyway.

Decide the path to revocation first — three architectures

Before writing any code, decide which route refund information takes into your system. There are effectively three options.

Option A: centralize on RevenueCat

Pairing a Rork-generated app with RevenueCat is the standard setup for in-app purchases, and it's also a strong choice for refund detection. RevenueCat receives server notifications from both Apple and Google, and when it detects a refund it expires the entitlement automatically. Your app's only job is to reconcile CustomerInfo.

Option B: receive server notifications yourself

If you run your own subscription backend, consume App Store Server Notifications V2 and Google Play's Real-time Developer Notifications (RTDN) directly. It's more work, but you also get first-party access to everything else — billing retries, cancellation intent, offer redemptions.

Option C: client-side reconciliation only

For a small app with no backend, you can reconcile StoreKit 2's currentEntitlements (queryPurchasesAsync on Android) at launch and drop revoked transactions. Detection slips to "next launch," but that's still a major improvement over an unattended local flag.

Since most of my apps already route purchases through RevenueCat, I default to Option A and add Option B only where I need purchases tied to my own user IDs.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
A Node.js webhook that verifies App Store Server Notifications V2 signatures and revokes access on REFUND, using @apple/app-store-server-library
How refunds propagate through RevenueCat, and how to stop a cached premium flag from outliving the refund
Android-side revocation with SUBSCRIPTION_REVOKED and the Voided Purchases API, plus practices that keep my refund rate around 0.5%
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

Dev Tools2026-05-04
Rork Max App Store & Google Play Submission Checklist 2026
The submission pitfalls specific to Rork Max-generated apps — privacy permissions, build numbers, Data Safety sections, and API key exposure. Use this before you hit submit.
Dev Tools2026-03-10
How to Publish Your Rork App to App Store and Google Play
Complete guide to publishing Rork apps to Apple App Store and Google Play Store with Rork original and Rork Max.
Business2026-03-25
The Complete Design for Automated Revenue Pipelines in Rork Apps— 5 Engines That Earn While You Sleep
A comprehensive guide to fully automating revenue for Rork-built apps through 5 pipeline engines: subscription retention, ad optimization, dynamic pricing, automated support, and review-driven improvement loops.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →