From f6628811704a76379a31cdbbdce9b88d80459034 Mon Sep 17 00:00:00 2001 From: Jeff Emmett Date: Fri, 6 Mar 2026 23:42:44 -0800 Subject: [PATCH] =?UTF-8?q?fix:=20cumulative=20fund=20claims=20=E2=80=94?= =?UTF-8?q?=20deposits=20accumulate=20until=20claimed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of expiring old claims, new deposits add to the existing pending claim's fiat_amount and extend the expiry. The claim email shows the updated total. Co-Authored-By: Claude Opus 4.6 --- src/encryptid/db.ts | 12 ++++++++++++ src/encryptid/server.ts | 25 +++++++++++++++++++------ 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/encryptid/db.ts b/src/encryptid/db.ts index a2eb4cb..a9664d6 100644 --- a/src/encryptid/db.ts +++ b/src/encryptid/db.ts @@ -1271,6 +1271,18 @@ export async function acceptFundClaim(token: string, userId: string): Promise { + const rows = await sql` + UPDATE fund_claims + SET fiat_amount = (COALESCE(fiat_amount::numeric, 0) + ${additionalAmount}::numeric)::text, + expires_at = ${new Date(expiresAt)} + WHERE id = ${claimId} AND status IN ('pending', 'resent') + RETURNING * + `; + if (rows.length === 0) return null; + return rowToFundClaim(rows[0]); +} + export async function expireFundClaim(claimId: string): Promise { await sql`UPDATE fund_claims SET status = 'expired', email = NULL WHERE id = ${claimId} AND status IN ('pending', 'resent')`; } diff --git a/src/encryptid/server.ts b/src/encryptid/server.ts index b67bbdc..c9cad71 100644 --- a/src/encryptid/server.ts +++ b/src/encryptid/server.ts @@ -77,7 +77,7 @@ import { getFundClaimByToken, getFundClaimsByEmailHash, acceptFundClaim, - expireFundClaim, + accumulateFundClaim, cleanExpiredFundClaims, sql, } from './db.js'; @@ -2926,17 +2926,30 @@ app.post('/api/internal/fund-claims', async (c) => { return c.json({ error: 'email and walletAddress are required' }, 400); } - const id = crypto.randomUUID(); - const token = generateToken(); const emailHashed = await hashEmail(email); const expiresAt = Date.now() + 7 * 24 * 60 * 60 * 1000; // 7 days - // Expire any existing pending claims for this email (one active claim per user) + // Check for existing pending claim — accumulate deposits const existingClaims = await getFundClaimsByEmailHash(emailHashed); - for (const ec of existingClaims) { - await expireFundClaim(ec.id); + if (existingClaims.length > 0 && fiatAmount) { + const existing = existingClaims[0]; + const updated = await accumulateFundClaim(existing.id, fiatAmount, expiresAt); + + let sent = false; + try { + const totalAmount = updated?.fiatAmount || fiatAmount; + sent = await sendClaimEmail(email, existing.token, totalAmount, fiatCurrency || 'USD'); + } catch (err) { + console.error('EncryptID: Failed to send claim email:', err); + } + + return c.json({ claimId: existing.id, accumulated: true, sent }); } + // No existing claim — create new + const id = crypto.randomUUID(); + const token = generateToken(); + const claim = await createFundClaim({ id, token,