Code Architecture
The DaxPay main application follows a layered pattern of base-layer aggregates + business modules + startup entry, with channel integration pushed down into independent sub-applications. Every sub-application keeps the same pattern, so the structures can be compared and kept in sync during maintenance.
Main App Layering (dax-pay-open)
dax-pay-open/
├── daxpay-platform/ # Base layer
│ ├── daxpay-platform-core # Common contracts: DTO / enums / exceptions / result wrapper
│ ├── daxpay-platform-common # Common technical infrastructure (aggregate)
│ │ ├── common-i18n # Backend i18n resources + JsonMessageSource
│ │ ├── common-mybatis-plus # ORM configuration
│ │ ├── common-redis # Redis configuration
│ │ ├── common-artemis # Message queue
│ │ ├── common-json # JSON serialization
│ │ ├── common-swagger # API documentation
│ │ ├── common-config # Encryption / platform config / deployment-mode enforcement
│ │ ├── common-request-context # Request context
│ │ ├── common-translate # Translation
│ │ └── common-spring # Spring extensions
│ ├── daxpay-platform-capability # Platform capabilities (aggregate)
│ │ ├── capability-auth # Authentication
│ │ ├── capability-audit-log # Audit log
│ │ ├── capability-file # Files
│ │ ├── capability-cache # Cache
│ │ ├── capability-nonce # Anti-replay
│ │ ├── capability-social # Social login
│ │ ├── capability-wechat # WeChat open platform
│ │ ├── capability-alipay # Alipay open platform
│ │ ├── capability-douyin # Douyin open platform
│ │ └── capability-sensitive-word # Sensitive words
│ └── daxpay-platform-service # Platform business services (aggregate)
│ ├── service-iam # Users / roles / menus / permission codes
│ ├── service-system # Dictionaries / configuration / logs
│ ├── service-baseapi # Base APIs
│ └── service-notify # In-app notifications / announcements / SSE
├── daxpay-payment/ # Payment business
│ ├── daxpay-payment-core # Payment domain core: domain models / transaction engine / open API contracts
│ ├── daxpay-payment-admin # Operator (admin) portal controllers and services
│ ├── daxpay-payment-merchant # Merchant self-service portal controllers and services
│ ├── daxpay-payment-unipay # Unified acquiring /unipay/**, gateway products, public H5
│ ├── daxpay-payment-app-admin # Operator mobile app / mini-program payment APIs
│ └── daxpay-payment-app-merchant # Merchant mobile app / mini-program payment APIs
├── daxpay-channel/ # Channel business (orchestration layer)
│ ├── daxpay-channel-alipay # Alipay channel (HTTP declaration + config CRUD)
│ ├── daxpay-channel-wechat # WeChat channel
│ ├── daxpay-channel-douyin # Douyin channel
│ ├── daxpay-channel-ums # UMS channel
│ ├── daxpay-channel-union # UnionPay (QuickPass) channel
│ └── ... # 20+ channel capabilities, all routed uniformly by the orchestration layer, no SDKs
├── daxpay-plugin/ # Payment plugins
│ ├── daxpay-plugin-easypay # EasyPay protocol plugin
│ └── daxpay-plugin-risk # Payment risk-control plugin (blacklist + hit records)
├── daxpay-demo/ # Feature demo module
└── daxpay-start/ # Startup entry (port 9999)Responsibility Boundary Between Orchestration and Integration
DaxPay's channel system strictly separates the orchestration layer (main application) from the integration layer (sub-applications). This is the core design behind SDK isolation:
| Responsibility | Main app daxpay-channel/* | Sub-app channel-one / channel-two |
|---|---|---|
| Third-party SDK calls | No SDK imports at all | Directly calls alipay-sdk / wxjava / etc. |
| Channel configuration CRUD | Entity / DAO / Controller | No persistence |
| Payment strategy orchestration | strategy → service | No business orchestration |
| HTTP declarative interfaces | @HttpExchange client | @RestController endpoints |
| Signing / signature verification | Business signatures (RSA) | Channel signatures (inside the SDK) |
| Callback assembly | Forwards to sub-apps for verification and parsing | Verifies and parses raw payloads |
Invocation mechanism — The main application creates an @HttpExchange proxy for each channel through the ChannelRestClientSupport factory, bound to the corresponding sub-application's baseUrl; requests pass through ChannelTransportEncryptInterceptor for AES-GCM transport encryption; sub-application responses use the unified {code, msg, data} (DaxResult), and the main application treats code == 0 as success.
Channel routing assignment — Each channel's XxxClientConfig is bound at compile time to channelProperties.getOne() (port 20100), getTwo() (port 20200), or getThree() (port 20300):
| Bound to channel-one (20100) | Bound to channel-two (20200) | Bound to channel-three (20300) |
|---|---|---|
| Alipay, WeChat, Douyin, UMS, UnionPay (QuickPass) | Lakala, Haikerongtong, Dougong, Leshua, Suixingfu, HemaPay, Adapay, Fuyou, YeePay | Stripe |
Sub-service Split Principles
The main application calls each independent sub-service over HTTP. The goals of the split:
- Channel SDK dependency isolation — Third-party SDKs stay out of the main application, avoiding transitive dependency conflicts from alipay-sdk / wxjava and the like
- Independent upgrades — Sub-services can be released per channel, so frequent channel upgrades do not affect less active channels
- Elastic scaling — High-volume channels (Alipay, WeChat) can scale out independently with multiple instances
Channel Sub-service List
| Sub-service | Language / framework | Exposed capability | Port | Channels |
|---|---|---|---|---|
dax-pay-channel-one | Java / Spring Boot 4.1 | Direct channel integration | 20100 | 5 (Alipay / WeChat / Douyin / UMS / UnionPay) |
dax-pay-channel-two | Java / Spring Boot 4.1 | Aggregate channel integration | 20200 | 9 (Lakala / Haikerongtong / Dougong / Leshua / Suixingfu / HemaPay / Adapay / Fuyou / YeePay) |
dax-pay-channel-three | Java / Spring Boot 4.1 | International payment channel integration | 20300 | 1 (Stripe) |
The channel sub-applications also have experimental Go copies — experimental in nature and outside the mainline support scope. See Applications.
How the main application calls each sub-service:
Sub-applications are a lightweight subset of the main project's structure: the platform layer keeps only core + common (config / i18n / json / util) + service-system, without capability or the other services. Common contracts (DTOs / interfaces / exceptions) live in platform-core so that later channel sub-applications can reuse them. Each sub-application is an independent git repository with its own version — there is no Maven dependency on the main project, only a structural correspondence.