# Midtrans CIFO Frontend Architecture Document ## Change Log | Date | Version | Description | Author | |------------|---------|----------------------------------------|--------| | 2025-11-07 | v1 | Initial frontend architecture scaffolding | Architect (Winston) | ## 1. Template and Framework Selection Context & Decision - Basis dokumen: `docs/prd.md` (UI multi-metode pembayaran: VA, QRIS, Kartu) dan belum ada dokumen arsitektur utama (`docs/architecture.md`). - Lingkungan proyek: Laragon (PHP) untuk backend; frontend akan diatur sebagai aplikasi SPA terpisah yang berkomunikasi via REST. - Starter pilihan: Vite + React + TypeScript. Rationale - Vite + React memberikan setup cepat, build cepat, dan ekosistem luas. TypeScript membantu kualitas kode meski QA dilakukan manual (type-safety, DX lebih baik). - React Router memadai untuk halaman Checkout, Status, dan Riwayat; TanStack Query menangani data fetching, status, cache, dan retry/polling untuk status transaksi. - Tailwind CSS mempercepat UI dengan gaya konsisten, aksesibilitas lebih terukur, dan footprint tooling rendah. - Tidak memilih Next.js karena kebutuhan SSR tidak kritikal untuk halaman checkout sederhana, dan menghindari kompleksitas deploy. Konsekuensi - Manual QA only: tidak ada pipeline test otomatis; struktur proyek dan standar coding harus tegas untuk menghindari regressi. - Build/deploy dilakukan manual; dokumentasi command harus jelas (dev/build). Observability di UI via logging ringan & event tracking internal. ## 2. Frontend Tech Stack (Draft untuk Validasi) | Category | Technology | Version | Purpose | Rationale | |---------------------|-------------------------|---------|------------------------------------------------|---------------------------------------------------------------------------| | Framework | React | ^18 | SPA untuk checkout dan status transaksi | Ekosistem luas, composable UI, dukungan community yang kuat | | UI Library | Tailwind CSS | ^3 | Styling utilitas cepat | Cepat, konsisten, mudah diadopsi, cocok untuk MVP | | State Management | TanStack Query | ^5 | Server state (fetch/poll/retry/cache) | Ideal untuk status transaksi & polling; minimal boilerplate | | Routing | React Router | ^6 | Navigasi halaman | Pola rute sederhana, proteksi rute mudah | | Build Tool | Vite | ^5 | Dev/build tooling | Startup cepat, konfigurasi sederhana | | Styling | Tailwind + CSS Modules | ^3 | Styling kombinasi utilitas + modular | Fleksibel; module untuk komponen kompleks | | Testing | Manual QA | N/A | Pengujian manual | Sesuai kebijakan proyek (tanpa CI/CD); template test dapat ditambahkan | | Component Library | Headless UI / Shadcn | latest | Komponen aksesibel minimal | Mempercepat pembuatan form/overlay; tetap fleksibel | | Form Handling | React Hook Form | ^7 | Validasi & kontrol form | Ringan, ergonomis, integrasi baik dengan TS | | Animation | Framer Motion | ^11 | Mikro-animasi & transisi | Peningkatan UX tanpa kompleksitas berlebih | | Dev Tools | ESLint + Prettier | latest | Konsistensi & kualitas kode | Membantu kualitas saat QA manual; cegah kesalahan umum | Rationale Tambahan - TanStack Query mempermudah implementasi polling status transaksi (fallback ketika webhook belum tiba) dan retry terukur sesuai PRD. - React Hook Form memudahkan validasi input kartu (tokenisasi) dan interaksi form minimal. - Headless UI/Shadcn memberi komponen aksesibel untuk dialog/menus tanpa mengikat ke desain tertentu. Elicit Options (1–9) 1. Proceed ke bagian berikutnya (Project Structure) 2. Risk & Challenge Analysis untuk pilihan stack 3. Structural Analysis (komponen vs halaman vs services) 4. Devil’s Advocate (tantangan stack ini untuk manual QA) 5. Multi-Persona (PO/SM/Dev/QA) feedback cepat 6. Creative Exploration (alternatif: Vue/Nuxt atau Angular) 7. 2025 Techniques (SSR-lite/Partial Hydration pertimbangan) 8. Process Control (tandai asumsi dan batasan eksplisit) 9. Reflective Review (cek keselarasan dengan PRD Epics 1–3) Select 1–9 or just type your question/feedback: ## 7. Routing Tujuan: mendefinisikan peta rute, alur navigasi Checkout → Status → Riwayat, serta praktik React Router v6 (lazy loading, error boundaries, guard ringan). ### 7.1 Route Map - `/checkout` — halaman utama untuk memilih metode (VA, QRIS/GoPay, Card, Cstore) dan memulai charge. - `/payments/:orderId/status` — halaman status transaksi real‑time (polling/sse) untuk semua metode. - `/history` — riwayat transaksi (read-only, filter/sort sederhana). - Opsional (Card 3DS): `/checkout/card/challenge` — halaman penanganan challenge bila diperlukan; atau gunakan `redirect_url` eksternal. - Fallback: `*` — NotFound. ### 7.2 Konfigurasi React Router (v6) ```tsx // src/app/router.tsx import { createBrowserRouter, RouterProvider, Navigate } from 'react-router-dom'; import { AppLayout } from './AppLayout'; import { CheckoutPage } from '../pages/CheckoutPage'; import { PaymentStatusPage } from '../pages/PaymentStatusPage'; import { PaymentHistoryPage } from '../pages/PaymentHistoryPage'; import { NotFoundPage } from '../pages/NotFoundPage'; const router = createBrowserRouter([ { path: '/', element: , errorElement:
Terjadi kesalahan. Coba muat ulang.
, children: [ { index: true, element: }, { path: 'checkout', element: }, { path: 'payments/:orderId/status', element: }, { path: 'history', element: }, { path: '*', element: }, ], }, ]); export function AppRouter() { return ; } ``` Catatan: - Gunakan lazy loading untuk halaman berat (mis. `PaymentHistoryPage`) guna mempercepat initial load checkout. - Tambahkan `` di layout bila perlu. ### 7.3 Alur Navigasi - Dari `/checkout`, setelah `charge` sukses (menerima `order_id`), arahkan ke `/payments/:orderId/status`. - Simpan `payment_type` di `location.state` atau query (contoh: `?type=gopay`) bila diperlukan untuk UI hint. - Terminal state: - `settlement`: tampilkan success dan CTA kembali ke `/history`. - `expire`/`cancel`/`deny`: tampilkan informasi dan CTA ulang di `/checkout`. - Card (3DS): bila response berisi `redirect_url`, buka di jendela/tab baru atau navigasi ke `/checkout/card/challenge` yang menangani proses lalu kembali ke `/payments/:orderId/status`. Contoh helper navigasi: ```ts // src/features/payments/lib/navigation.ts import { useNavigate } from 'react-router-dom'; export function usePaymentNavigation() { const navigate = useNavigate(); return { toStatus(orderId: string, paymentType?: string) { navigate(`/payments/${orderId}/status`, { state: paymentType ? { paymentType } : undefined, replace: true, }); }, toHistory() { navigate('/history'); }, toCheckout() { navigate('/checkout'); }, }; } ``` ### 7.4 Guards & Recovery - Validasi akses langsung ke `/payments/:orderId/status`: - Query `getPaymentStatus(orderId)` — jika 404/invalid, tampilkan NotFound atau ajak kembali ke `/checkout`. - Lindungi `/history` dengan batasan sederhana (misal hanya read-only; tidak memerlukan auth jika PRD tidak mensyaratkan). - Toleransi refresh: halaman status dapat dibuka ulang langsung selama `order_id` valid. ### 7.5 Status Updates (Polling / Push) - Default: polling 3 detik menggunakan TanStack Query seperti di bagian State Management. - Opsi 2025: SSE/WebSocket untuk push status agar real‑time dan hemat jaringan; fallback ke polling saat koneksi tidak stabil. ### 7.6 Aksesibilitas & Analytics - Announce perubahan status dengan ARIA live region (mis. `role="status"`, `aria-live="polite"`). - Catat event navigasi (route change) beserta `X-Correlation-Id` untuk tracing ringan di UI. --- Elicit Options (Routing → Next) 1. Lanjut ke Styling (Tailwind arsitektur dan utility patterns) 2. Tambah Testing (rencana manual QA dan template kasus uji) 3. Environment Config (env vars & build/dev commands) 4. Developer Standards (lint, format, commit hygiene) 5. Error UX polish (banner, retry, deep link handling) 6. Performance (lazy routes, code-splitting, prefetch) ## 8. Styling Tujuan: menetapkan arsitektur styling yang konsisten dan dapat dipelihara menggunakan Tailwind CSS (utilitas) dan CSS Modules (gaya terisolasi), termasuk varian komponen, responsif, dark mode, dan a11y visual. ### 8.1 Setup Tailwind (config + globals) Konfigurasi dasar Tailwind dengan dark mode berbasis class dan plugin formulir/typografi. ```ts // tailwind.config.ts import type { Config } from 'tailwindcss'; export default { content: ['./index.html', './src/**/*.{ts,tsx}'], darkMode: 'class', theme: { extend: { colors: { brand: { 50: '#fef2f2', 100: '#fee2e2', 200: '#fecaca', 300: '#fca5a5', 400: '#f87171', 500: '#ef4444', 600: '#dc2626', // merah utama 700: '#b91c1c', 800: '#991b1b', 900: '#7f1d1d', }, }, boxShadow: { focus: '0 0 0 3px rgba(220,38,38,0.45)', }, }, }, plugins: [require('@tailwindcss/forms'), require('@tailwindcss/typography')], } satisfies Config; ``` Global stylesheet minimal: reset, font, dan token CSS (opsional) untuk warna. ```css /* src/styles/globals.css */ @tailwind base; @tailwind components; @tailwind utilities; :root { --radius: 8px; } @layer base { html, body, #root { height: 100%; } body { @apply antialiased bg-white text-black; } .dark body { @apply bg-black text-white; } a { @apply text-brand-600 hover:text-brand-700; } } @layer utilities { .focus-ring { @apply outline-none ring-2 ring-brand-600 ring-offset-2 ring-offset-white dark:ring-offset-black; } .card { @apply rounded-[var(--radius)] border border-black/10 bg-white shadow-sm dark:bg-black dark:border-white/20; } } ``` ### 8.2 Pola Utilitas & Komposisi - Gunakan utilitas untuk layout: `flex`, `grid`, `gap`, `container`, `max-w-*`, `space-*`. - Terapkan skala spacing/typography konsisten: `text-sm/base/lg`, `p-2/3/4`, `gap-2/3/4`. - Komposisi class dinamis via `clsx`/`cn` helper. ```ts // src/lib/cn.ts import { clsx } from 'clsx'; import { twMerge } from 'tailwind-merge'; export function cn(...inputs: Array | undefined>) { return twMerge(clsx(inputs)); } ``` Contoh penggunaan: ```tsx // src/components/Section.tsx import { cn } from '../lib/cn'; export function Section(props: { title: string; className?: string; children: React.ReactNode }) { return (

{props.title}

{props.children}
); } ``` ### 8.3 Varian Komponen dengan CVA (opsional) Gunakan `class-variance-authority` untuk varian yang dapat dikontrol tanpa membengkakkan class string. ```ts // src/components/ui/button.tsx import { cva, type VariantProps } from 'class-variance-authority'; import { cn } from '../../lib/cn'; const buttonVariants = cva( 'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:focus-ring disabled:opacity-50 disabled:pointer-events-none', { variants: { variant: { primary: 'bg-brand-600 text-white hover:bg-brand-700', secondary: 'bg-white text-black hover:bg-white/90 dark:bg-black dark:text-white dark:hover:bg-black/90', outline: 'border border-black text-black hover:bg-black/5 dark:border-white dark:text-white dark:hover:bg-white/10', }, size: { sm: 'h-8 px-3', md: 'h-10 px-4', lg: 'h-11 px-6' }, }, defaultVariants: { variant: 'primary', size: 'md' }, } ); export interface ButtonProps extends React.ButtonHTMLAttributes, VariantProps {} export function Button({ className, variant, size, ...props }: ButtonProps) { return ); }; export default Button; ``` Contoh komponen formulir ringan (gunakan React Hook Form di page yang memakai form): ```tsx // components/FormField.tsx import React from 'react'; export type FormFieldProps = { label: string; name: string; type?: 'text' | 'number'; value: string | number; onChange: (value: string) => void; error?: string; required?: boolean; }; export const FormField: React.FC = ({ label, name, type = 'text', value, onChange, error, required, }) => { const id = `field-${name}`; return (
onChange(e.target.value)} aria-invalid={Boolean(error)} aria-describedby={error ? `${id}-error` : undefined} /> {error && (

{error}

)}
); }; ``` Prinsip umum: - Props selalu bertipe eksplisit; hindari `any`. - Aksesibilitas: gunakan `aria-*`, `htmlFor`, dan label jelas. - Tailwind untuk layout/warna; gunakan CSS Modules untuk gaya spesifik komponen bila perlu. - Komponen presentasional tidak mengakses API langsung; logika data di layer `services/` atau hooks. ### 4.2 Penamaan & Konvensi - Components: PascalCase untuk nama file dan komponen (`PaymentSummary.tsx`). - Hooks: `useCamelCase` untuk nama hook (`usePaymentStatus.ts`). Hanya named exports. - Services: file camelCase berakhiran `Service` bila berkelas atau fungsi di satu file (`paymentsService.ts`). Named exports. - Stores (state): `paymentsStore.ts`, `uiStore.ts` dengan named exports. - Types: nama tipe PascalCase (`Payment`, `PaymentStatus`). File `types/payment.ts` (lowercase singular). - Pages: PascalCase folder, file `index.tsx` per page; komponen utama bernama sama dengan folder. - Routes: `routes.tsx` sebagai pusat deklarasi rute. - CSS Modules: `Component.module.css` untuk gaya spesifik; global di `styles/`. - Icons/assets: kebab-case (`credit-card.svg`), impor dari `assets/icons`. - Test (opsional): `Component.test.tsx` berdekatan dengan file sumber; karena QA manual, gunakan hanya untuk unit kecil yang berisiko. Anti-pattern yang dihindari: - Komponen melakukan fetch langsung ke Midtrans; gunakan `services/paymentsService` atau hooks. - State global tanpa kontrol (gunakan TanStack Query untuk server-state, store terpisah untuk UI-state). - Default export untuk hooks/services (prefer named export untuk tree-shaking dan konsistensi). Rasional: - Template tipe + a11y mempercepat review QA manual dan mengurangi regresi UI. - Konvensi penamaan konsisten mempermudah navigasi struktur feature-first yang selaras dengan PRD. - Pemisahan presentational vs data logic mendukung keterujian manual dan perawatan. Elicit Options (1–9) 1. Proceed ke State Management (server-state vs UI-state) 2. Risk & Challenge (konsekuensi konvensi terhadap kecepatan dev) 3. Structural Analysis (komponen presentasional vs container) 4. Devil’s Advocate (apakah Tailwind + CSS Modules berlebih?) 5. Multi-Persona (PO/SM/Dev/QA sudut pandang) 6. Creative Exploration (atomic design vs feature-first) 7. 2025 Techniques (server components/partial hydration relevansi) 8. Process Control (standarisasi lint/format tanpa CI) 9. Reflective Review (cek keselarasan dengan PRD Epics 1–3) Select 1–9 or just type your question/feedback: ## 3. Project Structure Struktur direktori yang diusulkan (Vite + React + TS) untuk memenuhi PRD (Checkout, VA/QRIS/Kartu, Status, Riwayat), TanStack Query (polling/retry), dan Tailwind CSS. ``` frontend/ ├── index.html ├── package.json ├── tsconfig.json ├── vite.config.ts ├── .env.example ├── tailwind.config.js ├── postcss.config.js ├── src/ │ ├── main.tsx │ ├── app/ │ │ ├── App.tsx │ │ ├── routes.tsx │ │ └── providers/ │ │ ├── QueryProvider.tsx │ │ └── ThemeProvider.tsx │ ├── pages/ │ │ ├── Checkout/ │ │ │ ├── CheckoutPage.tsx │ │ │ └── components/ │ │ │ ├── MethodSelector.tsx │ │ │ └── SummaryPanel.tsx │ │ ├── PaymentStatus/ │ │ │ └── PaymentStatusPage.tsx │ │ └── TransactionHistory/ │ │ └── TransactionHistoryPage.tsx │ ├── features/ │ │ └── payments/ │ │ ├── VA/ │ │ │ ├── VAInstructions.tsx │ │ │ └── VAInfoCard.tsx │ │ ├── QRIS/ │ │ │ ├── QRISCanvas.tsx │ │ │ └── QRISCountdown.tsx │ │ ├── Card/ │ │ │ ├── CardForm.tsx │ │ │ └── ThreeDSFlow.tsx │ │ ├── components/ │ │ │ ├── ErrorBanner.tsx │ │ │ ├── LoadingSkeleton.tsx │ │ │ └── StatusBadge.tsx │ │ ├── hooks/ │ │ │ ├── usePaymentStatus.ts │ │ │ └── useCopyToClipboard.ts │ │ ├── services/ │ │ │ ├── apiClient.ts │ │ │ └── paymentsService.ts │ │ ├── types/ │ │ │ └── payment.ts │ │ └── store/ │ │ └── uiStore.ts │ ├── components/ │ │ ├── Button.tsx │ │ ├── Modal.tsx │ │ └── FormField.tsx │ ├── lib/ │ │ ├── logger.ts │ │ └── accessibility.ts │ ├── styles/ │ │ ├── index.css │ │ └── themes.css │ ├── assets/ │ │ └── icons/ │ ├── utils/ │ │ ├── formatting.ts │ │ └── validation.ts │ └── config/ │ └── env.ts └── README.md ``` Rationale - Memetakan PRD Epics ke struktur: `pages/Checkout` (pemilihan metode & create transaksi), `pages/PaymentStatus` (status real-time), `pages/TransactionHistory` (riwayat pengguna). - `features/payments` mengelompokkan komponen spesifik per-metode (VA/QRIS/Kartu), hooks (status/polling/copy), services (API client + payment service) agar mudah di-maintain. - TanStack Query di `providers/QueryProvider.tsx` untuk cache/polling/retry terpusat; `services/apiClient.ts` untuk interceptors (auth, error mapping) dan konfigurasi `VITE_API_BASE_URL`. - Tailwind + CSS Modules: utilitas cepat + modularitas untuk komponen kompleks; `styles/themes.css` untuk variabel global. - Observability ringan di `lib/logger.ts` (correlation id pada event UI) dan komponen `ErrorBanner/StatusBadge` konsisten. - `.env.example` mendokumentasikan variabel `VITE_...` (frontend) terpisah dari secret backend; `config/env.ts` membaca dan memvalidasi. Opsi Elicitasi (1–9) 1. Proceed ke Component Standards (template dan konvensi) 2. Structural Analysis (cek keseimbangan pages vs features) 3. Risk & Challenge (kompleksitas maintainability dan QA manual) 4. Devil’s Advocate (apakah struktur terlalu granular?) 5. Multi-Persona (PO/SM/Dev/QA sudut pandang) 6. Creative Exploration (varian struktur: feature-first vs route-first) 7. 2025 Techniques (module federation/partial hydration relevansi) 8. Process Control (tandai asumsi & batasan eksplisit) 9. Reflective Review (keselarasan dengan PRD & arsitektur backend) Select 1–9 or just type your question/feedback: