40 KiB
core-midtrans-cifo - Technical Specification
Author: BMad Date: 2025-11-25 Project Level: Quick-Flow (2 Stories) Change Type: UX Enhancement + Bug Fix Development Context: Brownfield - Payment Flow Improvements
Context
Available Documents
Loaded Documents:
- ✅ Problem-Solution Analysis (
problem-solution-2025-11-25.md) - Comprehensive problem analysis by Dr. Quinn identifying 3 interconnected payment flow issues with systematic root cause analysis and 15 evaluated solutions - ✅ Project Codebase - Existing React + TypeScript + Midtrans integration
Project Type: Brownfield - Existing functional payment application
Key Context:
- Target users: Non-tech-savvy customers (ibu-ibu awam)
- Platform: Mobile-first web application
- Payment Gateway: Midtrans (1 VA per transaction constraint)
- Current Issues: Duplicate VA generation, poor post-payment UX, inadequate error handling
Project Stack
Frontend Stack:
- React 19.1.1 - Latest React with modern hooks and concurrent features
- TypeScript 5.9.3 - Type-safe development
- Vite 7.1.7 - Fast build tool and dev server
- React Router DOM 7.9.5 - Client-side routing
- TailwindCSS 4.1.17 - Utility-first CSS framework
- Framer Motion 12.23.24 - Animation library
- React Hook Form 7.66.0 - Form state management
- TanStack React Query 5.90.7 - Server state management and data fetching
- Axios 1.13.2 - HTTP client
Backend Stack:
- Express 5.1.0 - Node.js web framework
- Midtrans Client 1.4.3 - Official Midtrans SDK for payment processing
- CORS 2.8.5 - Cross-origin resource sharing
- dotenv 17.2.3 - Environment variable management
Development Tools:
- ESLint 9.36.0 - Code linting
- Prettier 3.6.2 - Code formatting
- TypeScript ESLint 8.45.0 - TypeScript-specific linting rules
Project Type: Vite + React + TypeScript SPA with Express backend
Existing Codebase Structure
Directory Organization:
src/
├── features/ (16 feature modules) - Feature-based architecture
├── pages/ (6 pages) - Route-level components
├── components/ (3 shared components) - Reusable UI components
├── services/ (API services) - Backend communication layer
├── lib/ (utilities) - Helper functions and utilities
└── app/ (app config) - Application configuration
Architecture Pattern: Feature-based modular architecture
- Each feature is self-contained with its own components, hooks, and logic
- Shared components in
/componentsfor cross-feature reusability - Centralized API services in
/services - Utility functions in
/lib
Styling Approach: TailwindCSS utility classes
- Responsive mobile-first design
- Custom design system via
tailwind.config.ts
State Management:
- React Query for server state
- React Hook Form for form state
- React hooks (useState, useEffect) for local component state
The Change
Problem Statement
Core Problem: Mobile-first payment application untuk non-tech-savvy users (ibu-ibu awam) mengalami critical UX failures dan poor state management yang menyebabkan:
- Duplicate VA Generation - Users click generate VA button multiple times during 3-5 second delay, triggering HTTP 409 errors with technical error messages
- Post-Payment Confusion - After successful payment, users are redirected to status page with no clear next steps, causing them to press back button which triggers invalid transaction states
- Poor Error Handling - HTTP errors (409, 404) displayed as technical messages; invalid states show "Rp 0" instead of helpful recovery options
Business Impact:
- Abandoned transactions → lost revenue
- High support ticket volume
- User frustration and distrust
- Poor mobile UX for primary user demographic
Root Causes Identified:
- Lack of defensive UX design patterns (no loading states, no button disable)
- Poor React lifecycle management (useEffect re-triggering on navigation)
- Missing error handling framework (no user-friendly messages, no recovery paths)
Proposed Solution
Phase 1: Quick Wins Bundle - Implement 5 high-impact, low-risk solutions organized into 2 user stories:
Story 1: Prevent Duplicate VA Generation & Improve Feedback
- Solution #1: Button State Management - Disable button during processing
- Solution #2: Request Debouncing - 500ms debounce on VA generation
- Solution #3: User-Friendly Error Messages - Map HTTP codes to bahasa Indonesia
- Solution #4: Loading Overlay - Full-screen overlay with clear messaging
Story 2: Improve Post-Payment UX
- Solution #5: Auto-Redirect After Success - 5-second countdown with clear CTA
Expected Impact: Solves 70-80% of payment flow issues with minimal risk
Scope
In Scope:
✅ Story 1: Prevent Duplicate VA Generation & Improve Feedback
- Implement button disable/enable logic on VA generation button
- Add loading spinner to button during processing
- Implement 500ms debounce on VA generation function
- Create full-screen loading overlay component
- Map HTTP 409 → "Kode pembayaran Anda sudah dibuat! Klik di sini untuk melihat"
- Map HTTP 404 → "Terjadi kesalahan. Silakan coba lagi"
- Add "Coba Lagi" recovery button on error states
- Add "Lihat Kode Pembayaran" button on 409 errors
✅ Story 2: Improve Post-Payment UX
- Implement 5-second countdown timer on payment success page
- Add "Anda akan diarahkan ke dashboard dalam X detik..." message
- Add "Kembali Sekarang" button for impatient users
- Implement auto-redirect logic to dashboard/home after countdown
✅ Cross-Story Requirements
- Mobile-first responsive design (primary focus)
- Bahasa Indonesia for all user-facing text
- Consistent with existing TailwindCSS design system
- Accessible (keyboard navigation, screen reader friendly)
Out of Scope:
❌ Phase 2 Solutions (future work):
- Navigation guards (useEffect cleanup)
- Modal-based success page
- Progressive loading states
- Transaction state machine
- Idempotency key implementation
❌ Backend Changes:
- Midtrans API modifications
- Database schema changes
- Backend validation logic
❌ Complete Redesign:
- Single-page payment flow
- Optimistic UI patterns
- Guided tour/onboarding
Implementation Details
Source Tree Changes
Files to CREATE:
-
src/components/LoadingOverlay.tsx- NEW- Full-screen loading overlay component
- Props:
isLoading: boolean,message: string - Semi-transparent backdrop with centered spinner and message
-
src/components/CountdownRedirect.tsx- NEW- Countdown timer component for auto-redirect
- Props:
seconds: number,onComplete: () => void,destination: string - Display countdown with "Kembali Sekarang" button
-
src/lib/errorMessages.ts- NEW- Error message mapping utility
- Function:
mapErrorToUserMessage(error: AxiosError): string - Maps HTTP status codes to user-friendly Indonesian messages
-
src/lib/debounce.ts- NEW- Debounce utility function
- Function:
debounce<T>(func: T, delay: number): T - Generic debounce implementation for click handlers
Files to MODIFY:
-
src/features/payment/components/PaymentMethodSelector.tsx(or equivalent)- ADD: Button disable logic (disabled state during VA generation)
- ADD: Loading spinner on button
- ADD: Debounce wrapper on generateVA handler
- ADD: LoadingOverlay integration
- MODIFY: Error handling to use mapErrorToUserMessage
-
src/features/payment/pages/PaymentSuccessPage.tsx(or equivalent)- ADD: CountdownRedirect component
- ADD: Auto-redirect logic after 5 seconds
- MODIFY: Layout to include countdown timer and CTA button
-
src/features/payment/hooks/useGenerateVA.ts(or create if doesn't exist)- ADD: Loading state management
- ADD: Error state with user-friendly messages
- ADD: Button disable logic
- MODIFY: Error handling to use mapErrorToUserMessage
Files to ADD TESTS:
src/components/__tests__/LoadingOverlay.test.tsx- CREATEsrc/components/__tests__/CountdownRedirect.test.tsx- CREATEsrc/lib/__tests__/errorMessages.test.ts- CREATEsrc/lib/__tests__/debounce.test.ts- CREATE
Technical Approach
1. Button State Management (Solution #1)
Approach: Use React state to track loading status and disable button during API call
const [isGenerating, setIsGenerating] = useState(false);
const handleGenerateVA = async () => {
setIsGenerating(true);
try {
await generateVAAPI();
} finally {
setIsGenerating(false);
}
};
<button disabled={isGenerating}>
{isGenerating ? <Spinner /> : 'Generate VA'}
</button>
Why: Simple, effective, uses existing React patterns
2. Request Debouncing (Solution #2)
Approach: Wrap VA generation handler with debounce utility (500ms delay)
import { debounce } from '@/lib/debounce';
const debouncedGenerateVA = useMemo(
() => debounce(handleGenerateVA, 500),
[]
);
Why: Prevents rapid successive clicks, complements button disable
3. User-Friendly Error Messages (Solution #3)
Approach: Create error mapping utility that translates HTTP codes to Indonesian
// src/lib/errorMessages.ts
export const mapErrorToUserMessage = (error: AxiosError): string => {
if (error.response?.status === 409) {
return "Kode pembayaran Anda sudah dibuat! Klik di sini untuk melihat";
}
if (error.response?.status === 404) {
return "Terjadi kesalahan. Silakan coba lagi";
}
return "Terjadi kesalahan. Silakan coba lagi";
};
Why: Centralized error handling, easy to maintain and extend
4. Loading Overlay (Solution #4)
Approach: Create reusable LoadingOverlay component with Framer Motion animations
// src/components/LoadingOverlay.tsx
export const LoadingOverlay = ({ isLoading, message }) => {
if (!isLoading) return null;
return (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
className="fixed inset-0 bg-black/50 flex items-center justify-center z-50"
>
<div className="bg-white p-6 rounded-lg">
<Spinner />
<p>{message}</p>
</div>
</motion.div>
);
};
Why: Prevents interaction during processing, clear visual feedback
5. Auto-Redirect After Success (Solution #5)
Approach: Use countdown timer with useEffect and setTimeout
// src/components/CountdownRedirect.tsx
export const CountdownRedirect = ({ seconds, onComplete, destination }) => {
const [countdown, setCountdown] = useState(seconds);
useEffect(() => {
if (countdown === 0) {
onComplete();
return;
}
const timer = setTimeout(() => {
setCountdown(c => c - 1);
}, 1000);
return () => clearTimeout(timer);
}, [countdown, onComplete]);
return (
<div>
<p>Anda akan diarahkan ke {destination} dalam {countdown} detik...</p>
<button onClick={onComplete}>Kembali Sekarang</button>
</div>
);
};
Why: Clear user guidance, option for impatient users, prevents back button confusion
Existing Patterns to Follow
React Patterns:
- Functional components with hooks (no class components)
- Custom hooks for reusable logic (e.g.,
useGenerateVA) - TypeScript for type safety
- Props interfaces defined with
interfacekeyword
Styling Patterns:
- TailwindCSS utility classes
- Responsive design with mobile-first breakpoints (
sm:,md:,lg:) - Custom colors and spacing from
tailwind.config.ts - Framer Motion for animations (already in use)
State Management Patterns:
- React Query for API calls and server state
- React Hook Form for form state
- Local useState for component-specific state
Error Handling Patterns:
- Try-catch blocks for async operations
- Error states in components
- User-friendly error messages (to be improved by this change)
File Naming Conventions:
- PascalCase for components:
LoadingOverlay.tsx - camelCase for utilities:
errorMessages.ts - Test files:
ComponentName.test.tsx
Integration Points
1. Midtrans API Integration
- Location:
src/services/midtrans.ts(or similar) - Integration: VA generation API call
- Changes: Wrap with loading state, error handling, debounce
2. React Query Integration
- Usage: Manage VA generation API state
- Pattern: Use
useMutationfor VA generation - Changes: Add onMutate (loading), onError (error messages), onSuccess (redirect)
3. React Router Integration
- Usage: Navigate to success page, auto-redirect after payment
- Pattern: Use
useNavigatehook - Changes: Implement programmatic navigation in CountdownRedirect
4. TailwindCSS Design System
- Integration: All new components use Tailwind classes
- Consistency: Match existing color scheme, spacing, typography
- Responsive: Mobile-first breakpoints
5. Framer Motion Animations
- Usage: LoadingOverlay fade-in animation
- Pattern:
motion.divwith initial/animate props - Consistency: Match existing animation timing and easing
Development Context
Relevant Existing Code
Payment Feature Module:
- Location:
src/features/payment/(assumed based on feature-based architecture) - Components: PaymentMethodSelector, PaymentSuccessPage (names may vary)
- Hooks: Likely has custom hooks for payment logic
- Services: API calls to Midtrans via backend
Reference Patterns:
- Check existing components in
src/components/for styling patterns - Review existing API calls in
src/services/for error handling patterns - Look at existing hooks for state management patterns
Key Files to Review Before Implementation:
- Payment method selection component (where VA generation button lives)
- Payment success/status page component
- Existing API service layer for Midtrans calls
- Existing error handling utilities (if any)
Dependencies
Framework/Libraries:
Production Dependencies (Already Installed):
react@19.1.1- Core React libraryreact-dom@19.1.1- React DOM renderingreact-router-dom@7.9.5- Routing@tanstack/react-query@5.90.7- Server state managementaxios@1.13.2- HTTP clientframer-motion@12.23.24- Animationstailwindcss@4.1.17- Stylingclsx@2.1.1- Conditional class namestailwind-merge@3.3.1- Merge Tailwind classes
Development Dependencies (Already Installed):
typescript@5.9.3- TypeScript compilervite@7.1.7- Build tooleslint@9.36.0- Lintingprettier@3.6.2- Code formatting
No New Dependencies Required - All solutions use existing stack
Internal Modules
Modules to Create:
@/components/LoadingOverlay- Loading overlay component@/components/CountdownRedirect- Countdown redirect component@/lib/errorMessages- Error message mapping utility@/lib/debounce- Debounce utility function
Modules to Import:
@/services/midtrans(or equivalent) - Midtrans API servicereact-router-dom- useNavigate hook@tanstack/react-query- useMutation hookframer-motion- motion componentsclsxortailwind-merge- Conditional styling
Configuration Changes
No Configuration Changes Required
All solutions work within existing configuration:
- ✅ No new environment variables
- ✅ No Vite config changes
- ✅ No TailwindCSS config changes
- ✅ No ESLint/Prettier config changes
- ✅ No package.json changes (no new dependencies)
Existing Conventions (Brownfield)
Code Style:
- TypeScript: Strict mode enabled
- Quotes: Single quotes (based on Prettier config)
- Semicolons: Yes (TypeScript default)
- Indentation: 2 spaces
- Line Length: 80-100 characters (Prettier default)
Component Patterns:
- Functional components with TypeScript interfaces for props
- Named exports preferred
- Props interface named
ComponentNameProps
Import Organization:
- React imports first
- Third-party libraries second
- Internal imports last
- Absolute imports using
@/alias
Error Handling:
- Try-catch for async operations
- Error boundaries for component errors (if implemented)
- User-friendly error messages (to be improved)
Testing:
- Test files co-located with components in
__tests__/folder - File naming:
ComponentName.test.tsx - Framework: Likely Vitest (Vite's test framework) or Jest
Test Framework & Standards
Test Framework: Vitest (recommended for Vite projects) or Jest
Test File Patterns:
- Location:
src/components/__tests__/orsrc/lib/__tests__/ - Naming:
ComponentName.test.tsxorutilityName.test.ts - Structure: Describe blocks for component/function, it blocks for test cases
Testing Strategy:
- Unit tests for utilities (
debounce,errorMessages) - Component tests for UI components (
LoadingOverlay,CountdownRedirect) - Integration tests for payment flow (optional, recommended)
Coverage Requirements:
- Aim for 80%+ coverage on new code
- All utility functions must have tests
- All new components must have basic render tests
Implementation Stack
Runtime Environment:
- Node.js 20.x (recommended for Express 5.x)
- Browser: Modern browsers with ES2020+ support
Frontend Stack:
- React 19.1.1
- TypeScript 5.9.3
- Vite 7.1.7
- TailwindCSS 4.1.17
- Framer Motion 12.23.24
- React Router DOM 7.9.5
- TanStack React Query 5.90.7
- Axios 1.13.2
Backend Stack:
- Express 5.1.0
- Midtrans Client 1.4.3
Development Tools:
- ESLint 9.36.0
- Prettier 3.6.2
- TypeScript ESLint 8.45.0
Testing:
- Vitest (recommended) or Jest
- React Testing Library (for component tests)
Technical Details
Story 1: Prevent Duplicate VA Generation & Improve Feedback
Technical Implementation Details:
1. Button Disable Logic
- State variable:
isGenerating: boolean - Set to
trueon button click,falseon API response (success or error) - Button
disabledattribute bound toisGenerating - Visual feedback: Show spinner when
isGenerating === true
2. Debounce Implementation
- Debounce delay: 500ms
- Implementation: Custom debounce utility using setTimeout
- Applied to: VA generation click handler
- Edge case: Clear timeout on component unmount
3. Error Message Mapping
- HTTP 409 → "Kode pembayaran Anda sudah dibuat! Klik di sini untuk melihat"
- HTTP 404 → "Terjadi kesalahan. Silakan coba lagi"
- HTTP 500 → "Terjadi kesalahan server. Silakan coba lagi nanti"
- Network error → "Tidak dapat terhubung ke server. Periksa koneksi internet Anda"
- Default → "Terjadi kesalahan. Silakan coba lagi"
4. Loading Overlay
- Z-index: 50 (above all content)
- Backdrop: Semi-transparent black (
bg-black/50) - Content: White card with spinner and message
- Animation: Fade in (200ms) using Framer Motion
- Message: "Sedang membuat kode pembayaran..."
- Accessibility: Focus trap, ESC key to cancel (optional)
5. Recovery Buttons
- "Coba Lagi" button on all errors → Retry VA generation
- "Lihat Kode Pembayaran" button on 409 error → Navigate to existing VA view
- "Kembali" button on errors → Navigate back to payment method selection
Story 2: Improve Post-Payment UX
Technical Implementation Details:
1. Countdown Timer
- Initial value: 5 seconds
- Update interval: 1 second (1000ms)
- Implementation: useEffect with setTimeout
- Cleanup: Clear timeout on unmount
- Display format: "Anda akan diarahkan ke dashboard dalam {X} detik..."
2. Auto-Redirect
- Trigger: When countdown reaches 0
- Destination: Dashboard or home page (configurable)
- Method:
useNavigate()from react-router-dom - Fallback: Manual "Kembali Sekarang" button
3. Manual Redirect Button
- Label: "Kembali Sekarang"
- Action: Immediately navigate to destination (skip countdown)
- Styling: Primary button style (prominent, easy to find)
- Position: Below countdown message
Performance Considerations:
- Debounce prevents excessive API calls
- Loading overlay prevents UI interaction during processing
- Countdown timer uses single setTimeout (not setInterval) for better performance
Security Considerations:
- No sensitive data in error messages
- Error messages don't reveal system internals
- Rate limiting handled by backend (out of scope)
Accessibility Considerations:
- Loading overlay has proper ARIA labels
- Countdown timer announced by screen readers
- Buttons have clear labels and keyboard navigation
- Focus management during loading states
Development Setup
Prerequisites:
- Node.js 20.x installed
- npm or yarn package manager
- Git for version control
Initial Setup (if not already done):
# 1. Clone repository (if not already cloned)
git clone <repository-url>
cd core-midtrans-cifo
# 2. Install dependencies
npm install
# 3. Set up environment variables
cp .env.example .env
# Edit .env with your Midtrans credentials
# 4. Start development server
npm run dev
# 5. Start backend server (in separate terminal)
npm run server
Development Workflow:
# Start Vite dev server (frontend)
npm run dev
# Access at http://localhost:5173
# Start Express server (backend)
npm run server
# Access at http://localhost:3000 (or configured port)
# Run linter
npm run lint
# Run tests (when implemented)
npm test
# Build for production
npm run build
# Preview production build
npm run preview
Environment Variables Required:
VITE_MIDTRANS_CLIENT_KEY- Midtrans client keyMIDTRANS_SERVER_KEY- Midtrans server key (backend)MIDTRANS_IS_PRODUCTION- true/false for sandbox vs production
Implementation Guide
Setup Steps
Pre-Implementation Checklist:
-
✅ Create feature branch
git checkout -b feature/payment-ux-improvements -
✅ Verify development environment running
- Frontend dev server:
npm run dev - Backend server:
npm run server - Both running without errors
- Frontend dev server:
-
✅ Review existing payment flow code
- Locate payment method selection component
- Locate payment success page component
- Locate Midtrans API service layer
- Understand current error handling
-
✅ Set up test environment (if not already)
- Install Vitest or Jest
- Configure test runner
- Create test file structure
Implementation Steps
Story 1: Prevent Duplicate VA Generation & Improve Feedback
Phase 1: Create Utility Functions (30 minutes)
-
Create
src/lib/debounce.ts- Implement generic debounce function
- Add TypeScript types
- Write unit tests
-
Create
src/lib/errorMessages.ts- Implement error mapping function
- Map all HTTP status codes
- Add recovery action suggestions
- Write unit tests
Phase 2: Create UI Components (1 hour)
- Create
src/components/LoadingOverlay.tsx- Implement overlay with backdrop
- Add Framer Motion animations
- Add spinner and message
- Make responsive for mobile
- Write component tests
Phase 3: Modify Payment Method Component (1.5 hours)
- Locate and modify payment method selection component
- Add
isGeneratingstate - Wrap VA generation handler with debounce
- Add button disable logic
- Add loading spinner to button
- Integrate LoadingOverlay
- Update error handling to use
mapErrorToUserMessage - Add recovery buttons based on error type
- Add
Phase 4: Testing & Refinement (1 hour)
-
Manual testing
- Test duplicate click prevention
- Test loading overlay display
- Test error messages for different error types
- Test recovery buttons
- Test on mobile devices
-
Fix any issues found during testing
Story 2: Improve Post-Payment UX
Phase 1: Create Countdown Component (45 minutes)
- Create
src/components/CountdownRedirect.tsx- Implement countdown timer with useEffect
- Add auto-redirect logic
- Add manual redirect button
- Make responsive for mobile
- Write component tests
Phase 2: Modify Payment Success Page (45 minutes)
- Locate and modify payment success page component
- Integrate CountdownRedirect component
- Configure redirect destination
- Update layout for mobile-first
- Add clear success messaging
Phase 3: Testing & Refinement (30 minutes)
-
Manual testing
- Test countdown timer accuracy
- Test auto-redirect functionality
- Test manual redirect button
- Test on mobile devices
- Verify no back button issues
-
Fix any issues found during testing
Testing Strategy
Unit Tests:
-
debounce.test.ts- Test debounce delays function execution
- Test multiple rapid calls only execute once
- Test cleanup on unmount
-
errorMessages.test.ts- Test HTTP 409 maps to correct message
- Test HTTP 404 maps to correct message
- Test default error message
- Test network error message
Component Tests:
-
LoadingOverlay.test.tsx- Test renders when isLoading=true
- Test doesn't render when isLoading=false
- Test displays correct message
- Test animation
-
CountdownRedirect.test.tsx- Test countdown decrements every second
- Test onComplete called when countdown reaches 0
- Test manual redirect button works
- Test cleanup on unmount
Integration Tests (Recommended):
- Payment Flow Integration Test
- Test full VA generation flow with loading states
- Test error handling and recovery
- Test success flow with auto-redirect
Manual Testing Checklist:
- Click VA generation button multiple times rapidly → Only one request sent
- Loading overlay appears during VA generation
- Error 409 shows user-friendly message with "Lihat Kode" button
- Error 404 shows user-friendly message with "Coba Lagi" button
- Payment success page shows countdown timer
- Auto-redirect works after 5 seconds
- Manual "Kembali Sekarang" button works immediately
- All flows work on mobile devices (Chrome, Safari)
- All text is in Bahasa Indonesia
- Keyboard navigation works
- Screen reader announces loading states and countdown
Acceptance Criteria
Story 1: Prevent Duplicate VA Generation & Improve Feedback
AC1: Button Disable During Processing
- GIVEN user clicks "Generate VA" button
- WHEN API request is in progress
- THEN button is disabled and shows loading spinner
- AND user cannot click button again
- AND loading overlay appears with message "Sedang membuat kode pembayaran..."
AC2: Debounce Prevents Rapid Clicks
- GIVEN user clicks "Generate VA" button multiple times within 500ms
- WHEN debounce is active
- THEN only one API request is sent
- AND subsequent clicks within 500ms are ignored
AC3: User-Friendly Error Messages
- GIVEN VA generation fails with HTTP 409
- WHEN error is displayed to user
- THEN message shows "Kode pembayaran Anda sudah dibuat! Klik di sini untuk melihat"
- AND "Lihat Kode Pembayaran" button is displayed
- AND clicking button navigates to existing VA view
AC4: Error Recovery Options
- GIVEN VA generation fails with any error
- WHEN error is displayed to user
- THEN "Coba Lagi" button is displayed
- AND clicking button retries VA generation
- AND "Kembali" button is displayed
- AND clicking button returns to payment method selection
AC5: Loading Overlay Prevents Interaction
- GIVEN VA generation is in progress
- WHEN loading overlay is displayed
- THEN user cannot interact with page content
- AND overlay shows clear message in Bahasa Indonesia
- AND overlay is responsive on mobile devices
Story 2: Improve Post-Payment UX
AC6: Countdown Timer Display
- GIVEN user completes payment successfully
- WHEN payment success page loads
- THEN countdown timer shows "Anda akan diarahkan ke dashboard dalam 5 detik..."
- AND countdown decrements every second (5, 4, 3, 2, 1)
- AND timer is visible and readable on mobile devices
AC7: Auto-Redirect After Countdown
- GIVEN countdown timer reaches 0
- WHEN timer completes
- THEN user is automatically redirected to dashboard/home page
- AND redirect happens smoothly without errors
AC8: Manual Redirect Button
- GIVEN countdown timer is active
- WHEN user clicks "Kembali Sekarang" button
- THEN user is immediately redirected to dashboard/home page
- AND countdown is cancelled
AC9: Mobile-First Responsive Design
- GIVEN user accesses payment flow on mobile device
- WHEN any new UI element is displayed
- THEN all elements are properly sized and positioned for mobile
- AND all text is readable without zooming
- AND all buttons are easily tappable (minimum 44x44px)
AC10: Bahasa Indonesia Throughout
- GIVEN user interacts with any new UI element
- WHEN text is displayed
- THEN all text is in Bahasa Indonesia
- AND language is appropriate for non-tech-savvy users (ibu-ibu awam)
- AND no technical jargon is used
Developer Resources
File Paths Reference
New Files to Create:
src/components/LoadingOverlay.tsxsrc/components/CountdownRedirect.tsxsrc/lib/errorMessages.tssrc/lib/debounce.tssrc/components/__tests__/LoadingOverlay.test.tsxsrc/components/__tests__/CountdownRedirect.test.tsxsrc/lib/__tests__/errorMessages.test.tssrc/lib/__tests__/debounce.test.ts
Existing Files to Modify:
src/features/payment/components/PaymentMethodSelector.tsx(or equivalent)src/features/payment/pages/PaymentSuccessPage.tsx(or equivalent)src/features/payment/hooks/useGenerateVA.ts(or create if doesn't exist)
Configuration Files (No Changes):
package.json- No new dependenciesvite.config.ts- No changestailwind.config.ts- No changestsconfig.json- No changes
Key Code Locations
Payment Feature Module:
- Location:
src/features/payment/(assumed) - Key Components:
- Payment method selection component (contains VA generation button)
- Payment success/status page component
- Key Hooks:
- VA generation hook (may need to create)
- Key Services:
- Midtrans API service (
src/services/midtrans.tsor similar)
- Midtrans API service (
Shared Components:
- Location:
src/components/ - New Components:
- LoadingOverlay
- CountdownRedirect
Utilities:
- Location:
src/lib/ - New Utilities:
- errorMessages
- debounce
Important Functions to Locate:
- VA generation API call function
- Payment success navigation logic
- Error handling in payment flow
- Existing loading state management (if any)
Testing Locations
Unit Tests:
src/lib/__tests__/- Utility function testsdebounce.test.tserrorMessages.test.ts
Component Tests:
src/components/__tests__/- Component testsLoadingOverlay.test.tsxCountdownRedirect.test.tsx
Integration Tests (Recommended):
src/features/payment/__tests__/- Payment flow integration testsPaymentFlow.integration.test.tsx
Test Configuration:
vitest.config.tsorjest.config.js(if exists)- Test setup file (if exists)
Documentation to Update
Code Documentation:
- Add JSDoc comments to all new functions and components
- Add inline comments for complex logic (debounce, countdown timer)
- Add TypeScript interfaces with descriptions
Project Documentation:
- Update
README.mdwith new features (optional) - Update API documentation if error responses changed (unlikely)
- Add this tech-spec to
docs/folder for future reference
User Documentation (Optional):
- Update user guide with new payment flow behavior
- Update FAQ with error message explanations
- Create internal documentation for support team
UX/UI Considerations
UI Components Affected:
Story 1:
-
VA Generation Button
- MODIFY: Add disabled state styling
- MODIFY: Add loading spinner
- ENSURE: Button remains accessible (keyboard, screen reader)
-
Loading Overlay (NEW)
- CREATE: Full-screen overlay component
- DESIGN: Semi-transparent backdrop, centered content
- ANIMATION: Fade in/out with Framer Motion
- MOBILE: Full viewport coverage, readable text
-
Error Display (MODIFY)
- MODIFY: Replace technical errors with user-friendly messages
- ADD: Recovery action buttons
- DESIGN: Clear, non-threatening, helpful tone
Story 2:
- Payment Success Page
- ADD: Countdown timer component
- ADD: Manual redirect button
- MODIFY: Layout to accommodate new elements
- MOBILE: Ensure all elements visible without scrolling
UX Flow Changes:
Current Flow:
- User selects payment method
- User clicks "Generate VA"
- (3-5 second delay with no feedback)
- VA displayed OR error shown
- After payment: Success page with no clear next step
- User presses back → Invalid state
New Flow:
- User selects payment method
- User clicks "Generate VA"
- Button disables, loading overlay appears
- "Sedang membuat kode pembayaran..." message shown
- VA displayed OR user-friendly error with recovery options
- After payment: Success page with countdown timer
- Auto-redirect after 5 seconds OR user clicks "Kembali Sekarang"
- User lands on dashboard (no back button confusion)
Visual/Interaction Patterns:
Loading States:
- Spinner: Use existing spinner component or create simple CSS spinner
- Color: Match primary brand color
- Size: Appropriate for mobile (not too small)
- Animation: Smooth, not distracting
Error States:
- Color: Warning yellow or soft red (not harsh)
- Icon: Information icon (not scary error icon)
- Tone: Helpful, not blaming user
- Actions: Clear buttons with action verbs
Success States:
- Color: Success green
- Icon: Checkmark
- Tone: Positive, reassuring
- Actions: Clear next step
Responsive Design:
- Mobile-first: Design for 320px width minimum
- Breakpoints: Use TailwindCSS defaults (sm: 640px, md: 768px, lg: 1024px)
- Touch targets: Minimum 44x44px for buttons
- Text size: Minimum 16px for body text (prevents zoom on iOS)
Accessibility:
Keyboard Navigation:
- All interactive elements focusable
- Logical tab order
- Visible focus indicators
- ESC key to dismiss overlay (optional)
Screen Reader:
- ARIA labels for loading states
- ARIA live regions for countdown timer
- Descriptive button labels
- Alt text for icons
Color Contrast:
- WCAG AA compliance minimum
- Text on overlay readable
- Error messages high contrast
User Feedback:
Loading Feedback:
- Visual: Spinner + overlay
- Text: Clear message in Bahasa Indonesia
- Duration: Show immediately, hide on response
Error Feedback:
- Visual: Error message + icon
- Text: User-friendly explanation + recovery action
- Persistence: Stay until user takes action
Success Feedback:
- Visual: Success message + checkmark
- Text: Countdown timer with destination
- Action: Auto-redirect + manual option
Testing Approach
Test Framework: Vitest (recommended for Vite) or Jest
Testing Pyramid:
- 70% Unit Tests (utilities, hooks)
- 20% Component Tests (UI components)
- 10% Integration Tests (full flows)
Unit Tests:
-
debounce.test.tsdescribe('debounce', () => { it('delays function execution', () => { // Test implementation }); it('only executes once for multiple rapid calls', () => { // Test implementation }); it('clears timeout on cleanup', () => { // Test implementation }); }); -
errorMessages.test.tsdescribe('mapErrorToUserMessage', () => { it('maps HTTP 409 to user-friendly message', () => { // Test implementation }); it('maps HTTP 404 to user-friendly message', () => { // Test implementation }); it('returns default message for unknown errors', () => { // Test implementation }); });
Component Tests:
-
LoadingOverlay.test.tsxdescribe('LoadingOverlay', () => { it('renders when isLoading is true', () => { // Test implementation }); it('does not render when isLoading is false', () => { // Test implementation }); it('displays the provided message', () => { // Test implementation }); }); -
CountdownRedirect.test.tsxdescribe('CountdownRedirect', () => { it('decrements countdown every second', () => { // Test implementation with fake timers }); it('calls onComplete when countdown reaches 0', () => { // Test implementation }); it('redirects immediately when button clicked', () => { // Test implementation }); });
Integration Tests (Recommended):
- Payment Flow Integration Test
describe('Payment Flow', () => { it('prevents duplicate VA generation', () => { // Test full flow with multiple clicks }); it('shows user-friendly error on 409', () => { // Test error handling }); it('auto-redirects after payment success', () => { // Test success flow }); });
Coverage Goals:
- Overall: 80%+
- New utilities: 100%
- New components: 90%+
- Modified components: 80%+
Manual Testing Checklist:
- Desktop Chrome
- Desktop Firefox
- Desktop Safari
- Mobile Chrome (Android)
- Mobile Safari (iOS)
- Tablet (iPad)
- Slow 3G network simulation
- Offline scenario
- Keyboard-only navigation
- Screen reader (NVDA/JAWS/VoiceOver)
Deployment Strategy
Deployment Steps
Pre-Deployment:
-
Code Review
- Create pull request from feature branch
- Request review from team lead
- Address all review comments
- Ensure all tests pass
-
QA Testing
- Deploy to staging environment
- QA team tests all acceptance criteria
- Fix any bugs found
- Re-test until all criteria pass
-
Merge to Main
git checkout main git merge feature/payment-ux-improvements git push origin main
Deployment to Production:
-
Build Production Bundle
npm run build -
Deploy Frontend
- Upload
dist/folder to hosting (Vercel, Netlify, or custom server) - Verify environment variables are set
- Test production build locally with
npm run preview
- Upload
-
Deploy Backend (if changes)
- No backend changes in Phase 1
- Backend deployment not required
-
Verify Deployment
- Test VA generation on production
- Test error scenarios
- Test payment success flow
- Monitor for errors
Post-Deployment:
-
Monitor
- Watch error logs for 24 hours
- Monitor user behavior analytics
- Check support ticket volume
- Gather user feedback
-
Iterate
- Address any issues found
- Plan Phase 2 improvements based on data
Rollback Plan
If Critical Issues Found:
-
Immediate Rollback
# Revert to previous commit git revert HEAD git push origin main # Rebuild and redeploy npm run build # Deploy dist/ folder -
Verify Rollback
- Test that old behavior is restored
- Confirm no errors in logs
- Notify users if necessary
-
Post-Mortem
- Identify what went wrong
- Fix issues in feature branch
- Re-test thoroughly
- Re-deploy when ready
Rollback Triggers:
- Critical bug preventing payments
- Widespread errors (>5% of transactions)
- Security vulnerability discovered
- Performance degradation (>2s load time increase)
Monitoring
Metrics to Monitor:
User Behavior:
- VA generation success rate (target: >95%)
- Error rate (target: <5%)
- Payment completion rate (target: increase by 10%+)
- Time on payment success page (target: decrease by 50%)
- Back button usage after payment (target: decrease by 80%)
Technical Metrics:
- API response time (target: <3 seconds)
- Frontend load time (target: <2 seconds)
- Error logs (target: <1% error rate)
- 409 error frequency (target: near zero)
Business Metrics:
- Abandoned transaction rate (target: decrease by 20%+)
- Support ticket volume (target: decrease by 30%+)
- User satisfaction score (target: increase)
Monitoring Tools:
- Google Analytics for user behavior
- Sentry or similar for error tracking
- Server logs for API monitoring
- User feedback surveys
Alert Thresholds:
- Error rate >10% → Immediate alert
- API response time >5s → Warning alert
- Payment success rate <90% → Critical alert
End of Technical Specification
This tech-spec provides a comprehensive guide for implementing Phase 1 Quick Wins Bundle (2 stories, 5 solutions) to improve payment flow UX for non-tech-savvy users in a brownfield Midtrans payment application.