Spaces:
Runtime error
Runtime error
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"/> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"/> | |
| <title>SafeAIScan — Upgrade to Pro</title> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link href="https://fonts.googleapis.com/css2?family=Syne:wght@700;800&family=DM+Sans:wght@400;500;600&family=JetBrains+Mono:wght@400&display=swap" rel="stylesheet"> | |
| <link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.css" rel="stylesheet"> | |
| <link rel="stylesheet" href="styles.css"> | |
| <style> | |
| :root{--sg:#00ffa3;--pp-gold:#ffd140;--pp-blue:#009cde;} | |
| body{min-height:100vh;display:flex;flex-direction:column;background:var(--bg-base);} | |
| .co-nav{display:flex;align-items:center;justify-content:space-between;padding:0 28px;height:56px;background:rgba(6,11,20,.9);backdrop-filter:blur(20px);border-bottom:1px solid var(--border);position:sticky;top:0;z-index:100;} | |
| .co-brand{font-family:'Syne',sans-serif;font-weight:800;font-size:17px;letter-spacing:-.04em;text-decoration:none;color:var(--text-primary);display:flex;align-items:center;gap:8px;} | |
| .bico{width:28px;height:28px;border-radius:7px;background:linear-gradient(135deg,#5b7bfe,var(--sg));display:flex;align-items:center;justify-content:center;font-size:13px;} | |
| .co-body{flex:1;display:grid;grid-template-columns:1fr 360px;gap:40px;max-width:920px;margin:0 auto;width:100%;padding:40px 24px;align-items:start;} | |
| @media(max-width:768px){ | |
| .co-body{grid-template-columns:1fr;padding:20px 16px;gap:20px;} | |
| .order-col{order:-1;position:static;} | |
| .co-nav{padding:0 16px;height:52px;} | |
| .co-nav > div{font-size:11px;} | |
| .method-tabs{flex-direction:column;} | |
| .method-tab{justify-content:flex-start;padding:10px 12px;} | |
| .verify-wrap{flex-direction:column;} | |
| .verify-wrap button{width:100%;} | |
| .manual-step{font-size:12px;} | |
| .pp-btn{font-size:14px;padding:13px;} | |
| .trust-row{gap:10px;} | |
| } | |
| @media(max-width:420px){ | |
| .co-body{padding:14px 12px;} | |
| .order-col{padding:16px;} | |
| .legal-title, .order-title{font-size:13px;} | |
| } | |
| .order-col{background:var(--bg-2);border:1px solid var(--border);border-radius:18px;padding:22px;position:sticky;top:76px;} | |
| .order-title{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.8px;color:var(--text-muted);margin-bottom:16px;} | |
| .billing-toggle{display:inline-flex;background:var(--bg-1);border:1px solid var(--border);border-radius:99px;padding:3px;margin-bottom:24px;gap:2px;} | |
| .btog-opt{padding:7px 18px;border-radius:99px;font-size:12px;font-weight:500;cursor:pointer;color:var(--text-muted);border:none;background:transparent;transition:all .2s;position:relative;font-family:'DM Sans',sans-serif;} | |
| .btog-opt.active{background:var(--bg-3);color:var(--text-primary);box-shadow:0 2px 8px rgba(0,0,0,.4);} | |
| .save-pip{position:absolute;top:-8px;right:-4px;background:var(--sg);color:#021;font-size:8px;font-weight:700;padding:1px 6px;border-radius:99px;letter-spacing:.4px;white-space:nowrap;} | |
| .plan-row{display:flex;align-items:center;gap:12px;padding-bottom:14px;border-bottom:1px solid var(--border);margin-bottom:14px;} | |
| .plan-ico{width:42px;height:42px;border-radius:11px;flex-shrink:0;background:linear-gradient(135deg,rgba(91,123,254,.2),rgba(0,255,163,.1));border:1px solid rgba(91,123,254,.3);display:flex;align-items:center;justify-content:center;font-size:19px;color:var(--accent);} | |
| .plan-name{font-family:'Syne',sans-serif;font-size:15px;font-weight:800;} | |
| .plan-desc{font-size:11px;color:var(--text-muted);margin-top:2px;} | |
| .pline{display:flex;justify-content:space-between;font-size:13px;color:var(--text-muted);padding:4px 0;} | |
| .pline.total{font-size:15px;font-weight:700;color:var(--text-primary);padding-top:10px;border-top:1px solid var(--border);margin-top:4px;} | |
| .feat-list{margin-top:14px;padding-top:14px;border-top:1px solid var(--border);display:flex;flex-direction:column;gap:7px;} | |
| .feat-li{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--text-muted);} | |
| .feat-li i{color:var(--sg);font-size:12px;flex-shrink:0;} | |
| .method-tabs{display:flex;gap:5px;margin-bottom:20px;background:var(--bg-1);border:1px solid var(--border);border-radius:12px;padding:4px;} | |
| .method-tab{flex:1;padding:9px 6px;border-radius:8px;border:none;background:transparent;color:var(--text-muted);font-size:12px;font-weight:500;cursor:pointer;transition:all .2s;display:flex;align-items:center;justify-content:center;gap:5px;font-family:'DM Sans',sans-serif;} | |
| .method-tab.active{background:var(--bg-3);color:var(--text-primary);box-shadow:0 2px 8px rgba(0,0,0,.4);} | |
| .method-panel{display:none;} | |
| .method-panel.active{display:block;animation:panIn .2s ease both;} | |
| @keyframes panIn{from{opacity:0;transform:translateY(5px)}to{opacity:1;transform:translateY(0)}} | |
| .inp-g{margin-bottom:14px;} | |
| .inp-lbl{font-size:11px;color:var(--text-muted);text-transform:uppercase;letter-spacing:.7px;margin-bottom:5px;font-weight:500;display:block;} | |
| .inp-w{position:relative;} | |
| .inp-ic{position:absolute;left:12px;top:50%;transform:translateY(-50%);color:var(--text-faint);font-size:14px;pointer-events:none;} | |
| .inp-w .form-control{padding-left:36px!important;} | |
| .pp-btn{width:100%;padding:15px;border-radius:12px;font-size:15px;font-weight:700;font-family:'DM Sans',sans-serif;cursor:pointer;border:none;transition:all .2s;display:flex;align-items:center;justify-content:center;gap:10px;background:linear-gradient(135deg,var(--pp-gold),#ffb300);color:#1a1a2e;box-shadow:0 8px 24px rgba(255,179,0,.35);} | |
| .pp-btn:hover:not(:disabled){transform:translateY(-2px);box-shadow:0 12px 32px rgba(255,179,0,.5);} | |
| .pp-btn:disabled{opacity:.6;cursor:not-allowed;transform:none;} | |
| .pp-logo{font-size:16px;font-weight:900;font-style:italic;letter-spacing:-1px;} | |
| .pp-P{color:#003087;}.pp-ay{color:#009cde;}.pp-pal{color:#012169;} | |
| .annual-promo{background:linear-gradient(135deg,rgba(0,255,163,.08),rgba(91,123,254,.06));border:1px solid rgba(0,255,163,.2);border-radius:10px;padding:11px 14px;margin-bottom:16px;display:flex;align-items:center;gap:10px;font-size:12px;color:var(--text-muted);} | |
| .annual-promo i{color:var(--sg);font-size:15px;flex-shrink:0;} | |
| .manual-box{background:var(--bg-1);border:1px solid var(--border-bright);border-radius:12px;padding:16px;margin-bottom:14px;} | |
| .manual-step{display:flex;gap:10px;margin-bottom:12px;font-size:13px;color:var(--text-muted);align-items:flex-start;} | |
| .manual-step:last-child{margin-bottom:0;} | |
| .step-num{background:var(--accent);color:#fff;font-size:10px;font-weight:700;width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;flex-shrink:0;margin-top:1px;} | |
| .copy-btn{background:var(--bg-3);border:1px solid var(--border);color:var(--text-muted);border-radius:6px;padding:3px 8px;font-size:11px;cursor:pointer;transition:all .15s;white-space:nowrap;} | |
| .copy-btn:hover{border-color:var(--sg);color:var(--sg);} | |
| .verify-wrap{display:flex;gap:8px;margin-top:8px;} | |
| .verify-wrap input{flex:1;font-family:'JetBrains Mono',monospace;font-size:13px;} | |
| @media(max-width:768px){ | |
| .verify-wrap input{font-size:16px;} /* prevent iOS zoom-on-focus */ | |
| } | |
| .info-note{background:rgba(91,123,254,.06);border:1px solid rgba(91,123,254,.18);border-radius:10px;padding:12px 14px;margin-bottom:16px;font-size:12px;color:var(--text-muted);line-height:1.6;} | |
| .info-note i{color:var(--accent);} | |
| .warn-note{background:rgba(251,146,60,.06);border:1px solid rgba(251,146,60,.18);border-radius:10px;padding:12px 14px;margin-bottom:16px;font-size:12px;color:var(--text-muted);line-height:1.6;} | |
| .warn-note i{color:var(--warning);} | |
| .pay-error{display:none;background:rgba(244,63,94,.08);border:1px solid rgba(244,63,94,.25);border-radius:10px;padding:11px 14px;font-size:13px;color:var(--danger);margin-top:12px;line-height:1.5;} | |
| .trust-row{display:flex;justify-content:center;gap:18px;flex-wrap:wrap;margin-top:20px;} | |
| .trust-item{display:flex;align-items:center;gap:5px;font-size:11px;color:var(--text-faint);} | |
| .trust-item i{color:var(--sg);} | |
| .success-screen{display:none;text-align:center;padding:40px 20px;} | |
| .success-ico{width:72px;height:72px;border-radius:50%;margin:0 auto 20px;background:rgba(52,211,153,.12);border:2px solid rgba(52,211,153,.4);display:flex;align-items:center;justify-content:center;font-size:32px;color:var(--sg);animation:popIn .4s cubic-bezier(.34,1.56,.64,1) both;} | |
| @keyframes popIn{from{opacity:0;transform:scale(.7)}to{opacity:1;transform:scale(1)}} | |
| @keyframes confFall{0%{transform:translateY(0) rotate(0deg);opacity:1}100%{transform:translateY(100vh) rotate(720deg);opacity:0}} | |
| </style> | |
| </head> | |
| <body> | |
| <nav class="co-nav"> | |
| <a href="index.html" class="co-brand"> | |
| <span class="bico"><i class="bi bi-shield-lock-fill" style="font-size:12px;color:#fff;"></i></span> | |
| <span style="color:var(--accent);">Safe</span><span>AI</span><span style="color:var(--sg);">Scan</span> | |
| </a> | |
| <div style="display:flex;align-items:center;gap:6px;font-size:12px;color:var(--text-muted);"> | |
| <i class="bi bi-shield-lock-fill" style="color:var(--sg);"></i> | |
| Secure Checkout · PayPal Protected | |
| </div> | |
| </nav> | |
| <div class="co-body"> | |
| <!-- PAYMENT FORM --> | |
| <div> | |
| <div id="checkoutMain"> | |
| <div style="font-family:'Syne',sans-serif;font-size:22px;font-weight:800;letter-spacing:-.03em;margin-bottom:6px;" id="payTitle">Upgrade to Pro</div> | |
| <p style="font-size:13px;color:var(--text-muted);margin-bottom:20px;line-height:1.6;" id="paySub"> | |
| Unlimited scans, all findings revealed, PDF reports, and full AI analysis. | |
| </p> | |
| <!-- Billing toggle --> | |
| <div class="billing-toggle" id="billingToggle"> | |
| <button class="btog-opt active" id="btnMonthly" onclick="setBilling('monthly')">Monthly</button> | |
| <button class="btog-opt" id="btnAnnual" onclick="setBilling('annual')" style="position:relative;"> | |
| Annual<span class="save-pip">-20%</span> | |
| </button> | |
| </div> | |
| <!-- Loading --> | |
| <div id="loadingState" style="text-align:center;padding:32px;color:var(--text-muted);"> | |
| <span class="spinner-border spinner-border-sm me-2"></span>Checking payment options… | |
| </div> | |
| <!-- Tabs --> | |
| <div class="method-tabs" id="methodTabs" style="display:none;"> | |
| <button class="method-tab active" id="tab-paypal" onclick="setMethod('paypal')"> | |
| <span class="pp-logo" style="font-size:13px;"><span class="pp-P">P</span><span class="pp-ay">ay</span><span class="pp-pal">Pal</span></span> | |
| PayPal Checkout | |
| </button> | |
| <button class="method-tab" id="tab-manual" onclick="setMethod('manual')"> | |
| <i class="bi bi-send"></i> Send Payment | |
| </button> | |
| </div> | |
| <!-- PAYPAL TAB --> | |
| <div class="method-panel active" id="panel-paypal" style="display:none;"> | |
| <div class="info-note" id="paypalNote"> | |
| <i class="bi bi-info-circle me-1"></i> | |
| You'll be redirected to PayPal to complete payment securely. Your account upgrades automatically once PayPal confirms. | |
| </div> | |
| <div class="annual-promo" id="annualPromo" style="display:none;"> | |
| <i class="bi bi-gift-fill"></i> | |
| <div><strong style="color:var(--sg);">Save 20% with Annual</strong> — just $1.59/month, billed $19.08/year. Over 2 months free vs monthly.</div> | |
| </div> | |
| <div class="inp-g"> | |
| <label class="inp-lbl">Your Account Email</label> | |
| <div class="inp-w"> | |
| <i class="bi bi-envelope inp-ic"></i> | |
| <input id="ppEmail" type="email" class="form-control" placeholder="you@company.com" autocomplete="email"> | |
| </div> | |
| </div> | |
| <button class="pp-btn" id="ppBtn" onclick="handlePayPalCheckout()"> | |
| <span class="pp-logo"><span class="pp-P">P</span><span class="pp-ay">ay</span><span class="pp-pal">Pal</span></span> | |
| <span id="ppBtnLabel">Subscribe for $1.99/mo</span> | |
| </button> | |
| <div style="text-align:center;font-size:11px;color:var(--text-faint);margin-top:8px;" id="ppSubNote"> | |
| Recurring monthly. Cancel anytime from your account. | |
| </div> | |
| <div class="pay-error" id="ppError"></div> | |
| </div> | |
| <!-- MANUAL PAYMENT TAB --> | |
| <div class="method-panel" id="panel-manual" style="display:none;"> | |
| <div class="info-note"> | |
| <i class="bi bi-info-circle me-1"></i> | |
| Send payment via PayPal, include your email in the note, then enter the transaction ID below to activate Pro. | |
| </div> | |
| <div class="manual-box"> | |
| <div class="manual-step"> | |
| <span class="step-num">1</span> | |
| <div> | |
| Open PayPal and send <strong id="manualAmount">$1.99 USD</strong> to: | |
| <div style="display:flex;align-items:center;gap:8px;margin-top:6px;"> | |
| <code id="paypalEmailAddr" style="font-size:13px;color:var(--sg);font-family:'JetBrains Mono',monospace;background:var(--bg-2);padding:4px 10px;border-radius:6px;border:1px solid var(--border);">payments@safeaiscan.io</code> | |
| <button class="copy-btn" onclick="copyPayPalEmail(this)"><i class="bi bi-clipboard me-1"></i>Copy</button> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="manual-step"> | |
| <span class="step-num">2</span> | |
| <div>In the PayPal payment note, include: <strong>your account email address</strong></div> | |
| </div> | |
| <div class="manual-step"> | |
| <span class="step-num">3</span> | |
| <div style="width:100%;"> | |
| Enter your PayPal <strong>Transaction ID</strong> (from your PayPal receipt): | |
| <div class="verify-wrap"> | |
| <input id="txnId" type="text" class="form-control" placeholder="e.g. 7GH12345AB678901C"> | |
| <button class="btn btn-primary" onclick="verifyPayment()" id="verifyBtn" style="font-size:13px;padding:8px 16px;white-space:nowrap;"> | |
| Verify & Activate | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="warn-note"> | |
| <i class="bi bi-clock me-1"></i> | |
| Manual verification is typically processed within 30 minutes. For instant access, use the PayPal Checkout tab. | |
| </div> | |
| <div class="pay-error" id="manualError"></div> | |
| </div> | |
| <div class="trust-row" id="trustRow" style="display:none;"> | |
| <div class="trust-item"><i class="bi bi-shield-check"></i>PayPal protected</div> | |
| <div class="trust-item"><i class="bi bi-arrow-counterclockwise"></i>30-day refund</div> | |
| <div class="trust-item"><i class="bi bi-x-circle"></i>Cancel anytime</div> | |
| </div> | |
| <div style="text-align:center;margin-top:14px;font-size:11px;color:var(--text-faint);" id="poweredBy" style="display:none;"> | |
| <i class="bi bi-lock-fill" style="color:var(--accent);margin-right:4px;"></i> | |
| Payments processed securely by PayPal | |
| </div> | |
| </div> | |
| <!-- SUCCESS --> | |
| <div class="success-screen" id="successScreen"> | |
| <div class="success-ico"><i class="bi bi-check-lg"></i></div> | |
| <div style="font-family:'Syne',sans-serif;font-size:24px;font-weight:800;margin-bottom:8px;">You're on Pro! 🎉</div> | |
| <div style="font-size:14px;color:var(--text-muted);margin-bottom:24px;line-height:1.6;" id="successMsg"> | |
| Your Pro plan is now active. Start scanning without limits. | |
| </div> | |
| <div style="display:flex;flex-direction:column;gap:10px;max-width:280px;margin:0 auto;"> | |
| <a href="pro.html" style="display:block;background:linear-gradient(135deg,#5b7bfe,#4361ee);color:#fff;border:none;padding:13px;border-radius:11px;font-size:14px;font-weight:600;text-decoration:none;text-align:center;"> | |
| <i class="bi bi-lightning-charge-fill me-1"></i>Open Pro Scanner | |
| </a> | |
| <a href="index.html" style="display:block;background:var(--bg-2);color:var(--text-muted);border:1px solid var(--border);padding:11px;border-radius:11px;font-size:13px;text-decoration:none;text-align:center;"> | |
| <i class="bi bi-grid me-1"></i>Dashboard | |
| </a> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- ORDER SUMMARY --> | |
| <div class="order-col"> | |
| <div class="order-title">Order Summary</div> | |
| <div class="plan-row"> | |
| <div class="plan-ico"><i class="bi bi-lightning-charge-fill"></i></div> | |
| <div> | |
| <div class="plan-name">SafeAIScan Pro</div> | |
| <div class="plan-desc" id="orderBillingDesc">Billed monthly · cancel anytime</div> | |
| </div> | |
| </div> | |
| <div class="pline"><span>Pro plan</span><span id="orderPrice">$1.99/mo</span></div> | |
| <div class="pline" id="orderSavingsRow" style="display:none;"> | |
| <span style="color:var(--sg);font-size:12px;"><i class="bi bi-tag me-1"></i>Annual savings</span> | |
| <span style="color:var(--sg);">-$4.80/yr</span> | |
| </div> | |
| <div class="pline"><span>Tax</span><span style="color:var(--text-faint);">Based on location</span></div> | |
| <div class="pline total"><span>Total today</span><span id="orderTotal">$1.99</span></div> | |
| <div class="feat-list"> | |
| <div class="feat-li"><i class="bi bi-check-circle-fill"></i>Unlimited scans</div> | |
| <div class="feat-li"><i class="bi bi-check-circle-fill"></i>All findings revealed</div> | |
| <div class="feat-li"><i class="bi bi-check-circle-fill"></i>GitHub repo scanning</div> | |
| <div class="feat-li"><i class="bi bi-check-circle-fill"></i>Full AI analysis + CVE lookup</div> | |
| <div class="feat-li"><i class="bi bi-check-circle-fill"></i>PDF report download</div> | |
| <div class="feat-li"><i class="bi bi-check-circle-fill"></i>REST API access</div> | |
| <div class="feat-li"><i class="bi bi-check-circle-fill"></i>30-day money-back guarantee</div> | |
| </div> | |
| <div style="margin-top:18px;padding:12px 14px;background:var(--bg-1);border-radius:10px;border:1px solid var(--border);"> | |
| <div style="font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.7px;color:var(--text-faint);margin-bottom:8px;">What happens next</div> | |
| <div style="font-size:12px;color:var(--text-muted);line-height:1.75;"> | |
| <i class="bi bi-1-circle me-1" style="color:var(--accent);"></i>Approve via PayPal (30 sec)<br> | |
| <i class="bi bi-2-circle me-1" style="color:var(--accent);"></i>Account upgrades instantly<br> | |
| <i class="bi bi-3-circle me-1" style="color:var(--accent);"></i>Scan without limits | |
| </div> | |
| </div> | |
| <div style="margin-top:14px;display:flex;align-items:center;gap:8px;padding:10px 12px;background:rgba(0,255,163,.04);border:1px solid rgba(0,255,163,.12);border-radius:8px;"> | |
| <i class="bi bi-people-fill" style="color:var(--sg);font-size:14px;flex-shrink:0;"></i> | |
| <div style="font-size:11px;color:var(--text-muted);line-height:1.5;">Trusted by security engineers worldwide.</div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| var API = 'https://rathious-safeaiscan.hf.space'; | |
| var TOKEN = localStorage.getItem('access_token') || ''; | |
| var BILLING = 'monthly'; | |
| var PAYPAL_CONFIGURED = false; | |
| var currentMethod = 'paypal'; | |
| var PRICES = { | |
| monthly: { label:'$1.99/mo', total:'$1.99', btnLabel:'Subscribe for $1.99/mo', subNote:'Recurring monthly. Cancel anytime.', manual:'$1.99 USD' }, | |
| annual: { label:'$19.08/yr (~$1.59/mo)', total:'$19.08', btnLabel:'Subscribe for $19.08/yr', subNote:'Billed once yearly. Cancel anytime.', manual:'$19.08 USD' } | |
| }; | |
| function esc(s){ return String(s||'').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"'); } | |
| function extractError(d){ | |
| if(!d) return ''; | |
| if(typeof d==='string') return d; | |
| return (d.detail&&d.detail.error)||d.error||d.detail||d.message||''; | |
| } | |
| /* Boot */ | |
| (function boot(){ | |
| var p = new URLSearchParams(window.location.search); | |
| if(p.get('welcome')==='1'){ | |
| localStorage.setItem('is_pro','true'); | |
| localStorage.setItem('user_plan','pro'); | |
| var billing = p.get('billing')||'monthly'; | |
| showSuccess('Your <strong>Pro '+(billing==='annual'?'Annual':'Monthly')+'</strong> subscription is now active. Unlimited scans, PDF reports, and full AI analysis are unlocked.'); | |
| return; | |
| } | |
| if(p.get('cancelled')==='1'){ | |
| document.getElementById('loadingState').style.display='none'; | |
| revealUI(); | |
| showErr('ppError','Payment cancelled. No charge was made. You can try again anytime.'); | |
| return; | |
| } | |
| if(p.get('error')){ | |
| document.getElementById('loadingState').style.display='none'; | |
| revealUI(); | |
| var errMap={ | |
| missing_user_id:'Session error — please log in and try again.', | |
| missing_subscription_id:'Payment confirmation missing. Contact support if you were charged.', | |
| subscription_not_active:'Subscription not yet active. Please wait a moment and try again.', | |
| activation_failed:'Upgrade activation failed. Contact support — you will not be charged twice.', | |
| capture_failed:'Payment capture failed. Please try again or contact support.' | |
| }; | |
| var code=p.get('error'); | |
| showErr('ppError', errMap[code]||'An error occurred: '+esc(decodeURIComponent(code).slice(0,120))+'. Please try again or contact support.'); | |
| return; | |
| } | |
| if(p.get('reason')==='trial_expired'){ | |
| document.getElementById('payTitle').textContent='Your Trial Has Ended'; | |
| document.getElementById('paySub').innerHTML='Your 30-day free trial has expired. Continue with Pro at <strong>$1.99/month</strong> — less than a coffee.'; | |
| } else if(p.get('reason')==='pro_required'){ | |
| document.getElementById('paySub').innerHTML='<span style="color:var(--warning);"><i class="bi bi-lock me-1"></i>Pro plan required</span> — upgrade below to unlock this feature.'; | |
| } | |
| prefillEmail(); | |
| checkPayPalConfig(); | |
| })(); | |
| /* Billing toggle */ | |
| function setBilling(mode){ | |
| BILLING=mode; | |
| document.getElementById('btnMonthly').classList.toggle('active',mode==='monthly'); | |
| document.getElementById('btnAnnual').classList.toggle('active',mode==='annual'); | |
| var pr=PRICES[mode]; | |
| var lblEl=document.getElementById('ppBtnLabel'); | |
| var noteEl=document.getElementById('ppSubNote'); | |
| var promoEl=document.getElementById('annualPromo'); | |
| if(lblEl) lblEl.textContent=pr.btnLabel; | |
| if(noteEl) noteEl.textContent=pr.subNote; | |
| if(promoEl) promoEl.style.display=(mode==='annual')?'flex':'none'; | |
| var manEl=document.getElementById('manualAmount'); | |
| if(manEl) manEl.textContent=pr.manual; | |
| document.getElementById('orderPrice').textContent=pr.label; | |
| document.getElementById('orderTotal').textContent=pr.total; | |
| document.getElementById('orderBillingDesc').textContent= | |
| mode==='annual'?'Billed annually \xb7 cancel anytime':'Billed monthly \xb7 cancel anytime'; | |
| var savRow=document.getElementById('orderSavingsRow'); | |
| if(savRow) savRow.style.display=(mode==='annual')?'flex':'none'; | |
| } | |
| /* Check PayPal config */ | |
| async function checkPayPalConfig(){ | |
| try{ | |
| var res=await fetch(API+'/api/pricing'); | |
| var data=(await res.json()).data||{}; | |
| PAYPAL_CONFIGURED=!!(data.has_subscription_plans||data.pro_monthly); | |
| }catch(e){ PAYPAL_CONFIGURED=false; } | |
| document.getElementById('loadingState').style.display='none'; | |
| revealUI(); | |
| if(!PAYPAL_CONFIGURED){ | |
| var ppNote=document.getElementById('paypalNote'); | |
| if(ppNote){ | |
| ppNote.className='warn-note'; | |
| ppNote.innerHTML='<i class="bi bi-exclamation-triangle me-1"></i>'+ | |
| 'Automatic PayPal checkout is being set up. Use the <strong>Send Payment</strong> tab for instant manual activation, '+ | |
| 'or try the PayPal tab — it may still work.'; | |
| } | |
| } | |
| } | |
| function revealUI(){ | |
| document.getElementById('methodTabs').style.display='flex'; | |
| ['panel-paypal','panel-manual'].forEach(function(id){ | |
| var el=document.getElementById(id); | |
| if(el) el.style.removeProperty('display'); | |
| }); | |
| document.getElementById('trustRow').style.display='flex'; | |
| document.getElementById('poweredBy').style.removeProperty('display'); | |
| setMethod(currentMethod); | |
| } | |
| function setMethod(m){ | |
| currentMethod=m; | |
| ['paypal','manual'].forEach(function(t){ | |
| var tb=document.getElementById('tab-'+t); | |
| var pb=document.getElementById('panel-'+t); | |
| if(tb) tb.classList.toggle('active',t===m); | |
| if(pb) pb.classList.toggle('active',t===m); | |
| }); | |
| clearErrors(); | |
| } | |
| function clearErrors(){ | |
| ['ppError','manualError'].forEach(function(id){ | |
| var el=document.getElementById(id); | |
| if(el) el.style.display='none'; | |
| }); | |
| } | |
| function showErr(id,msg){ | |
| var el=document.getElementById(id); | |
| if(!el) return; | |
| el.innerHTML='<i class="bi bi-exclamation-circle me-1"></i>'+msg; | |
| el.style.display='block'; | |
| el.scrollIntoView({behavior:'smooth',block:'nearest'}); | |
| } | |
| /* PayPal Checkout */ | |
| async function handlePayPalCheckout(){ | |
| clearErrors(); | |
| if(!TOKEN||TOKEN==='undefined'||TOKEN==='null'){ | |
| sessionStorage.setItem('pending_upgrade',BILLING); | |
| window.location.href='login.html?redirect=checkout'; | |
| return; | |
| } | |
| var email=(document.getElementById('ppEmail')||{}).value||''; | |
| email=email.trim(); | |
| if(!email){ | |
| showErr('ppError','Please enter your email address so we can confirm the upgrade.'); | |
| document.getElementById('ppEmail').focus(); | |
| return; | |
| } | |
| var btn=document.getElementById('ppBtn'); | |
| if(btn){ btn.disabled=true; btn.innerHTML='<span class="spinner-border spinner-border-sm me-2"></span>Connecting to PayPal…'; } | |
| try{ | |
| var res=await fetch(API+'/payment/create?billing='+BILLING,{ | |
| method:'POST', | |
| headers:{'Content-Type':'application/json','Authorization':'Bearer '+TOKEN}, | |
| body:JSON.stringify({paypal_email:email,billing:BILLING}) | |
| }); | |
| var raw=await res.json(); | |
| var data=raw.data||raw; | |
| if(res.status===401||res.status===403){ | |
| sessionStorage.setItem('pending_upgrade',BILLING); | |
| window.location.href='login.html?redirect=checkout'; | |
| return; | |
| } | |
| if(res.status===503){ | |
| showErr('ppError',(extractError(data)||'PayPal checkout not available right now.')+' <strong>Use the Send Payment tab</strong> for immediate activation.'); | |
| setMethod('manual'); | |
| return; | |
| } | |
| if(!res.ok){ | |
| showErr('ppError',extractError(data)||'Payment setup failed. Please try again or use Send Payment.'); | |
| return; | |
| } | |
| var approveUrl=data.approve_url||data.approval_url; | |
| if(approveUrl){ | |
| if(btn) btn.innerHTML='<span class="spinner-border spinner-border-sm me-2"></span>Redirecting to PayPal…'; | |
| setTimeout(function(){ window.location.href=approveUrl; },300); | |
| return; | |
| } | |
| showErr('ppError','Unexpected response from payment server. Please try the Send Payment tab or contact support.'); | |
| }catch(e){ | |
| var msg=e.message||'Unknown error'; | |
| if(msg.includes('fetch')||msg.includes('Network')){ | |
| showErr('ppError','Network error — could not reach the payment server. Check your connection and try again.'); | |
| }else{ | |
| showErr('ppError','Error: '+esc(msg)+'. Try again or use the Send Payment tab.'); | |
| } | |
| }finally{ | |
| setTimeout(function(){ | |
| var btn2=document.getElementById('ppBtn'); | |
| if(btn2&&btn2.disabled){ | |
| btn2.disabled=false; | |
| btn2.innerHTML='<span class="pp-logo"><span class="pp-P">P</span><span class="pp-ay">ay</span><span class="pp-pal">Pal</span></span><span>'+esc(PRICES[BILLING].btnLabel)+'</span>'; | |
| } | |
| },5000); | |
| } | |
| } | |
| /* Manual payment verification */ | |
| async function verifyPayment(){ | |
| clearErrors(); | |
| var txnId=(document.getElementById('txnId')||{}).value||''; | |
| txnId=txnId.trim().replace(/\s+/g,''); | |
| if(!txnId){ showErr('manualError','Please enter your PayPal Transaction ID.'); return; } | |
| if(txnId.length<8){ showErr('manualError','Transaction ID seems too short — check your PayPal email receipt.'); return; } | |
| if(!/^[A-Z0-9]+$/i.test(txnId)){ showErr('manualError','Transaction ID should only contain letters and numbers.'); return; } | |
| if(!TOKEN||TOKEN==='undefined'){ | |
| sessionStorage.setItem('pending_upgrade','manual:'+txnId); | |
| window.location.href='login.html?redirect=checkout'; | |
| return; | |
| } | |
| var btn=document.getElementById('verifyBtn'); | |
| if(btn){ btn.disabled=true; btn.innerHTML='<span class="spinner-border spinner-border-sm me-2"></span>Verifying…'; } | |
| try{ | |
| var res=await fetch(API+'/payment/activate',{ | |
| method:'POST', | |
| headers:{'Content-Type':'application/json','Authorization':'Bearer '+TOKEN}, | |
| body:JSON.stringify({transaction_id:txnId,billing:BILLING,amount:PRICES[BILLING].manual}) | |
| }); | |
| var raw=await res.json(); | |
| var data=raw.data||raw; | |
| if(res.status===401||res.status===403){ window.location.href='login.html?redirect=checkout'; return; } | |
| if(!res.ok){ | |
| localStorage.setItem('pending_txn',txnId); | |
| showErr('manualError',extractError(data)|| | |
| 'Could not auto-verify. Your Transaction ID <strong>'+esc(txnId)+'</strong> has been saved. '+ | |
| 'Contact support and we\'ll activate your account within 1 hour.'); | |
| return; | |
| } | |
| localStorage.setItem('is_pro','true'); | |
| localStorage.setItem('user_plan','pro'); | |
| showSuccess('Pro plan activated. Transaction ID <strong>'+esc(txnId)+'</strong> verified.'); | |
| }catch(e){ | |
| localStorage.setItem('pending_txn',txnId); | |
| showErr('manualError','Network error. Your Transaction ID <strong>'+esc(txnId)+'</strong> has been saved. Please contact support with this ID.'); | |
| }finally{ | |
| if(btn){ btn.disabled=false; btn.innerHTML='Verify & Activate'; } | |
| } | |
| } | |
| /* Copy PayPal email */ | |
| function copyPayPalEmail(btn){ | |
| var el=document.getElementById('paypalEmailAddr'); | |
| var text=el?el.textContent.trim():'payments@safeaiscan.io'; | |
| navigator.clipboard.writeText(text).then(function(){ | |
| btn.innerHTML='<i class="bi bi-check me-1"></i>Copied!'; | |
| btn.style.color='var(--sg)'; | |
| setTimeout(function(){ btn.innerHTML='<i class="bi bi-clipboard me-1"></i>Copy'; btn.style.color=''; },2000); | |
| }).catch(function(){ | |
| var ta=document.createElement('textarea'); | |
| ta.value=text; ta.style.cssText='position:fixed;opacity:0;'; | |
| document.body.appendChild(ta); ta.select(); | |
| try{ document.execCommand('copy'); }catch(e){} | |
| document.body.removeChild(ta); | |
| btn.innerHTML='<i class="bi bi-check me-1"></i>Copied!'; | |
| setTimeout(function(){ btn.innerHTML='<i class="bi bi-clipboard me-1"></i>Copy'; },2000); | |
| }); | |
| } | |
| /* Pre-fill email */ | |
| function prefillEmail(){ | |
| var email=localStorage.getItem('user_email')||''; | |
| if(!email) return; | |
| var el=document.getElementById('ppEmail'); | |
| if(el&&!el.value) el.value=email; | |
| } | |
| /* Success screen */ | |
| function showSuccess(msg){ | |
| localStorage.setItem('is_pro','true'); | |
| localStorage.setItem('user_plan','pro'); | |
| document.getElementById('checkoutMain').style.display='none'; | |
| document.getElementById('billingToggle').style.display='none'; | |
| var ss=document.getElementById('successScreen'); | |
| if(ss) ss.style.display='block'; | |
| if(msg){ var me=document.getElementById('successMsg'); if(me) me.innerHTML=msg; } | |
| spawnConfetti(); | |
| } | |
| function spawnConfetti(){ | |
| var cols=['#5b7bfe','#00ffa3','#38bdf8','#e879f9','#fb923c']; | |
| for(var i=0;i<28;i++){ | |
| (function(d){ | |
| setTimeout(function(){ | |
| var el=document.createElement('div'); | |
| el.style.cssText='position:fixed;left:'+(10+Math.random()*80)+'vw;top:-10px;width:'+(5+Math.random()*8)+'px;height:'+(5+Math.random()*8)+'px;border-radius:'+(Math.random()>.5?'50%':'2px')+';pointer-events:none;z-index:99999;background:'+cols[Math.floor(Math.random()*cols.length)]+';animation:confFall '+(1.2+Math.random()*1.5)+'s ease-in forwards;'; | |
| document.body.appendChild(el); | |
| setTimeout(function(){ el.remove(); },3500); | |
| },d); | |
| })(i*50); | |
| } | |
| } | |
| /* Keyboard submit */ | |
| document.addEventListener('keydown',function(e){ | |
| if(e.key!=='Enter') return; | |
| var active=document.activeElement; | |
| if(active&&active.id==='txnId'){ verifyPayment(); return; } | |
| if(currentMethod==='paypal') handlePayPalCheckout(); | |
| else verifyPayment(); | |
| }); | |
| </script> | |
| </body> | |
| </html> | |