Skip to content

feat: implement subscription payment retry with intelligent scheduling (#964) - #1057

Open
Clement-coder wants to merge 1 commit into
Smartdevs17:mainfrom
Clement-coder:feat/964-payment-retry-intelligent-scheduling
Open

feat: implement subscription payment retry with intelligent scheduling (#964)#1057
Clement-coder wants to merge 1 commit into
Smartdevs17:mainfrom
Clement-coder:feat/964-payment-retry-intelligent-scheduling

Conversation

@Clement-coder

Copy link
Copy Markdown

Summary

Closes #964

Implements production-ready subscription payment retry with intelligent scheduling across both the TypeScript backend service layer and the Soroban smart contract.


What's changed

backend/services/billing/dunning.ts (new)

IntelligentRetryScheduler

  • Classifies all 8 decline codes with code-specific base delays and outreach channels
  • Exponential backoff: delay = baseDelay × multiplier^attempts, capped at maxDelayHours
  • Historical success-time shifting — tracks UTC hours of past successes and nudges the next retry toward the highest-success window for that subscriber
  • Per-plan RetryPolicy (maxRetries, backoffMultiplier, maxDelayHours, circuitBreakerThreshold, jitterHours)
  • Circuit breaker: trips after N consecutive failures, auto-resets after a configurable cooldown
  • Card-updater trigger on card_expired (fires once per invoice)
  • Automatic 50 % amount split suggested for charges > $500 on the first retry
  • RetryAnalytics: success rate, avg attempts-to-success, per-decline breakdown, 24-bucket UTC hourly heatmap

DunningOrchestrator

  • Wires scheduler + grace-period management + injectable OutreachDispatcher
  • Grace periods: configurable duration, per-interval email reminders, sweepExpiredGracePeriods for subscription suspension, auto-recovers on success
  • Dispatcher is non-fatal — a failed notification never blocks the retry schedule

backend/services/billing/__tests__/dunning.test.ts (new)

40+ test cases covering:

  • Policy management and caps
  • All 8 decline codes (retry / no-retry paths)
  • Exponential backoff ceiling
  • Circuit breaker open / reset
  • Card-updater trigger (fires once)
  • Amount split (first retry only)
  • Grace period create / recover / expire / sweep
  • Reminder interval gating (sendReminders: false respected)
  • Analytics: heatmap, decline breakdown, exhausted counter, success rate
  • Orchestrator integration: dispatcher called, non-fatal on throw, auto-register

contracts/subscription/src/charging.rs (updated)

  • On-chain retry queue with deduplication (enqueue_retry / dequeue_retry)
  • Per-subscription retry policy storage and default fallback
  • start_charge / mark_attempting / mark_completed / mark_failed lifecycle
  • mark_failed applies jitter-bounded exponential backoff and schedules next_retry_at
  • Circuit breaker: trips at configurable threshold, resets after cooldown
  • Grace period: start_grace_period / recover_grace_period / expire_grace_period / is_in_grace_period
  • process_retry_queue — callable by a cron/keeper to advance due retries
  • Full charge history (get_charge_history)
  • Deterministic jitter seeded from subscription ID (no randomness dependency in tests)
  • 40+ inline tests covering all state transitions and edge cases

contracts/types/src/lib.rs (updated)

  • RetryConfig — on-chain retry policy struct
  • ChargeAttempt — full attempt record (status, timestamps, next_retry_at)
  • ChargeStatus enum (Pending / Attempting / Completed / Failed)

Test coverage

Area Cases
Policy management 3
register 2
Decline-code hard blocks 3
Normal retry path (all codes) 8
Circuit breaker 1
recordSuccess 2
Analytics 4
getAllRecords 1
Integration (exhaustion) 1
Orchestrator construction 3
Orchestrator processFailed 5
Grace period lifecycle 6
Reminder scheduling 3
Analytics passthrough 2
Plan policy delegation 2
Total 46

Acceptance criteria

  • Feature implemented with full functionality
  • Unit tests added with >80% coverage
  • Integration tests for critical paths (orchestrator end-to-end)
  • No regression introduced (branch based on upstream/main HEAD)
  • Documentation in code (JSDoc on all public methods)
  • Performance: O(1) per retry decision, O(n) sweep — no blocking I/O in hot path

Smartdevs17#964)

- Add IntelligentRetryScheduler with exponential backoff, jitter, and
  historical success-time shifting (moves retries toward UTC hours that
  historically succeed for each subscriber)
- Add per-plan RetryPolicy with configurable maxRetries, baseDelayHours,
  backoffMultiplier, maxDelayHours, circuitBreakerThreshold, and jitterHours
- Add circuit-breaker that pauses retries after N consecutive failures and
  auto-resets after a configurable cooldown window
- Classify all 8 decline codes (insufficient_funds, card_expired,
  card_lost_stolen, do_not_honor, authentication_required, generic_decline,
  network_error, processing_error) with code-specific base delays and
  outreach channels
- Trigger card-updater flow on card_expired (once per invoice)
- Suggest automatic 50% amount split for charges above $500 on first retry
- Add DunningOrchestrator wiring scheduler + grace-period management +
  injectable OutreachDispatcher
- Grace periods: configurable duration, per-interval email reminders,
  sweep-expired helper for subscription suspension, recovered on success
- Add RetryAnalytics: success rate, avg attempts to success, per-decline
  breakdown, 24-bucket UTC hourly success heatmap
- Extend Soroban charging contract (contracts/subscription/src/charging.rs)
  with on-chain retry queue, grace period tracking, and circuit-breaker state
- Add supporting types to contracts/types/src/lib.rs (RetryConfig,
  ChargeAttempt, ChargeStatus)
- Add comprehensive test suite (>80% coverage): 40+ cases covering policy
  management, all decline codes, circuit breaker, backoff, split suggestion,
  grace period lifecycle, reminder scheduling, analytics, and orchestrator
  integration

Closes Smartdevs17#964
@drips-wave

drips-wave Bot commented Aug 27, 2026

Copy link
Copy Markdown

@Clement-coder Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

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 subscription payment retry with intelligent scheduling

1 participant