Skip to content
 
 

Repository files navigation

Digital Shield Pay
链上支付,尽在掌控。

官方网站:ds.pro  |  DsPay GitHub

Digital Shield Pay — 链上支付


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)

DsPay(DigitalShieldPay) 是一个 USDT/USDC 链上支付网关,买家扫码进入托管收银台用链上稳定币付款,商家自动收款发货。本仓库已将其作为内置支付渠道

  • 用户下单 → 跳转 DsPay 托管收银台 → 链上支付 → 回调验签 → 自动发货,全流程无需人工干预。
  • 内置三层查单补单兜底:网关回调之外,用户打开支付页会每 5 秒向网关核实并补单;服务端还有 asynq 周期任务(每分钟)扫描近 24 小时待支付记录主动查单,回调丢失也能自动补上。
  • 金额链路与订单同源:订单按人民币金额÷汇率折算 USDT 支付金额(向下截断 2 位),回调以创建时快照比对金额,杜绝链上尾数造成的金额偏差。

后台配置商户参数(装好后只需做这一步)

  1. 找 DsPay 商务获取:商户编号(merchantNo)API 密钥(apiSecret)、API 网关地址。
  2. 登录管理后台 → 支付渠道 新建渠道,支付商选 DsPay (USDT),填入:
    • 网关地址(gateway_url):如 https://wallet.ds.pro注意是 wallet 域名,不是收银台域名
    • 商户编号(merchant_no)
    • API 密钥(api_secret)
    • 汇率(exchange_rate):1 美元兑换多少人民币,如 7.20
    • 支付回跳地址(return_url):可选,留空使用站点默认回跳地址
  3. DsPay 商户后台把回调地址(notifyUrl)配置为:
    https://你的域名/api/v1/payments/webhook/dspay
    
    (如在后台「回调路由配置」自定义了 DsPay Webhook 路径,以自定义后的为准)
  4. 保存即可收款。

⚠️ API 密钥等同资金凭证,切勿泄露到公开场合;怀疑泄露请立即在商户后台轮换。 ⚠️ 商品价格低于汇率值时(如 0.01 元 ÷ 7),换算后金额为 0 会导致无法下单,测试请使用 ≥ 汇率值的商品价格。

接入实现细节(签名契约、三条链路落点文件、测试说明)见 docs/dspay-接入总结.md。周期兜底任务依赖 Redis(asynq);未启用 Redis 时回调 + 前端轮询两层兜底仍然生效。

❤️ Brand Partners (Sponsors)

CCTK.AI Vmrack.com 全球自动化云基础设施服务商 提供先进的云服务器、裸金属、CDN、媒体处理、对象存储和网络解决方案,助力企业轻松上云。 ⚡️官方合作闪购款,仅需76刀/年,三网优化线路,助力您业务起飞,👉点我直达
openmodel 99CDN 自建 CDN 平台,自主管理节点 · 智能流量调度 · 多级缓存加速。 99CDN 是 EasyLink 旗下的商业化自建 CDN 与 DNS 智能调度平台,支持边缘缓存、分片缓存、多级回源、GTM 调度与边缘计算能力。
openmodel NIUB — 数字服务,一站直达(DujiaoNext自营旗舰店) 正在寻找更便捷的 AI 服务、社交账号或数字礼品卡?NIUB(niub.me)专注提供多种虚拟商品与数字服务,让不同类型的数字需求都能在一个站点完成选购。 我们重视清晰的商品信息、明确的交付方式和负责任的售后支持。每件商品的账号类型、适用地区、有效期限、使用条件与售后范围,均以对应商品页面说明为准。 访问 NIUB,探索更多数字服务与虚拟商品。

Tech Stack

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

Repository Layout

.
├── 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/.

Architecture

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:

  • domain must not reach into application, infrastructure, or transport
  • application must not import Gin or asynq — no transport libraries in use cases
  • only a module's infrastructure/gormstore adapter may import GORM
  • transport depends on application contracts, never on concrete stores
  • internal/shared stays free of modules, GORM, Gin, and asynq
  • internal/platform must 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>/.

RBAC

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.

Build Tags

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.

Run Modes

./dujiao-next                 # all    — HTTP server + background worker (default)
./dujiao-next -mode api       # HTTP server only
./dujiao-next -mode worker    # background worker only

Operator 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-2fa

Frontend Notes

Two 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> and window.location navigation must go through adminUrl() in src/utils/adminBase.ts
  • <router-link :to> and router.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.

Quick Start (Deploy)

部署完成后,按 DsPay 支付 章节配置商户参数即可收款。

Official one-click installer (Ubuntu / Debian)

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 install

The 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-manager

Common 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 uninstall

The 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.

Manual binary installation

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-next

Full 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:latest

Quick Start (Develop)

Run 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   # :5174

Both 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 pnpm via corepack. pnpm --dir X does not read the packageManager field of the target directory and will pick the wrong version — cd into the package first.

Building the Full-Stack Binary

goreleaser build --snapshot --single-target --clean

This 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/server

Note that admin uses build:fullstack, not build. Plain build produces a bundle pinned to /, which silently breaks a custom web.admin_path.

Testing

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 build

Health check endpoint: GET /health

Notes on Data Access

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.

Online Documentation

About

Dujiao-Next

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages