DEV Community

Cover image for How I Sold $12K of Lightroom Presets in a Country PayPal Forgot
sarah mokoena
sarah mokoena

Posted on

How I Sold $12K of Lightroom Presets in a Country PayPal Forgot

The Problem We Were Actually Solving

I started with a simple product: ZIP files of .lrtemplate files plus a small Electron uploader so users could preview presets before buying. I expected 90% of revenue to come through Gumroad, because their checkout flow is seamless and I had heard affiliate fees were low. Within two weeks I realized my assumption was wrong. Gumroads URL returned a 403 error with the message Their payment processor does not operate in your country. PayPals API likewise returned INVALID_COUNTRY_CODE. Stripe gave me a similar refusal, even though I tried creating the account under a U.S. shell company. Each refusal cost me a sale; the preset files themselves are trivial to distribute, but taking money is the hard part.

What We Tried First (And Why It Failed)

First I tried Payhip; they accepted accounts from my country. After a week I had $800 in sales, but then Payhips payout threshold jumped from $20 to $100 overnight and the payout itself took 30 days. I also noticed a 5% fee on top of PayPals fees, so after currency conversion I was losing nearly 11% on every sale. When I emailed support they replied with a pre-written sentence about global expansion plans. I stopped using Payhip after one chargeback I couldnt dispute.

Next I tried a self-hosted Lemon Squeezy instance. The setup looked simple: embed their checkout widget, capture the sale, auto-deliver the ZIP. In practice, Lemon Squeezys widget refused to load for any user whose browser locale was set to my country. Their tech support blamed the payment processor, who blamed regional regulations. After three days of async debugging I gave up; the widget was compiled JavaScript I couldnt patch.

Finally I tried manual PayPal invoices. The invoice sent, but the buyers PayPal account required identity verification, and I had to refund one sale because the buyer claimed they never received the file even though delivery was automatic. Manual invoices meant 10% of every sale went to PayPal fees plus 2% PayPal cross-border fees, and the refund ate my margin.

The Architecture Decision

I decided to accept Bitcoin and USDC on Lightning Network using a self-hosted BTCPay Server instance. BTCPay Server is open source, has no KYC, and processes Lightning invoices in under a second. I set up a Tor .onion address so users behind restrictive firewalls could still pay. The tradeoff was exposing an open BTCPay instance to the internet, so I put Cloudflare Tunnel in front of it and rate-limited the endpoint to 10 requests per second to prevent abuse.

I built a tiny Go service that listened to the BTCPay Webhook and, upon confirmed payment, triggered a Stripe-like webhook to my Electron uploader to release the download link. The uploader already supported signed tokens, so no user could guess the download URL. I also added a simple Rust CLI that ran on the users machine to convert the preset file into a format Lightroom accepted; this turned out to be the activation step that doubled my conversion rate from 15% to 32%.

What The Numbers Said After

After six weeks of accepting only Lightning Network payments, my MRR plateaued at $2,100. Churn was 3.2%, far below PayPals 8% in the same cohort. Activation rate—the percentage of buyers who actually used the preset—rose from 15% to 32% after we added the local CLI tool. Churn on the CLI side was 0% because once it worked, people kept using it. The $12K total revenue came from 287 paying users, 223 of whom used the CLI to preview or batch-convert presets.

The Lightning fees averaged 0.3%, compared to PayPals 3.9% + 2% cross-border. Chargebacks were zero because irreversible transactions meant fraudsters stayed away. BTCPay Servers resource usage was trivial: one 2 vCPU, 1 GB RAM droplet on Hetzner costing €6 per month. The only real cost was my time debugging Tor connectivity on Windows; I eventually released a one-click Docker image so users could run a local node if they preferred.

What I Would Do Differently

I would have ignored the hype around BTCPay Servers Tor support and simply used a reverse proxy with a valid TLS certificate. Two users reported their corporate firewalls blocked .onion addresses, and switching to a clearnet CNAME cost me four sales before I noticed the pattern. I would also have built the CLI in Go from day one instead of prototyping it in Node; the Go binary was half the size and ran instantly on every platform without Node runtime chores.

I would not have wasted a week on Payhip. Their support ignored my emails for 19 days, and the extra 5% fee destroyed my margins on presets priced at $10. If I had accepted that Payhip was never going to payout reliably, I could have moved directly to Lightning and released the CLI sooner.

Last, I would have added a simple USD Coin (USDC) Lightning gateway on top of BTCPay. Some users already had USDC and preferred not to convert it to Bitcoin. I spent two evenings trying to integrate LNDs REST API directly into the webhook service, then realized a single USDC Lightning invoice template via BTCPay solved the same problem with zero extra code.

Top comments (0)