Partner-code credits arrive as a drip; unclaimed installments expire

Marty (2026-09-19): partner codes keep working with no monthly cap, but the credits stop
being a dump. A fifth is paid the day the code is redeemed, then a fifth on each day the
member finishes their daily ad set, five days in all, and whatever is unclaimed 30 days after
redemption is never paid. Same headline number for the partner to promote; paid only to
people who show up. 49 of the 52 PARTNER redemptions were still sitting on 500+ unspent.

Every redemption now records the site the ?promo= link was opened from (iap.promoref cookie
set from the Referer at link-open), so a code's traffic can be attributed to the partner page
it was supposed to come from. Member-funded codes charge the funder one installment at a
time; a short funder leaves the installment due, not lost.

Pre-drip redemptions were paid in full up front and are marked paid=credits at start-up, so
nothing is paid twice. Covered by a module test (13 checks: per-day cadence, gaps, rounding,
expiry, pre-drip rows, declined payer) and an end-to-end HTTP test on a local copy (9 checks).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-09-19 06:24:14 -05:00
parent 5ebe890760
commit 415a5f3848
5 changed files with 69 additions and 13 deletions
+25 -4
View File
@@ -76,6 +76,7 @@ function dropAdminSession(req) { const t = adminTokenOf(req); if (t && adminSess
function adminCookie(t) { return 'iap.adm=' + encodeURIComponent(t) + '; Path=/; HttpOnly; SameSite=Lax; Max-Age=' + (ADMIN_TTL / 1000) + (IS_PROD ? '; Secure' : ''); }
function clearAdminCookie() { return 'iap.adm=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0'; }
const IS_PROD = process.env.NODE_ENV === 'production';
const todayCT = () => new Date().toLocaleDateString('en-CA', { timeZone: 'America/Chicago' }); // Marty's day, never UTC
const SITE_FILE = path.join(DATA_DIR, 'site.json');
const db = require('./db');
@@ -701,6 +702,20 @@ async function sponsorGainNudge(sp, buyerName, lostNames) {
await messages.deliver(1, ADMIN_EMAIL || 'house@instantadpay.com', [sp.email], subject, html, 'notice');
} catch (e) {}
}
// Partner-code credits arrive as a drip (Marty, 2026-09-19): a fifth on the day the code is
// redeemed, then a fifth on each day the member finishes their daily ad set, five days in all,
// nothing after 30 days. Same headline number for the partner to promote; paid only to people
// who show up. Member-funded codes charge the funder one installment at a time.
async function payPromo(email, day) {
return promos.payDue(email, day, async (r, amount, n) => {
if (r.funder && r.funder !== email) {
const ok = await ads.spendEarned(r.funder, amount, { log: { kind: 'promo', note: 'Partner code ' + r.code + ' funded for ' + email + ' (day ' + n + ' of ' + promos.STEPS + ')' } });
if (!ok) { console.log('promo funder short', r.code, r.funder); return false; }
}
await ads.addEarned(email, amount, { log: { kind: 'promo', note: 'Partner code ' + r.code + ': ' + amount + ' of ' + r.credits + ' (day ' + n + ' of ' + promos.STEPS + ')' } });
return true;
});
}
// anti-fraud admin alert (Telegram admin chat, else email): who, which flags, and whether the sign-up was blocked
function fraudAlert(email, fc, spAcct, blocked) {
try {
@@ -1053,6 +1068,7 @@ const server = http.createServer(async (req, res) => {
const set = [];
set.push('iap.sponsor=' + tok + cookieTail); // last touch wins
const promo = promos.norm(u.searchParams.get('promo')); if (promo) set.push('iap.promo=' + promo + cookieTail); // partner code, redeemed at signup
if (promo) { const pref = String(req.headers.referer || '').replace(/^https?:\/\//, '').split('/')[0].toLowerCase().slice(0, 80); if (pref) set.push('iap.promoref=' + encodeURIComponent(pref) + cookieTail); } // which partner page it came from
if (ang) set.push('iap.angle=' + angle + cookieTail);
if (!cookies['iap.ref']) set.push('iap.ref=' + encodeURIComponent(coach.refHost(req.headers.referer)) + cookieTail); // first-touch source
return serveJoinPage(res, tok, ang ? angle : '', ang, set);
@@ -1305,7 +1321,11 @@ const server = http.createServer(async (req, res) => {
// partner promo code carried on the join link: redeem once per account (ignored if invalid/used)
try {
const pc = parseCookies(req)['iap.promo'];
if (pc) { const g = await promos.redeem(pc, e, 'link'); if (g.ok) { if (g.funder && g.funder !== e) { if (await ads.spendEarned(g.funder, g.credits, { log: { kind: 'promo', note: 'Partner code ' + g.code + ' funded for ' + e } })) { await ads.addEarned(e, g.credits, { log: { kind: 'promo', note: 'Partner code ' + g.code } }); console.log('promo redeemed (member-funded)', g.code, g.credits, e, 'by', g.funder); } else console.log('promo funder short', g.code, g.funder); } else { await ads.addEarned(e, g.credits, { log: { kind: 'promo', note: 'Partner code ' + g.code } }); console.log('promo redeemed', g.code, g.credits, e); } } }
if (pc) {
const pref = parseCookies(req)['iap.promoref'] ? decodeURIComponent(parseCookies(req)['iap.promoref']) : '';
const g = await promos.redeem(pc, e, 'link', pref);
if (g.ok) { const paid = await payPromo(e, todayCT()); console.log('promo redeemed', g.code, 'day 1:', paid.map(x => x.amount).join('+') || 0, 'of', g.credits, e, pref ? 'from ' + pref : ''); }
}
} catch (err) { console.error('promo redeem', err.message); }
// legacy bridge: a listed former Faucet Wave / Tier One Ads member gets welcome-back credits once
if (r.created && /^(fw|t1)-(adv|earn)$/.test(via)) {
@@ -1561,10 +1581,10 @@ const server = http.createServer(async (req, res) => {
const s = await auth.fromRequest(req);
if (!s || !s.email) return json(res, 401, { error: 'Sign in first.' });
const b = await readBody(req);
const g = await promos.redeem(b.code, s.email, 'dashboard');
const g = await promos.redeem(b.code, s.email, 'dashboard', '');
if (g.error) return json(res, 400, g);
await ads.addEarned(s.email, g.credits, { log: { kind: 'promo', note: 'Partner code ' + String(b.code || '').toUpperCase() } });
return json(res, 200, { ok: true, credits: g.credits, code: g.code, partner: g.partner });
const paid = await payPromo(s.email, todayCT());
return json(res, 200, { ok: true, credits: g.credits, now: paid.reduce((n, x) => n + x.amount, 0), step: g.step, steps: g.steps, code: g.code, partner: g.partner });
}
// -- admin: member card. GET ?q= resolves email / @username / #id / share code / wallet;
// PATCH edits username, sponsor, main wallet or grants credits; DELETE removes a free account (2026-09-13)
@@ -2085,6 +2105,7 @@ const server = http.createServer(async (req, res) => {
const s = await auth.fromRequest(req);
if (!s || !s.email) return json(res, 401, { error: 'Sign in first.' });
const r = await ads.claimDaily(s.email);
if (!r.error) { try { const paid = await payPromo(s.email, todayCT()); if (paid.length) r.promo = paid; } catch (e) { console.error('promo drip', e.message); } }
return json(res, r.error ? 400 : 200, r);
}
// -- downline lineage: 3 levels, usernames+IDs; email only for directs