官方网站:ds.pro | DsPay GitHub
Dujiao-Next
A digital goods e-commerce platform: the Go backend, the customer storefront, and the admin panel — with DsPay (USDT) built in.
关于本仓库:本仓库是 dujiao-next(数字商品电商平台) 的 DsPay(DigitalShieldPay)接入版,Go 后端 + 用户前台 + 管理后台一体化交付,内建 DsPay USDT 支付渠道。DsPay 相关问题请优先在本仓库提交 Issue;DsPay 网关本身见 https://github.com/DigitalShieldOfficial/USDT-Payment-Gateway 。
DsPay(DigitalShieldPay) 是一个 USDT/USDC 链上支付网关,买家扫码进入托管收银台用链上稳定币付款,商家自动收款发货。本仓库已将其作为内置支付渠道:
- 用户下单 → 跳转 DsPay 托管收银台 → 链上支付 → 回调验签 → 自动发货,全流程无需人工干预。
- 内置三层查单补单兜底:网关回调之外,用户打开支付页会每 5 秒向网关核实并补单;服务端还有 asynq 周期任务(每分钟)扫描近 24 小时待支付记录主动查单,回调丢失也能自动补上。
- 金额链路与订单同源:订单按人民币金额÷汇率折算 USDT 支付金额(向下截断 2 位),回调以创建时快照比对金额,杜绝链上尾数造成的金额偏差。
- 找 DsPay 商务获取:商户编号(merchantNo)、API 密钥(apiSecret)、API 网关地址。
- 登录管理后台 → 支付渠道 新建渠道,支付商选 DsPay (USDT),填入:
- 网关地址(gateway_url):如
https://wallet.ds.pro(注意是 wallet 域名,不是收银台域名) - 商户编号(merchant_no)
- API 密钥(api_secret)
- 汇率(exchange_rate):1 美元兑换多少人民币,如
7.20 - 支付回跳地址(return_url):可选,留空使用站点默认回跳地址
- 网关地址(gateway_url):如
- 到 DsPay 商户后台把回调地址(notifyUrl)配置为:
(如在后台「回调路由配置」自定义了 DsPay Webhook 路径,以自定义后的为准)
https://你的域名/api/v1/payments/webhook/dspay - 保存即可收款。
⚠️ API 密钥等同资金凭证,切勿泄露到公开场合;怀疑泄露请立即在商户后台轮换。⚠️ 商品价格低于汇率值时(如 0.01 元 ÷ 7),换算后金额为 0 会导致无法下单,测试请使用 ≥ 汇率值的商品价格。接入实现细节(签名契约、三条链路落点文件、测试说明)见 docs/dspay-接入总结.md。周期兜底任务依赖 Redis(asynq);未启用 Redis 时回调 + 前端轮询两层兜底仍然生效。
![]() |
Vmrack.com 全球自动化云基础设施服务商 提供先进的云服务器、裸金属、CDN、媒体处理、对象存储和网络解决方案,助力企业轻松上云。 ⚡️官方合作闪购款,仅需76刀/年,三网优化线路,助力您业务起飞,👉点我直达 |
![]() |
99CDN 自建 CDN 平台,自主管理节点 · 智能流量调度 · 多级缓存加速。 99CDN 是 EasyLink 旗下的商业化自建 CDN 与 DNS 智能调度平台,支持边缘缓存、分片缓存、多级回源、GTM 调度与边缘计算能力。 |
![]() |
NIUB — 数字服务,一站直达(DujiaoNext自营旗舰店) 正在寻找更便捷的 AI 服务、社交账号或数字礼品卡?NIUB(niub.me)专注提供多种虚拟商品与数字服务,让不同类型的数字需求都能在一个站点完成选购。 我们重视清晰的商品信息、明确的交付方式和负责任的售后支持。每件商品的账号类型、适用地区、有效期限、使用条件与售后范围,均以对应商品页面说明为准。 访问 NIUB,探索更多数字服务与虚拟商品。 |
| Layer | Stack |
|---|---|
| Backend | Go 1.26 · Gin · GORM · SQLite / PostgreSQL |
| Auth | JWT (separate admin / user realms) · Casbin RBAC · TOTP 2FA |
| Async | asynq on Redis (optional — the server runs without it) |
| Config | Viper (config.yml) |
| Frontend | Vue 3 · Vite · TypeScript · Tailwind CSS v4 · pnpm 10 |
| Admin UI | shadcn-vue / reka-ui |
.
├── cmd/server/ # entry point; also hosts the `admin` operator subcommands
├── internal/
│ ├── app/ # composition root
│ │ ├── container/ # dependency-injection container
│ │ ├── httpserver/ # Gin router, route groups, middleware
│ │ └── jobs/ # asynq worker service and consumers
│ ├── bootstrap/ # per-module wiring (adapters.go + wiring.go)
│ ├── modules/ # 35 business modules — one vertical slice per domain
│ ├── workflows/ # use cases that span several modules
│ ├── platform/ # framework-facing infrastructure
│ │ ├── database/gormdb/ # connection, auto-migration
│ │ └── http/ # response envelope, Gin helpers
│ ├── shared/ # dependency-free primitives (money, jsonmap, serial …)
│ ├── authz/ # Casbin RBAC: policy model, built-in role seeds
│ ├── web/ # SPA embedding and mounting (build-tag gated)
│ ├── architecture/ # architecture guard tests — no production code
│ ├── cache/ config/ constants/ crypto/ i18n/ logger/ queue/ version/
│ └── admincmd/ htmltext/ persistence/ telegramidentity/ testkit/ upstream/
├── frontend/
│ ├── admin/ # admin panel SPA (dev :5174)
│ └── user/ # customer storefront SPA (dev :5173)
├── config.yml.example
├── Dockerfile # single full-stack image
└── .goreleaser.yaml
Runtime directories created on first start: db/ (SQLite), uploads/, logs/.
A modular monolith. Each domain under internal/modules/<name>/ is a vertical slice with its
own layers:
| Layer | Holds | May import |
|---|---|---|
domain/ |
entities, value objects, business invariants | nothing from the other layers |
application/ |
use cases, port interfaces | domain, contract |
infrastructure/ |
GORM stores, gateways, queue adapters | domain, application ports |
transport/ |
HTTP handlers, presenters | application contracts |
contract/ |
port interfaces the application layer depends on, and the module's public surface for other modules | — |
These rules are enforced by tests, not convention. internal/architecture/ parses every
import in the tree and fails the build on violations. The main ones:
domainmust not reach intoapplication,infrastructure, ortransportapplicationmust not import Gin or asynq — no transport libraries in use cases- only a module's
infrastructure/gormstoreadapter may import GORM transportdepends on application contracts, never on concrete storesinternal/sharedstays free of modules, GORM, Gin, and asynqinternal/platformmust not depend on business modules
Run them with the rest of the suite: go test ./internal/architecture/...
Modules never import each other's internals — they talk through contract/, and the wiring
lives in internal/bootstrap/<module>/.
Every /api/v1/admin/... route passes through Casbin. The permission catalog is generated
from the live route table, but the built-in roles are hand-maintained in
internal/authz/bootstrap.go. Adding an admin route without adding it to a role seed leaves
that route reachable only by the super admin. internal/app/httpserver/rbac_coverage_test.go
checks that every registered route is covered.
| Tag | Effect |
|---|---|
| (none) | API only. No SPAs mounted — the default for local development. |
fullstack |
Embeds internal/web/dist/{admin,user} into the binary via go:embed. |
release |
Production behavior for outbound URL building. |
go:embed all:dist/admin all:dist/user requires both directories to exist, so a
fullstack build fails outright if the frontends were not built first. A plain go build
does not compile embed_fullstack.go — after touching internal/web/, verify with
go build -tags release,fullstack ./cmd/server.
./dujiao-next # all — HTTP server + background worker (default)
./dujiao-next -mode api # HTTP server only
./dujiao-next -mode worker # background worker onlyOperator subcommands ship in the same binary, so a container needs no extra tooling:
./dujiao-next admin list-admins
./dujiao-next admin reset-password
./dujiao-next admin reset-2faTwo independent SPAs, both built with Vite and embedded at release time.
Mount points. The storefront is served at /; the admin panel at web.admin_path
(default /admin). /api, /uploads, and /health are reserved prefixes — an unmatched
path under them returns 404 instead of falling through to the SPA shell. Adding a new
top-level backend prefix means updating reservedPaths in internal/web/handler.go.
The admin base path is resolved at runtime, not at build time. Since web.admin_path is
configurable, pnpm run build:fullstack only injects a <base href="__DJ_ADMIN_BASE__/">
placeholder, which the server rewrites on startup. Consequences for admin code:
- native
<a href>andwindow.locationnavigation must go throughadminUrl()insrc/utils/adminBase.ts <router-link :to>androuter.push()must not — vue-router already carries the base, and prefixing again yields/admin/admin/...
Storefront templates. The customer frontend ships more than one look, selected by the
storefront_template site setting (classic, vault). Template pages live in
src/templates/<name>/ and fall back to src/views/ when a page has no template-specific
version; see src/templates/registry.ts. Append ?template=vault to preview one locally.
i18n. Both frontends and all API responses are localized — Simplified Chinese, Traditional Chinese, and English. Do not hard-code user-facing strings on either side.
部署完成后,按 DsPay 支付 章节配置商户参数即可收款。
On a fresh Ubuntu 22.04+ or Debian 12+ server, download and run the official interactive installer:
curl -fsSL https://raw.githubusercontent.com/dujiao-next/dujiao-next/main/scripts/dujiao-next-manager.sh \
-o /tmp/dujiao-next-manager.sh
sudo bash /tmp/dujiao-next-manager.sh installThe installer deploys the release binary with systemd, an isolated local Redis, Nginx, SQLite, and a Let's Encrypt certificate. After installation, reopen the management menu with:
sudo dujiao-next-managerCommon automation-friendly commands are also available:
sudo dujiao-next-manager status
sudo dujiao-next-manager logs app
sudo dujiao-next-manager restart
sudo dujiao-next-manager configure-domain
sudo dujiao-next-manager configure-admin-path
sudo dujiao-next-manager renew-cert
sudo dujiao-next-manager admin-reset-password
sudo dujiao-next-manager admin-reset-2fa
sudo dujiao-next-manager uninstallThe first release supports a single non-wildcard domain on Ubuntu/Debian only.
It does not adopt an existing manual installation. If SMTP is skipped, configure
it in the admin panel before enabling email-verification registration. Application
data lives in /opt/dujiao-next; installer state is stored in
/etc/dujiao-next/install-state.json. TLS failures leave only the ACME challenge
endpoint enabled, and install can be rerun after DNS or firewall repair. Safe
uninstall creates and verifies a 0600 recovery archive under
/var/backups/dujiao-next before deleting managed data.
Download the latest dujiao-next_*.tar.gz from Releases:
tar -xzf dujiao-next_*.tar.gz
cp config.yml.example config.yml
# edit config.yml: set jwt.secret, user_jwt.secret, and web.admin_path
./dujiao-nextFull instructions: https://dujiao-next.com/deploy/
Or with Docker:
docker run -d -p 8080:8080 -v $PWD/config.yml:/app/config.yml:ro dujiaonext/dujiao-next:latestRun the backend and the two frontends separately for hot reload:
go mod tidy && go run ./cmd/server # :8080 — API only, no SPAs mounted
cd frontend/user && pnpm install && pnpm run dev # :5173
cd frontend/admin && pnpm install && pnpm run dev # :5174Both dev servers proxy /api, /uploads, /sitemap.xml, and /robots.txt to
localhost:8080. In production everything is same-origin, so these proxies are a
development-only concern.
Use
pnpmvia corepack.pnpm --dir Xdoes not read thepackageManagerfield of the target directory and will pick the wrong version —cdinto the package first.
goreleaser build --snapshot --single-target --cleanThis builds both frontends, embeds them, and compiles with -tags fullstack — the same path
CI uses for releases. The manual equivalent:
(cd frontend/admin && pnpm run build:fullstack) # injects the <base> placeholder
(cd frontend/user && pnpm run build)
rm -rf internal/web/dist && mkdir -p internal/web/dist
cp -r frontend/admin/dist internal/web/dist/admin
cp -r frontend/user/dist internal/web/dist/user
go build -tags release,fullstack -o dujiao-next ./cmd/serverNote that admin uses build:fullstack, not build. Plain build produces a bundle pinned to
/, which silently breaks a custom web.admin_path.
go test ./... # full suite
go test ./internal/architecture/... # dependency and layering guards
go test ./internal/modules/order/... # one module
cd frontend/user && pnpm run build # includes vue-tsc type checking
cd frontend/admin && pnpm run buildHealth check endpoint: GET /health
SQLite runs with MaxOpenConns=1. A store opens a transaction through
WithinTransaction(func(tx contract.Transaction) error), and every query inside the closure
must go through that tx handle or a store bound to it via WithTx(tx). Reaching for the
global DB handle instead asks for a second connection that will never be granted, deadlocking
the process — including indirectly, by calling a service that queries on its own. Read any
settings you need before opening the transaction, and keep outbound HTTP calls (payment
gateways and the like) outside it.



