Demo Shop
HashNut provides fully working demo merchant applications that demonstrate the complete payment integration flow. Use them as a reference for your own implementation.
| Component | Language | Repository |
|---|---|---|
| Demo backend (Go) | Go + Gin | hashnut-demo-go |
| Demo backend (Java) | Java + Spring Boot | hashnut-demo |
| Demo frontend | React + TypeScript | hashnut-demo-web |
Both backends implement the same API; the frontend works with either.
TIP
To actually run it (clone the code, start the database, fill in the API key, start the backend and frontend, complete a payment), see 2.5 Verify the API Key — that page is written as an ordered walkthrough.
This page is reference material: architecture, database schema, and the details that matter when you write your own implementation.
Architecture
Browser (localhost:5173) Demo Backend (localhost:1800) HashNut API
| | |
| GET /api/products | |
|---------------------------------->| |
| GET /api/chains | |
|---------------------------------->| (reads from DB) |
| | |
| POST /api/orders | |
| {productId, blockChain, tokenSymbol} | |
|---------------------------------->| SDK.createOrder() |
| |------------------------------->|
| | payOrderId + receiptAddress |
| payUrl (redirect to HashNut) |<-------------------------------|
|<----------------------------------| |
| | |
| (user pays on HashNut page) | |
| | POST /api/notify (webhook) |
| |<-------------------------------|
| | update order status |
| | |
| redirect to /payment-result | |
| (frontend shows success) | |API list
What the backend exposes to the frontend. Identical in both language versions:
| Method | Path | Description |
|---|---|---|
| GET | /api/products | List products |
| GET | /api/chains | List supported chains and tokens (read from the database) |
| POST | /api/orders | Create an order {productId, blockChain, tokenSymbol} |
| GET | /api/orders/:id | Query order status |
| POST | /api/orders/:id/confirm | Submit the payment transaction hash {payTxId} |
| POST | /api/notify | HashNut payment notification callback |
Database schema
The demo uses PostgreSQL with the following tables:
| Table | Purpose |
|---|---|
t_coin_info | Supported chains and tokens (decides what the frontend offers) |
t_hashnut_api_key | Per-chain splitter address + API credentials |
products | Products (price only, not bound to a chain/token) |
orders | Orders (records the chain and token the user chose) |
t_coin_info decides which chains and tokens appear on the payment page. Configure it for the chains you actually accept:
INSERT INTO t_coin_info (block_chain, token_symbol, chain_label, coin_label, contract_address, decimals) VALUES
('ETH', 'usdt', 'Ethereum', 'USDT', '0xdAC17F958D2ee523a2206206994597C13D831ec7', 6),
('TRON', 'usdt', 'Tron', 'USDT', 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t', 6);t_hashnut_api_key holds one row per chain, with values taken from the one-key setup export — see 2.5.4 Put the account info into migrate.sql.
Key implementation details
Before copying the demo, these are the parts most likely to cause trouble in your own code.
Multi-chain support
Products are not bound to a specific chain. The frontend lets users choose which chain and token to pay with, and passes blockChain + tokenSymbol in the order request. The backend looks up the corresponding splitter and API credentials from the database.
Webhook handling
The /api/notify endpoint receives payment notifications from HashNut. Your handler must:
- Parse the JSON payload (
payOrderId,state,payTxId) - Update the order status in your database
- Return the string
"success"(HTTP 200) to acknowledge receipt
If you don't return "success", HashNut will retry the notification.
The full notification payload and signature scheme are documented in Notifications.
Payment page redirect
When an order is created, the backend returns a payUrl pointing to the HashNut payment page. The frontend redirects the user there. After payment, the user is redirected back to your callbackUrl with query parameters including state and merchantOrderId.
notifyUrl and callbackUrl point in opposite directions
This is the most commonly confused pair of parameters:
| Field | Who calls whom | Must be publicly reachable |
|---|---|---|
notifyUrl | HashNut server → your server | Yes |
callbackUrl | User's browser → your site | No |
In the demo the corresponding paths are /api/notify and /payment-result. During local development notifyUrl needs a tunnel to be publicly reachable — see Appendix: Making notifyUrl publicly reachable.
See also
- 2.5 Verify the API Key — the full setup and run walkthrough
- Create Order / Query Order / Notifications — the APIs your backend actually calls
- The two repositories' READMEs contain deployment notes and further implementation details