MoonPay card on-ramp for crypto-new members (zero custody)

- GET /api/public/moonpay-url: returns a MoonPay checkout link for POL on
  Polygon (pol_polygon). With partner keys in config (moonpayPublicKey/
  moonpaySecretKey, now PATCHable), the URL is HMAC-signed and prefilled
  with the member's own wallet address + shortfall amount; without keys it
  falls back to MoonPay's generic buy page. MoonPay is merchant of record -
  the site never touches funds.
- Join page: when the connected wallet can't cover the Premium entry, a
  funding box appears with the exact shortfall, a buy button (new tab), and
  a refresh-balance button.
- Training page: "buy POL with a card" callout after the funding video with
  the three things to get right (POL, Polygon network, own address).
- Start page: one-line card-buy pointer under the sponsor card.
- Chatbot canned answer + AI system prompt updated (funding guidance).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-08-19 09:44:01 -05:00
parent f0a35a0127
commit d620555209
6 changed files with 52 additions and 4 deletions
+1 -1
View File
@@ -21,7 +21,7 @@
{k:['metamask','wallet','create wallet','install','set up wallet','setup wallet'],
a:()=>`Install MetaMask only from the <a href="https://metamask.io/" target="_blank" rel="noopener noreferrer">official site</a> or your device's official app store, then create your wallet. <a href="/training">Video 2 in the training</a> walks through it step by step. Never share your Secret Recovery Phrase with anyone — not the team, not this site, not a "support agent".`},
{k:['fund','funding','buy pol','get pol','deposit','exchange','transfer','send pol'],
a:()=>`You'll need about ${pol()} POL plus a little extra for gas, on Polygon Mainnet. <a href="/training">Video 3 in the training</a> shows how to fund your wallet. Double-check you're sending POL on the Polygon network — not another chain.`},
a:()=>`You'll need about ${pol()} POL plus a little extra for gas, on Polygon Mainnet. <a href="/training">Video 3 in the training</a> shows how to fund your wallet. Brand new to crypto? You can buy POL directly with a debit/credit card, Apple Pay, or Google Pay via <a href="https://www.moonpay.com/buy/pol" target="_blank" rel="noopener noreferrer">MoonPay</a> — pick POL on the Polygon network and send it to your own wallet address. The join page also offers this automatically if your connected wallet is short. Double-check you're getting POL on Polygon — not another chain.`},
{k:['join','buy','purchase','premium position','enter','sign up','signup','register','get started','start'],
a:()=>`Ready to join? Go to the <a href="/start">Getting Started page</a> — it shows the current team sponsor and lets you connect your wallet and buy your position right on the site, with the sponsor ID filled in automatically. <a href="/training">Video 4 in the training</a> walks through the whole process (and Video 5 covers the dApp backup method).`},
{k:['sponsor','current sponsor','who do i join under','placement','whose link','referral'],
+22 -1
View File
@@ -57,7 +57,26 @@
}catch(e){ $('sponsorLine').textContent='Could not load the current sponsor — please refresh.'; }
}
async function refreshBalance(){ try{ var b=await req('eth_getBalance',[account,'latest']); $('bal').textContent=polStr(BigInt(b))+' POL'; }catch(e){} }
var lastBal=null;
async function refreshBalance(){ try{ var b=await req('eth_getBalance',[account,'latest']); lastBal=BigInt(b); $('bal').textContent=polStr(lastBal)+' POL'; updateFundBox(); }catch(e){} }
// Card on-ramp: shown only when the connected wallet can't cover the entry.
function updateFundBox(){
var box=$('fundBox'); if(!box||!costs||lastBal===null) return;
var need=regValue();
if(lastBal>=need){ box.style.display='none'; return; }
var shortPol=Math.max(62,Math.ceil(Number(need-lastBal)/1e18)+5);
$('fundMsg').textContent='This wallet holds '+polStr(lastBal)+' POL; the Premium entry is '+polStr(need)+' POL plus a little gas. You can buy the remaining ~'+shortPol+' POL with a debit/credit card, Apple Pay, or Google Pay — delivered straight to this wallet.';
box.dataset.pol=shortPol;
box.style.display='block';
}
async function openMoonpay(){
try{
var box=$('fundBox');
var r=await (await fetch('/api/public/moonpay-url?address='+encodeURIComponent(account||'')+'&pol='+encodeURIComponent((box&&box.dataset.pol)||''))).json();
if(r&&r.url){ window.open(r.url,'_blank','noopener'); log(r.signed?'MoonPay opened in a new tab with your wallet address pre-filled — complete the purchase there, then come back and refresh your balance.':'MoonPay opened in a new tab. Choose POL on the Polygon network and paste YOUR wallet address ('+account+') as the destination, then come back and refresh your balance.','ok'); }
}catch(e){ log('Could not open MoonPay: '+(e.message||e),'err'); }
}
async function connect(){
try{
@@ -133,5 +152,7 @@
loadSponsor();
$('connectBtn').addEventListener('click',connect);
$('joinBtn').addEventListener('click',doJoin);
var fb=$('fundBtn'); if(fb)fb.addEventListener('click',openMoonpay);
var fr=$('fundRefresh'); if(fr)fr.addEventListener('click',function(){ log('Checking your balance…'); refreshBalance(); });
});
})();
+1 -1
View File
@@ -5,7 +5,7 @@
<body>
<header class="wrap nav"><a class="brand" href="/"><div class="brand-mark">RM</div><span><span id="brandName">RM Circle</span><small>Getting Started</small></span></a><div class="nav-actions"><a class="btn btn-secondary" href="/">← Strategy</a><a class="btn btn-secondary" href="/training">Training</a></div></header>
<main class="sponsor-shell"><div class="wrap"><div class="section-head"><div class="eyebrow">Current placement</div><h1 style="font-size:clamp(34px,5vw,54px);margin:8px 0 10px">Use the sponsor shown on <span class="gold">this page.</span></h1><p>Team placements rotate as positions become qualified. Do not use an old screenshot or saved referral link.</p></div>
<div class="sponsor-grid"><aside class="sponsor-card"><span class="live-badge"><span class="dot"></span> Current team sponsor</span><div class="sponsor-id" id="sponsorId">—</div><div class="sponsor-name" id="sponsorName">Loading current placement…</div><div id="progressArea"><div style="display:flex;justify-content:space-between;color:var(--muted);font-size:13px"><span>Qualification progress</span><strong id="progressText">0 / 2</strong></div><div class="progress-line"><span id="progressBar" style="width:0%"></span></div></div><div class="facts"><div class="fact"><small>Program</small><strong>Premium</strong></div><div class="fact"><small>Entry</small><strong><span id="entryPol">362</span> POL</strong></div><div class="fact"><small>Network</small><strong>Polygon</strong></div><div class="fact"><small>Gas token</small><strong>POL</strong></div></div><a id="joinButton" class="btn btn-primary" style="width:100%" href="/join-now">Join With Current Sponsor →</a><button id="copyButton" class="btn btn-secondary" style="width:100%;margin-top:9px">Copy Sponsor ID</button><p class="micro" id="queueText" style="text-align:center"></p><a id="dappJoinLink" class="micro hidden" href="#" target="_blank" rel="noopener noreferrer" style="display:block;text-align:center;margin-top:4px">Prefer the official RM Circle dApp? Join there →</a></aside>
<div class="sponsor-grid"><aside class="sponsor-card"><span class="live-badge"><span class="dot"></span> Current team sponsor</span><div class="sponsor-id" id="sponsorId">—</div><div class="sponsor-name" id="sponsorName">Loading current placement…</div><div id="progressArea"><div style="display:flex;justify-content:space-between;color:var(--muted);font-size:13px"><span>Qualification progress</span><strong id="progressText">0 / 2</strong></div><div class="progress-line"><span id="progressBar" style="width:0%"></span></div></div><div class="facts"><div class="fact"><small>Program</small><strong>Premium</strong></div><div class="fact"><small>Entry</small><strong><span id="entryPol">362</span> POL</strong></div><div class="fact"><small>Network</small><strong>Polygon</strong></div><div class="fact"><small>Gas token</small><strong>POL</strong></div></div><a id="joinButton" class="btn btn-primary" style="width:100%" href="/join-now">Join With Current Sponsor →</a><button id="copyButton" class="btn btn-secondary" style="width:100%;margin-top:9px">Copy Sponsor ID</button><p class="micro" style="text-align:center;margin-top:9px">💳 No crypto yet? You can <a href="https://www.moonpay.com/buy/pol" target="_blank" rel="noopener nofollow" style="color:var(--teal)">buy POL with a card via MoonPay</a> — sent to your own wallet. How-to in <a href="/training" style="color:var(--teal)">the training</a>.</p><p class="micro" id="queueText" style="text-align:center"></p><a id="dappJoinLink" class="micro hidden" href="#" target="_blank" rel="noopener noreferrer" style="display:block;text-align:center;margin-top:4px">Prefer the official RM Circle dApp? Join there →</a></aside>
<section><div class="callout" style="margin-bottom:16px"><strong>First time?</strong> <a href="/training" style="color:var(--teal)">Watch the four training videos</a> — how the build works, creating your MetaMask wallet, funding it, and buying your position.</div><div class="instruction-list"><article class="instruction"><div class="num">1</div><div><h3>Install or open a wallet</h3><p>Use <strong>MetaMask</strong> or <strong>Trust Wallet</strong> — only from the official website or your device's official app store. Never install a wallet from a link sent by a stranger.</p><div style="margin-top:10px"><a class="btn btn-secondary btn-sm" href="https://metamask.io/" target="_blank" rel="noopener noreferrer">Official MetaMask Site ↗</a></div></div></article>
<article class="instruction"><div class="num">2</div><div><h3>Use Polygon Mainnet</h3><p>In MetaMask, select Polygon. Polygon Mainnet uses chain ID <strong>137</strong>, and POL is the native gas token.</p></div></article>
<article class="instruction"><div class="num">3</div><div><h3>Fund your wallet with POL</h3><p>Premium entry is <strong><span id="entryPol2">362</span> POL</strong>. Keep a small additional POL balance available for network gas. POL's USD value changes with the market.</p></div></article>
+2
View File
@@ -14,6 +14,8 @@
<div class="video-card" style="max-width:880px;margin:0 auto 26px"><div style="padding:6px 6px 0"><div class="eyebrow">Video 3 · 3 min</div><h2 style="margin:6px 0 6px">Funding your wallet</h2><p style="color:var(--muted);margin:0 0 12px">Get POL onto Polygon Mainnet — enough for the 362 POL Premium entry plus a little extra for gas.</p></div><video class="video-frame" style="display:block;width:100%;height:auto;aspect-ratio:16/9" controls preload="metadata" poster="/training/funding_your_wallet_poster.jpg" src="/training/funding_your_wallet.mp4"></video></div>
<div class="callout" style="max-width:880px;margin:0 auto 26px"><strong>💳 Brand new to crypto? You can buy POL with a card.</strong> If you don't have crypto anywhere yet, the simplest path is <a href="https://www.moonpay.com/buy/pol" target="_blank" rel="noopener nofollow">MoonPay</a> — buy with a debit/credit card, Apple Pay, or Google Pay. Three things to get right: choose <strong>POL on the Polygon network</strong>, paste <strong>your own wallet address</strong> from Video 2 as the destination, and buy about <strong>385 POL</strong> (the 362 entry plus a little gas). The crypto is delivered straight to your wallet — this site never touches your money. MoonPay handles ID verification and charges its own card fee (~4.5%); a first purchase can take a few minutes to arrive. The join page will also offer this automatically if your connected wallet is short.</div>
<div class="video-card" style="max-width:880px;margin:0 auto 26px"><div style="padding:6px 6px 0"><div class="eyebrow">Video 4 · 4 min · the new way to join</div><h2 style="margin:6px 0 6px">Joining right on the site — connect wallet &amp; buy your position</h2><p style="color:var(--muted);margin:0 0 12px">The whole join process, start to finish: open your invite link (or <a href="/start" style="color:var(--teal)">the start page</a>), connect your wallet, and buy your Premium position in one guided flow — the sponsor ID is filled in for you automatically.</p></div><video class="video-frame" style="display:block;width:100%;height:auto;aspect-ratio:16/9" controls preload="metadata" poster="/training/new_integrated_join_process_poster.jpg" src="/training/new_integrated_join_process.mp4"></video></div>
<div class="video-card" style="max-width:880px;margin:0 auto 26px"><div style="padding:6px 6px 0"><div class="eyebrow">Video 5 · 5 min · backup method</div><h2 style="margin:6px 0 6px">Buying the Premium position in the dApp</h2><p style="color:var(--muted);margin:0 0 12px">If the on-site wallet connect isn't an option for you, you can still join the original way: connect to the RM Circle dApp with the current team sponsor and complete your Premium entry there. Double-check the sponsor ID matches the start page before signing.</p></div><video class="video-frame" style="display:block;width:100%;height:auto;aspect-ratio:16/9" controls preload="metadata" poster="/training/buying_the_premium_position_poster.jpg" src="/training/buying_the_premium_position.mp4"></video></div>