E
Epic Devs
Guest
Hey everyone
I'm Usama. By day, I work as a Project Manager. By night, I'm a solo dev building a Kotlin Multiplatform (KMP) notes app called EpicNotes, targeting the "forgotten combo" of Android phones and MacBooks.
The tech stack was hard (KMP, Supabase, local-first sync). But the part that actually broke me was the payment infrastructure.
I thought wiring up Stripe for the Mac app and RevenueCat for the Android app would take a weekend. Instead, I ended up in a 36-hour debug marathon, buying my own subscriptions, refunding them, watching database rows flip, and debugging at 2 AM after a full day of meetings.
If you are an indie dev about to wire up payments, here are the 3 brutal lessons I learned so you don't have to.
Stripe and RevenueCat test environments are great for happy paths, but they lie to you in subtle ways.
During my testing, the Android RevenueCat SDK had a blank API key that nobody noticed until I tried a real purchase with a real credit card. Test mode was perfectly happy passing simulated webhooks, masking the fact that my Android SDK was essentially flying blind.
The fix: Test with real money. Buy a $2 subscription on your own phone. Refund it. Watch your database. The peace of mind is worth the $2.
Webhooks are not guaranteed to arrive in order, and they will retry if your server takes more than a few seconds to respond.
I ran into a nightmare scenario where a
The fix: Never trust the webhook payload blindly. When a webhook arrives, use the
I had a bug where the X button on my Mac paywall wouldn't close the window unless the Stripe checkout session actually finished. If a user opened the paywall and changed their mind, they were trapped. I had to literally force-quit the app to get out.
It sounds silly, but when you are deep in the weeds of Kotlin Multiplatform UI state and async Stripe callbacks, you lose sight of the basic UX.
Building a dual-platform SaaS on top of a day job is a feature, not a bug—it forces you to be disciplined. But it also means when the webhooks fail, you aren't just losing company time; you're losing your sleep and your time with your family.
Having a working payment pipe is the most stressful part of the journey, but it's also the moment your hobby officially becomes a business.
I finally got the Stripe and RevenueCat webhooks fully certified and bulletproof, and EpicNotes officially launched this week.
If you want to see the result of those 36 hours of trench warfare, you can check out the app here: EpicNotes.
I'd love to hear from other solo devs: What's the most painful infrastructure you've had to wire yourself? Payment processing? Auth? Let me know in the comments!
The tech stack was hard (KMP, Supabase, local-first sync). But the part that actually broke me was the payment infrastructure.
I thought wiring up Stripe for the Mac app and RevenueCat for the Android app would take a weekend. Instead, I ended up in a 36-hour debug marathon, buying my own subscriptions, refunding them, watching database rows flip, and debugging at 2 AM after a full day of meetings.
If you are an indie dev about to wire up payments, here are the 3 brutal lessons I learned so you don't have to.
1. Test Mode is a Sandbox, Not Reality
Stripe and RevenueCat test environments are great for happy paths, but they lie to you in subtle ways.
During my testing, the Android RevenueCat SDK had a blank API key that nobody noticed until I tried a real purchase with a real credit card. Test mode was perfectly happy passing simulated webhooks, masking the fact that my Android SDK was essentially flying blind.
The fix: Test with real money. Buy a $2 subscription on your own phone. Refund it. Watch your database. The peace of mind is worth the $2.
2. Webhook Retries Create "Zombie" Users
Webhooks are not guaranteed to arrive in order, and they will retry if your server takes more than a few seconds to respond.
I ran into a nightmare scenario where a
customer.subscription.updated event arrived before the customer.subscription.deleted event due to network retries. My Supabase database saw the update, flipped the user back to is_pro: true, and completely ignored the subsequent deletion event. I had accidentally built a system that resurrected cancelled subscriptions into "Zombie Pro Users."The fix: Never trust the webhook payload blindly. When a webhook arrives, use the
subscription_id to make a direct API call back to Stripe/RevenueCat to fetch the live, current state of the subscription.3. The "Hostage Paywall" Bug
I had a bug where the X button on my Mac paywall wouldn't close the window unless the Stripe checkout session actually finished. If a user opened the paywall and changed their mind, they were trapped. I had to literally force-quit the app to get out.
It sounds silly, but when you are deep in the weeds of Kotlin Multiplatform UI state and async Stripe callbacks, you lose sight of the basic UX.
The Mental Toll of the Side Hustle
Building a dual-platform SaaS on top of a day job is a feature, not a bug—it forces you to be disciplined. But it also means when the webhooks fail, you aren't just losing company time; you're losing your sleep and your time with your family.
Having a working payment pipe is the most stressful part of the journey, but it's also the moment your hobby officially becomes a business.
I finally got the Stripe and RevenueCat webhooks fully certified and bulletproof, and EpicNotes officially launched this week.
If you want to see the result of those 36 hours of trench warfare, you can check out the app here: EpicNotes.
I'd love to hear from other solo devs: What's the most painful infrastructure you've had to wire yourself? Payment processing? Auth? Let me know in the comments!