Skip to content

[Enhancement] On-chain revenue analytics API: platform volume, top educators, and per-educator earnings from settled payments #61

Description

@zeemscript

Summary

Build a read-only on-chain revenue analytics API over the confirmed Stellar payments the platform already records: platform-wide volume over time, top-earning educators, per-educator revenue breakdowns, best-selling courses/books, and buyer spend — all computed with MongoDB aggregation over the Transaction collection (optionally cross-checked against Horizon). Today the only way to read transactions is one user's paginated history; there is no aggregate reporting for admins or for an educator's own earnings dashboard.

Current state

  • getTransactionHistory (src/controllers/stellar/paymentController.js) is the only reporting endpoint: it filters { buyer } or { creator } for the calling user and paginates raw rows. There is no $group/$sum anywhere — no totals, no time-series, no leaderboards.
  • The data to power analytics already exists on every confirmed row (src/models/Transaction.js): buyer, creator, itemType (book/course), itemId, itemTitle, amount (string, precise), currency, network, status (confirmed is the settled state), confirmedAt, and indexes on { creator, status }, { buyer, status }, { itemType, itemId }.
  • amount is stored as a string to preserve precision, so any sum must convert carefully ($toDecimal) — a naive JS Number sum would lose precision on USDC's 7 decimals.
  • There is no admin surface and no educator earnings dashboard endpoint. User.role is ["student", "tutor"] with no admin, so aggregate/platform-wide analytics need a privilege gate.
  • Educator-facing earnings live only implicitly in transactions; the earnings-ledger issue ([Enhancement] Educator earnings ledger and atomic batch payouts from the platform wallet #28) is about balances and payouts, not reporting/aggregation, so this fills the reporting gap without overlapping.

What to build

  1. Analytics service (src/services/analytics/paymentAnalytics.js) built on aggregation pipelines that only count status: "confirmed" and use $toDecimal for money math:
    • Platform overview: total settled volume, transaction count, unique buyers, split by itemType and by currency/network.
    • Time-series: volume and count bucketed by day/week/month over a date range ($dateTrunc on confirmedAt).
    • Top educators: group by creator, sum amount, count sales, join User for name/avatar; support limit and date filter.
    • Top items: group by itemId/itemTitle.
    • Per-educator detail: one educator's totals, time-series, and top items.
  2. Endpoints (new src/routes/analytics/analyticsRoutes.js, mounted /api/analytics):
  3. Precision + correctness: all monetary outputs are decimal strings (never floats). Guard against unbounded scans with date-range defaults and the existing indexes; add a compound index on { status: 1, confirmedAt: 1 } if the explain plan needs it.
  4. Optional on-chain cross-check: an admin ?verify=true mode that spot-checks a sample of top transactions against Horizon via the existing verifyTransaction helper and flags mismatches (recorded-but-not-on-chain), reusing the reconciliation ideas from [Enhancement] Horizon payment ingestion and reconciliation worker for unreported on-chain payments #26 without duplicating the ingestion worker.
  5. Caching: these are expensive read aggregations that change slowly — cache with the existing src/utils/cache.js helpers keyed by query params, invalidated on new confirmed transactions.

Acceptance criteria

  • Aggregations count only status: "confirmed" and compute sums via $toDecimal, returning money as precise decimal strings (a test with fractional-cent amounts proves no float drift).
  • GET /api/analytics/me/earnings returns the caller's own totals/time-series/top-items and never leaks other educators' data.
  • Platform/top-educator/top-item endpoints reject unauthenticated (401) and non-admin (403) callers.
  • Time-series buckets correctly by interval over [from, to], including empty buckets, using confirmedAt.
  • Each list/leaderboard endpoint supports limit and date filtering and is backed by an index (verified via explain), not a full-collection scan by default.
  • Jest + supertest tests seed a known set of transactions and assert exact totals, top-educator ordering, and authorization behavior; CI's Mongo service runs them for real.

Pointers

Difficulty

Medium — no protocol work, but correct decimal money aggregation, multi-dimensional pipelines with $dateTrunc/$lookup, index-aware querying, and admin authorization make it a substantial backend feature.


🏆 GrantFox OSS — Official Campaign | FWC26. Apply for this issue through the GrantFox campaign page. The maintainer assigns one contributor before work starts; unassigned PRs may not be reviewed. PRs target the dev branch. Quality bar: CI must stay green.

💬 Questions or need help? Reach the maintainers and other contributors on the DeenBridge Telegram: https://t.me/+nst9lXNj1wc4ZDE0

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions