2.5 Verify the API Key
The previous steps verified the split contract and the fund flow, all through the HashNut dashboard. This step switches to your own server for placing orders, verifying that the API key produced by one-key setup can create orders, query them, and receive HashNut's payment notifications.
The quickest way is to run the demo shop — a minimal working integration you can copy from.
NOTE
The demo shop comes in a Go and a Java version with identical behaviour; pick one. Both share the same React frontend and the same migrate.sql, both listen on port 1800, and both use the demo_shop database.
2.5.1 Requirements
| Dependency | Version | Notes |
|---|---|---|
| Docker | Any recent version | Used to run PostgreSQL so you do not have to install it locally |
| Node.js | 18+ | Frontend |
| Go | 1.21+ | Only for the Go backend |
| Java + Maven | Java 11+ / Maven 3.6+ | Only for the Java backend |
| A tunnel | — | Local development only, so HashNut can reach your machine with payment notifications. ngrok / frp / Cloudflare Tunnel all work — see the appendix |
2.5.2 Clone the code
Go version:
git clone https://github.com/nuttybounty/hashnut-demo-go.git
cd hashnut-demo-goJava version:
git clone https://github.com/nuttybounty/hashnut-demo.git
cd hashnut-demoNOTE
The Java version pulls the HashNut SDK from JitPack automatically — nothing to install by hand.
2.5.3 Start PostgreSQL with Docker
docker run -d \
--name hashnut-demo-pg \
-e POSTGRES_PASSWORD=postgres \
-e POSTGRES_DB=demo_shop \
-p 5432:5432 \
postgres:16These values are the defaults the backend expects. If you change them here, change the backend config to match:
| Argument | Value | Backend setting |
|---|---|---|
| Username | postgres (image default) | user / username |
POSTGRES_PASSWORD | postgres | password |
POSTGRES_DB | demo_shop | dbname / the database in the JDBC url |
-p 5432:5432 | Mapped to local 5432 | host / port |
Check that the container is up:
docker exec hashnut-demo-pg pg_isready -U postgresaccepting connections means you are good.
NOTE
POSTGRES_DB=demo_shop creates the database on the container's first start, so there is no need to run CREATE DATABASE yourself. If you are reusing an existing container that variable has no effect and you will need to create it manually: docker exec -i hashnut-demo-pg psql -U postgres -c "CREATE DATABASE demo_shop;"
2.5.4 Put the account info into migrate.sql
Open migrate.sql in the repository root, find the t_hashnut_api_key block, and replace the placeholders with the values exported in 1.4 Save the account info.
The exported TXT already ends with SQL you can paste directly:
INSERT INTO t_hashnut_api_key (block_chain, splitter, access_key_id, secret_key) VALUES
('ETH', '0xbd09...fca', '01...YW', 'H1...Tt')
ON CONFLICT (block_chain) DO UPDATE SET
splitter = EXCLUDED.splitter,
access_key_id = EXCLUDED.access_key_id,
secret_key = EXCLUDED.secret_key;WARNING
The two rows shipped in migrate.sql are placeholders (${split address} / secret-key). Running them as-is makes order creation fail signature verification. One run of one-key setup covers one chain, so if you want both ETH and TRON, switch wallets in the client and run it twice, then paste both statements.
Also check the t_coin_info block and make sure the chains and tokens are the ones you actually use — that table decides which chains the payment page offers.
Then apply it:
docker exec -i hashnut-demo-pg psql -U postgres -d demo_shop < migrate.sqlVerify the rows landed:
docker exec -i hashnut-demo-pg psql -U postgres -d demo_shop -c "select block_chain, splitter, access_key_id from t_hashnut_api_key;"2.5.5 Configure and start the backend
Go version — edit etc/application.yaml:
server:
port: 1800
database:
host: "localhost"
port: 5432
user: "postgres"
password: "postgres"
dbname: "demo_shop"
sslmode: "disable"
hashnut:
testMode: false # always false — production
baseURL: "" # leave empty for the defaultgo run main.goJava version — edit src/main/resources/application.yml:
server:
port: 1800
spring:
datasource:
url: jdbc:postgresql://localhost:5432/demo_shop
username: postgres
password: postgres
hashnut:
test-mode: false # always false — production
base-url: "" # leave empty for the defaultmvn spring-boot:runEither way the backend starts on http://localhost:1800. Check that it reached the database:
curl http://localhost:1800/api/chainsIt should return the chains and tokens you configured in t_coin_info.
WARNING
testMode / test-mode must stay false. Setting it to true points the SDK at a different backend address where your API key does not exist, and order creation fails outright.
2.5.6 Confirm notifyUrl is reachable
notifyUrl is the address HashNut calls you on, so it must be publicly reachable. Use your public server if you have one; during local development use a tunnel, as described in Appendix: Making notifyUrl publicly reachable.
All you need to check here is that the address currently works:
curl https://your-public-address/api/chainsChains and tokens coming back means it is open. On the ngrok free tier note that the address changes after a restart; when it does, update the API key's notifyUrl in the dashboard, otherwise notifications go to a dead address — which looks like "the payment succeeded but the order status never changed".
2.5.7 Start the frontend
The frontend is a separate repository, shared by both backends:
git clone https://github.com/nuttybounty/hashnut-demo-web.git
cd hashnut-demo-web
npm install
npm run devIt starts on http://localhost:5173 and Vite proxies /api to localhost:1800, so there is no CORS setup to do.
2.5.8 Run a full payment
Open http://localhost:5173 and:
- Pick any product and click Pay with Crypto
- Choose a chain and token (the ones from
t_coin_info) - You are taken to the HashNut payment page — pay with your wallet
- You are redirected back to
http://localhost:5173/payment-resultshowing the payment succeeded
The whole path looks like this:
Browser (localhost:5173)
→ POST /api/orders (Vite proxies to localhost:1800; the backend uses the API key to create the order)
→ redirect to the HashNut payment page
→ user pays on-chain
→ HashNut server → tunnel → localhost:1800/api/notify (notifyUrl: your backend is notified)
→ HashNut payment page → localhost:5173/payment-result?state=4 (callbackUrl: your frontend redirect)Getting through this means order creation, order queries and callback notifications all work with your API key, which is what this section set out to prove.
Where to look when it goes wrong
| Symptom | Likely cause |
|---|---|
| Order creation fails with a signature error | migrate.sql still has the placeholders, or a character was lost copying accessKey / secretKey |
| "API key does not exist" | testMode / test-mode is not set to false |
| The payment page offers no chains | That chain is missing from t_coin_info, or the backend never reached the database |
| Payment succeeds but the order status never changes | notifyUrl is unreachable: the tunnel is down, the address changed and was not updated, or notifyUrl and callbackUrl are the wrong way round |
| Order rejected with an IP error | The API key's bindIp is neither * nor your egress IP |
2.5.9 Next
The full API list, database schema and deployment notes live in the two repositories' READMEs:
- hashnut-demo-go
- hashnut-demo (Java)
To adapt this to your own application, the three places to look at in the backend are order creation, order queries, and the signature check and handling in /api/notify. See Create Order, Query Order and Notifications.