示范商城
HashNut 提供完整可运行的示范商户应用,演示完整的支付对接流程。可以作为你自己实现的参考。
| 组件 | 语言 | 仓库 |
|---|---|---|
| Demo 后端 (Go) | Go + Gin | hashnut-demo-go |
| Demo 后端 (Java) | Java + Spring Boot | hashnut-demo |
| Demo 前端 | React + TypeScript | hashnut-demo-web |
两个后端实现相同的 API 接口,前端通用。
TIP
想把它跑起来(克隆代码、起数据库、填 api key、启动前后端、跑通一笔支付), 见 2.5 验证 api key —— 那一页是按操作顺序写的完整步骤。
本页是参考资料:架构、数据库表结构、以及你自己实现时需要注意的关键细节。
架构
浏览器 (localhost:5173) Demo 后端 (localhost:1800) HashNut API
| | |
| GET /api/products | |
|---------------------------------->| |
| GET /api/chains | |
|---------------------------------->| (从数据库读取) |
| | |
| POST /api/orders | |
| {productId, blockChain, tokenSymbol} | |
|---------------------------------->| SDK.createOrder() |
| |------------------------------->|
| | payOrderId + receiptAddress |
| payUrl (跳转到 HashNut 支付页) |<-------------------------------|
|<----------------------------------| |
| | |
| (用户在 HashNut 页面支付) | |
| | POST /api/notify (回调通知) |
| |<-------------------------------|
| | 更新订单状态 |
| | |
| 跳转到 /payment-result | |
| (前端显示支付成功) | |接口清单
后端对前端暴露的接口,两个语言版本完全一致:
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/products | 获取商品列表 |
| GET | /api/chains | 获取支持的链+币种(从数据库读取) |
| POST | /api/orders | 创建订单 {productId, blockChain, tokenSymbol} |
| GET | /api/orders/:id | 查询订单状态 |
| POST | /api/orders/:id/confirm | 提交支付交易哈希 {payTxId} |
| POST | /api/notify | HashNut 支付结果回调 |
数据库表结构
Demo 使用 PostgreSQL,包含以下表:
| 表 | 用途 |
|---|---|
t_coin_info | 支持的链和币种(决定前端能选哪些) |
t_hashnut_api_key | 每条链的 splitter 地址 + API 密钥 |
products | 商品(只有价格,不绑定链/币种) |
orders | 订单(记录用户选择的链+币种) |
t_coin_info 决定支付页面上能选哪些链和币种,按你实际接入的链配置:
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 一条链一行,值来自一键开户导出的信息 —— 具体怎么填见 2.5.4 把开户信息填进 migrate.sql。
关键实现细节
照抄 demo 之前,这几点是你自己实现时最容易出问题的地方。
多链支持
商品不绑定特定链。前端让用户选择支付链和币种,将 blockChain + tokenSymbol 传给后端。 后端从数据库查找对应的 splitter 和 API 密钥。
Webhook 处理
/api/notify 端点接收 HashNut 的支付通知。你的处理逻辑需要:
- 解析 JSON(
payOrderId、state、payTxId) - 更新数据库中的订单状态
- 返回字符串
"success"(HTTP 200)确认收到
如果不返回 "success",HashNut 会重试通知。
完整的通知字段与验签方式见 支付通知。
支付页面跳转
创建订单后,后端返回 payUrl 指向 HashNut 支付页面。前端跳转用户到该页面。 支付完成后,用户被跳转回你的 callbackUrl,带有 state 和 merchantOrderId 等查询参数。
notifyUrl 与 callbackUrl 的方向相反
这是接入时最容易搞反的一对参数:
| 字段 | 谁请求谁 | 是否需要公网可达 |
|---|---|---|
notifyUrl | HashNut 服务器 → 你的服务器 | 需要 |
callbackUrl | 用户浏览器 → 你的站点 | 不需要 |
Demo 里对应的路径分别是 /api/notify 和 /payment-result。本地开发时 notifyUrl 需要 内网穿透才能公网可达,做法见 附录:本地调试让 notifyUrl 公网可达。
相关
- 2.5 验证 api key —— 搭建与运行的完整步骤
- 创建订单 / 查询订单 / 支付通知 —— 后端实际调用的接口
- 两个仓库的 README 里有部署说明与更多实现注记