Payment and missed-payment notices also land in the on-site inbox (login pop-up + Messages card), with who bought, share in POL and dollars, and the transaction link
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -85,7 +85,7 @@ FACTS:
|
|||||||
- PROMO TOOLKIT BY BADGE + AI COPY ENGINE (2026-09-14): Promo tools > AI Copy Engine shows the ladder: Free (links, posts, swipes, banners, wall, objections, shorts, badge pages), Spark (payouts on: one-tap campaign templates aimed at the member's link, printable handout with their QR at /handout/<username>), Surge (first qualifying buyer; the AI Copy Engine is live: posts, DMs, follow-ups, objection replies, emails, story posts, team broadcasts in the member's name with their link, honesty rules built in; 20 free generations a month), Circuit (60 free; Video Maker renders every promo video and short with the member's own end card and QR, hosted for download; split tester compares join angles by views, joins, buyers), Nexus (150 free; Leader Ops: three-level team triage with stalled flags and one-click nudges, AI-drafted team broadcasts, credit grants from the leader's earned pool to anyone in their line, co-branded join page showing the sponsor's bio, and the member's own partner code: welcome credits funded from the leader's pool at each redemption, plus a partner kit page /partners?ref=<username>&promo=CODE). After the free allowance each generation costs 10 ad credits from the earned pool. Credits are advertising, never money.
|
- PROMO TOOLKIT BY BADGE + AI COPY ENGINE (2026-09-14): Promo tools > AI Copy Engine shows the ladder: Free (links, posts, swipes, banners, wall, objections, shorts, badge pages), Spark (payouts on: one-tap campaign templates aimed at the member's link, printable handout with their QR at /handout/<username>), Surge (first qualifying buyer; the AI Copy Engine is live: posts, DMs, follow-ups, objection replies, emails, story posts, team broadcasts in the member's name with their link, honesty rules built in; 20 free generations a month), Circuit (60 free; Video Maker renders every promo video and short with the member's own end card and QR, hosted for download; split tester compares join angles by views, joins, buyers), Nexus (150 free; Leader Ops: three-level team triage with stalled flags and one-click nudges, AI-drafted team broadcasts, credit grants from the leader's earned pool to anyone in their line, co-branded join page showing the sponsor's bio, and the member's own partner code: welcome credits funded from the leader's pool at each redemption, plus a partner kit page /partners?ref=<username>&promo=CODE). After the free allowance each generation costs 10 ad credits from the earned pool. Credits are advertising, never money.
|
||||||
- MEMBER UPDATE EMAILS (2026-09-14, admin only): Admin > Releases > Email an update to members: pick release notes, add an intro, choose an audience (newsletter opt-ins, opt-ins active in the last 30 days, or everyone including those who declined), preview, send a test to the admin, then send; plain text from no-reply@instantadpay.com with an opt-out link; the log shows sent/opted out/failed.
|
- MEMBER UPDATE EMAILS (2026-09-14, admin only): Admin > Releases > Email an update to members: pick release notes, add an intro, choose an audience (newsletter opt-ins, opt-ins active in the last 30 days, or everyone including those who declined), preview, send a test to the admin, then send; plain text from no-reply@instantadpay.com with an opt-out link; the log shows sent/opted out/failed.
|
||||||
- LINKED POSITIONS AND BADGES (2026-09-14): qualifying buyers on a member's linked positions count toward their achievement badges (Spark/Surge/Circuit/Nexus) and light their chip on their sponsor's line, and a badge once earned never regresses. The contract still pays levels 2 and 3 to each position based on that position's OWN qualifying buyers, so the Qualifying buyers tile shows the main position's count with the linked positions' count underneath.
|
- LINKED POSITIONS AND BADGES (2026-09-14): qualifying buyers on a member's linked positions count toward their achievement badges (Spark/Surge/Circuit/Nexus) and light their chip on their sponsor's line, and a badge once earned never regresses. The contract still pays levels 2 and 3 to each position based on that position's OWN qualifying buyers, so the Qualifying buyers tile shows the main position's count with the linked positions' count underneath.
|
||||||
- MISSED-PAYOUT EMAIL (2026-09-15): when a level-2 or level-3 share passes a member by because that level is not open on their account, the member gets an email the same minute: who bought, the POL and dollar amount they missed, how many qualifying buyers they have versus the 2 or 5 needed, and the two ways to close the gap (bring buyers, or Qualified Start). Qualified members whose wallet rejected a transfer get a different email telling them to link a regular wallet.
|
- PAYMENT + MISSED-PAYMENT NOTICES (2026-09-15): every payout that lands (who bought, level, share in POL and dollars, transaction link) and every missed payout is delivered BOTH by email and as an on-site inbox message from the company account, shown in the login pop-up and the Messages card, so it is waiting when the member signs in. MISSED-PAYOUT detail: when a level-2 or level-3 share passes a member by because that level is not open on their account, the member gets an email the same minute: who bought, the POL and dollar amount they missed, how many qualifying buyers they have versus the 2 or 5 needed, and the two ways to close the gap (bring buyers, or Qualified Start). Qualified members whose wallet rejected a transfer get a different email telling them to link a regular wallet.
|
||||||
- LAUNCH WEEK SWIPES (2026-09-15): the founding-week checklist page (/launch) ends with four promoter emails members send to their OWN lists, one a day toward Monday's opening, with the member's invite link and the FOUNDER code (500 credits for anyone who joins before Mon 2026-09-21 9 AM Central) filled in, each with a Copy button. These are swipes for members, not emails the site sends.
|
- LAUNCH WEEK SWIPES (2026-09-15): the founding-week checklist page (/launch) ends with four promoter emails members send to their OWN lists, one a day toward Monday's opening, with the member's invite link and the FOUNDER code (500 credits for anyone who joins before Mon 2026-09-21 9 AM Central) filled in, each with a Copy button. These are swipes for members, not emails the site sends.
|
||||||
- LOGIN ADS CHARGE ONLY ON DAYS SHOWN (2026-09-15): the login ad daily fee (100 credits) is charged only for days the ad was actually shown at least once; with many login ads sharing the sign-ins, an ad that was not picked that day pays nothing. Featured links now count views (one per viewer per hour) as well as clicks.
|
- LOGIN ADS CHARGE ONLY ON DAYS SHOWN (2026-09-15): the login ad daily fee (100 credits) is charged only for days the ad was actually shown at least once; with many login ads sharing the sign-ins, an ad that was not picked that day pays nothing. Featured links now count views (one per viewer per hour) as well as clicks.
|
||||||
- SPONSOR HOLD (2026-09-14): if a member's account names a sponsor that cannot be paid right now (sponsor has not switched on payouts, or the chain lookup failed), the first purchase and the payouts activation are held with a message instead of silently crediting the company. Fix: the sponsor switches on payouts, or the member retries; the admin gets a Telegram alert.
|
- SPONSOR HOLD (2026-09-14): if a member's account names a sponsor that cannot be paid right now (sponsor has not switched on payouts, or the chain lookup failed), the first purchase and the payouts activation are held with a message instead of silently crediting the company. Fix: the sponsor switches on payouts, or the member retries; the admin gets a Telegram alert.
|
||||||
|
|||||||
@@ -599,12 +599,32 @@ async function sendWelcome(email, ref) {
|
|||||||
// on-chain event emails: a payout received, or a payout that passed you by
|
// on-chain event emails: a payout received, or a payout that passed you by
|
||||||
const weiToPol = w => { try { return (Number(BigInt(w) / (10n ** 14n)) / 10000).toString(); } catch (e) { return '?'; } };
|
const weiToPol = w => { try { return (Number(BigInt(w) / (10n ** 14n)) / 10000).toString(); } catch (e) { return '?'; } };
|
||||||
async function emailOnEvent(ev) {
|
async function emailOnEvent(ev) {
|
||||||
if (!mailer.hasKey() || !ev) return;
|
if (!ev) return;
|
||||||
const notify = async (memberId, subject, body) => {
|
const notify = async (memberId, subject, body) => {
|
||||||
if (!memberId) return;
|
if (!memberId || !mailer.hasKey()) return;
|
||||||
const a = await accounts.byMemberId(memberId);
|
const a = await accounts.byMemberId(memberId);
|
||||||
if (a && a.email) mailer.send(a.email, subject, body + '\n\nSee it on the live ledger: https://instantadpay.com/ledger\n\nInstantAdPay').catch(() => {});
|
if (a && a.email) mailer.send(a.email, subject, body + '\n\nSee it on the live ledger: https://instantadpay.com/ledger\n\nInstantAdPay').catch(() => {});
|
||||||
};
|
};
|
||||||
|
// Marty (2026-09-15): payment and missed-payment notices also land in the on-site inbox, so
|
||||||
|
// they are waiting (login modal + Messages card) whether or not the email was read. Sent as
|
||||||
|
// the company account (#1), the same sender the credit-return notes used. Plain text in, HTML out.
|
||||||
|
const inboxNote = async (memberId, subject, text) => {
|
||||||
|
if (!memberId) return;
|
||||||
|
try {
|
||||||
|
const a = await accounts.byMemberId(memberId);
|
||||||
|
if (!a || !a.email) return;
|
||||||
|
const html = '<p>' + String(text).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
||||||
|
.replace(/(https:\/\/[^\s]+)/g, '<a href="$1" target="_blank" rel="noopener">$1</a>').split('\n\n').join('</p><p>').replace(/\n/g, '<br>') + '</p>';
|
||||||
|
await messages.deliver(1, ADMIN_EMAIL || 'house@instantadpay.com', [a.email], subject, html);
|
||||||
|
} catch (e) {}
|
||||||
|
};
|
||||||
|
const tell = async (memberId, subject, text) => { await notify(memberId, subject, text); await inboxNote(memberId, subject, text); };
|
||||||
|
const PCT = { 1: 50, 2: 20, 3: 10 };
|
||||||
|
// the Purchase event of the same tx is already indexed (it precedes every payout log), so the
|
||||||
|
// dollar side of any share is that purchase's price times the tier percentage
|
||||||
|
const purchaseOf = tx => { try { return chain.recentEvents(600).find(e => e.tx === tx && e.type === 'Purchase'); } catch (e) { return null; } };
|
||||||
|
const usdShare = (pur, pct) => pur ? (' (about ' + ('$' + (pur.priceCents * pct / 10000).toFixed(2)).replace(/\.00$/, '') + ')') : '';
|
||||||
|
const txUrlOf = tx => { const cc = chain.getConfig(); return (cc.explorer ? cc.explorer.replace(/\/+$/, '') : 'https://polygonscan.com') + '/tx/' + tx; };
|
||||||
if (ev.type === 'Purchase') {
|
if (ev.type === 'Purchase') {
|
||||||
const cc = chain.getConfig();
|
const cc = chain.getConfig();
|
||||||
const txUrl = (cc.explorer ? cc.explorer.replace(/\/+$/, '') : 'https://polygonscan.com') + '/tx/' + ev.tx;
|
const txUrl = (cc.explorer ? cc.explorer.replace(/\/+$/, '') : 'https://polygonscan.com') + '/tx/' + ev.tx;
|
||||||
@@ -635,36 +655,35 @@ async function emailOnEvent(ev) {
|
|||||||
}
|
}
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
else if (ev.type === 'TierPaid') await notify(ev.recipientId, 'You just got paid on InstantAdPay', 'A level-' + ev.tier + ' payout of ' + weiToPol(ev.amountWei) + ' POL just landed in your wallet.');
|
else if (ev.type === 'TierPaid') {
|
||||||
else if (ev.type === 'AwardPaid') await notify(ev.toId, 'You just got paid on InstantAdPay', weiToPol(ev.amountWei) + ' POL just landed in your wallet.');
|
const pct = PCT[ev.tier] || 0; const pur = purchaseOf(ev.tx);
|
||||||
|
let buyer = 'a member on your level ' + ev.tier; try { buyer = await memberLabel(ev.buyerId); } catch (e) {}
|
||||||
|
const pol = weiToPol(ev.amountWei);
|
||||||
|
await tell(ev.recipientId, 'You just got paid ' + pol + ' POL on InstantAdPay',
|
||||||
|
buyer + ' just bought an ad package on your level ' + ev.tier + (pur ? ' ($' + (pur.priceCents / 100).toFixed(2).replace(/\.00$/, '') + ')' : '') + '. Your ' + pct + ' percent share, ' + pol + ' POL' + usdShare(pur, pct) + ', landed in your wallet in the same transaction' + (ev.hops ? ', passed up to you because someone between you was not qualified for this level' : '') + '.\n\n' +
|
||||||
|
'Transaction: ' + txUrlOf(ev.tx) + '\n\nYour dashboard: https://instantadpay.com/my');
|
||||||
|
}
|
||||||
|
else if (ev.type === 'AwardPaid') await tell(ev.toId, 'You just got paid ' + weiToPol(ev.amountWei) + ' POL on InstantAdPay', weiToPol(ev.amountWei) + ' POL just landed in your wallet.\n\nTransaction: ' + txUrlOf(ev.tx) + '\n\nYour dashboard: https://instantadpay.com/my');
|
||||||
else if (ev.type === 'PassedUp' && ev.reason === 'send-failed') {
|
else if (ev.type === 'PassedUp' && ev.reason === 'send-failed') {
|
||||||
// the member WAS qualified but their wallet rejected the POL (usually a smart-contract
|
// the member WAS qualified but their wallet rejected the POL (usually a smart-contract
|
||||||
// wallet that needs more than the capped gas): tell them and the admin, loudly
|
// wallet that needs more than the capped gas): tell them and the admin, loudly
|
||||||
await notify(ev.skippedId, 'Your wallet rejected a payout on InstantAdPay', 'A level-' + ev.tier + ' payout tried to reach your linked wallet and the wallet refused the transfer, so it passed to the next qualified member. This happens with some smart-contract wallets. Link a regular wallet address (MetaMask, SafePal, Phantom) on the Wallet tab so the next payout lands.');
|
await tell(ev.skippedId, 'Your wallet rejected a payout on InstantAdPay', 'A level-' + ev.tier + ' payout tried to reach your linked wallet and the wallet refused the transfer, so it passed to the next qualified member. This happens with some smart-contract wallets. Link a regular wallet address (MetaMask, SafePal, Phantom) on the Wallet tab so the next payout lands.\n\nTransaction: ' + txUrlOf(ev.tx));
|
||||||
if (ADMIN_EMAIL) mailer.send(ADMIN_EMAIL, 'InstantAdPay: payout send-failed for member #' + ev.skippedId, 'A level-' + ev.tier + ' payout to member #' + ev.skippedId + ' failed at the wallet (send-failed) and passed up. Tx: ' + ev.tx + '\n\nThe member has been emailed to link a regular wallet.').catch(() => {});
|
if (ADMIN_EMAIL) mailer.send(ADMIN_EMAIL, 'InstantAdPay: payout send-failed for member #' + ev.skippedId, 'A level-' + ev.tier + ' payout to member #' + ev.skippedId + ' failed at the wallet (send-failed) and passed up. Tx: ' + ev.tx + '\n\nThe member has been emailed to link a regular wallet.').catch(() => {});
|
||||||
}
|
}
|
||||||
else if (ev.type === 'PassedUp') {
|
else if (ev.type === 'PassedUp') {
|
||||||
// Marty (2026-09-15): a member who missed a payout because they were not qualified must be told
|
// Marty (2026-09-15): a member who missed a payout because they were not qualified must be told
|
||||||
// exactly what they missed. The Purchase event of the same tx is already indexed (it precedes
|
// exactly what they missed: who bought, the share in POL and dollars, and how to close the gap.
|
||||||
// the PassedUp log), so the amount is that purchase's share for the tier: 50 / 20 / 10 percent.
|
const pct = PCT[ev.tier] || 0; const pur = purchaseOf(ev.tx);
|
||||||
const PCT = { 1: 50, 2: 20, 3: 10 }; const pct = PCT[ev.tier] || 0;
|
let pol = '', buyer = 'a member on your level ' + ev.tier;
|
||||||
let pol = '', usd = '', buyer = 'a member on your level ' + ev.tier;
|
try { if (pur) pol = weiToPol((BigInt(pur.paidWei) * BigInt(pct) / 100n).toString()); buyer = await memberLabel(ev.buyerId); } catch (e) {}
|
||||||
try {
|
|
||||||
const pur = chain.recentEvents(600).find(e => e.tx === ev.tx && e.type === 'Purchase');
|
|
||||||
if (pur) {
|
|
||||||
pol = weiToPol((BigInt(pur.paidWei) * BigInt(pct) / 100n).toString());
|
|
||||||
usd = ('$' + (pur.priceCents * pct / 10000).toFixed(2)).replace(/\.00$/, '');
|
|
||||||
}
|
|
||||||
buyer = await memberLabel(ev.buyerId);
|
|
||||||
} catch (e) {}
|
|
||||||
let bc = 0; try { bc = (await chain.member(ev.skippedId)).buyerCount || 0; } catch (e) {}
|
let bc = 0; try { bc = (await chain.member(ev.skippedId)).buyerCount || 0; } catch (e) {}
|
||||||
const need = ev.tier === 3 ? 5 : 2, short = Math.max(0, need - bc);
|
const need = ev.tier === 3 ? 5 : 2, short = Math.max(0, need - bc);
|
||||||
const what = pol ? pol + ' POL (about ' + usd + ')' : 'a level-' + ev.tier + ' payout';
|
const what = pol ? pol + ' POL' + usdShare(pur, pct) : 'a level-' + ev.tier + ' payout';
|
||||||
await notify(ev.skippedId, 'You missed ' + (pol ? pol + ' POL' : 'a payout') + ' on InstantAdPay',
|
await tell(ev.skippedId, 'You missed ' + (pol ? pol + ' POL' : 'a payout') + ' on InstantAdPay',
|
||||||
buyer + ' just bought an ad package on your level ' + ev.tier + '. Your share was ' + what + ', and it passed you by because level ' + ev.tier + ' is not open on your account yet. The contract paid it to the next qualified person above you.\n\n' +
|
buyer + ' just bought an ad package on your level ' + ev.tier + '. Your share was ' + what + ', and it passed you by because level ' + ev.tier + ' is not open on your account yet. The contract paid it to the next qualified person above you.\n\n' +
|
||||||
'Level ' + ev.tier + ' opens at ' + need + ' qualifying buyers (people you referred who bought a $20 or larger package). You have ' + bc + (short ? ', so you are ' + short + ' buyer' + (short === 1 ? '' : 's') + ' away.' : '.') + '\n\n' +
|
'Level ' + ev.tier + ' opens at ' + need + ' qualifying buyers (people you referred who bought a $20 or larger package). You have ' + bc + (short ? ', so you are ' + short + ' buyer' + (short === 1 ? '' : 's') + ' away.' : '.') + '\n\n' +
|
||||||
'Two ways to close the gap: bring ' + (short || 1) + ' more buyer' + (short === 1 ? '' : 's') + ' from your My line page, or use Qualified Start under Buy packages to be your own buyer today. Every package on level ' + ev.tier + ' pays you ' + pct + ' percent once it is open, and the next one is coming whether you are ready or not.\n\n' +
|
'Two ways to close the gap: bring ' + (short || 1) + ' more buyer' + (short === 1 ? '' : 's') + ' from your My line page, or use Qualified Start under Buy packages to be your own buyer today. Every package on level ' + ev.tier + ' pays you ' + pct + ' percent once it is open, and the next one is coming whether you are ready or not.\n\n' +
|
||||||
'Your dashboard: https://instantadpay.com/my');
|
'Transaction: ' + txUrlOf(ev.tx) + '\n\nYour dashboard: https://instantadpay.com/my');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// payment-proof Telegram feed (same pattern as the RM Circle proof channel): one compact
|
// payment-proof Telegram feed (same pattern as the RM Circle proof channel): one compact
|
||||||
|
|||||||
Reference in New Issue
Block a user