Skip to content

Configuration ​

Updated: 10/15/26, 5:44:21 PM

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 ​

AppConfig file locationNotes
Main app dax-pay-opendax-pay-open/daxpay-start/src/main/resources/application.yml + application-dev.yml + application-prod.yml
dax-pay-channel-onedax-pay-channel-one/daxpay-channel-start/src/main/resources/Same profile structure
dax-pay-channel-twodax-pay-channel-two/daxpay-channel-start/src/main/resources/Same profile structure
dax-pay-channel-threedax-pay-channel-three/daxpay-channel-start/src/main/resources/Same profile structure (international channels, Stripe)
dax-pay-channel-one-godax-pay-channel-one-go/configs/config.yamlGo experimental copy; YAML config; path can be specified via the DAXPAY_CONFIG environment variable
dax-pay-channel-two-godax-pay-channel-two-go/configs/config.yamlSame 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 Webdax-pay-ui/apps/daxpay-merchant/.env*Same as above, with VITE_APP_CLIENT_CODE=merchant
H5dax-pay-h5/.env*.env / .env.development
uni-app admindax-pay-app-admin/env/.env*The env/ directory required by unibest convention
uni-app merchantdax-pay-app-merchant/env/.env*Same as above
Cashier mini-programdax-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 ​

ProfileDescriptionMonitoring endpointsLog levelAPI docsSuper admin
dev (default)Local development, troubleshooting friendlyAll openDEBUG for business packagesEnabledEnabled
prodProduction; credentials must be injected via environment variables; graceful shutdownRestricted (health/info/metrics)INFO for business packagesDisabledDisabled

How to switch:

bash
# 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=prod

App Ports ​

AppPortHealth
Main app dax-pay-open9999http://127.0.0.1:9999/actuator/health
Channel sub-app dax-pay-channel-one20100http://127.0.0.1:20100/actuator/health
Channel sub-app dax-pay-channel-two20200http://127.0.0.1:20200/actuator/health
Channel sub-app dax-pay-channel-three20300http://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:

yaml
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 ​

yaml
spring:
  data:
    redis:
      host: localhost
      port: 6379
      database: 0
      password: your_password
      lettuce:
        pool:
          max-wait: 1000ms

Artemis Message Queue ​

Artemis is used for JMS messaging scenarios such as delayed payment notifications:

yaml
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: true

Channel Sub-App Routing ​

The main app calls channel sub-apps over HTTP. Routing configuration:

yaml
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: fedcba9876543210fedcba9876543210

All 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 ​

yaml
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 list

RSA Certificates ​

Used for signing API requests and responses:

yaml
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:

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

yaml
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: 1

Rolling 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 enable once 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 keys list a restart is required. There is no admin-side rotation entry, and no re-encryption service for historical data

Exception Message Display ​

yaml
daxpay:
  platform:
    common:
      exception:
        show-full-message: true     # on in dev / off in prod

Cache 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:

yaml
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 (default secure:) or are listed in secure-names have 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 ​

yaml
# 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 types

Docs URL in dev: http://127.0.0.1:9999/swagger-ui/index.html

Monitoring Endpoints ​

yaml
# 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
yaml
# 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 ready

Logging 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 (.gz compressed 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_PATH environment 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_LEVEL environment 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:

yaml
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 banner

Sessions 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:

GroupConfigurable itemsNotes
Session managementToken validity / active timeout / max concurrent sessions / concurrency strategy / concurrency scopeReplaces yml timeout / active-timeout / is-concurrent; defaults: 72h validity, 24h active timeout, 5 concurrent sessions
Login securityLogin failure lockout / captcha triggerFailure count threshold, lockout duration, captcha trigger count
Password policyPassword strength / rotation period / history countMinimum length, uppercase/lowercase/digit/special-character requirements
Two-factor authentication2FA (TOTP) toggle / issuer / backup codesWorks with an Authenticator app
API securityOpen API anti-replayNonce validation, request time window
Payment securityPayment risk controlBlacklist blocking, IP region lists, overseas IP interception, pre-payment risk checks
IAM anti-replayAdmin API anti-replayNonce 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, or LocalDateTime

Production Environment Variables ​

In production, all credentials are injected via environment variables. The following are all placeholders defined in application-prod.yml:

VariableDescriptionDefault
SERVER_PORTService port9999
DEPLOY_MODEDeployment mode: PROD (fail-fast validation at startup; forcibly disables dev-only features such as sandbox and super admin) / DEV (local integration only)PROD
DB_HOSTDatabase hostpostgresql
DB_PORTDatabase port5432
DB_NAMEDatabase namedaxpay-prod
DB_USERNAMEDatabase user(required)
DB_PASSWORDDatabase password(required)
REDIS_HOSTRedis hostredis
REDIS_PORTRedis port6379
REDIS_DATABASERedis database number0
REDIS_PASSWORDRedis password(required)
ARTEMIS_BROKER_URLArtemis broker addresstcp://artemis:61616
ARTEMIS_USERArtemis user(strongly recommended; yml defaults to an empty string)
ARTEMIS_PASSWORDArtemis password(strongly recommended; yml defaults to an empty string)
LOG_ROOT_LEVELLog level (INFO / WARN / DEBUG; DEBUG not recommended in production)INFO
ENABLE_ADMINWhether to enable the super administrator (allowed only for DEV integration; enabling under PROD is rejected)false
IP2REGION_FILE_PATHIPv4 address database path/data/ip/ip2region_v4.xdb
IP2REGION_V6_FILE_PATHIPv6 address database path (optional; IPv6 lookups return null when unset)/data/ip/ip2region_v6.xdb
IP2REGION_DATA_VERSIONIPv4 data version (open_source / base / high / pro; must match the tier of the actual xdb file)open_source
IP2REGION_V6_DATA_VERSIONIPv6 data version (may differ from v4; must match the actual xdb file)open_source
SANDBOX_ENABLEDSandbox switch (effective only in DEV; enabling under PROD is blocked by DeploymentModeEnforcer)false
TRADE_SYNC_ENABLEDScheduled trade sync switch (recommended on in production)true
RSA_PRIVATE_KEYRSA private key (PEM)(required)
RSA_PUBLIC_KEYRSA public key (PEM)(required)
ENCRYPT_KEYAES encryption key (32 characters)(required)
CHANNEL_ONE_BASE_URLChannel sub-app 1 addresshttp://channel-one:20100
CHANNEL_ONE_TRANSPORT_KEYchannel-one transport AES key (32 characters)(required; startup fails if missing)
CHANNEL_TWO_BASE_URLChannel sub-app 2 addresshttp://channel-two:20200
CHANNEL_TWO_TRANSPORT_KEYchannel-two transport AES key (32 characters)(required; startup fails if missing)
CHANNEL_THREE_BASE_URLChannel sub-app 3 addresshttp://channel-three:20300
CHANNEL_THREE_TRANSPORT_KEYchannel-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 ​

ItemValueNotes
Port20100server.port in application.yml
DatabaseSame database as the main appdev daxpay-dev / prod daxpay-prod
Profiledev / prodSwitched 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 ​

ItemValueNotes
Port20200server.port in application.yml
DatabaseSame database as the main appdev daxpay-dev / prod daxpay-prod
Profiledev / prodSwitched 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 ​

ItemValueNotes
Port20300server.port in application.yml
DatabaseSame database as the main appdev daxpay-dev / prod daxpay-prod
Profiledev / prodSwitched 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):

VariableDescriptionAdmin value
VITE_APP_TITLEApplication titleDaxPay Admin
VITE_APP_NAMESPACENamespace (cache/store prefix, for isolation)daxpay-web-admin
VITE_APP_CLIENT_CODEClient identity code (aligns with the backend ClientEnum)admin
VITE_APP_STORE_SECURE_KEYStore persistence encryption keyCustom

Development environment (.env.development) additions:

VariableDescriptionValue
VITE_PORTdev portAdmin 6999 / Merchant 7999
VITE_GLOB_API_URLAPI address prefix/api
VITE_NITRO_MOCKWhether to enable the mock servertrue
VITE_INJECT_APP_LOADINGInject the global loadingtrue

The merchant client (apps/daxpay-merchant/.env) has the same structure, with these differences:

VariableMerchant value
VITE_APP_TITLEDaxPay Merchant
VITE_APP_NAMESPACEdaxpay-web-merchant
VITE_APP_CLIENT_CODEmerchant
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.

VariableDescriptionValue
VITE_PORTdev port9500
VITE_PROXYdev 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.

ConfigurationDescription
env/.envBase environment variables (VITE_APP_PORT=9000, etc.)
Build targetsH5 / WeChat / Alipay / Douyin mini-programs / App
Internationalization10 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.

ConfigurationDescription
env/.envBase environment variables (VITE_APP_PORT=9100, etc.)
Build targetsH5 / WeChat / Alipay / Douyin mini-programs / App
Internationalization10 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.

ConfigurationDescription
Build targetsWeChat / Alipay / Douyin mini-programs (no H5 / App)
InternationalizationChinese and English only

See Cross-Platform Apps for build commands.


  • 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