RecoverX is a full-stack Dunning Management System designed to automatically recover failed recurring payments for SaaS businesses.
It replaces static retry logic with a Smart State Machine that analyzes failure reasons (e.g., "Insufficient Funds" vs. "Fraud"), schedules intelligent retries using exponential backoff, and provides a "Self-Cure" portal for users to update payment methods instantly.
The system is built on a Microservices-ready architecture separating the core recovery logic from the user-facing dashboard.
Instead of blindly retrying, RecoverX uses a customized strategy pattern:
- Insufficient Funds: Retries after 3, 7, and 14 days (Payday logic).
- Fraud/Expired Card: Cancels subscription immediately to save processing fees.
- Network Errors: Retries with randomized jitter.
- Uses a cron-based scheduler to poll the database for "Due" subscriptions every minute.
- Designed for horizontal scaling using Optimistic Locking (
@Version) to prevent race conditions if multiple instances run simultaneously.
- Simulates real-world latency and random failure scenarios (Stripe/Razorpay simulation).
- Implements Idempotency Keys to ensure no user is ever charged twice for the same attempt.
- Finite State Machine (FSM): Manages complex lifecycle states (
ACTIVE->DUNNING->RETRY_1->RECOVERED/FAILED). - Optimistic Locking: JPA
@Versionfield ensures data consistency during concurrent updates. - Self-Cure Workflow: A secure API endpoint allows users to trigger an immediate retry outside the scheduler cycle.
- Webhook Integration: Simulates receiving
invoice.payment_failedevents from Stripe. - Real-time Analytics: Executive dashboard visualizing Revenue Recovered vs. Churn Rate using
Recharts.
- Language: Java 17
- Framework: Spring Boot 3 (Web, JPA, Validation)
- Database: PostgreSQL
- Architecture: Layered (Controller -> Service -> Repository -> Entity)
- Framework: React + Vite
- UI Library: Material UI (MUI)
- Visualization: Recharts
- State Management: React Hooks
- Java 17+ and Maven
- Node.js 18+
- PostgreSQL running locally
Create a database named recoverx in Postgres:
CREATE DATABASE recoverx;