You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
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.
Endpoints (new src/routes/analytics/analyticsRoutes.js, mounted /api/analytics):
GET /api/analytics/me/earnings — the authenticated educator's own revenue (scoped to creator === req.user._id), no elevated role needed.
Shared query params: from, to, interval, currency, network, limit.
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.
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
src/models/Transaction.js (fields + existing indexes; amount is a string, status enum, confirmedAt), src/controllers/stellar/paymentController.js (getTransactionHistory as the existing single-user pattern; verifyTransaction import for the optional on-chain check), src/services/stellar/stellarService.js (verifyTransaction, getExplorerUrl), src/utils/cache.js.
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
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
Transactioncollection (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/$sumanywhere — no totals, no time-series, no leaderboards.src/models/Transaction.js):buyer,creator,itemType(book/course),itemId,itemTitle,amount(string, precise),currency,network,status(confirmedis the settled state),confirmedAt, and indexes on{ creator, status },{ buyer, status },{ itemType, itemId }.amountis stored as a string to preserve precision, so any sum must convert carefully ($toDecimal) — a naive JSNumbersum would lose precision on USDC's 7 decimals.User.roleis["student", "tutor"]with no admin, so aggregate/platform-wide analytics need a privilege gate.What to build
src/services/analytics/paymentAnalytics.js) built on aggregation pipelines that only countstatus: "confirmed"and use$toDecimalfor money math:itemTypeand bycurrency/network.$dateTrunconconfirmedAt).creator, sum amount, count sales, joinUserfor name/avatar; supportlimitand date filter.itemId/itemTitle.src/routes/analytics/analyticsRoutes.js, mounted/api/analytics):GET /api/analytics/me/earnings— the authenticated educator's own revenue (scoped tocreator === req.user._id), no elevated role needed.GET /api/analytics/platform,/platform/timeseries,/top-educators,/top-items— admin-gated (smallrequireRolemiddleware aligned with [Enhancement] Introduce role-based authorization and fix registration privilege escalation #20's RBAC; do not widentutor).from,to,interval,currency,network,limit.{ status: 1, confirmedAt: 1 }if the explain plan needs it.?verify=truemode that spot-checks a sample of top transactions against Horizon via the existingverifyTransactionhelper 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.src/utils/cache.jshelpers keyed by query params, invalidated on new confirmed transactions.Acceptance criteria
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/earningsreturns the caller's own totals/time-series/top-items and never leaks other educators' data.401) and non-admin (403) callers.intervalover[from, to], including empty buckets, usingconfirmedAt.limitand date filtering and is backed by an index (verified viaexplain), not a full-collection scan by default.Pointers
src/models/Transaction.js(fields + existing indexes;amountis a string,statusenum,confirmedAt),src/controllers/stellar/paymentController.js(getTransactionHistoryas the existing single-user pattern;verifyTransactionimport for the optional on-chain check),src/services/stellar/stellarService.js(verifyTransaction,getExplorerUrl),src/utils/cache.js.parseFloata USDC amount — use$toDecimal/$sumin the pipeline.User.rolehas noadminvalue yet, so coordinate the role gate with [Enhancement] Introduce role-based authorization and fix registration privilege escalation #20. CI (.github/workflows/ci.yml) provides a real Mongo 7 service for aggregation tests. PRs targetdev.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
devbranch. Quality bar: CI must stay green.💬 Questions or need help? Reach the maintainers and other contributors on the DeenBridge Telegram: https://t.me/+nst9lXNj1wc4ZDE0