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.

Next.jsReactTypeScriptTailwind CSSFinTech

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.