Blog
The Paywall I Built Myself, and Why I Switched to RevenueCat
Why my serverless paywall could not really verify a purchase, and what moving Memset's subscriptions to RevenueCat actually fixed.
For a few months, Memset ran on a paywall I had written entirely by myself. It took payments, unlocked the paid features, and looked completely ordinary to anyone using it. It also could not prove that a single one of those payments was real.
I did not set out to cut corners. I am a software engineer with about ten years in mobile apps, and I wanted to understand my own monetization instead of handing it to a service I did not control. A serverless subscription layer felt elegant. The app talks to the store, the store returns a receipt, the app checks that receipt and unlocks the features. No backend to run, no extra bill, complete ownership of the flow. I even planned to reuse the code in my next projects so I would never have to think about payments again.
The plan held up for exactly as long as nothing went wrong.
Then I started thinking about refunds, cancellations, expired cards, and the people who would inevitably tap buy twice or restore on a second device. The longer I looked, the clearer it got that my tidy system only handled the easy half of the job. The half that mattered, confirming and tracking what someone actually owned over time, was the half I had skipped without realizing it.

The paywall I built myself
My setup was simple enough to explain in one breath. When someone subscribed, the app sent the App Store receipt off for verification, got back a confirmation, and wrote the unlocked state into that user’s record in the database. From then on, the app trusted the record. The free plan in Memset stops at three Memsets, three Categories, and one custom Strategy, and the paywall is the thing that lifts those limits, so getting it right was not optional.
Two problems were hiding in that description. Both took me longer to take seriously than they should have, and both come down to the same missing piece.
Why receipt validation needs a server
The first problem was receipt validation. To trust a purchase you have to verify the store receipt, and to verify it safely you have to do that somewhere the user cannot reach. On a phone, every check you write runs on hardware the user owns. Someone determined enough can intercept the call, fake a successful response, and walk off with the paid features for nothing.
Apple also tells you not to do it the way I was doing it. The endpoint I leaned on, the old verifyReceipt API, is now officially deprecated, and the current guidance is to validate on a server you control through Apple’s newer App Store Server API. With no backend at all, I had no clean way to follow that advice. My validation worked for the honest customer on a normal device and fell apart quietly everywhere else.
The events I never received
The second problem was the bigger one, and it is what finally changed my mind. A subscription is not a single moment. It is a relationship that keeps changing. People cancel. People get refunds. Cards expire. Trials convert or lapse. The stores announce every one of those changes through server-to-server notifications, messages they send to a backend URL the instant something happens.
I had no backend URL, so I received none of them.
My app could unlock access at the moment of sale and then never hear that the sale had been undone. A person could subscribe, use everything, request a refund, and keep full access, because nothing was listening when the store tried to tell me otherwise. I could not act on cancellations. I could not even build honest numbers, because I only ever recorded the start of a subscription and never its end. App Store Server Notifications and Google’s equivalent exist precisely to close that gap, and I was deaf to both.
A paywall that only handles the sale is not a payment system. It is a hopeful guess that nobody ever cancels.
I was planning to reuse this everywhere
Here is the part that stings. I had not built this subscription layer just for Memset. I built it to reuse. The idea was a clean, self-contained monetization module I could drop into the next app, and the one after that.
Once I understood its limits, the plan inverted on me. Reusing the code did not mean reusing a head start. It meant copying a liability into every future project, each one inheriting the same blindness to refunds and the same validation a motivated user could defeat. A foundation you cannot trust is worse than no foundation, because you keep building on it before you notice the cracks.
So the choice stopped being about one app. If I wanted something I could genuinely carry forward, it had to be proven, and it had to already handle the cases I kept turning up. That made the migration a strategic decision rather than a cleanup task, and it is why I treated it as urgent instead of eventually.
What changed when RevenueCat took over
I moved Memset’s subscriptions to RevenueCat, the tool a lot of people had recommended to me long before I listened. The system changed shape in one decisive way. I stopped being the source of truth about who paid.
Now the app asks RevenueCat what a user is entitled to and listens for updates. RevenueCat talks to both stores, receives all the notifications I used to miss, and reports when a subscription renews, lapses, is cancelled, or is refunded. On my side, a small server function listens to RevenueCat’s webhook and mirrors the current entitlement into each user’s record, so the rest of my backend can make decisions without trusting anything the app claims about itself. The device no longer gets to crown itself a paying customer. Refunds and cancellations finally reach me, and they change access the way they always should have.
The reporting was a bonus I underrated. For the first time I can see real subscription behavior instead of a tally of checkout attempts.
It also made the plans themselves easier to manage. Memset now offers a monthly subscription, an annual plan with a trial, and a one-time lifetime purchase, and all three resolve to the same unlimited entitlement that RevenueCat keeps in sync across devices. Adjusting or adding a plan is a configuration change now, not a careful edit to fragile receipt-handling code.
The cost I was afraid of
My one genuine hesitation was money. RevenueCat is a paid service, and avoiding recurring costs was the whole reason I went serverless in the first place. I assumed it would tax every sale in a way I would come to resent.
Reading the actual pricing ended that worry fast. RevenueCat is free until an app is earning a few thousand dollars a month, and beyond that it takes a small percentage of tracked revenue. For an indie app at my stage that is close to nothing, and what it buys, correct entitlements, real refund handling, and reporting I would otherwise have to build and babysit myself, is worth far more than the fee.
The math only turns the other way at serious scale. Building and owning your own billing stack starts to pay off somewhere around hundreds of thousands of dollars in monthly revenue, where one percent becomes real money and you can staff a team to maintain it. Memset is nowhere near that line, and most apps never reach it. Optimizing for a problem I do not have would have been its own mistake.
Use the proven thing first
The technical takeaway is plain. Subscriptions need a server somewhere in the loop, because the stores are built on the assumption that one exists. But the lesson I keep returning to has less to do with code.
Friends had recommended RevenueCat. Indie developers I respect were already using it. It showed up in nearly every video and thread about app monetization. I heard all of it and built my own version anyway, partly because doing it myself felt like the real engineer’s path and partly because I wanted to understand every piece. I do understand every piece now. I also spent that understanding learning the slow way why everyone had been pointing me elsewhere.
When a problem is genuinely solved, your first version should be the proven solution, not the one you write from scratch. Ship the reliable, slightly boring option first and live with it. If you ever hit a real wall, you will know exactly what you are replacing and why. That order, proven first and custom later, would have saved me weeks, and it is how I intend to meet the next “I could just build this myself” the moment it tempts me.
RevenueCat now runs the part of Memset I once insisted on owning, which frees me to spend my time on the part that actually helps people, remembering what they learn. If you are facing the same decision in your own app, take the shortcut I skipped and start with the thing that already works.