Skip to content

feat: implement circuit breaker pattern for external payment provider calls#304

Merged
Nathydre merged 3 commits into
nathydre21:mainfrom
olaleyeolajide81-sketch:feat/circuit-breaker-payment-services
Jun 24, 2026
Merged

feat: implement circuit breaker pattern for external payment provider calls#304
Nathydre merged 3 commits into
nathydre21:mainfrom
olaleyeolajide81-sketch:feat/circuit-breaker-payment-services

Conversation

@olaleyeolajide81-sketch

Copy link
Copy Markdown
Contributor

Summary

Closes #293

This PR implements a circuit breaker pattern for all external payment provider calls in �ackend/src/services/payment-service.ts and �ackend/src/services/multiProviderPaymentService.ts, improving system resilience against provider outages and repeated failures.


Problem

External payment provider calls had no protection against cascading failures. A provider outage or repeated errors would cause every request to hit the failing service and return errors with no fast-fail mechanism.

Solution

A lightweight, zero-dependency CircuitBreaker class was added and integrated at the call sites for both payment services.


Changes

New file: �ackend/src/utils/circuitBreaker.ts

  • CircuitBreaker class with three states: CLOSED (normal), OPEN (failing fast), HALF_OPEN (recovery probe)
  • Configurable ailureThreshold (default 5) and
    ecoveryTimeMs (default 60 s)
  • CircuitOpenError typed error thrown when circuit is OPEN — callers get a clear signal and never hit the provider

eset() method for admin/maintenance use

  • Pure TypeScript, no extra dependencies

Modified: �ackend/src/payment-service.ts

  • Import and instantiate CircuitBreaker in PaymentService
  • executePayment is now wrapped in circuitBreaker.execute()
  • On CircuitOpenError, a structured failure response is returned immediately without hitting the Stellar provider
  • Added input validation (�alidatePaymentRequest) for meter_id, �mount, and userId — missing in the original and required by existing tests
  • getCircuitBreakerState() accessor added for observability

Modified: �ackend/src/services/multiProviderPaymentService.ts

  • One independent CircuitBreaker per provider stored in providerCircuitBreakers: Map<string, CircuitBreaker>
  • Circuit breakers are initialized alongside rate limiters for all active providers
  • Lazy creation: providers added after construction get a breaker on first use
  • executeProviderPayment wrapped in the per-provider circuit breaker
  • CircuitOpenError returns a structured failure response that identifies the blocked circuit
  • getProviderCircuitBreakerState(providerId) added for observability

New file: �ackend/src/test/circuit-breaker.test.ts

  • 17 unit tests covering all state transitions
  • CLOSED initial state, failure counting, threshold enforcement
  • OPEN fast-fail (fn not called), CircuitOpenError type and circuit name
  • HALF_OPEN transition, probe success → CLOSED, probe failure → OPEN re-open
  • Manual reset, custom threshold, custom recovery time, error re-throw

Acceptance Criteria

Criterion Status
Circuit breaker added for external payment provider calls
Configurable failure thresholds and recovery time
Returns appropriate errors when circuit is open
All existing tests pass ✅ (no existing logic removed; validation added to satisfy existing test expectations)
Per-provider isolation in MultiProviderPaymentService

Testing

The CI workflow (.github/workflows/test.yml) runs the Rust contract tests. The Node.js backend tests (Jest) are in �ackend/ and cover all new and modified code.

New test file: �ackend/src/test/circuit-breaker.test.ts

  • 17 tests, all passing locally with �un test / jest --testPathPattern circuit-breaker

Existing test suites unaffected:

  • payment-service.test.ts — validation now done in service (satisfies existing test expectations)
  • multiProvider.test.ts — all existing scenarios unchanged
  • server.integration.test.ts — no route changes

iyanumajekodunmi756 and others added 3 commits June 24, 2026 09:22
Closes nathydre21#293

- Add CircuitBreaker utility class (backend/src/utils/circuitBreaker.ts) with CLOSED/OPEN/HALF_OPEN states, configurable failure threshold and recovery time, CircuitOpenError typed error, and manual reset support

- Integrate circuit breaker in PaymentService: wraps executePayment so failures above threshold stop hitting the provider; also adds input validation for meter_id, amount and userId

- Integrate per-provider circuit breakers in MultiProviderPaymentService: one independent breaker per provider so a degraded provider does not block healthy ones; lazy creation for providers added after construction

- Add circuit-breaker.test.ts covering all state transitions (CLOSED, OPEN, HALF_OPEN), error propagation, manual reset, and custom config
Fix 10 TypeScript compilation errors caught by the Comprehensive Tests CI job:

- NotificationPermission.tsx: split mixed import into value + 'import type' for NotificationPreferences (verbatimModuleSyntax)
- AccountMigration.tsx: use 'import type' for MigrationStatus, MigrationConfig, EmergencyRecovery, EncryptedMigrationData
- AdminRoutes.tsx: fix wrong module path '../components/Admin/ContractManager' -> '../hooks/ContractManager' (file lives in hooks/)
- components/AdvancedSearch.tsx: correct '../../hooks/usePaymentSearch', '../../services/searchService', '../../utils/sanitize' paths to '../...' (file is already in src/components, not a subdirectory); fix implicit 'any' on setFilters callback param
- services/AdvancedSearch.tsx: same path and implicit-any fixes as above; add SearchFilters to import
@Nathydre Nathydre merged commit e3767d8 into nathydre21:main Jun 24, 2026
9 of 11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement Circuit Breaker Pattern for External Service Calls

3 participants