Configuration
This page walks through the configuration system of each DaxPay app, one section per app. The main app has the richest configuration (profiles, database, Redis, message queue, keys, monitoring, etc.); for the channel sub-apps and frontend apps, their config file locations and key differences are listed.
Reading guide
For a first deployment you only need the main app configuration and the production environment variables; consult the remaining apps on demand.
Configuration File Locations
| App | Config file location | Notes |
|---|---|---|
| Main app dax-pay-open | dax-pay-open/daxpay-start/src/main/resources/ | application.yml + application-dev.yml + application-prod.yml |
| dax-pay-channel-one | dax-pay-channel-one/daxpay-channel-start/src/main/resources/ | Same profile structure |
| dax-pay-channel-two | dax-pay-channel-two/daxpay-channel-start/src/main/resources/ | Same profile structure |
| dax-pay-channel-three | dax-pay-channel-three/daxpay-channel-start/src/main/resources/ | Same profile structure (international channels, Stripe) |
| dax-pay-channel-one-go | dax-pay-channel-one-go/configs/config.yaml | Go experimental copy; YAML config; path can be specified via the DAXPAY_CONFIG environment variable |
| dax-pay-channel-two-go | dax-pay-channel-two-go/configs/config.yaml | Same as above; Go experimental copy of the second channel |
| Admin Web (operations) | dax-pay-ui/apps/daxpay-admin/.env* | .env / .env.development / .env.production |
| Merchant Web | dax-pay-ui/apps/daxpay-merchant/.env* | Same as above, with VITE_APP_CLIENT_CODE=merchant |
| H5 | dax-pay-h5/.env* | .env / .env.development |
| uni-app admin | dax-pay-app-admin/env/.env* | The env/ directory required by unibest convention |
| uni-app merchant | dax-pay-app-merchant/env/.env* | Same as above |
| Cashier mini-program | dax-pay-mini-cashier/env/.env* | Same as above |
Configuration Dependencies
App configurations are not isolated islands: the API address in a frontend .env decides where requests land, and the main app connects to each channel sub-app through daxpay.channel.* routing and transport keys:
Main App Configuration
The main app separates environments with Spring profiles; core configuration lives in daxpay-start/src/main/resources/.
Profile System
| Profile | Description | Monitoring endpoints | Log level | API docs | Super admin |
|---|---|---|---|---|---|
dev (default) | Local development, troubleshooting friendly | All open | DEBUG for business packages | Enabled | Enabled |
prod | Production; credentials must be injected via environment variables; graceful shutdown | Restricted (health/info/metrics) | INFO for business packages | Disabled | Disabled |
How to switch:
# Command line
mvnd spring-boot:run -Dspring-boot.run.profiles=prod
# Environment variable
SPRING_PROFILES_ACTIVE=prod
# Environment variable (Spring Boot style)
export SPRING_PROFILES_ACTIVE=prodApp Ports
| App | Port | Health |
|---|---|---|
| Main app dax-pay-open | 9999 | http://127.0.0.1:9999/actuator/health |
| Channel sub-app dax-pay-channel-one | 20100 | http://127.0.0.1:20100/actuator/health |
| Channel sub-app dax-pay-channel-two | 20200 | http://127.0.0.1:20200/actuator/health |
| Channel sub-app dax-pay-channel-three | 20300 | http://127.0.0.1:20300/actuator/health |
| Admin Web (dev) | 6999 | — |
| Merchant Web (dev) | 7999 | — |
| Standalone H5 (dev) | 9500 | — |
| uni-app admin (H5 dev) | 9000 | — |
| uni-app merchant (H5 dev) | 9100 | — |
Database Configuration
PostgreSQL + HikariCP
Development environment application-dev.yml:
spring:
datasource:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://localhost:5432/daxpay-dev?autoReconnect=true&reWriteBatchedInserts=true
username: your_username
password: your_password
hikari:
minimumIdle: 5 # minimum connections
maximumPoolSize: 50 # maximum connections
leak-detection-threshold: 5000 # connection leak detection (ms)
connection-timeout: 30000 # connection acquisition timeout (ms)
idle-timeout: 600000 # idle connection timeout (ms)
max-lifetime: 1800000 # maximum connection lifetime (ms)In-repo dev files use hosts-mapped hostnames
The application-dev.yml in the repository actually uses the hostnames postgresql / redis / mq (mapped through local hosts entries to the machines hosting the middleware; the Redis and Artemis examples below are the same). When setting up your own environment, either add the matching hosts entries or change the hostnames back to localhost and adjust the credentials accordingly.
Redis Configuration
spring:
data:
redis:
host: localhost
port: 6379
database: 0
password: your_password
lettuce:
pool:
max-wait: 1000msArtemis Message Queue
Artemis is used for JMS messaging scenarios such as delayed payment notifications:
spring:
artemis:
mode: native # connect to an external standalone broker (not embedded)
broker-url: tcp://localhost:61616 # broker address
user: admin
password: admin
pool:
enabled: true
max-connections: 10 # 20 recommended for production
idle-timeout: 30s
jms:
cache:
enabled: true
session-cache-size: 10 # 20 recommended for production
pub-sub-domain: false # point-to-point queues by default
listener:
auto-startup: trueChannel Sub-App Routing
The main app calls channel sub-apps over HTTP. Routing configuration:
daxpay:
channel:
# Sub-app 1: direct channels for Alipay / WeChat / Douyin / UnionPay Merchant Services / UnionPay (QuickPass) (enabled)
one:
base-url: http://127.0.0.1:20100
# Channel transport AES key (always on; injected from CHANNEL_ONE_TRANSPORT_KEY in production)
transport-encrypt:
key: 0123456789abcdef0123456789abcdef
# Sub-app 2: aggregate channels such as Lakala / Haike Rongtong / Dougong / Leshua / Suixingfu / Hemafu / Adapay / Fuiou / Yeepay (enabled)
two:
base-url: http://127.0.0.1:20200
# Channel transport AES key (always on; injected from CHANNEL_TWO_TRANSPORT_KEY in production)
transport-encrypt:
key: fedcba9876543210fedcba9876543210
# Sub-app 3: international payment channels (Stripe etc., enabled)
three:
base-url: http://127.0.0.1:20300
# Channel transport AES key (always on; injected from CHANNEL_THREE_TRANSPORT_KEY in production)
transport-encrypt:
key: fedcba9876543210fedcba9876543210All three sub-apps (one / two / three) are enabled by default; their numbers are fixed and must not be renamed — they map one-to-one to the channel assignments in the routing strategy. Each sub-app's transport-encrypt.key is always on and must match the sub-app side; in production it must be injected via CHANNEL_ONE_TRANSPORT_KEY / CHANNEL_TWO_TRANSPORT_KEY / CHANNEL_THREE_TRANSPORT_KEY (startup fails if missing).
DaxPay Platform Configuration
Namespace daxpay.platform.*:
Super Administrator
daxpay:
platform:
starter:
auth:
enable-admin: true # whether to enable the super administrator (must be off in production)
admin-in-list: true # whether to show the super admin in the user listRSA Certificates
Used for signing API requests and responses:
daxpay:
platform:
config:
key-config:
private-key: '-----BEGIN PRIVATE KEY-----
...your private key...
-----END PRIVATE KEY-----'
public-key: '-----BEGIN PUBLIC KEY-----
...your public key...
-----END PUBLIC KEY-----'In production they are injected via environment variables:
RSA_PRIVATE_KEY='-----BEGIN PRIVATE KEY-----...'
RSA_PUBLIC_KEY='-----BEGIN PUBLIC KEY-----...'Data Encryption
Business-sensitive fields (channel keys, credentials, social login configuration, etc.) are stored with transparent AES-256-GCM encryption; a MyBatis-Plus TypeHandler encrypts and decrypts automatically on read and write, invisible to the business layer.
daxpay:
platform:
config:
encrypt:
enable: true # once enabled, sensitive fields are encrypted automatically on write and decrypted on read
keys: # key list (rolling keys supported; version numbers are unique within the list)
- key: your-current-32-byte-aes-key # current key (encrypts new data), 32 characters
version: 2
- key: your-legacy-32-byte-aes-key # legacy key (decrypts old data only), 32 characters
version: 1Rolling key mechanism:
- Ciphertext is stored in the format
v{version}:{base64(IV + AES-GCM ciphertext)}; on decryption the key is matched by version prefix - The first entry of the list is the current key, which encrypts newly written data; the rest are legacy keys used only to decrypt old ciphertext
- Rotating a key: generate a new 32-character key → put it at the head of the list with a larger
version→ keep the old keys → restart the app; from then on new data is encrypted with the new key while historical ciphertext still decrypts normally - Legacy keys that still have corresponding ciphertext must not be removed, otherwise that historical data can no longer be decrypted
Two cautions
- Do not disable
enableonce it is on: after disabling, ciphertext already written is no longer decrypted on read and is returned as raw ciphertext - Keys do not support online hot swapping: the configuration is loaded into beans at startup; after changing the
keyslist a restart is required. There is no admin-side rotation entry, and no re-encryption service for historical data
Exception Message Display
daxpay:
platform:
common:
exception:
show-full-message: true # on in dev / off in prodCache Configuration
Two-level cache architecture (L1 Caffeine local cache + L2 Redis shared cache), configured uniformly under daxpay.platform.common.cache.*. Every key has a default value, and the whole cache stack is on by default:
daxpay:
platform:
common:
cache:
enabled: true # master switch, default true; when off, both L1 and L2 degrade to NoOp (calls pass straight through) — for troubleshooting/diagnostics only
l1:
enabled: true # L1-only switch, default true; turning it off keeps only the L2 Redis (pure Redis mode)
default-ttl: 60 # default L1 TTL (seconds)
maximum-size: 10000 # default L1 maximum size (entries)
l2:
default-ttl: 1800 # default L2 TTL (seconds, 30 minutes)
secure-prefix: secure: # prefix marking sensitive cache names, default secure:
secure-names: [] # exact list of additional cacheNames treated as sensitive (optional)Key points:
- Master switch
enabled: L1 and L2 are a strongly coupled whole (acceleration + sharing). Turning it off degrades the entire two-level cache to NoOp — intended for troubleshooting/diagnostics only. L2 has no separate switch; disabling L2 is equivalent to disabling the master switch - L1 switch
l1.enabled: while the master switch is on, L1 can be turned off alone to keep only the L2 Redis (pure Redis mode) — suitable when strict consistency is required, when troubleshooting stale local reads, or for single-node setups with no need for local acceleration - Sensitive caches: caches whose names match
secure-prefix(defaultsecure:) or are listed insecure-nameshave their entire L2 value encrypted at rest with AES-GCM. When data encryption is disabled, sensitive caches use L1 only and never write to Redis (a security fallback) — see FAQ - why a cache may become "L1 read-only, never touching Redis"
API Docs Control
# The system uses springdoc by default; on in dev / off in prod
springdoc:
api-docs:
enabled: true # dev: true / prod: false
swagger-ui:
enabled: true # dev: true / prod: false
default-flat-param-object: true # flatten GET parameter object typesDocs URL in dev: http://127.0.0.1:9999/swagger-ui/index.html
Monitoring Endpoints
# dev: full set of endpoints (troubleshooting friendly)
management:
endpoints:
web:
exposure:
include: >
health,info,httpexchanges,
metrics,loggers,threaddump,
beans,mappings,scheduledtasks,
caches,conditions,startup
endpoint:
health:
show-details: always# prod: strictly restricted
management:
endpoints:
web:
exposure:
include: health,info,metrics
endpoint:
health:
show-details: never
probes:
enabled: true # k8s liveness/readiness
group:
liveness:
include: ping # process alive only, no DB cascade
readiness:
include: db,redis # accept traffic only after DB/Redis are readyLogging Configuration
The logging policy is defined by logback-spring.xml and differs per profile:
- dev: console output only, no log files (avoids leftover stale logs)
- prod: console (for docker logs / kubectl logs collection) + rolling files. Container quotas are tightened: main log single file 50MB, retention 7 days, total cap 600MB (
.gzcompressed archives; expired history is cleaned at startup); the standalone ERROR log is kept 30 days with a total cap of 150MB - Async writing: the main log is written through an AsyncAppender (
neverBlock=true— never blocks business threads when the queue is full;discardingThreshold=0— discards no event regardless of level) - Log directory: set by the
LOG_PATHenvironment variable or-DLOG_PATH, default/data/logs/daxpay(fits Linux/Docker volume mounts); when switching to prod locally on Windows, override it with a local directory (e.g.LOG_PATH=./logs) - Log levels: in prod, business packages are fixed at INFO and third-party framework noise (Spring / Hikari / MyBatis / Redis / Artemis / JMS) is uniformly suppressed to WARN; the global level is injected via the
LOG_ROOT_LEVELenvironment variable (default INFO; DEBUG is not recommended in production)
Sa-Token Authentication
Authentication is based on Sa-Token. The yml only holds startup-time fixed settings; session and security policies are managed dynamically from the "Security Config" menu in the admin console:
sa-token:
token-name: Accesstoken # token name (also the cookie/header name); frontend SDKs hard-depend on it — do not change
timeout: 259200 # fallback token validity (seconds), effective only while session management is disabled; once enabled, the database configuration takes precedence
is-log: false # Sa-Token debug logging
is-print: false # startup bannerSessions and security policies are configured in the admin console (System Management → Security Config, permission code system:security-config, API prefix /platform/config/security), stored in the system_platform_config table, and injected into Sa-Token at login:
| Group | Configurable items | Notes |
|---|---|---|
| Session management | Token validity / active timeout / max concurrent sessions / concurrency strategy / concurrency scope | Replaces yml timeout / active-timeout / is-concurrent; defaults: 72h validity, 24h active timeout, 5 concurrent sessions |
| Login security | Login failure lockout / captcha trigger | Failure count threshold, lockout duration, captcha trigger count |
| Password policy | Password strength / rotation period / history count | Minimum length, uppercase/lowercase/digit/special-character requirements |
| Two-factor authentication | 2FA (TOTP) toggle / issuer / backup codes | Works with an Authenticator app |
| API security | Open API anti-replay | Nonce validation, request time window |
| Payment security | Payment risk control | Blacklist blocking, IP region lists, overseas IP interception, pre-payment risk checks |
| IAM anti-replay | Admin API anti-replay | Nonce validity, timestamp tolerance |
Scope of dynamic changes
Admin-configured session settings apply only to newly created login sessions — tokens of already logged-in users keep their original timeout/concurrency attributes. is-share is fixed at false (sessions isolated per terminal) and is not configurable.
API requests and responses support RSA signing; the public key is configured through key-config.
Timezone and Time
- Database time columns uniformly use
timestamptz(6)(timestamp with time zone) - Entity classes use
java.time.OffsetDateTime - Serialization uses UTC globally; Jackson
OffsetDateTime→ ISO UTC - Never use
timestamp/timestamp without time zone,java.util.Date, orLocalDateTime
Production Environment Variables
In production, all credentials are injected via environment variables. The following are all placeholders defined in application-prod.yml:
| Variable | Description | Default |
|---|---|---|
SERVER_PORT | Service port | 9999 |
DEPLOY_MODE | Deployment mode: PROD (fail-fast validation at startup; forcibly disables dev-only features such as sandbox and super admin) / DEV (local integration only) | PROD |
DB_HOST | Database host | postgresql |
DB_PORT | Database port | 5432 |
DB_NAME | Database name | daxpay-prod |
DB_USERNAME | Database user | (required) |
DB_PASSWORD | Database password | (required) |
REDIS_HOST | Redis host | redis |
REDIS_PORT | Redis port | 6379 |
REDIS_DATABASE | Redis database number | 0 |
REDIS_PASSWORD | Redis password | (required) |
ARTEMIS_BROKER_URL | Artemis broker address | tcp://artemis:61616 |
ARTEMIS_USER | Artemis user | (strongly recommended; yml defaults to an empty string) |
ARTEMIS_PASSWORD | Artemis password | (strongly recommended; yml defaults to an empty string) |
LOG_ROOT_LEVEL | Log level (INFO / WARN / DEBUG; DEBUG not recommended in production) | INFO |
ENABLE_ADMIN | Whether to enable the super administrator (allowed only for DEV integration; enabling under PROD is rejected) | false |
IP2REGION_FILE_PATH | IPv4 address database path | /data/ip/ip2region_v4.xdb |
IP2REGION_V6_FILE_PATH | IPv6 address database path (optional; IPv6 lookups return null when unset) | /data/ip/ip2region_v6.xdb |
IP2REGION_DATA_VERSION | IPv4 data version (open_source / base / high / pro; must match the tier of the actual xdb file) | open_source |
IP2REGION_V6_DATA_VERSION | IPv6 data version (may differ from v4; must match the actual xdb file) | open_source |
SANDBOX_ENABLED | Sandbox switch (effective only in DEV; enabling under PROD is blocked by DeploymentModeEnforcer) | false |
TRADE_SYNC_ENABLED | Scheduled trade sync switch (recommended on in production) | true |
RSA_PRIVATE_KEY | RSA private key (PEM) | (required) |
RSA_PUBLIC_KEY | RSA public key (PEM) | (required) |
ENCRYPT_KEY | AES encryption key (32 characters) | (required) |
CHANNEL_ONE_BASE_URL | Channel sub-app 1 address | http://channel-one:20100 |
CHANNEL_ONE_TRANSPORT_KEY | channel-one transport AES key (32 characters) | (required; startup fails if missing) |
CHANNEL_TWO_BASE_URL | Channel sub-app 2 address | http://channel-two:20200 |
CHANNEL_TWO_TRANSPORT_KEY | channel-two transport AES key (32 characters) | (required; startup fails if missing) |
CHANNEL_THREE_BASE_URL | Channel sub-app 3 address | http://channel-three:20300 |
CHANNEL_THREE_TRANSPORT_KEY | channel-three transport AES key (32 characters) | (required; startup fails if missing) |
Meaning of "required"
Variables marked (required) are written in application-prod.yml as ${VAR:?missing VAR}: the app fails to start when one is missing, preventing placeholder literals from being bound as invalid values (e.g. an illegal key length).
Channel Sub-App Configuration
Channel sub-apps share the same database with the main app — no separate datasource configuration is needed. Their config files live in daxpay-channel-start/src/main/resources/, with the same profile structure as the main app (dev / prod).
dax-pay-channel-one
| Item | Value | Notes |
|---|---|---|
| Port | 20100 | server.port in application.yml |
| Database | Same database as the main app | dev daxpay-dev / prod daxpay-prod |
| Profile | dev / prod | Switched the same way as the main app |
Credential injection
Production credentials (DB / Redis, etc.) for channel sub-apps are likewise injected via environment variables, with the same variable names as the main app. In Docker deployments, share a single .env through env_file.
dax-pay-channel-two
| Item | Value | Notes |
|---|---|---|
| Port | 20200 | server.port in application.yml |
| Database | Same database as the main app | dev daxpay-dev / prod daxpay-prod |
| Profile | dev / prod | Switched the same way as the main app |
channel-two is already enabled in the main app's default configuration (no uncommenting needed). In production, the sub-app address is injected via CHANNEL_TWO_BASE_URL, and the transport encryption key for communicating with it via CHANNEL_TWO_TRANSPORT_KEY (required; must match the sub-app side).
dax-pay-channel-three
| Item | Value | Notes |
|---|---|---|
| Port | 20300 | server.port in application.yml |
| Database | Same database as the main app | dev daxpay-dev / prod daxpay-prod |
| Profile | dev / prod | Switched the same way as the main app |
It hosts international payment channels (Stripe etc.). It is likewise already enabled in the main app's default configuration; in production it is wired up via CHANNEL_THREE_BASE_URL and CHANNEL_THREE_TRANSPORT_KEY (required).
Frontend App Configuration
Frontend apps are configured primarily through Vite environment variables (.env* files), injected at build time.
Web Admin dax-pay-ui
The operations (admin) and merchant clients share the same monorepo and are told apart by the VITE_APP_CLIENT_CODE environment variable.
Admin client (apps/daxpay-admin/.env):
| Variable | Description | Admin value |
|---|---|---|
VITE_APP_TITLE | Application title | DaxPay Admin |
VITE_APP_NAMESPACE | Namespace (cache/store prefix, for isolation) | daxpay-web-admin |
VITE_APP_CLIENT_CODE | Client identity code (aligns with the backend ClientEnum) | admin |
VITE_APP_STORE_SECURE_KEY | Store persistence encryption key | Custom |
Development environment (.env.development) additions:
| Variable | Description | Value |
|---|---|---|
VITE_PORT | dev port | Admin 6999 / Merchant 7999 |
VITE_GLOB_API_URL | API address prefix | /api |
VITE_NITRO_MOCK | Whether to enable the mock server | true |
VITE_INJECT_APP_LOADING | Inject the global loading | true |
The merchant client (apps/daxpay-merchant/.env) has the same structure, with these differences:
| Variable | Merchant value |
|---|---|
VITE_APP_TITLE | DaxPay Merchant |
VITE_APP_NAMESPACE | daxpay-web-merchant |
VITE_APP_CLIENT_CODE | merchant |
VITE_PORT (dev) | 7999 |
Client-level isolation
Both clients share the packages/ framework packages. VITE_APP_CLIENT_CODE injects request headers and login parameters at build time, and together with the backend iam_perm_menu.client_code it achieves menu/data isolation between the two clients.
The .env differences between the two clients are shown in the three tables above; the full client-level isolation chain:
Mobile H5 dax-pay-h5
Config files live at dax-pay-h5/.env / .env.development / .env.production.
| Variable | Description | Value |
|---|---|---|
VITE_PORT | dev port | 9500 |
VITE_PROXY | dev cross-origin proxy | /api → http://127.0.0.1:9999/ (local main app) |
The single H5 build contains both PC and mobile pages, dispatched by runtime device detection — no per-device configuration is needed.
uni-app Admin dax-pay-app-admin
Config files live in dax-pay-app-admin/env/ (the directory required by unibest convention); compilable targets are determined by the unibest.platforms field.
| Configuration | Description |
|---|---|
env/.env | Base environment variables (VITE_APP_PORT=9000, etc.) |
| Build targets | H5 / WeChat / Alipay / Douyin mini-programs / App |
| Internationalization | 10 languages across all targets; mini-program builds are compiled with Chinese and English only |
See Cross-Platform Apps for build commands.
uni-app Merchant dax-pay-app-merchant
Config files live in dax-pay-app-merchant/env/, structured the same as the uni-app admin.
| Configuration | Description |
|---|---|
env/.env | Base environment variables (VITE_APP_PORT=9100, etc.) |
| Build targets | H5 / WeChat / Alipay / Douyin mini-programs / App |
| Internationalization | 10 languages across all targets; mini-program builds are compiled with Chinese and English only |
See Cross-Platform Apps for build commands.
Cashier Mini-Program dax-pay-mini-cashier
Config files live in dax-pay-mini-cashier/env/, structured the same as the other uni-app clients.
| Configuration | Description |
|---|---|
| Build targets | WeChat / Alipay / Douyin mini-programs (no H5 / App) |
| Internationalization | Chinese and English only |
See Cross-Platform Apps for build commands.
Related Sections
- Run: the overall entry point for choosing how to run per scenario
- Build: source compilation and output paths per app (one section per app)
- Cross-Platform Apps: running, building, and publishing the uni-app clients