ADR-001 — Unified Adapter Strategy over Raw Imperative Callbacks
Context
TapPay's legacy JS SDK uses disparate imperative patterns: (err, result) callbacks for some wallets, single (result) objects for others, and DOM-scraping events for credit cards. Consumers must memorize a different shape per rail.
Decision
Wrap every payment rail in dedicated classes (CardPayment, GooglePayment, ApplePayment, SamsungPayment, WalletPayment) that expose standardized ES6 Promises and EventEmitter-style subscriptions: init(), renderButton(), getPrime().
Consequences
Gain: type-safe error handling, async/await pipelines, deterministic state testing.
Trade-off: minor object-wrapping overhead over raw window callback invocation.
ADR-002 — Universal Strategy Adapter for Digital Wallets (WalletPayment)
Context
13 regional payment methods (JKO Pay, LINE Pay, Pi Wallet, Easy Wallet, iPASS MONEY, PXPay Plus, Plus Pay, GoGo Pay, OPPay, Pay Later, AFTEE, ATM, COD) share an identical lifecycle: getPrime(cb) followed by server-side payment-URL redirect.
Decision
Implement one parameterized bounded adapter (WalletPayment) mapped via metadata definitions (WALLET_METADATA) instead of 13 redundant concrete classes.
Consequences
Gain: massive reduction in bundle size and maintenance surface while keeping complete Taiwan & Asian rail coverage.
Trade-off: if TapPay introduces divergent init parameters for one specific wallet, that rail must be promoted to a standalone adapter class.
ADR-003 — Vanilla Web Components & CSS Variables vs. Framework Component Libraries
Context
A modern checkout UX must work across heterogeneous consumer stacks — React, Vue, Next.js, Django, WordPress, and plain static HTML.
Decision
Zero-dependency native JS DOM assembly styled purely through CSS Custom Properties (--tp-*), avoiding Tailwind runtime requirements and framework bundlers.
Consequences
Gain: universal compatibility, instant startup, zero production dependencies, dynamic runtime dark/light theme switching.
Trade-off: UI components cannot consume application-level React/Vue context engines directly without a slim wrapper.
Trade-Off Analysis Matrix
| Design Concern | Selected Approach | Alternative | Why | Trade-Off |
|---|---|---|---|---|
| SDK Dependency | Dynamic async injection via loadTapPaySDK |
Hardcoded <script src> tags |
No global script-block delay; SRI integrity support | Requires async await client.ready() |
| Error Handling | Normalized typed TapPayError objects |
Raw SDK error codes & strings | Standardized error classification, easy debugging | Hides vendor console verbosity by default |
| State Management | Event-driven observable hooks (statuschange) |
Polling DOM states / React state coupling | Decouples DOM form rendering from domain validation | Requires explicit listener cleanup in SPAs |