Hand the sponsor to PolHunter, and put its payouts in the P&L
PolHunter now pays a bounty when a hunter brings someone who turns up and hunts, and the person who gets it has to be the one that member actually joined under here. The sign-in payload carries sponsorRef and the join date across so PolHunter is never guessing from a cookie. Its payouts are real POL from its own faucet and none of it passes through the ad contract, so the P&L could not derive it. It now reads the figures across, cached and fail-soft, and shows finds, bounties and matches as their own card. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -371,6 +371,23 @@ async function imageCheck(url) {
|
||||
// A video campaign's source has to actually exist. The create form only matched the URL
|
||||
// shape, so a placeholder (yourdomain.com/....mp4) went live on 2026-09-11 and sat there for
|
||||
// eleven days handing every Tier 1 viewer a black player they could never finish.
|
||||
// PolHunter pays real POL out of its own faucet and none of it passes through this contract, so
|
||||
// the P&L cannot derive it from chain events and has to read it across (Marty, 2026-09-23). Cached
|
||||
// and fail-soft: the P&L page must never hang or break because the other site is down.
|
||||
let huntCache = { at: 0, data: null };
|
||||
async function huntLedger() {
|
||||
if (Date.now() - huntCache.at < 120000) return huntCache.data;
|
||||
const base = String(process.env.HUNT_URL || 'https://polhunter.com').replace(/\/+$/, '');
|
||||
try {
|
||||
const r = await fetch(base + '/api/ledger', { signal: AbortSignal.timeout(6000) });
|
||||
if (!r.ok) throw new Error('HTTP ' + r.status);
|
||||
const j = await r.json();
|
||||
huntCache = { at: Date.now(), data: { totals: j.totals || null, referrals: j.referrals || null, pools: j.pools || null, at: Date.now() } };
|
||||
} catch (e) {
|
||||
huntCache = { at: Date.now(), data: huntCache.data || { error: e.message } };
|
||||
}
|
||||
return huntCache.data;
|
||||
}
|
||||
async function videoCheck(url) {
|
||||
const u = String(url || '').trim();
|
||||
if (/^\/uploads\//.test(u)) return { ok: true }; // uploaded here, already on our disk
|
||||
@@ -2223,7 +2240,11 @@ const server = http.createServer(async (req, res) => {
|
||||
const memberId = (acct && acct.memberId) || s.memberId || 0;
|
||||
if (!memberId) { res.writeHead(302, { Location: '/my#wallet' }); return res.end(); }
|
||||
const b64u = b => Buffer.from(b).toString('base64').replace(/=+$/, '').replace(/\+/g, '-').replace(/\//g, '_');
|
||||
const payload = JSON.stringify({ iat: Date.now(), exp: Date.now() + 5 * 60000, nonce: crypto.randomBytes(12).toString('hex'), memberId: Number(memberId), email: s.email, wallet: (acct && acct.address) || s.address || null, username: (acct && acct.username) || null });
|
||||
// the sponsor rides across too (Marty, 2026-09-23): PolHunter pays a referral bounty, and the
|
||||
// person who gets it has to be the one this member actually joined under here, not a cookie
|
||||
// PolHunter guessed at. sponsorRef is the upline's username or code, matching /join/<ref>.
|
||||
let sponsorRef = (acct && acct.sponsorRef) || '';
|
||||
const payload = JSON.stringify({ iat: Date.now(), exp: Date.now() + 5 * 60000, nonce: crypto.randomBytes(12).toString('hex'), memberId: Number(memberId), email: s.email, wallet: (acct && acct.address) || s.address || null, username: (acct && acct.username) || null, sponsorRef: sponsorRef || null, joinedAt: (acct && acct.created) || null });
|
||||
const tok = b64u(payload) + '.' + b64u(crypto.createHmac('sha256', secret).update(payload).digest());
|
||||
res.writeHead(302, { Location: huntUrl + '/auth?t=' + tok, 'Cache-Control': 'no-store', 'Set-Cookie': 'iap.return=; Path=/; SameSite=Lax; Max-Age=0' + (IS_PROD ? '; Secure' : '') }); return res.end(); // the loop is closed
|
||||
}
|
||||
@@ -3136,6 +3157,7 @@ const server = http.createServer(async (req, res) => {
|
||||
// wallets' live balances and an admin-entered fixed monthly cost
|
||||
if (p === '/api/admin/pnl' && req.method === 'GET') {
|
||||
if (!isAdmin(req)) return json(res, 401, { error: 'auth' });
|
||||
const hunt = await huntLedger(); // marketing spend that never touches this contract
|
||||
const days = Math.max(0, Number(u.searchParams.get('days') || 30));
|
||||
let latest = 0; try { latest = parseInt(await chain.rpc('eth_blockNumber', []), 16); } catch (e) {}
|
||||
const fromBlock = days ? latest - Math.round(days * 43200) : 0;
|
||||
@@ -3157,7 +3179,7 @@ const server = http.createServer(async (req, res) => {
|
||||
purchases: { count: purchases.length, volumeWei: sum(purchases, e => e.paidWei).toString(), usdCents: purchases.reduce((n, e) => n + (e.priceCents || 0), 0), byPackage: byPkg },
|
||||
platformWei: sum(admin, e => e.amountWei).toString(), memberPayoutsWei: sum(tier, e => e.amountWei).toString(), byTier,
|
||||
passedUp: { count: passed.length, unqualified: passed.filter(e => e.reason === 'unqualified').length, sendFailed: passed.filter(e => e.reason === 'send-failed').length },
|
||||
wallets, balances, fixedMonthlyUsd: Number(siteConfig().pnlFixedMonthlyUsd) || 0, burner: burner.status() });
|
||||
wallets, balances, fixedMonthlyUsd: Number(siteConfig().pnlFixedMonthlyUsd) || 0, burner: burner.status(), polhunter: hunt });
|
||||
}
|
||||
if (p === '/api/admin/burner' && req.method === 'GET') {
|
||||
if (!isAdmin(req)) return json(res, 401, { error: 'auth' });
|
||||
|
||||
Reference in New Issue
Block a user