When you want to accept payments online today, the playbook is the same: sign up for Stripe, copy a few lines of code, and you are done. In Nigeria, we mostly use Paystack or Flutterwave. It works. But it also means you are paying a certain percentage on every transaction, your payment data lives on someone else's servers, and your customers need a credit card.
For a lot of businesses, that is fine. For a lot of others — especially developers, small merchants, and communities that care about freedom — it is a compromise they never really chose. It was just the only option.
I wanted to know if there was another way. That question led me to experimenting and building OpenCheckout using Open Payments.
The Problem With How We Accept Payments
Traditional payment checkout products are excellent. They abstract away enormous complexity. But that abstraction comes at a cost you feel every single month from per-transaction fees, data, and a checkout experience you cannot fully control.
More fundamentally, these platforms sit between you and your customer's money. They are not just processing payments, but instead, they are the entire payment relationship. If Stripe decides to hold your funds, change their pricing, or deprecate an API, you adapt or you leave. There is no self-hosting option. There is no alternative provider you can swap in without rewriting your integration.
I started asking, "What would a checkout system look like if it were built like an open protocol that any merchant can implement, where you can choose your host, and where the software is yours?"
Looking At Open Payments
Around this time last year I did something similar for CTRL+Pay, but this time I did some proper research into Open Payments. I like that Open Payment defines a way for applications to issue payment instructions to account servicing entities without touching funds. I also like how it is designed for interoperability.
So, I read through the documentation and the SDKs word for word. The more I understood it, the more it felt like the missing piece. What was missing was the checkout layer — the thing that makes Open Payments usable for a merchant who just wants to sell a product globally.
OpenCheckout
OpenCheckout is a self-hosted checkout system. You deploy it on your own server with a single docker compose up command. It gives you a REST API for creating checkout sessions and a hosted payment page where your customers enter their wallet address to pay.
Under the hood, OpenCheckout orchestrates the full Open Payments flow: it creates an incoming payment on your wallet, requests a quote from your customer's account provider to confirm the cost, facilitates the interactive grant where your customer approves the payment at their own bank, and issues the outgoing payment instruction. The customer is redirected back to your success URL. A webhook fires to your backend. The session is marked complete.
Here is what that looks like from your backend:
POST /api/checkout/sessions
Authorization: Bearer sk_xxx
{
"mode": "payment",
"line_items": [{
"price_data": {
"currency": "usd",
"product_data": { "name": "T-shirt" },
"unit_amount": 2000
},
"quantity": 1
}],
"success_url": "https://yourstore.com/order/123",
"cancel_url": "https://yourstore.com/cart"
}
The response includes a url field. You redirect your customer there. OpenCheckout handles the rest.
Why I Like How This Works
There are a few things that I have come to like about building OpenCheckout, as my first proper Open Payments-related experiment.
The protocol does the heavy lifting. Open Payments is genuinely well-designed. The separation of payment instructions from execution means OpenCheckout never touches money. The SDK handles key management, request signing, grant negotiation, and hash verification. Most of the security-critical work is inherited from the protocol, not reinvented in the application.
Wallet addresses are a better primitive than card numbers. A wallet address URL is discoverable, shareable, and verifiable. You can GET it to find out everything you need. There is no PAN, no CVV, and no expiry date to store. The customer authenticates at their own ASE. There is nothing to steal from the checkout page.
Self-hosted means zero marginal cost. SQLite just seems to be the best option as the database for now. The only external dependency is the Open Payments SDK. A merchant on a $0 or $6 VPS can run this indefinitely with no per-transaction overhead.
REST API reduces adoption friction. The checkout sessions API mirrors Stripe's shape. This means the same endpoint, field names, and idempotency behavior. A developer who has integrated Stripe can switch their backend to OpenCheckout by changing a base URL and an API key. This matters a lot for adoption.
The Architecture, Briefly
OpenCheckout is a Next.js application with three main surfaces:
The merchant API (/api/checkout/sessions) is the backend-facing REST interface. It is authenticated with API keys, supports idempotency, and validates all input against Zod schemas.
The checkout page (/pay/:sessionId) is the customer-facing UI. It shows the order summary in a cream color block, accepts a wallet address URL, and handles the redirect to the customer's authorization server for payment approval. After approval, a success page confirms the payment before forwarding to the merchant's success URL.
The orchestration engine (src/lib/open-payments/) wraps the Open Payments SDK and manages the full protocol lifecycle: wallet address resolution, incoming payment creation, quote requests, interactive grant handling, outgoing payment creation, and hash verification.
The dashboard (/dashboard) gives the merchant a view of their transactions, API key management, and webhook configuration. It uses a beautiful design system with pastel color blocks. I did this deliberately to make the checkout feel trustworthy.
What Testing Looked Like
I ran 20 real transactions through the Interledger test network — from $3.00 to $2,000.00 — between two wallet addresses, one denominated in EUR and one in USD. Every payment completed successfully. The exchange rate was applied automatically by the test ASE. The product descriptions also appeared in the transaction history on both sides. The redirect flow worked. The success page showed a countdown and forwarded to the merchant URL.
I think there is something satisfying about watching money move through software you built, through a protocol you did not build but chose to trust, between accounts at a provider you do not control. For me, it reinforces that the protocol is the product and that the protocol works.
What Comes Next
Although OpenCheckout is working as an experimental MVP, I still think there is so much for me to do. For instance, Open Payments adoption among account servicing entities is still early. The Interledger test network works today for development and testing. I believe that if more real ASEs implement the standard, the addressable market for OpenCheckout grows automatically.
Also, for OpenCheckout itself, I already have a roadmap that includes every other use case Open Payments supports. I also have to make sure there are more helpers I need to work on. However, I have made the code open-source under AGPLv3 at github.com/temidayoxyz/opencheckout.
Contributions, feedback, and questions are welcome.
If you have an Open Payments wallet and five minutes, you can be accepting payments on your own domain. That feels like something worth sharing.
Top comments (0)