Skip to content
SolRengine
Case study

Mercado — a Solana storefront with on-chain receipts

A digital-goods storefront that takes SOL payments and mints an on-chain receipt PDA for every order — built end to end in Rails 8 with SolRengine.

auth rpc tokens transactions programs devnet
Live demo View source
mercado.solrengine.org
Mercado — a Solana storefront with on-chain receipts screenshot

Mercado is a digital-goods storefront where the checkout is a Solana transaction and every paid order leaves a permanent, verifiable record on-chain. A buyer signs in with their wallet, fills a cart, pays in SOL, and the server mints a receipt PDA — a program-derived account that proves the sale happened, independent of the Rails database.

It’s a complete Rails 8 app — catalog, cart, admin CRUD, order history — with the payment rail and the receipts living on Solana. Built for the Colosseum Frontier Hackathon, running on devnet.

What it does

How it’s built

Mercado uses a hybrid data model: everything that’s cheap and mutable lives in Rails; everything that needs to be trustless lives on Solana.

Lives in Rails (SQLite) Lives on Solana
Products, Orders, OrderItems, Cart The SOL payment transfer
Order status + history The receipt PDA (buyer, seller, amount, order id, timestamp)

Under the hood it runs five processes via bin/dev (web, js, css, jobs, ws) and four SQLite databases (primary, cache, queue, cable) — the standard SolRengine Solid stack. Background work (payment confirmation, receipt minting) runs on Solid Queue; the ws process is the Solana WebSocket monitor that streams account changes.

The payment flow

This is the heart of the app — a two-party signing dance where the buyer signs the payment and the server signs the receipt:

1. Checkout      Server creates Order + OrderItems, returns blockhash + params
2. Pay           Client builds a SOL transfer (@solrengine/wallet-utils), wallet signs
3. Report        Client posts the signature; server records a Transfer, enqueues a job
4. Confirm       ConfirmationJob polls the chain; on success the Order becomes "paid"
5. Mint receipt  MintReceiptJob builds the receipt instruction, signs with the server
                 keypair, and sends it — Order becomes "receipt_minted"

The buyer never trusts Mercado with a private key, and Mercado never fronts the buyer’s funds. Each side signs exactly what it’s responsible for.

On-chain receipts

The receipt is what makes Mercado more than a Stripe clone. After payment confirms, MintReceiptJob calls a custom Anchor program to create a receipt PDA — a deterministic account derived from the buyer and the order:

seeds = ["receipt", buyer_pubkey, order_id_le_bytes]
instruction = create_receipt(order_id: u64, amount: u64)

Because the address is derived from the buyer and order id, anyone can recompute it and read the receipt straight from the chain — the proof of sale doesn’t depend on Mercado’s database staying online. The server keypair pays the PDA’s rent and signs the mint; the Solrengine::Programs gem handles the IDL parsing and Borsh encoding that turns create_receipt(...) into bytes the program understands.

The order state machine

Orders move through a small, explicit state machine, with failure branches at each on-chain step:

pending → paid → receipt_minted
   ↓        ↓
 failed   failed

A payment that never confirms stalls at pending → failed; a confirmed payment whose receipt mint fails stops at paid → failed, so the money is never lost track of even if the on-chain receipt step needs a retry.

The SolRengine gems behind it

Mercado pulls in the solrengine meta-gem and leans on five pieces of the stack:

Merchant-only actions are gated by a single comparison — current_user.wallet_address == ENV["MERCHANT_WALLET"] — no roles table, no admin password. The wallet is the authorization.

Try it

Mercado runs on devnet, so you can connect a wallet, pay with devnet SOL, and watch a real receipt PDA get minted — no real money required.

← All showcases Build your own →