diff --git a/server/notification-service.ts b/server/notification-service.ts index b9b7a77..ef74878 100644 --- a/server/notification-service.ts +++ b/server/notification-service.ts @@ -67,7 +67,7 @@ async function getSmtpTransport() { // TYPES // ============================================================================ -export type NotificationCategory = 'space' | 'module' | 'system' | 'social'; +export type NotificationCategory = 'space' | 'module' | 'system' | 'social' | 'payment'; export type NotificationEventType = // Space @@ -85,7 +85,9 @@ export type NotificationEventType = // Delegation | 'delegation_received' | 'delegation_revoked' | 'delegation_expired' // Commitment (rTime) - | 'commitment_requested' | 'commitment_accepted' | 'commitment_declined'; + | 'commitment_requested' | 'commitment_accepted' | 'commitment_declined' + // Payment + | 'payment_sent' | 'payment_received' | 'payment_request_fulfilled'; export interface NotifyOptions { userDid: string; diff --git a/src/encryptid/server.ts b/src/encryptid/server.ts index 2bf5b6c..096de10 100644 --- a/src/encryptid/server.ts +++ b/src/encryptid/server.ts @@ -4260,6 +4260,66 @@ app.get('/api/internal/user-by-email', async (c) => { }); }); +// GET /api/internal/user-by-wallet — look up user by wallet address +app.get('/api/internal/user-by-wallet', async (c) => { + const serviceKey = c.req.header('X-Service-Key'); + if (!INTERNAL_SERVICE_KEY || serviceKey !== INTERNAL_SERVICE_KEY) { + return c.json({ error: 'Unauthorized' }, 401); + } + + const wallet = c.req.query('wallet'); + if (!wallet) return c.json({ found: false }, 200); + + const normalizedWallet = wallet.toLowerCase().trim(); + // Look up user by wallet_address field in users table + const rows = await sql`SELECT * FROM users WHERE LOWER(wallet_address) = ${normalizedWallet} LIMIT 1`; + if (rows.length === 0) return c.json({ found: false }, 200); + + const profile = await getUserProfile(rows[0].id); + return c.json({ + found: true, + email: profile?.profileEmail || rows[0].email || undefined, + username: profile?.username || rows[0].username || undefined, + userId: rows[0].id, + did: rows[0].did || undefined, + }); +}); + +// POST /api/internal/notify — trigger in-app notification for a user by DID or wallet +app.post('/api/internal/notify', async (c) => { + const serviceKey = c.req.header('X-Service-Key'); + if (!INTERNAL_SERVICE_KEY || serviceKey !== INTERNAL_SERVICE_KEY) { + return c.json({ error: 'Unauthorized' }, 401); + } + + const { userDid, wallet, category, eventType, title, body, actionUrl, actorUsername, metadata } = await c.req.json(); + + // Resolve DID from wallet if not provided + let resolvedDid = userDid; + if (!resolvedDid && wallet) { + const normalizedWallet = wallet.toLowerCase().trim(); + const rows = await sql`SELECT did FROM users WHERE LOWER(wallet_address) = ${normalizedWallet} LIMIT 1`; + if (rows.length > 0) resolvedDid = rows[0].did; + } + + if (!resolvedDid) { + return c.json({ error: 'Could not resolve user — provide userDid or a registered wallet' }, 400); + } + + const notification = await notify({ + userDid: resolvedDid, + category: category || 'payment', + eventType: eventType || 'payment_received', + title: title || 'Payment received', + body, + actionUrl, + actorUsername, + metadata, + }); + + return c.json({ success: true, notificationId: notification.id }); +}); + // ============================================================================ // USER LOOKUP // ============================================================================