MonieWorld
A fintech platform enabling international money transfers from the UK, competing directly with established players like Wise (formerly TransferWise). I architected the frontend from scratch and owned Payments, Recipients, Cards, VAS, and E2E testing with BDD.
MonieWorld: Engineering Excellence in International Payments
Project Overview
MonieWorld is a next-generation international payment platform enabling seamless cross-border transactions from the UK to global destinations. Built on the robust infrastructure of Moniepoint, Nigeria’s leading payment provider, MonieWorld competes directly with established players like Wise (formerly TransferWise).
As a founding senior frontend engineer, I architected and built the entire frontend infrastructure from the ground up, establishing patterns and systems that became the foundation for all subsequent development. While the platform initially supports UK → Nigeria transfers, it was architected to scale globally to support transfers in every currency in the world—a requirement that fundamentally shaped our architectural decisions.
Core Capabilities
MonieWorld delivers fast, secure, and reliable international money transfers with:
- Multiple Payment Options: Card payments, bank transfers, Google Pay, Apple Pay, and direct GBP currency account withdrawals
- Variable Recurring Payments (VRP): Open Banking integration for faster, more frequent payments
- Saved Payment Methods: Reduced friction through secure payment method storage
- Real-time Exchange Rates: Live rate updates with transparent fee calculation
- Comprehensive Recipient Management: Add, verify, edit, and delete recipients with COP validation
- Bill Payments (VAS): Dynamic, backend-driven system for utility and service payments
- Secure Card Management: PCI-DSS compliant card viewing and management
My Ownership Areas
Working in a lean, high-performing team of 4 engineers, I was responsible for delivering mission-critical modules:
Payments Module
- Send money flows with multi-currency support
- Add money functionality with multiple payment providers
- Transaction history and real-time status tracking
- VRP implementation with saved bank accounts
Recipients Module
- Recipient list views with smart filtering
- Add/Edit/Delete recipient workflows
- COP (Confirmation of Payee) validation for UK recipients
- Recipient insights and transaction history
- Cross-currency recipient support
Cards Module
- Secure card viewing with 2FA and auto-purging
- Physical and virtual card management
- Card activation and delivery tracking
- Freeze/Unfreeze functionality
VAS (Bill Payments)
- Fully dynamic, backend-driven bill payment system
- Support for Electricity, Cable TV, Education payments
- Runtime form generation with zero hardcoding
Support Module
- Customer support infrastructure with Zendesk integration
- Feedback system with API integration
- Help center and documentation
E2E Testing Infrastructure
- First engineer to implement BDD using Cucumber & Playwright
- Established testing standards adopted company-wide
Business Impact & Technical Excellence
Platform Performance at Launch
MonieWorld achieved remarkable success from day one, demonstrating both technical reliability and market fit:
- £30 million processed since launch in transfers
- 99.6% transaction success rate maintained across all payment flows
- 90% reduction in production bugs through comprehensive testing strategy
- 55% increase in payment success rate after digital wallet integration (Apple Pay, Google Pay)
- Sub-second page load times for all critical user journeys through Next.js optimization
Quality & Reliability Standards
Established enterprise-grade quality standards that enabled confident, rapid deployments:
- 90% unit test coverage (Jest) across all modules, exceeding industry standards (70-80%)
- 95% E2E test coverage (Playwright) for critical payment flows, cards, and VAS modules
- Zero security vulnerabilities in card management and sensitive data handling
- Zero financial discrepancies due to robust testing and validation
- First engineer to implement BDD using Cucumber & Playwright, establishing standards for entire team
Complete Module Ownership
Architected and delivered four mission-critical modules from conception to production:
- Payments Module: End-to-end payment flows with VRP, multiple payment providers, and real-time transaction tracking
- Recipients Module: Comprehensive recipient management with COP validation, smart filtering, and transaction insights
- Cards Module: PCI-DSS compliant secure card viewing, activation, and management system
- VAS (Bill Payments): Fully dynamic, backend-driven bill payment system supporting unlimited providers without code deployments
Tech Stack
Frontend Core
- Framework: Next.js 14 (App Router) with React 18
- Language: TypeScript (100% type-safe codebase)
- Styling: TailwindCSS with custom design system
- State Management: React Query (TanStack Query) for server state
- Form Management: React Hook Form with Zod validation
Testing & Quality
- Unit/Integration Testing: Jest + React Testing Library
- E2E Testing: Playwright with Cucumber (BDD)
- Code Quality: SonarQube, ESLint, Prettier
- Coverage Tools: Jest coverage reports, Playwright test reports
Build & Deployment
- CI/CD: Jenkins pipeline with automated testing
- Containerization: Docker with Kubernetes (K8s)
- Monitoring: New Relic APM for performance tracking
- Version Control: Git with feature-branch workflow
APIs & Integration
- API Layer: RESTful APIs with Axios interceptors
- Authentication: JWT with 2FA (OTP-based)
- Payment Gateways: Multiple providers including Open Banking, VRP
- Third-party Services: Zendesk (Support), Apple Pay, Google Pay
Architecture & System Design
Scaling Through Domain-Driven Design
Business Challenge: Enable rapid feature development across multiple payment methods, currencies, and regulatory requirements while maintaining code quality as the team scales.
Technical Solution: I championed a feature-based architecture using Domain-Driven Design principles that organized 50+ features into clear bounded contexts, reducing cognitive load and enabling parallel development without conflicts.
Feature Organization (50+ Features):
monieworld/
├── src/features/ # Core domains (bounded contexts)
│ ├── payments/ # Payment processing domain
│ │ ├── send-money/ # Cross-border transfers
│ │ │ ├── select-recipient/
│ │ │ ├── enter-amount/
│ │ │ ├── select-payment-method/
│ │ │ └── review-transaction/
│ │ ├── add-money/ # Account funding
│ │ │ ├── select-method/
│ │ │ ├── payment-providers/
│ │ │ └── success/
│ │ └── transactions/ # Transaction history & details
│ │ ├── transaction-list/
│ │ ├── transaction-details/
│ │ └── transaction-filters/
│ ├── recipients/ # Recipient management domain
│ │ ├── recipient-list/ # List view with filters
│ │ ├── add-recipient/ # Add new recipient
│ │ │ ├── add-recipient-ng/ # Nigeria-specific
│ │ │ └── add-recipient-gb/ # UK-specific with COP
│ │ ├── edit-recipient/ # Edit existing recipient
│ │ ├── recipient-details/ # Details and insights
│ │ └── recipient-transactions/ # Transaction history per recipient
│ ├── cards/ # Card management domain
│ │ ├── home/ # Card overview & management
│ │ │ ├── card-carousel/
│ │ │ ├── card-actions/
│ │ │ └── delivery-status/
│ │ ├── manage/ # Card actions (freeze, unfreeze)
│ │ └── replace/ # Card replacement flows
│ ├── vas/ # Bill payments domain (dynamic)
│ │ ├── amount/ # Amount selection (dynamic)
│ │ ├── recipients/ # VAS recipient management
│ │ ├── review/ # Transaction review
│ │ └── shared/ # Shared VAS utilities
│ ├── accounts/ # Account management domain
│ │ ├── registration/
│ │ ├── profile/
│ │ └── security/
│ ├── onboarding/ # KYC domain
│ │ ├── identity-verification/
│ │ ├── document-upload/
│ │ └── compliance-checks/
│ ├── settings/ # User settings domain
│ ├── support/ # Customer support domain
│ │ ├── feedback/
│ │ ├── help-center/
│ │ └── zendesk-integration/
│ └── shared/ # Shared domain features
│ ├── recipient/ # Cross-feature recipient utilities
│ ├── transaction/ # Cross-feature transaction utilities
│ ├── payment-method/ # Payment method abstractions
│ └── validation/ # Shared validation rules
├── ui/ # Design system components
│ ├── forms/
│ ├── layouts/
│ ├── notifications/
│ └── data-display/
└── lib/ # Infrastructure & utilities
├── api-config/
├── analytics-service/
├── feature-flags/
├── cookie-consent/
└── telemetry/
DDD Principles Applied:
Core Domains (Root Level)
- Payment processing (send-money, add-money, transactions)
- Recipient management (add, edit, delete, verify)
- Card management (view, activate, freeze/unfreeze)
- Bill payments (VAS - fully dynamic)
- Account management
- KYC/Compliance
- Customer support
Bounded Contexts
Each core domain contains its bounded context with:
- Domain models and business logic
- UI components specific to that domain
- Domain events and state management
- API integrations and data fetching
- Feature-specific utilities
Shared Kernel
- UI component library used across features
- Common validation rules
- API client abstractions
- Utility functions
- Design system (Kamona UI built on Tailwind CSS)
Why This Architecture Delivers Business Value:
- Faster feature delivery: Clear boundaries enable engineers to work on different domains simultaneously without conflicts
- Reduced onboarding time: New engineers can contribute to specific domains (e.g., Recipients) without understanding the entire codebase
- Lower maintenance costs: Changes in one domain don’t ripple through others, reducing regression risks
- Regulatory compliance: Clear separation simplifies audit trails and compliance documentation
- Team scalability: As the team grows, domains can be owned by different engineers without coordination overhead
Building Trust: Complete Recipient Management System
Business Challenge: Every international transfer requires a verified recipient. Users needed a simple, trustworthy way to save and manage recipients while preventing fraudulent transfers.
Technical Solution: I architected and built the complete Recipients module from scratch, handling multi-country support, real-time validation, and transaction insights.
Complexity Handled:
- Multi-country support: Different validation rules for UK (COP) vs Nigeria recipients
- Dynamic form fields: Country-specific requirements (Sort Code for UK, Bank Code for Nigeria)
- Real-time validation: COP (Confirmation of Payee) verification for UK recipients
- Transaction insights: Recipient-specific transaction history and spending analytics
- Currency support: Recipients in multiple destination currencies
- Search and filtering: Smart search across thousands of recipients
- Compliance: AML checks and recipient verification workflows
Technical Implementation:
// Recipient domain types
interface Recipient {
id: string;
recipientName: string;
accountNumber: string;
currency: CurrencyCode;
countryCode: CountryCode;
bankName: string;
bankCode: string;
// UK-specific
sortCode?: string;
copStatus?: COPStatus;
// Insights
totalSent?: number;
transactionCount?: number;
averagePerTransaction?: number;
lastTransactionDate?: string;
// Metadata
createdAt: string;
updatedAt: string;
}
// COP validation for UK recipients
const useCOPValidation = () => {
const { mutate: validateRecipient, data: copResult } = useMutation({
mutationFn: async (details: RecipientDetails) => {
return await recipientAPI.validateCOP({
accountNumber: details.accountNumber,
sortCode: details.sortCode,
accountHolderName: details.name,
});
},
onSuccess: (result) => {
// Handle match status: MATCH, NO_MATCH, PARTIAL_MATCH
switch (result.matchStatus) {
case 'MATCH':
// Proceed with recipient creation
break;
case 'NO_MATCH':
// Show error, prevent creation
break;
case 'PARTIAL_MATCH':
// Show warning, allow user decision
break;
}
},
});
return { validateRecipient, copResult };
};
// Recipient list with smart filtering
const useRecipientFilters = () => {
const [filters, setFilters] = useState({
search: '',
currency: 'ALL',
sortBy: 'recent', // recent, name, mostUsed
});
const filteredRecipients = useMemo(() => {
return recipients
.filter((r) => {
// Search filter (name, account number, bank name)
if (filters.search && !matchesSearch(r, filters.search)) {
return false;
}
// Currency filter
if (filters.currency !== 'ALL' && r.currency !== filters.currency) {
return false;
}
return true;
})
.sort((a, b) => {
// Sort logic based on sortBy
switch (filters.sortBy) {
case 'recent':
return (
new Date(b.lastTransactionDate) - new Date(a.lastTransactionDate)
);
case 'name':
return a.recipientName.localeCompare(b.recipientName);
case 'mostUsed':
return b.transactionCount - a.transactionCount;
}
});
}, [recipients, filters]);
return { filters, setFilters, filteredRecipients };
};
Business Impact:
- Reduced friction: Users can save unlimited recipients for future transfers
- Increased trust: COP validation ensures money reaches correct account
- Better UX: Smart filtering and search help users find recipients quickly
- Repeat usage: Recipient insights encourage repeat transactions
- Compliance: Proper validation prevents fraudulent transfers
Enabling Business Agility: Zero-Code Bill Payment System
Business Challenge: The business team needed to add new bill payment providers (electricity, cable TV, education) quickly without engineering bottlenecks or code deployments.
Technical Solution: I designed a completely backend-driven bill payment system where all form fields, validations, and UI elements are configured server-side, enabling instant provider onboarding.
Key Features:
- Zero hardcoding: All form fields, validations, and UI elements are configured server-side
- Runtime form generation: Forms are dynamically constructed based on API responses
- Flexible validation: Regex patterns, min/max constraints, and custom error messages from backend
- Provider-agnostic: Supports any bill payment provider (Electricity, Cable TV, Education, etc.)
- Type-safe: Full TypeScript support despite dynamic nature
Technical Implementation:
// Dynamic form field structure from backend
interface FormFields {
fieldName: string;
label: string;
type: 'DROPDOWN' | 'NUMERIC' | 'MOBILE_NUMBER' | 'EMAIL' | 'TEXT';
options?: Option[];
required: boolean;
regex?: string;
errorMessage?: string;
maxAllowed?: number;
minAllowed?: number;
}
// Dynamic form renderer component
const DynamicFormField = ({ field }: { field: FormFields }) => {
const {
register,
formState: { errors },
} = useFormContext();
// Runtime validation from backend config
const validationRules = {
required: field.required
? field.errorMessage || 'This field is required'
: false,
pattern: field.regex
? {
value: new RegExp(field.regex),
message: field.errorMessage || 'Invalid format',
}
: undefined,
min: field.minAllowed,
max: field.maxAllowed,
};
switch (field.type) {
case 'DROPDOWN':
return (
<Select {...register(field.fieldName, validationRules)}>
{field.options?.map((opt) => (
<option key={opt.key} value={opt.key}>
{opt.value}
</option>
))}
</Select>
);
case 'NUMERIC':
return (
<Input
type="number"
{...register(field.fieldName, validationRules)}
error={errors[field.fieldName]?.message}
/>
);
// Additional field types handled dynamically...
default:
return <Input {...register(field.fieldName, validationRules)} />;
}
};
Business Impact:
- Zero deployment time for new bill payment providers—configuration changes only
- 90% reduction in development time for new payment categories
- Business team autonomy: Product managers can add providers without engineering involvement
- Instant market response: Support new providers same day as partnership agreements
- A/B testing capability: Test different form configurations in real-time
Meeting Regulatory Requirements: Secure Card Management
Business Challenge: Enable card product launch by meeting PCI-DSS compliance requirements while providing excellent user experience for viewing and managing card details.
Technical Solution: Implemented an enterprise-grade secure card viewing system with multi-layered security, 2FA verification, and automatic data purging.
Security Layers:
End-to-end encryption
Card details never touch client-side storage
2FA verification
Multi-factor authentication required before viewing
Ephemeral data
Card details purged from memory immediately after viewing
No client-side caching
Zero persistence of sensitive data
Audit logging
All card view actions logged for compliance
Implementation Highlights:
- Encrypted API responses that are decrypted only in secure contexts
- Copy-to-clipboard functionality without exposing data in DOM
- Auto-redaction of sensitive fields after timeout
- Secure formatting functions that prevent data leakage
Code Example - Secure Card Viewing Hook:
// Custom hook for secure card details with automatic cleanup
const useSecureCardDetails = (cardId: string) => {
const [isVisible, setIsVisible] = useState(false);
const timeoutRef = useRef<NodeJS.Timeout>();
const { mutate: fetchDetails, data: cardDetails } = useMutation({
mutationFn: async () => {
// 2FA verification required before API call
const verified = await verify2FA();
if (!verified) throw new Error('Verification failed');
// Fetch encrypted card details
return await secureAPI.getCardDetails(cardId);
},
onSuccess: (data) => {
setIsVisible(true);
// Auto-purge sensitive data after 2 minutes
timeoutRef.current = setTimeout(() => {
setIsVisible(false);
// Clear from memory
}, 120000);
},
});
// Cleanup on unmount
useEffect(() => {
return () => {
if (timeoutRef.current) clearTimeout(timeoutRef.current);
// Purge sensitive data from memory
};
}, []);
const copyToClipboard = (value: string) => {
// Secure copy without DOM exposure
navigator.clipboard.writeText(value);
// Show temporary success notification
};
return { fetchDetails, cardDetails, isVisible, copyToClipboard };
};
Business Impact:
- Full regulatory compliance achieved: Enabled card product launch meeting PCI-DSS requirements
- Zero security incidents: Multi-layered security prevented any data breaches
- Enhanced user trust: Customers feel safe viewing sensitive card information
- Compliance audit ready: Complete audit trail for regulatory reviews
Enabling Confident Deployments: CI/CD Pipeline
Building Quality Into Every Release
Business Challenge: Deploy features rapidly without introducing bugs that could cause financial losses or erode customer trust.
Technical Solution: As founding engineer, I established a comprehensive CI/CD pipeline with automated quality gates that catch issues before they reach production.
Pipeline Configuration:
// Simplified Jenkins pipeline structure
pipeline {
agent any
stages {
stage('Install') {
steps {
sh 'pnpm install --frozen-lockfile'
}
}
stage('Code Quality') {
parallel {
stage('Lint') { steps { sh 'pnpm run lint' } }
stage('Type Check') { steps { sh 'pnpm run type-check' } }
stage('Format') { steps { sh 'pnpm run format:check' } }
}
}
stage('Unit Tests') {
steps {
sh 'pnpm run test:unit --coverage'
}
post {
always {
publishHTML([reportDir: 'coverage', reportName: 'Coverage Report'])
}
}
}
stage('E2E Tests') {
steps { sh 'pnpm run test:e2e' }
}
stage('Build') {
steps { sh 'pnpm run build' }
}
stage('Docker Build') {
steps {
script {
docker.build("monieworld-web:${BUILD_NUMBER}")
}
}
}
stage('Deploy') {
steps { sh './scripts/deploy.sh' }
}
}
}
Quality Gates Enforced:
- ✅ 90% minimum unit test coverage - Builds fail if coverage drops
- ✅ 95% E2E coverage for critical payment flows
- ✅ Zero TypeScript errors - Strict type checking enforced
- ✅ Zero ESLint errors - Code quality standards must be met
- ✅ All tests passing - No broken tests in main/develop branches
- ✅ Security audit - No high/critical vulnerabilities allowed
- ✅ Successful build - Application must compile without errors
Business Impact:
- 90% reduction in production bugs: Automated quality gates catch issues before deployment
- Faster time-to-market: Confidence in testing enables rapid feature releases
- Reduced operational costs: Fewer hotfixes and emergency patches needed
- Team empowerment: New engineers protected by automated guardrails
- Regulatory compliance: Complete audit trail of all deployments and test results
Preventing Regressions: Comprehensive Testing Strategy
Pioneering Behavior-Driven Development
Business Challenge: Ensure complex payment flows work correctly across all scenarios without manual testing bottlenecks slowing down releases.
Technical Solution: I became the first engineer to implement BDD using Cucumber and Playwright, establishing testing standards that protected critical user journeys.
E2E Testing Achievements:
- 46 comprehensive feature files covering all critical user journeys
- 7 payment flow feature files with complete scenario coverage
- 3 VAS category feature files (Electricity, Cable TV, Education)
- 95% E2E coverage of my owned modules
Example Feature Coverage:
Feature: Pay Exam Bill in Nigeria via MonieWorld using WAEC and JAMB
Scenario: Successfully pay Exam bill (JAMB)
When I select "Nigeria JAMB" from the list of Education body
Then I should be navigated to the "Recipient details" screen
And I should see input fields for JAMB profile code
When I enter valid profile code
Then the "Continue" button should become enabled
When I tap "Continue"
Then I should be navigated to the amount selection screen
# ... complete scenario coverage
Business Impact:
- 95% E2E coverage for payment-critical flows prevents costly transaction failures
- Faster bug detection: Issues caught in development, not production
- Improved team confidence: Engineers ship features knowing tests validate all scenarios
- Reduced QA bottlenecks: Automated tests enable continuous deployment
Maintaining Code Quality at Scale
Established comprehensive unit testing practices that enabled confident refactoring and rapid feature development:
- 90% unit test coverage across all modules (industry standard: 70-80%)
- Comprehensive test suites for all components, hooks, and utilities
- Test-driven development (TDD) for critical payment flows
- Mock Service Worker (MSW) for reliable API mocking
Example Test Suite:
describe('DynamicFormField', () => {
it('renders dropdown with options from backend config', () => {
const mockField: FormFields = {
fieldName: 'meterType',
label: 'Meter Type',
type: 'DROPDOWN',
required: true,
options: [
{ key: 'PREPAID', value: 'Prepaid Meter' },
{ key: 'POSTPAID', value: 'Postpaid Meter' },
],
};
render(<DynamicFormField field={mockField} />);
expect(screen.getByRole('combobox')).toBeInTheDocument();
expect(screen.getByText('Prepaid Meter')).toBeInTheDocument();
expect(screen.getByText('Postpaid Meter')).toBeInTheDocument();
});
it('validates numeric field with min/max from backend', async () => {
const mockField: FormFields = {
fieldName: 'amount',
label: 'Amount',
type: 'NUMERIC',
required: true,
minAllowed: 100,
maxAllowed: 10000,
errorMessage: 'Amount must be between 100 and 10,000',
};
const { user } = setup(<DynamicFormField field={mockField} />);
await user.type(screen.getByRole('spinbutton'), '50');
await user.tab();
expect(
await screen.findByText(/Amount must be between/)
).toBeInTheDocument();
});
it('applies regex validation from backend config', async () => {
const mockField: FormFields = {
fieldName: 'meterNumber',
label: 'Meter Number',
type: 'TEXT',
required: true,
regex: '^[0-9]{10}$',
errorMessage: 'Meter number must be 10 digits',
};
const { user } = setup(<DynamicFormField field={mockField} />);
await user.type(screen.getByRole('textbox'), 'ABC123');
await user.tab();
expect(
await screen.findByText('Meter number must be 10 digits')
).toBeInTheDocument();
});
});
Business Impact:
- Comprehensive validation testing ensures dynamic forms work correctly regardless of backend configuration
- Prevents payment failures by catching validation issues before users encounter them
- Enables confident refactoring: High test coverage allows improving code without fear of breaking features
Leading Quality Culture
Beyond writing tests, I championed quality practices across the team:
- Established BDD guidelines for QA team and engineers
- Provided feedback to improve feature file quality and maintainability
- Championed code quality: Actively resolved SonarQube issues to raise quality bar
- Code review ownership: Mentored team on testing and architectural best practices
Solving Complex Technical Challenges
Multi-Payment Provider Integration
Business Challenge: Enable higher transaction volumes and frequency to drive revenue growth while reducing payment friction.
Technical Solution: Implemented Variable Recurring Payments (VRP) with Open Banking integration, saved bank accounts, and comprehensive error handling.
Business Impact: Increased payment frequency and transaction success rates, driving higher transaction volumes and customer satisfaction.
Dynamic Bill Payment System
Business Challenge: Support unlimited bill payment providers without requiring engineering time for each new provider.
Technical Solution: Architected runtime form generation engine with type-safe abstractions and backend-driven validation processing.
Business Impact:
- Zero deployment time for new providers
- Business team autonomy in payment category management
- 90% reduction in development time for new bill payment types
Secure Card Details Viewing
Business Challenge: Enable card product launch by meeting PCI-DSS compliance while providing excellent user experience.
Technical Solution: Designed multi-layered security architecture with 2FA, encrypted communication, secure clipboard functionality, and automatic data purging.
Business Impact:
- Regulatory compliance achieved: Enabled card product launch
- Zero security incidents: Protected sensitive financial data
- Enhanced customer trust: Users feel safe managing their cards
Cross-Browser Payment Support
Business Challenge: Maximize payment success rates across different browsers and devices to reduce cart abandonment.
Technical Solution: Built unified payment method selection with browser detection, feature availability checks, and fallback flows for unsupported methods.
Business Impact: 55% increase in payment success rate after adding Apple Pay and Google Pay support, significantly improving conversion.
Comprehensive E2E Testing
Business Challenge: Prevent bugs in critical payment flows that could cause financial losses or regulatory issues.
Technical Solution: Introduced Cucumber BDD framework with comprehensive feature files, reusable step definitions, fixture management, and parallel test execution.
Example Reusable Step Definitions:
// Reusable authentication steps
Given('I am logged in as a KYC2 user', async ({ page }) => {
await page.goto('/login');
await page.fill('[data-testid="email-input"]', process.env.TEST_USER_EMAIL!);
await page.fill(
'[data-testid="password-input"]',
process.env.TEST_USER_PASSWORD!
);
await page.click('[data-testid="login-button"]');
await page.waitForURL('/dashboard');
});
// Reusable payment flow steps
When('I navigate to send money flow', async ({ page }) => {
await page.click('[data-testid="send-money-button"]');
await expect(
page.locator('[data-testid="recipient-selector"]')
).toBeVisible();
});
When('I select recipient {string}', async ({ page }, recipientName: string) => {
await page.click(`[data-testid="recipient-${recipientName}"]`);
await page.waitForSelector('[data-testid="amount-input"]');
});
Then(
'I should see transaction status {string}',
async ({ page }, status: string) => {
const statusElement = page.locator('[data-testid="transaction-status"]');
await expect(statusElement).toHaveText(status);
}
);
// Reusable dynamic form steps for VAS
When('I fill in the dynamic form fields', async ({ page }, dataTable) => {
const fields = dataTable.hashes();
for (const field of fields) {
const input = page.locator(`[name="${field.fieldName}"]`);
if ((await input.getAttribute('type')) === 'select-one') {
await input.selectOption(field.value);
} else {
await input.fill(field.value);
}
}
});
Business Impact:
- 95% E2E coverage for payment-critical flows prevents costly failures
- Faster releases: Automated testing eliminates manual QA bottlenecks
- Improved team confidence: Engineers ship features knowing tests validate all scenarios
Delivering Seamless Payment Experiences
Complete Payment Flow Architecture
Business Challenge: Enable seamless international money transfers from recipient selection through transaction completion with multiple payment options.
Technical Solution: I architected and implemented the complete end-to-end payment journey encompassing:
Recipient Management
- Add/Edit/Delete recipients (Nigeria & UK)
- COP (Confirmation of Payee) validation for UK recipients
- Sort code and account number validation
- Recipient insights and transaction history
Amount Calculation
- Real-time exchange rate fetching
- Fee calculation and display
- Multi-currency support (GBP, NGN, USD)
- Smart input handling for amount entry
Payment Method Selection
- Saved bank accounts with VRP support
- Card payments (Debit/Credit)
- Apple Pay & Google Pay integration
- Bank transfer (Manual & Open Banking)
- GBP currency account
Review & Confirmation
- Transaction summary with all details
- Fraud alert warnings
- Reason for sending (compliance)
- 2FA verification
Transaction Status & Tracking
- Real-time status updates with 1-second polling
- Status timeline visualization
- Manual bank transfer instructions
- Receipt generation and sharing
Business Impact:
- 99.6% transaction success rate through robust error handling and validation
- Sub-second page load times ensure smooth user experience
- Real-time tracking reduces customer support inquiries about transaction status
Managing Payment State Efficiently
// Transaction Context Architecture
interface TransactionContext {
recipientId: string;
sourceAmount: number;
destinationAmount: number;
sourceCurrency: CurrencyCode;
destinationCurrency: CurrencyCode;
paymentMethod: PaymentMethod;
quote: QuoteResponse;
step: PaymentStep;
}
// Real-time transaction status polling (1-second intervals)
const useTransactionStatus = (transactionId: string) => {
const queryClient = useQueryClient();
const { data: transaction } = useQuery({
queryKey: ['transaction', transactionId],
queryFn: () => fetchTransactionDetails(transactionId),
refetchInterval: (data) => {
// Stop polling when transaction reaches terminal state
const terminalStates = ['COMPLETED', 'FAILED', 'CANCELLED'];
return data?.status && terminalStates.includes(data.status)
? false
: 1000; // Poll every 1 second for pending/in-progress
},
enabled: !!transactionId,
});
const getStatusTimeline = (status: TransactionStatus) => {
const timeline = [
{ label: 'Payment Initiated', status: 'completed' },
{ label: 'Processing', status: 'in-progress' },
{ label: 'Funds Sent', status: 'pending' },
];
// Dynamic timeline based on current status
return timeline.map((step, index) => ({
...step,
status: getStepStatus(status, index),
}));
};
return { transaction, timeline: getStatusTimeline(transaction?.status) };
};
Technical Benefits:
- Efficient state management reduces unnecessary API calls
- Real-time polling provides instant transaction status updates
- Smart polling stops when transaction completes, conserving resources
Delivering Measurable Results
Platform Performance & Reliability
MonieWorld launched successfully with strong technical and business performance:
- Delivered 4 major modules from inception to production: Payments, Cards, VAS, Support
- Zero critical bugs in production for card security implementation
- 99.6% transaction success rate maintained across payment flows
- Sub-second page load times for all critical user journeys
User-Centric Design Excellence
Built features with accessibility, mobile responsiveness, and error handling as core requirements:
- Seamless onboarding: KYC-1 and KYC-2 flows with progressive disclosure
- Mobile-first design: Responsive UI across all device sizes
- Accessibility: WCAG 2.1 AA compliance in UI components
- Error handling: Comprehensive error states with user-friendly messaging
Error Handling Pattern:
// Centralized error handler for payment flows
const usePaymentErrorHandler = () => {
const { showNotification } = useNotification();
const handleError = (error: PaymentError) => {
// Log error for monitoring
logError(error, { context: 'payment-flow' });
// Map backend error codes to user-friendly messages
const errorMessages: Record<string, string> = {
INSUFFICIENT_BALANCE:
"You don't have enough funds. Please add money to continue.",
RATE_EXPIRED: "Exchange rate expired. Let's get you a fresh rate.",
RECIPIENT_INVALID: 'This recipient is no longer available.',
PAYMENT_LIMIT_EXCEEDED:
'This exceeds your daily limit. Upgrade your account for higher limits.',
};
const userMessage =
errorMessages[error.code] ||
'Something went wrong. Please try again or contact support.';
showNotification({
type: 'error',
title: 'Payment Failed',
message: userMessage,
action:
error.code === 'RATE_EXPIRED'
? {
label: 'Get New Rate',
onClick: () => refetchRate(),
}
: undefined,
});
};
return { handleError };
};
Enabling Revenue Growth
Delivered features that directly enabled business expansion and new revenue streams:
- VRP implementation: Enabled higher transaction volumes and payment frequency
- Saved bank accounts: Reduced friction in repeat transactions, improving retention
- Bill payments (VAS): Opened new revenue stream with zero-code scalability for unlimited providers
- Card management: Met regulatory requirements, enabling successful card product launch
- Digital wallets: 55% increase in payment success rate after Apple Pay and Google Pay integration
Growing Team Capabilities
Established practices and standards that elevated team performance:
- BDD advocate: Testing standards adopted by entire engineering team
- Code quality champion: Actively improved codebase quality, raising team standards
- Knowledge sharing: Created documentation and guidelines for QA and engineering teams
- Mentorship: Guided junior engineers on testing and architecture best practices
Key Technical Learnings & Growth
Mastering Security-First Engineering
Building a fintech platform taught me to architect systems with security as the foundation, not an afterthought. The secure card viewing system—with its multi-layered encryption, 2FA, and automatic purging—demonstrates that regulatory compliance and excellent UX can coexist when properly designed.
Building Systems That Scale
The VAS (Bill Payments) architecture exemplifies building for tomorrow, not just today. By designing a fully backend-driven system, I enabled the business to scale without accumulating technical debt—new providers require configuration, not code deployments.
Testing Enables Velocity
Comprehensive testing (90% unit, 95% E2E coverage) isn’t just about catching bugs—it’s what enables confident refactoring and rapid feature development. The 90% reduction in production bugs came from systematic quality processes, not luck.
Collaboration Across Disciplines
Successfully introducing BDD improved collaboration between engineering, QA, and product teams. Behavior-driven development creates a shared language that helps everyone understand what we’re building and why it matters.
Architecting for Team Scale
The Domain-Driven Design structure enabled the team to grow from 1 to multiple frontend engineers without chaos. Clear bounded contexts meant new engineers could contribute to specific domains without needing to understand the entire codebase—this is what scalable architecture looks like in practice.
Tech Stack Summary
Framework: Next.js 14 + React 18 + TypeScript
Architecture: Domain-Driven Design (50+ features)
Design System: Kamona UI (built on Tailwind CSS)
State Management: TanStack Query (React Query) + React Context + URL
Forms: React Hook Form
Testing: Jest (90% coverage) + Playwright (95% coverage)
CI/CD: Jenkins + Docker + Kubernetes
Code Quality: SonarQube + ESLint + Prettier
Payment SDKs: Apple Pay, Google Pay, Checkout
Infrastructure: Docker containers, automated deployments
Monitoring: New Relic APM for performance tracking
Analytics: Mixpanel (product analytics), Singular (attribution)
Marketing: Customer.io (lifecycle messaging)
APIs: RESTful APIs with Axios interceptors
Summary: Building Fintech That Scales
As a founding senior frontend engineer at MonieWorld, I didn’t just build features—I built the foundation that enables the platform to compete with established players like Wise.
Technical Excellence Drives Business Results
The 99.6% transaction success rate, £30 million processed since launch, and 55% increase in payment success after digital wallet integration demonstrate that technical excellence directly drives business outcomes. Every architectural decision—from Domain-Driven Design to comprehensive testing—was made to enable rapid, confident deployments while maintaining financial-grade reliability.
Architectural Impact
The patterns and systems I established became organizational DNA:
- Domain-Driven Design: Enabled the team to scale without chaos—new engineers can contribute to specific domains without understanding the entire codebase
- Dynamic Systems: The VAS architecture enables business agility—new bill payment providers require configuration, not code deployments
- Security-First: The card viewing system meets PCI-DSS compliance with zero security incidents
- Testing Culture: Being first to implement BDD with 90%/95% test coverage protects the team and enables confident refactoring
Complete Ownership Delivers Value
Taking complete ownership of four critical modules—Payments, Recipients, Cards, and VAS—taught me that true ownership means thinking through every edge case, handling every error state, and building features that delight users while meeting regulatory requirements.
The Real Measure of Success
MonieWorld is live, processing real international transfers, and serving customers across borders. Users trust the platform with their money because we built it with security, reliability, and user experience as non-negotiable requirements.
The architectural patterns I established enable others to ship features confidently, quickly, and correctly. That’s the real impact of thoughtful engineering.