Skip to content

feat: background job queue, cron recurring billing, and split payments (#952, #918, #917) - #983

Merged
github-actions[bot] merged 1 commit into
Smartdevs17:mainfrom
danieloche635-bit:feat/issues-917-918-952
Sep 28, 2026
Merged

github-actions[bot] merged 1 commit into
Smartdevs17:mainfrom
danieloche635-bit:feat/issues-917-918-952

Conversation

@danieloche635-bit

Copy link
Copy Markdown
Contributor

Overview

Implements the three open issues assigned in this repo, each as a self-contained, tested domain module following the existing BaseService / Result + repository conventions, with an HTTP surface and docs.

Closes #952
Closes #918
Closes #917


#952 — Background job queue with retries

backend/src/services/job-queue/

  • Retries with exponential back-off — computeBackoffDelayMs(failedAttempt, policy) grows initialDelayMs * multiplier ** (attempt - 1), capped at maxDelayMs, with optional full jitter. maxAttempts counts the first attempt.
  • Dead-letter handling — once attempts are exhausted, the job moves to a DLQ with its last error and can be replayed via requeueDeadLetter(id).
  • Observability — metrics() counters plus lifecycle events (job.enqueued, job.started, job.completed, job.failed, job.retrying, job.dead_lettered) through an injectable onEvent sink, and structured logging.
  • Deterministic — the caller drives drain()/processNext(), so retry timing is testable with an injected clock; start(intervalMs) is available for workers. No silent data loss: a job is completed only after its handler resolves.
  • REST at /api/v1/job-queue: enqueue, metrics, list, dead-letters, requeue.
  • Docs: backend/docs/JOB_QUEUE.md.

#918 — Recurring payment schedules with cron-based billing

backend/src/services/recurring-billing/

  • Cron cadence via cron-parser with presets (hourly/daily/weekly/monthly/yearly) or a raw expression, evaluated in a validated IANA timezone (Intl).
  • Schedules carry startAt/endAt/maxRuns bounds; the first run is the first occurrence on/after max(startAt, now) (no backfill from a past start).
  • Due-run invoicing — runDue(now) generates a pending invoice per due schedule and advances nextRunAt, completing the schedule when maxRuns is reached or the next run falls after endAt. On persistence failure the schedule is left untouched and a failure event fires.
  • Lifecycle — create, list, get, pause/resume, cancel, reschedule, preview upcoming runs, list invoices.
  • Wired into the cron registry (src/config/scheduled-tasks.ts) as a */15 * * * * sweep.
  • REST at /api/v1/recurring-payments; Docs: backend/docs/RECURRING_BILLING.md.

#917 — Split payments between multiple recipients

backend/src/services/split-payments/

  • Exact allocation — percentages are normalised to integer basis points and distributed with the largest-remainder (Hamilton) method on minor units, so platformFeeMinor + Σ share.amountMinor === totalMinor and unallocatedMinor === 0. No drifting cents, even at 33.33/33.33/33.34 or one-cent splits across many recipients.
  • Validation — shares (plus platform fee) must sum to exactly 100; rejects duplicate/missing recipient ids, missing wallets, out-of-range percentages/fees, unsupported currencies and >25 recipients.
  • Plans + execution — create/list/get/archive plans, execute a payment, preview an allocation without persisting, list executions and a per-plan summary. Per-recipient minimumAmount flags skipped shares.
  • REST at /api/v1/split-payments; Docs: backend/docs/SPLIT_PAYMENTS.md.

Conventions followed

  • BaseService + Result error mapping, asyncHandler routes, ServiceError → HTTP status.
  • Persistence behind repository interfaces with in-memory implementations for tests/local dev (matching the existing payments services).
  • Domain events via injected publishers.
  • Route param coercion (firstParam) matching the existing splits route.

Verification

vitest run \
  src/services/job-queue/jobQueue.test.ts \
  src/services/recurring-billing/recurring-billing.test.ts \
  src/services/split-payments/split-payments.test.ts \
  src/jobs/__tests__/scheduler.test.ts
→ 68 passed (job queue 16, recurring billing 25, split payments 25, scheduler 2)

npx tsc --noEmit -p backend/tsconfig.json
→ same 241 baseline error files; the new/changed files add no errors

npx eslint <new files>
→ 0 errors (only the repo's no-console warnings, consistent with existing services)

Acceptance criteria

Issue Criteria Status
#952 Queue processes tasks reliably; retries behave as configured; no silent data loss ✅ bounded retries + DLQ + replay
#952 Tests cover success, failure and retry paths ✅ 16 tests
#952 Documentation with usage examples ✅ JOB_QUEUE.md
#918 Feature works end-to-end and is documented ✅ routes + scheduler sweep + docs
#918 Unit tests covering success and failure paths ✅ 25 tests
#918 Linting and type checks pass ✅ eslint clean, no new tsc errors
#917 Feature works end-to-end and is documented ✅ plans + execution + preview + docs
#917 Unit tests covering success and failure paths ✅ 25 tests
All No regressions ✅ additive only; existing splits.ts//splits untouched

Notes for the reviewer

  • Implement split payments between multiple recipients #917 already had a legacy /api/v1/splits percentage-of-total implementation; it is left untouched. This PR adds the exact-allocation engine at /api/v1/split-payments (noted in SPLIT_PAYMENTS.md).
  • Prisma persistence is intentionally not wired into the new modules' code paths — the services depend on repository interfaces and ship in-memory implementations, exactly like the existing payments services. Backing them with Prisma is a matter of implementing the same interfaces.

…martdevs17#917, Smartdevs17#918, Smartdevs17#952)

Implements three payment-platform capabilities, each as a self-contained,
tested domain module following the existing BaseService/Result + repository
conventions, with an HTTP surface and docs.

Smartdevs17#952 — background job queue (backend/src/services/job-queue)
  Retries with configurable exponential back-off (+ optional full jitter),
  a dead-letter queue with replay, metrics, and lifecycle events. The caller
  drives the queue, so retry timing is deterministic and testable.

Smartdevs17#918 — cron-based recurring billing (backend/src/services/recurring-billing)
  Schedules validated against cron-parser with IANA timezone checks, run-due
  invoicing with start/end/maxRuns bounds, pause/resume/cancel/reschedule, and
  a sweep wired into the existing scheduled-tasks registry.

Smartdevs17#917 — split payments (backend/src/services/split-payments)
  Exact largest-remainder allocation on integer basis points, so
  platform fee + recipient shares reconcile to the payment down to the minor
  unit. Plans, execution, preview, per-recipient minimums and analytics.

Tests: 66 new unit tests (job queue 16, recurring billing 25, split payments
25), all passing. New/changed files add no tsc errors over the repo baseline
and eslint is clean.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
@drips-wave

drips-wave Bot commented Sep 28, 2026

Copy link
Copy Markdown

@danieloche635-bit 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

@vercel

vercel Bot commented Sep 28, 2026

Copy link
Copy Markdown

@danieloche635-bit is attempting to deploy a commit to the smartdevs17's projects Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions
github-actions Bot merged commit 2f4a2a2 into Smartdevs17:main Sep 28, 2026
2 of 3 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

1 participant