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
- Product catalog with admin CRUD (create, edit, reorder, activate)
- Shopping cart held in a signed cookie (7-day TTL) — no DB row until checkout
- SOL payments via client-signed transactions
- On-chain receipt PDAs minted by the server once a payment confirms
- Order history with live status tracking
- Admin dashboard with stats and auto-refresh
- Wallet sign-in (SIWS) via the bundled
Solrengine::Auth::Engine
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:
- auth — SIWS wallet sign-in; the wallet address is the user identity
- rpc — blockhashes, transaction confirmation, account reads
- transactions — the
Transferableconcern that models and tracks the SOL payment - programs — IDL parsing + Borsh encoding for the receipt instruction
- tokens — SOL/USD price display
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.