DEV Community

Cover image for How Payhip Locked Us Out of a Market and What We Built Instead
Alice Nkosi
Alice Nkosi

Posted on

How Payhip Locked Us Out of a Market and What We Built Instead

The problem we were actually solving

In 2024 our open-source documentation tool had become popular enough that people in Kazakhstan and Iran started asking if they could pay us directly. At first I thought Stripe had rolled out to those countries. One morning I checked and learned Payhip blocks IP ranges in both markets because their processor, Stripe, will not settle transactions there. The message came back on every attempt to set up a sale: your country is restricted. Then Gumroad did the same. Even PayPal, our fallback, started rejecting transfers from Kazakhstani banks. The platforms werent failing us; they were failing our users.

We could have told users to create foreign shell companies or use VPNs, but that felt like asking them to break the law just to read good docs. So we had to accept a hard truth: our revenue pipeline had a geographic on/off switch controlled by Payhips processor, and we had no override.

What we tried first (and why it failed)

Our first attempt was to embed Stripe Connect so each contributor could become their own merchant. We wrote Terraform to spin up Stripe accounts under our platforms business model. The legal counsel said: fine, as long as no single transaction exceeds the de minimis threshold. Then Stripe froze one of the first contributor accounts because the IP matched a sanctions list. The entire flow ground to a halt while we waited three weeks for manual review.

Next we tried PayPal Payouts. Their API refused to disburse to Iranian bank codes, returning error code 10523: Receiver country not supported. We appealed three times; each response said the restriction was final. The overhead of appealing for every payout made the model unsustainable.

Finally we built a simple Payhip clone using Laravel and Stripes legacy Checkout. We naively thought Stripe Checkout would accept any user that Payhip rejected. It did not. The same IP block came back as 10019: Country unsupported.

The architecture decision

In August 2025 we decided to bypass global card networks entirely. Instead of trying to convince Stripe or PayPal that our users countries were respectable, we built an on-chain cash-in-advance flow. Users purchase USDT on a regional exchange (like Binance P2P or LocalBitcoins) and send the stablecoin to a wallet we control. The wallet is an Ethereum smart contract that verifies the sender has at least the required amount before minting a non-transferable ERC-1155 token representing a license key. The token is burned when the user redeems it in our SaaS portal. The whole process is atomic: the users transaction either succeeds or rolls back on-chain.

We chose Polygon PoS for low fees and near-instant confirmations. Polygons bridge to Ethereum mainnet gave us an off-ramp if we ever needed to liquidate USDT to fiat, but 95 percent of our revenue stays on-chain. We deployed a single Solidity contract with 137 lines of code and zero upgradeability; we wanted immutability so regulators couldnt retroactively freeze funds.

What the numbers said after

Six months in, the data looks grim for the old platforms and rosy for us.

Payhips monthly revenue from Kazakhstani users dropped from $3 200 to $110 after the block. Gumroad never responded to our appeals; their volume in Iran went to zero. Our on-chain pipeline captured 94 percent of those lost sales, translating into $47 000 in new revenue that would otherwise have vanished.

Cost per transaction on Polygon averaged $0.0045, versus Stripes 2.9 percent + $0.30. Even after factoring in the regional exchanges 0.5–1 percent spread on USDT purchases, we still cleared 2.1 percent more than we did with Stripe.

Retention among users in restricted regions jumped from 39 percent to 71 percent because they no longer had to lie about their location.

What I would do differently

I should have asked earlier why Payhips API returned success but their processor rejected the sale. A simple cURL to Stripes test endpoint with a Kazakhstani card number would have saved three months of integration work. We assumed platform-level APIs were truthful, but they lie by omission.

I also regret using ERC-1155 for something so simple; a single ERC-20 with a minting allowance would have cut contract size by 40 percent. The extra complexity bought us nothing.

Last, I would have secured a fiat off-ramp in each target country before launch. We discovered too late that Binance P2P in Kazakhstan is seasonal; during currency-control spikes we had to hold USDT for weeks before converting. Next time Ill pre-open accounts with two local exchangers to keep liquidity above the 7-day moving average.

Top comments (0)