dubs-server 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (304) hide show
  1. package/.claude/settings.local.json +280 -0
  2. package/CLAUDE.md +46 -0
  3. package/CONNECT4_PRODUCTION_DEPLOY.md +155 -0
  4. package/CURRENT_SESSION.md +171 -0
  5. package/CURRENT_SESSION_DRAW.md +516 -0
  6. package/MARCH_MADNESS_SURVIVOR.md +254 -0
  7. package/PANDA.md +166 -0
  8. package/Procfile +4 -0
  9. package/README.md +476 -0
  10. package/controllers/livescoresController.js +376 -0
  11. package/controllers/pickemController.js +554 -0
  12. package/controllers/survivorAdminController.js +887 -0
  13. package/controllers/survivorController.js +623 -0
  14. package/cron/oracleMonitor.js +77 -0
  15. package/cron/pickemOracleMonitor.js +73 -0
  16. package/data/jackpot-history.json +952 -0
  17. package/data/ncaaTeams.js +406 -0
  18. package/documentation/API_SECURITY_GUIDE.md +327 -0
  19. package/documentation/ARCADE_API.md +593 -0
  20. package/documentation/ARCADE_IMPLEMENTATION_SUMMARY.md +399 -0
  21. package/documentation/ARCADE_QUICKSTART.md +242 -0
  22. package/documentation/AUTOMATIC_MODE_ORACLE.md +321 -0
  23. package/documentation/BUG_FIX_COHORT_DATE_DISPLAY.md +171 -0
  24. package/documentation/CLAIM_MIGRATION_INSTRUCTIONS.md +52 -0
  25. package/documentation/CLAIM_STATUS_FIX.md +67 -0
  26. package/documentation/CLI_TOOL_GUIDE.md +372 -0
  27. package/documentation/COHORT_RETENTION_ANALYSIS.md +295 -0
  28. package/documentation/COHORT_RETENTION_IMPLEMENTATION_COMPLETE.md +461 -0
  29. package/documentation/COHORT_RETENTION_SUMMARY.md +204 -0
  30. package/documentation/COMPLETE_PROJECT_SUMMARY.md +490 -0
  31. package/documentation/DATABASE_QUERIES.md +269 -0
  32. package/documentation/DATABASE_RETENTION_POLICY.md +390 -0
  33. package/documentation/DATABASE_SETUP_GUIDE.md +361 -0
  34. package/documentation/DATABASE_SETUP_SUMMARY.md +247 -0
  35. package/documentation/DEMO_API_CURL_COMMANDS.md +656 -0
  36. package/documentation/DEPLOYMENT_SUMMARY.txt +100 -0
  37. package/documentation/DUPLICATE_NOTIFICATIONS_FIXED.md +201 -0
  38. package/documentation/EXCHANGE_RATES_INTEGRATION.md +371 -0
  39. package/documentation/FINAL_API_PROTECTION_TABLE.md +175 -0
  40. package/documentation/GAME_START_NOTIFICATIONS_DEPLOYMENT.md +256 -0
  41. package/documentation/GAME_START_NOTIFICATIONS_INTEGRATION.md +275 -0
  42. package/documentation/HEROKU_DEPLOYMENT.md +134 -0
  43. package/documentation/HEROKU_SCHEDULER_SETUP.md +271 -0
  44. package/documentation/JACKPOT_API.md +521 -0
  45. package/documentation/JACKPOT_DEPLOYMENT_GUIDE.md +362 -0
  46. package/documentation/JWT_IMPLEMENTATION_SUMMARY.md +373 -0
  47. package/documentation/JWT_QUICK_SETUP.md +268 -0
  48. package/documentation/JWT_TESTING_GUIDE.md +404 -0
  49. package/documentation/KEEPER_RECOVERY_GUIDE.md +381 -0
  50. package/documentation/KEEPER_SETUP.md +206 -0
  51. package/documentation/KEEPER_STATE_MACHINE.md +423 -0
  52. package/documentation/LATEST_PRODUCTION_SETUP.md +387 -0
  53. package/documentation/LOCAL_VOTING_TEST.md +279 -0
  54. package/documentation/ORACLE_FIXES_SUMMARY.md +188 -0
  55. package/documentation/ORACLE_POSTGRESQL_UPDATE.md +202 -0
  56. package/documentation/PAYMENT_DEPLOYMENT.md +209 -0
  57. package/documentation/PNL_TRACKING_SETUP.md +189 -0
  58. package/documentation/PREVENTING_LOCKUP_ERRORS.md +472 -0
  59. package/documentation/PRODUCTION_READY_SUMMARY.md +227 -0
  60. package/documentation/PUBLIC_VS_PRIVATE_ENDPOINTS.md +278 -0
  61. package/documentation/QUICK_AUTH_SETUP.md +99 -0
  62. package/documentation/QUICK_DEPLOY.md +224 -0
  63. package/documentation/QUICK_FIX.md +114 -0
  64. package/documentation/QUICK_START.md +152 -0
  65. package/documentation/REFEREE_MODE_GUIDE.md +392 -0
  66. package/documentation/RETENTION_CORE_ACTION_UPDATE.md +313 -0
  67. package/documentation/RETENTION_UPDATE_SUMMARY.md +108 -0
  68. package/documentation/RUN_MIGRATION_NOW.md +39 -0
  69. package/documentation/SCRIPTS_UPDATE_SUMMARY.md +251 -0
  70. package/documentation/SETUP_GUIDE.md +184 -0
  71. package/documentation/STATE_MACHINE_IMPLEMENTATION.md +250 -0
  72. package/documentation/TELEGRAM_NOTIFICATIONS_DIAGNOSIS.md +361 -0
  73. package/documentation/UNIFIED_ARCHITECTURE.md +231 -0
  74. package/documentation/VOTING_DEPLOYMENT_SUMMARY.md +392 -0
  75. package/documentation/WEBSOCKET_ARCHITECTURE.md +881 -0
  76. package/documentation/WHAT_WE_BUILT_TODAY.md +369 -0
  77. package/documentation/latest/LATEST_PRODUCTION_SETUP.md +865 -0
  78. package/ecosystem.config.js +65 -0
  79. package/env.template +125 -0
  80. package/middleware/apiKeyAuth.js +136 -0
  81. package/middleware/authenticate.js +214 -0
  82. package/middleware/developerUserAuth.js +76 -0
  83. package/middleware/socketAuth.js +69 -0
  84. package/package.json +49 -0
  85. package/postman/Dubs-API-v1-With-Voting.postman_collection.json +555 -0
  86. package/postman/Dubs-API-v1.postman_collection.json +205 -0
  87. package/postman/Dubs_Developer_API.postman_collection.json +662 -0
  88. package/postman/QUICKSTART.md +118 -0
  89. package/postman/QUICK_REFERENCE.md +246 -0
  90. package/postman/README.md +71 -0
  91. package/postman/VOTING_API_GUIDE.md +426 -0
  92. package/refactor/Animations.md +148 -0
  93. package/refactor/Chat.md +252 -0
  94. package/routes/actionsRoutes.js +699 -0
  95. package/routes/adminRoutes.js +370 -0
  96. package/routes/analyticsRoutes.js +1262 -0
  97. package/routes/arcadeRoutes.js +557 -0
  98. package/routes/authRoutes.js +2310 -0
  99. package/routes/avatarRoutes.js +85 -0
  100. package/routes/botRoutes.js +211 -0
  101. package/routes/chatRoutes.js +377 -0
  102. package/routes/cryptoPriceRoutes.js +105 -0
  103. package/routes/developerRoutes.js +4201 -0
  104. package/routes/deviceRoutes.js +214 -0
  105. package/routes/dmRoutes.js +167 -0
  106. package/routes/esportsRoutes.js +806 -0
  107. package/routes/exchangeRateRoutes.js +233 -0
  108. package/routes/gamesRoutes.js +3028 -0
  109. package/routes/jackpotRoutes.js +754 -0
  110. package/routes/keeperMonitoringRoutes.js +156 -0
  111. package/routes/keeperWebhookRoutes.js +466 -0
  112. package/routes/livescoresRoutes.js +31 -0
  113. package/routes/pickemAdminRoutes.js +199 -0
  114. package/routes/pickemRoutes.js +231 -0
  115. package/routes/playerStatsRoutes.js +147 -0
  116. package/routes/portfolioRoutes.js +217 -0
  117. package/routes/promoRoutes.js +418 -0
  118. package/routes/referralEarningsRoutes.js +392 -0
  119. package/routes/socialRoutes.js +459 -0
  120. package/routes/sportsRoutes.js +1271 -0
  121. package/routes/survivorAdminRoutes.js +345 -0
  122. package/routes/survivorRoutes.js +756 -0
  123. package/routes/uploadRoutes.js +256 -0
  124. package/routes/userProfileRoutes.js +244 -0
  125. package/routes/whatsNewRoutes.js +331 -0
  126. package/scripts/.claude/settings.local.json +15 -0
  127. package/scripts/README.md +170 -0
  128. package/scripts/RESTART_EVERYTHING.sh +104 -0
  129. package/scripts/add-claim-columns.sql +48 -0
  130. package/scripts/add-crypto-prices-cache.sql +27 -0
  131. package/scripts/add-exchange-rates-cache.sql +40 -0
  132. package/scripts/add-game-invite-column.sql +23 -0
  133. package/scripts/add-game-invite-notification.sql +33 -0
  134. package/scripts/add-game-invite-telegram-pref.sql +16 -0
  135. package/scripts/add-game-joined-notification.sql +16 -0
  136. package/scripts/add-game-joined-pref.js +40 -0
  137. package/scripts/add-game-joined-preference.sql +6 -0
  138. package/scripts/add-game-start-notifications.sql +41 -0
  139. package/scripts/add-notification-flags-to-games.sql +55 -0
  140. package/scripts/add-pending-game-dismissals.sql +19 -0
  141. package/scripts/add-preferred-currency.sql +34 -0
  142. package/scripts/add-winner-columns.js +61 -0
  143. package/scripts/add_mention_system.sql +53 -0
  144. package/scripts/add_payment_system.sql +96 -0
  145. package/scripts/add_sports_event_id_column.sql +22 -0
  146. package/scripts/analyze-cohort-data-heroku.js +276 -0
  147. package/scripts/analyze-cohort-data.js +295 -0
  148. package/scripts/analyze-prod-cohorts.sh +10 -0
  149. package/scripts/backfill-matchup-images.js +245 -0
  150. package/scripts/backfill-missing-signatures.js +175 -0
  151. package/scripts/backfill-referral-earnings.js +202 -0
  152. package/scripts/check-chat-schema.js +130 -0
  153. package/scripts/check-db.sh +14 -0
  154. package/scripts/check_oracle_in_game.js +54 -0
  155. package/scripts/cleanup-database.js +193 -0
  156. package/scripts/clear-notification-cache.js +85 -0
  157. package/scripts/convert-mnemonic.js +50 -0
  158. package/scripts/create-users-table.sql +44 -0
  159. package/scripts/debug-cohort-counts.js +248 -0
  160. package/scripts/debug-winner-calc.js +84 -0
  161. package/scripts/deploy-payment-system.sh +118 -0
  162. package/scripts/deploy-to-heroku.sh +63 -0
  163. package/scripts/diagnose-locked-round.js +143 -0
  164. package/scripts/dubs-cli.js +720 -0
  165. package/scripts/dump-account.js +65 -0
  166. package/scripts/find-vrf-offset.js +48 -0
  167. package/scripts/fix-chat-notifications-constraint.sql +122 -0
  168. package/scripts/fix-claim-columns.js +124 -0
  169. package/scripts/fix-constraint-now.js +44 -0
  170. package/scripts/fix-lock-timestamps.js +96 -0
  171. package/scripts/fix-locked-round.sh +126 -0
  172. package/scripts/fix-missing-badges.sql +91 -0
  173. package/scripts/fix-payment-notifications.sql +41 -0
  174. package/scripts/force-new-round.js +55 -0
  175. package/scripts/force-resolve-and-claim.js +278 -0
  176. package/scripts/important/README.md +115 -0
  177. package/scripts/important/authority-force-lock.js +197 -0
  178. package/scripts/important/authority-resolve-game.js +267 -0
  179. package/scripts/important/check-game-status.js +373 -0
  180. package/scripts/important/list-pending-games-by-version.js +270 -0
  181. package/scripts/important/reconcile-v1-v2-payouts.js +270 -0
  182. package/scripts/initialize-jackpot.js +111 -0
  183. package/scripts/jackpot/.claude/settings.local.json +10 -0
  184. package/scripts/jackpot/force-reset.js +84 -0
  185. package/scripts/jackpot/initialize-mainnet.js +100 -0
  186. package/scripts/jackpot/keeper.js +742 -0
  187. package/scripts/jackpot/status.js +107 -0
  188. package/scripts/jackpot/update-round-duration.js +143 -0
  189. package/scripts/keeper-bot.js +112 -0
  190. package/scripts/list-pending-games.js +131 -0
  191. package/scripts/migrate-chat-v2.js +127 -0
  192. package/scripts/migrate-chat-winners.js +84 -0
  193. package/scripts/migrate-chat.sh +17 -0
  194. package/scripts/migrate-game-invite.js +83 -0
  195. package/scripts/migrate-heroku-game-notifications.sh +159 -0
  196. package/scripts/migrations/001_analytics_tables.sql +422 -0
  197. package/scripts/migrations/002_add_matchup_image_url.sql +14 -0
  198. package/scripts/migrations/003_referral_earnings.sql +208 -0
  199. package/scripts/migrations/004_add_whats_new_notification_type.sql +62 -0
  200. package/scripts/migrations/005_add_connect4_your_turn_notification.sql +61 -0
  201. package/scripts/migrations/005_push_notifications.sql +55 -0
  202. package/scripts/migrations/006_add_draw_team_players.sql +28 -0
  203. package/scripts/migrations/006_add_game_cancelled_notification.sql +62 -0
  204. package/scripts/migrations/007_add_gif_url.sql +8 -0
  205. package/scripts/migrations/008_add_connect4_columns.sql +139 -0
  206. package/scripts/migrations/008_add_pool_tracking.sql +22 -0
  207. package/scripts/migrations/009_create_survivor_pool_tables.sql +174 -0
  208. package/scripts/migrations/010_add_survivor_pool_outcome.sql +28 -0
  209. package/scripts/migrations/011_create_developer_tables.sql +67 -0
  210. package/scripts/migrations/011_fix_keeper_tables.sql +85 -0
  211. package/scripts/migrations/012_create_developer_webhooks.sql +31 -0
  212. package/scripts/migrations/013_add_network_mode.sql +18 -0
  213. package/scripts/migrations/014_create_developer_app_users.sql +19 -0
  214. package/scripts/migrations/015_add_ui_config.sql +4 -0
  215. package/scripts/migrations/016_add_resolution_secret.sql +4 -0
  216. package/scripts/migrations/017_add_external_game_id.sql +3 -0
  217. package/scripts/migrations/018_create_pickem_tables.sql +115 -0
  218. package/scripts/migrations/019_expo_push_tokens.sql +19 -0
  219. package/scripts/migrations/create_whats_new_tables.sql +88 -0
  220. package/scripts/migrations/drop_live_games_tables.sql +34 -0
  221. package/scripts/open-jackpot-round.js +85 -0
  222. package/scripts/purge-all-data.sh +329 -0
  223. package/scripts/purge-all-data.sql +142 -0
  224. package/scripts/purge-heroku-data.sh +149 -0
  225. package/scripts/purge-heroku-data.sql +62 -0
  226. package/scripts/rebuild-heroku-database.sh +113 -0
  227. package/scripts/recover-funds.js +357 -0
  228. package/scripts/regenerate-epl-images.js +278 -0
  229. package/scripts/resize-s3-matchup-images.js +374 -0
  230. package/scripts/resolve-direct.js +88 -0
  231. package/scripts/resolve-mock-game.js +124 -0
  232. package/scripts/resolve-pickem-game.js +55 -0
  233. package/scripts/resolve-round-manual.js +83 -0
  234. package/scripts/resolve-stuck-game.js +382 -0
  235. package/scripts/resolve-stuck-round.js +42 -0
  236. package/scripts/run-connect4-migration.sh +16 -0
  237. package/scripts/run-mention-migration.sh +32 -0
  238. package/scripts/run-payment-migration.sh +51 -0
  239. package/scripts/run-preferred-currency-migration.sh +31 -0
  240. package/scripts/run-referral-earnings-migration.sh +32 -0
  241. package/scripts/run-survivor-outcome-migration.sh +16 -0
  242. package/scripts/seed-test-users.js +346 -0
  243. package/scripts/setup-auth-tables.js +78 -0
  244. package/scripts/setup-complete-database.sql +992 -0
  245. package/scripts/setup-database-fresh.sh +359 -0
  246. package/scripts/setup-heroku-keeper.sh +48 -0
  247. package/scripts/setup-keeper-database.js +83 -0
  248. package/scripts/setup-keeper-state-db.sql +110 -0
  249. package/scripts/setup-oracle.sh +39 -0
  250. package/scripts/setup-pnl-tracking.js +111 -0
  251. package/scripts/start-devnet.sh +14 -0
  252. package/scripts/test-arcade-devnet.sh +160 -0
  253. package/scripts/test-arcade-match.sh +109 -0
  254. package/scripts/test-automatic-mode.sh +239 -0
  255. package/scripts/test-connect4-cancel-claim.js +370 -0
  256. package/scripts/test-connect4-e2e.js +369 -0
  257. package/scripts/test-connect4-resolve.js +369 -0
  258. package/scripts/test-game-state-endpoint.js +136 -0
  259. package/scripts/test-invite-notification.js +86 -0
  260. package/scripts/test-jackpot-api.sh +71 -0
  261. package/scripts/test-poll-confirmation.js +267 -0
  262. package/scripts/test-resolve-game.js +271 -0
  263. package/scripts/test-resolve-signature.js +223 -0
  264. package/scripts/test-signature-preservation.js +124 -0
  265. package/scripts/test-state-machine.js +291 -0
  266. package/scripts/test-webhook-receiver.js +60 -0
  267. package/scripts/update-notification-constraint.js +52 -0
  268. package/scripts/verify-account-layout.js +145 -0
  269. package/scripts/verify-winner-algorithm.js +278 -0
  270. package/server.js +5259 -0
  271. package/services/arcadeMatchService.js +763 -0
  272. package/services/automaticGameOracle.js +1596 -0
  273. package/services/chatService.js +1612 -0
  274. package/services/connect4GameService.js +1049 -0
  275. package/services/connect4NotificationService.js +374 -0
  276. package/services/cryptoPriceService.js +223 -0
  277. package/services/customGameResolver.js +260 -0
  278. package/services/db.js +79 -0
  279. package/services/directMessageService.js +389 -0
  280. package/services/discordNotifications.js +160 -0
  281. package/services/exchangeRateService.js +289 -0
  282. package/services/expoPushService.js +314 -0
  283. package/services/gamesCacheService.js +539 -0
  284. package/services/jackpotHistory.js +331 -0
  285. package/services/jackpotService.js +856 -0
  286. package/services/keeperStateService.js +355 -0
  287. package/services/matchupImageService.js +591 -0
  288. package/services/notificationCacheService.js +407 -0
  289. package/services/pickemOracle.js +440 -0
  290. package/services/playerStatsService.js +389 -0
  291. package/services/portfolioService.js +555 -0
  292. package/services/promoService.js +757 -0
  293. package/services/promoTreasuryService.js +239 -0
  294. package/services/pushNotifications.js +353 -0
  295. package/services/redisService.js +422 -0
  296. package/services/referralEarningsService.js +728 -0
  297. package/services/s3Service.js +396 -0
  298. package/services/socialService.js +1202 -0
  299. package/services/survivorOracle.js +469 -0
  300. package/services/survivorSimulator.js +475 -0
  301. package/services/telegramNotifications.js +461 -0
  302. package/services/userProfileStatsService.js +1185 -0
  303. package/services/whatsNewService.js +388 -0
  304. package/utils/urlHelper.js +95 -0
@@ -0,0 +1,392 @@
1
+ /**
2
+ * 💰 Referral Earnings API Routes
3
+ *
4
+ * Endpoints for viewing and managing referral commissions.
5
+ * Referrers earn 1% of their referees' buy-ins.
6
+ */
7
+
8
+ const express = require('express');
9
+ const router = express.Router();
10
+ const { authenticate } = require('../middleware/authenticate');
11
+ const referralEarningsService = require('../services/referralEarningsService');
12
+
13
+ module.exports = () => {
14
+
15
+ /**
16
+ * GET /api/referral-earnings/me
17
+ * Get authenticated user's referral earnings
18
+ * Requires authentication
19
+ */
20
+ router.get('/me', authenticate, async (req, res) => {
21
+ try {
22
+ const walletAddress = req.user.walletAddress;
23
+ const { limit = 50, offset = 0, status } = req.query;
24
+
25
+ console.log(`[ReferralEarnings API] Getting earnings for ${walletAddress.slice(0, 8)}...`);
26
+
27
+ const result = await referralEarningsService.getEarningsForWallet(walletAddress, {
28
+ limit: parseInt(limit),
29
+ offset: parseInt(offset),
30
+ status: status || null
31
+ });
32
+
33
+ res.json({
34
+ success: true,
35
+ ...result
36
+ });
37
+
38
+ } catch (error) {
39
+ console.error('[ReferralEarnings API] Error getting earnings:', error.message);
40
+ res.status(500).json({
41
+ success: false,
42
+ error: error.message
43
+ });
44
+ }
45
+ });
46
+
47
+ /**
48
+ * GET /api/referral-earnings/me/summary
49
+ * Get quick summary for authenticated user (for dashboard widgets)
50
+ * Requires authentication
51
+ */
52
+ router.get('/me/summary', authenticate, async (req, res) => {
53
+ try {
54
+ const walletAddress = req.user.walletAddress;
55
+
56
+ const result = await referralEarningsService.getEarningsForWallet(walletAddress, {
57
+ limit: 1,
58
+ offset: 0
59
+ });
60
+
61
+ res.json({
62
+ success: true,
63
+ summary: result.summary
64
+ });
65
+
66
+ } catch (error) {
67
+ console.error('[ReferralEarnings API] Error getting summary:', error.message);
68
+ res.status(500).json({
69
+ success: false,
70
+ error: error.message
71
+ });
72
+ }
73
+ });
74
+
75
+ /**
76
+ * GET /api/referral-earnings/me/recent
77
+ * Get recent earnings for authenticated user (last 10)
78
+ * Requires authentication
79
+ */
80
+ router.get('/me/recent', authenticate, async (req, res) => {
81
+ try {
82
+ const walletAddress = req.user.walletAddress;
83
+
84
+ const result = await referralEarningsService.getEarningsForWallet(walletAddress, {
85
+ limit: 10,
86
+ offset: 0
87
+ });
88
+
89
+ res.json({
90
+ success: true,
91
+ recentEarnings: result.earnings
92
+ });
93
+
94
+ } catch (error) {
95
+ console.error('[ReferralEarnings API] Error getting recent earnings:', error.message);
96
+ res.status(500).json({
97
+ success: false,
98
+ error: error.message
99
+ });
100
+ }
101
+ });
102
+
103
+ /**
104
+ * GET /api/referral-earnings/leaderboard
105
+ * Get referral earnings leaderboard (public)
106
+ */
107
+ router.get('/leaderboard', async (req, res) => {
108
+ try {
109
+ const { limit = 20 } = req.query;
110
+
111
+ const leaderboard = await referralEarningsService.getLeaderboard(parseInt(limit));
112
+
113
+ res.json({
114
+ success: true,
115
+ leaderboard
116
+ });
117
+
118
+ } catch (error) {
119
+ console.error('[ReferralEarnings API] Error getting leaderboard:', error.message);
120
+ res.status(500).json({
121
+ success: false,
122
+ error: error.message
123
+ });
124
+ }
125
+ });
126
+
127
+ /**
128
+ * GET /api/referral-earnings/stats
129
+ * Get platform-wide referral stats (public)
130
+ */
131
+ router.get('/stats', async (req, res) => {
132
+ try {
133
+ const stats = await referralEarningsService.getPlatformStats();
134
+
135
+ res.json({
136
+ success: true,
137
+ stats
138
+ });
139
+
140
+ } catch (error) {
141
+ console.error('[ReferralEarnings API] Error getting platform stats:', error.message);
142
+ res.status(500).json({
143
+ success: false,
144
+ error: error.message
145
+ });
146
+ }
147
+ });
148
+
149
+ /**
150
+ * GET /api/referral-earnings/user/:walletAddress
151
+ * Get public referral stats for any user
152
+ * Returns only summary (no detailed earnings)
153
+ */
154
+ router.get('/user/:walletAddress', async (req, res) => {
155
+ try {
156
+ const { walletAddress } = req.params;
157
+
158
+ const result = await referralEarningsService.getEarningsForWallet(walletAddress, {
159
+ limit: 0, // Don't fetch individual earnings
160
+ offset: 0
161
+ });
162
+
163
+ res.json({
164
+ success: true,
165
+ walletAddress,
166
+ summary: {
167
+ totalEarnedSOL: result.summary.totalEarnedSOL,
168
+ uniqueReferees: result.summary.uniqueReferees,
169
+ gamesCount: result.summary.gamesCount
170
+ }
171
+ });
172
+
173
+ } catch (error) {
174
+ console.error('[ReferralEarnings API] Error getting user stats:', error.message);
175
+ res.status(500).json({
176
+ success: false,
177
+ error: error.message
178
+ });
179
+ }
180
+ });
181
+
182
+ /**
183
+ * GET /api/referral-earnings/wallet/:walletAddress
184
+ * Get referral earnings with transaction signatures for transaction history linking
185
+ * Used by frontend GameLinkingService
186
+ */
187
+ router.get('/wallet/:walletAddress', async (req, res) => {
188
+ try {
189
+ const { walletAddress } = req.params;
190
+
191
+ const result = await referralEarningsService.getEarningsForWallet(walletAddress, {
192
+ limit: 100, // Get recent earnings for transaction matching
193
+ offset: 0
194
+ });
195
+
196
+ res.json({
197
+ success: true,
198
+ earnings: result.earnings
199
+ });
200
+
201
+ } catch (error) {
202
+ console.error('[ReferralEarnings API] Error getting wallet earnings:', error.message);
203
+ res.status(500).json({
204
+ success: false,
205
+ error: error.message
206
+ });
207
+ }
208
+ });
209
+
210
+ /**
211
+ * POST /api/referral-earnings/process-game
212
+ * Process referral commissions for a resolved game
213
+ * Called by the oracle after game resolution
214
+ *
215
+ * Note: This can be secured with API key or made internal-only
216
+ */
217
+ router.post('/process-game', async (req, res) => {
218
+ try {
219
+ const { gameId, gameType, potSizeLamports } = req.body;
220
+
221
+ // Validate required fields
222
+ if (!gameId || !gameType || !potSizeLamports) {
223
+ return res.status(400).json({
224
+ success: false,
225
+ error: 'Missing required fields: gameId, gameType, potSizeLamports'
226
+ });
227
+ }
228
+
229
+ console.log(`[ReferralEarnings API] Processing commissions for game ${gameId}`);
230
+
231
+ const result = await referralEarningsService.processGameCommissions(
232
+ gameId,
233
+ gameType,
234
+ parseInt(potSizeLamports)
235
+ );
236
+
237
+ res.json({
238
+ success: true,
239
+ message: `Processed ${result.commissions.length} referral commissions`,
240
+ summary: result
241
+ });
242
+
243
+ } catch (error) {
244
+ console.error('[ReferralEarnings API] Error processing game:', error.message);
245
+ res.status(500).json({
246
+ success: false,
247
+ error: error.message
248
+ });
249
+ }
250
+ });
251
+
252
+ /**
253
+ * GET /api/referral-earnings/pending-payouts
254
+ * Get all pending payouts grouped by referrer (admin/operator only)
255
+ * For batch payout processing
256
+ */
257
+ router.get('/pending-payouts', async (req, res) => {
258
+ try {
259
+ const { minAmount = 0 } = req.query;
260
+
261
+ const pendingPayouts = await referralEarningsService.getPendingPayouts(parseInt(minAmount));
262
+
263
+ // Calculate totals
264
+ const totalPending = pendingPayouts.reduce((sum, p) => sum + p.totalPendingLamports, 0);
265
+
266
+ res.json({
267
+ success: true,
268
+ totalReferrers: pendingPayouts.length,
269
+ totalPendingLamports: totalPending,
270
+ totalPendingSOL: totalPending / referralEarningsService.LAMPORTS_PER_SOL,
271
+ payouts: pendingPayouts
272
+ });
273
+
274
+ } catch (error) {
275
+ console.error('[ReferralEarnings API] Error getting pending payouts:', error.message);
276
+ res.status(500).json({
277
+ success: false,
278
+ error: error.message
279
+ });
280
+ }
281
+ });
282
+
283
+ /**
284
+ * POST /api/referral-earnings/mark-paid
285
+ * Mark earnings as paid after successful payout transaction
286
+ * Called by the payout script/cron
287
+ */
288
+ router.post('/mark-paid', async (req, res) => {
289
+ try {
290
+ const { referrerWallet, txSignature, batchId } = req.body;
291
+
292
+ if (!referrerWallet || !txSignature) {
293
+ return res.status(400).json({
294
+ success: false,
295
+ error: 'Missing required fields: referrerWallet, txSignature'
296
+ });
297
+ }
298
+
299
+ const count = await referralEarningsService.markEarningsAsPaid(
300
+ referrerWallet,
301
+ txSignature,
302
+ batchId || null
303
+ );
304
+
305
+ res.json({
306
+ success: true,
307
+ message: `Marked ${count} earnings as paid`,
308
+ recordsUpdated: count
309
+ });
310
+
311
+ } catch (error) {
312
+ console.error('[ReferralEarnings API] Error marking as paid:', error.message);
313
+ res.status(500).json({
314
+ success: false,
315
+ error: error.message
316
+ });
317
+ }
318
+ });
319
+
320
+ /**
321
+ * POST /api/referral-earnings/record
322
+ * Record an on-chain referral commission payment
323
+ * Called by the oracle after game resolution with referrer
324
+ *
325
+ * This is for tracking/display purposes - the actual SOL transfer
326
+ * happens on-chain during game resolution.
327
+ */
328
+ router.post('/record', async (req, res) => {
329
+ const { gameId, referrerWallet, commissionLamports, paidOnChain, txSignature, gameType } = req.body;
330
+ const logPrefix = `[RECORD-COMMISSION:${gameId?.slice(-8) || 'unknown'}]`;
331
+
332
+ console.log(`${logPrefix} 📝 Incoming request to record on-chain commission`);
333
+ console.log(`${logPrefix} Request body: ${JSON.stringify(req.body)}`);
334
+
335
+ try {
336
+ if (!gameId || !referrerWallet || commissionLamports === undefined) {
337
+ console.log(`${logPrefix} ❌ Missing required fields`);
338
+ console.log(`${logPrefix} gameId: ${gameId || 'MISSING'}`);
339
+ console.log(`${logPrefix} referrerWallet: ${referrerWallet || 'MISSING'}`);
340
+ console.log(`${logPrefix} commissionLamports: ${commissionLamports === undefined ? 'MISSING' : commissionLamports}`);
341
+ return res.status(400).json({
342
+ success: false,
343
+ error: 'Missing required fields: gameId, referrerWallet, commissionLamports'
344
+ });
345
+ }
346
+
347
+ console.log(`${logPrefix} ✅ All required fields present`);
348
+ console.log(`${logPrefix} Game ID: ${gameId}`);
349
+ console.log(`${logPrefix} Referrer: ${referrerWallet}`);
350
+ console.log(`${logPrefix} Commission: ${commissionLamports} lamports (${commissionLamports / 1_000_000_000} SOL)`);
351
+ console.log(`${logPrefix} Paid on-chain: ${paidOnChain}`);
352
+ console.log(`${logPrefix} TX Signature: ${txSignature || 'NOT PROVIDED'}`);
353
+ console.log(`${logPrefix} Game type: ${gameType || 'sports (default)'}`);
354
+
355
+ if (!txSignature) {
356
+ console.log(`${logPrefix} ⚠️ WARNING: No transaction signature provided - this record cannot be verified on-chain!`);
357
+ }
358
+
359
+ // Record in database for tracking
360
+ console.log(`${logPrefix} Calling recordOnChainCommission service...`);
361
+ const result = await referralEarningsService.recordOnChainCommission(
362
+ gameId,
363
+ referrerWallet,
364
+ parseInt(commissionLamports),
365
+ paidOnChain === true,
366
+ txSignature || null,
367
+ gameType || 'sports' // Default to sports for backwards compatibility
368
+ );
369
+
370
+ console.log(`${logPrefix} ✅ Service call completed`);
371
+ console.log(`${logPrefix} Result: ${JSON.stringify(result)}`);
372
+
373
+ res.json({
374
+ success: true,
375
+ message: `Recorded ${commissionLamports / 1_000_000_000} SOL commission`,
376
+ ...result
377
+ });
378
+
379
+ } catch (error) {
380
+ console.error(`${logPrefix} ❌ ERROR recording commission:`);
381
+ console.error(`${logPrefix} Message: ${error.message}`);
382
+ console.error(`${logPrefix} Stack: ${error.stack}`);
383
+ res.status(500).json({
384
+ success: false,
385
+ error: error.message
386
+ });
387
+ }
388
+ });
389
+
390
+ return router;
391
+ };
392
+