- Add GBP, JPY, RUB, BRL currencies - Auto-detect currency from browser locale (de-CH→CHF, ru→RUB, etc.) - USD as default fallback - Language toggle: globe icon only (compact on mobile), full names in dropdown - Countdown text updates on language switch - CoinGecko proxy supports dynamic currency list
2 lines
14 KiB
JavaScript
2 lines
14 KiB
JavaScript
(function(){"use strict";const It="/api/rates.php?c=eur,usd,chf,gbp,jpy,rub,brl",Tt=/^[48][1-9A-HJ-NP-Za-km-z]{94}$/,_t=/^4[1-9A-HJ-NP-Za-km-z]{105}$/;let y=null,dt=0,T=null,tt=null,j=!1,_=null;const i=e=>document.querySelector(e),u=i("#addr"),w=i("#amount"),g=i("#currency"),D=i("#desc"),F=i("#timerCustom"),M=i("#deadlineBadges");let f=0;const et=i("#generate"),z=i("#result"),v=i("#qr"),nt=i("#uri"),ut=i("#openWallet"),Ft=i("#copyAddr"),S=i("#countdown"),h=i("#fiatHint"),at=i("#toast"),U=i("#shareLink"),Et=i("#copyShareLink"),Rt=i("#newRequest"),bt=i("#homeLink"),Pt=i("#proofToggle"),q=i("#proofPanel"),E=i("#txHash"),O=i("#txKey"),R=i("#verifyProof"),p=i("#proofResult"),W=i("#paymentStatus"),ft=i("#paymentSummary"),kt=i("#downloadPdf");let vt=!1,Ht=!1,C=null;function Xt(){for(var e={de:"EUR",fr:"EUR",it:"EUR",es:"EUR",pt:"EUR",nl:"EUR","de-CH":"CHF","fr-CH":"CHF","it-CH":"CHF","de-AT":"EUR","en-GB":"GBP","en-US":"USD",en:"USD",ja:"JPY",ru:"RUB","pt-BR":"BRL"},t=navigator.languages||[navigator.language||"en"],n=0;n<t.length;n++){var a=t[n];if(e[a]){g.value=e[a];return}var r=a.substring(0,2).toLowerCase();if(e[r]){g.value=e[r];return}}}Xt(),xt(),Nt()||Ot(),Wt(),I18n.onChange(function(){var e=v.querySelector(".qr-hint");e&&(e.textContent=I18n.t("qr_hint"));var t=v.querySelector(".paid-stamp");if(t&&(t.textContent=I18n.t("status_paid")),C&&it(C),z.classList.contains("visible")){var n=ot(),a=D.value.trim();ht(n,a,f),gt(n,a)}tt&&tt()}),u.addEventListener("input",rt),w.addEventListener("input",G),g.addEventListener("change",G),et.addEventListener("click",mt),Ft.addEventListener("click",()=>Ct(u.value.trim())),Et.addEventListener("click",()=>Ct(U.value)),v.addEventListener("click",Bt),Rt.addEventListener("click",pt),bt.addEventListener("click",function(e){e.preventDefault(),pt()}),M.querySelectorAll(".badge").forEach(function(e){e.addEventListener("click",function(){const t=parseInt(e.getAttribute("data-days"));e.classList.contains("active")?(e.classList.remove("active"),f=0,F.value=""):(M.querySelectorAll(".badge").forEach(function(n){n.classList.remove("active")}),e.classList.add("active"),f=t,F.value="")})}),F.addEventListener("input",function(){M.querySelectorAll(".badge").forEach(function(e){e.classList.remove("active")}),f=parseInt(F.value)||0}),kt.addEventListener("click",Kt),Pt.addEventListener("click",Yt),E.addEventListener("input",Lt),O.addEventListener("input",Lt),R.addEventListener("click",Vt);function pt(){u.value="",w.value="",g.value="EUR",D.value="",f=0,F.value="",M.querySelectorAll(".badge").forEach(function(e){e.classList.remove("active")}),h.textContent="",h.classList.remove("error"),u.classList.remove("valid","invalid"),et.disabled=!0,z.classList.remove("visible"),T&&clearInterval(T),v.innerHTML="",nt.textContent="",U.value="",_=null,q.classList.remove("open"),E.value="",O.value="",R.disabled=!0,p.innerHTML="",p.className="proof-result",W.innerHTML="",W.className="payment-status",ft.innerHTML="",document.title="xmrpay.link \u2014 Monero Invoice Generator",history.replaceState(null,"",location.pathname),window.scrollTo({top:0,behavior:"smooth"}),u.focus()}function N(e){return Tt.test(e)||_t.test(e)}function rt(){const e=u.value.trim();u.classList.remove("valid","invalid"),e.length!==0&&(N(e)?u.classList.add("valid"):e.length>=10&&u.classList.add("invalid"),Dt())}function Dt(){const e=u.value.trim();et.disabled=!N(e)}function G(){const e=parseFloat(w.value),t=g.value;if(!e||e<=0){h.textContent="",h.classList.remove("error");return}if(t!=="XMR"&&!y){h.textContent=j?I18n.t("rates_offline"):"",h.classList.toggle("error",j);return}if(h.classList.remove("error"),t==="XMR")if(y){const n=(e*y.eur).toFixed(2);h.textContent="\u2248 "+n+" EUR"}else h.textContent="";else{const n=y[t.toLowerCase()];if(n&&n>0){const a=(e/n).toFixed(8);h.textContent="\u2248 "+a+" XMR"}}}function ot(){const e=parseFloat(w.value),t=g.value;if(!e||e<=0)return null;if(t==="XMR")return e;if(y){const n=y[t.toLowerCase()];if(n&&n>0)return e/n}return null}function Mt(e,t,n){let a="monero:"+e;const r=[];return t&&r.push("tx_amount="+t.toFixed(12)),n&&r.push("tx_description="+encodeURIComponent(n)),r.length&&(a+="?"+r.join("&")),a}function Ut(e,t,n,a){const r=new URLSearchParams;return r.set("a",e),t&&r.set("x",t.toFixed(12)),n&&r.set("d",n),a&&r.set("t",a),r.toString()}async function qt(e){try{const t=await fetch("/api/shorten.php",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({hash:e})});if(!t.ok)throw new Error("HTTP "+t.status);const n=await t.json();return _||(_=n.code),location.origin+"/s/"+n.code}catch(t){return console.warn("Short URL failed:",t),null}}function mt(){const e=u.value.trim();if(!N(e))return;const t=ot(),n=D.value.trim(),a=f,r=Mt(e,t,n);z.classList.add("visible"),nt.textContent=r,ut.onclick=function(){window.location.href=r},ht(t,n,a),gt(t,n);const c=Ut(e,t,n,a);U.value=location.origin+"/#"+c,qt(c).then(function(l){l&&(U.value=l)}),v.innerHTML="",new QRCode(v,{text:r,width:256,height:256,colorDark:"#000000",colorLight:"#ffffff",correctLevel:QRCode.CorrectLevel.M});const d=document.createElement("div");d.className="qr-hint",d.textContent=I18n.t("qr_hint"),v.appendChild(d),At(),zt(e),z.scrollIntoView({behavior:"smooth",block:"start"})}function Nt(){const e=location.hash.substring(1);if(!e)return!1;const t=new URLSearchParams(e),n=t.get("a");if(!n||!N(n))return!1;u.value=n,rt();const a=t.get("x");a&&(w.value=parseFloat(a),g.value="XMR");const r=t.get("d");r&&(D.value=r);const c=t.get("t");if(c&&parseInt(c)>0){f=parseInt(c);const l=M.querySelector('.badge[data-days="'+f+'"]');l?l.classList.add("active"):F.value=f}const d=t.get("c");return d&&(_=d,setTimeout(function(){$t(d)},200)),setTimeout(mt,100),!0}function ht(e,t,n){var a="";if(e){a+='<div class="summary-amount">'+e.toFixed(8)+" XMR</div>";var r=parseFloat(w.value),c=g.value;c!=="XMR"&&r&&(a+='<div class="summary-fiat">\u2248 '+r.toFixed(2)+" "+c+"</div>")}t&&(a+='<div class="summary-desc">'+t.replace(/</g,"<")+"</div>"),ft.innerHTML=a}function gt(e,t){var n=[];e&&n.push(e.toFixed(4)+" XMR"),t&&n.push(t),n.length&&(document.title=n.join(" \u2014 ")+" | xmrpay.link")}function At(){if(T&&clearInterval(T),S.textContent="",S.className="countdown",!f||f<=0)return;const e=Date.now()+f*864e5;S.classList.add("active");function t(){const n=e-Date.now();if(n<=0){clearInterval(T),S.textContent=I18n.t("countdown_expired"),S.className="countdown expired";return}const a=Math.floor(n/864e5),r=Math.floor(n%864e5/36e5),c=Math.floor(n%36e5/6e4);a>0?S.textContent=I18n.t("countdown_remaining_days").replace("{d}",a).replace("{h}",r):S.textContent=I18n.t("countdown_remaining_hours").replace("{h}",yt(r)).replace("{m}",yt(c))}tt=t,t(),T=setInterval(t,6e4)}function yt(e){return e<10?"0"+e:""+e}function Bt(){const e=v.querySelector("canvas");if(!e)return;const t=document.createElement("a");t.download="xmrpay-qr.png",t.href=e.toDataURL("image/png"),t.click()}function Ct(e){navigator.clipboard.writeText(e).then(()=>{jt(I18n.t("toast_copied"))})}function jt(e){at.textContent=e,at.classList.add("show"),setTimeout(()=>at.classList.remove("show"),2e3)}function zt(e){try{localStorage.setItem("xmrpay_addr",e)}catch{}}function Ot(){try{const e=localStorage.getItem("xmrpay_addr");e&&(u.value=e,rt())}catch{}}async function xt(){if(!(y&&Date.now()-dt<6e4))try{const e=await fetch(It);if(!e.ok)throw new Error("HTTP "+e.status);y=(await e.json()).monero,dt=Date.now(),j=!1,G()}catch(e){console.warn("Kurse konnten nicht geladen werden:",e),j=!0,G(),setTimeout(xt,1e4)}}function Wt(){"serviceWorker"in navigator&&navigator.serviceWorker.register("sw.js").catch(function(){})}function Gt(){return new Promise(function(e,t){if(window.jspdf){e();return}var n=document.createElement("script");n.src="lib/jspdf.min.js",n.onload=function(){Ht=!0,e()},n.onerror=function(){t(new Error("Failed to load jsPDF"))},document.head.appendChild(n)})}async function Kt(){await Gt();var e=window.jspdf.jsPDF,t=new e({orientation:"portrait",unit:"mm",format:"a4"}),n=u.value.trim(),a=ot(),r=D.value.trim(),c=parseFloat(w.value),d=g.value,l=t.internal.pageSize.getWidth(),s=20,x=l-s*2,o=s;t.setFillColor(242,104,33),t.rect(0,0,l,8,"F"),o=22,t.setFont("helvetica","bold"),t.setFontSize(22),t.setTextColor(242,104,33),t.text(I18n.t("pdf_title"),s,o),t.setFont("helvetica","normal"),t.setFontSize(10),t.setTextColor(120,120,120);var st=new Date().toLocaleDateString(I18n.getLang()==="de"?"de-CH":"en-US",{year:"numeric",month:"long",day:"numeric"});t.text(I18n.t("pdf_date")+": "+st,l-s,o,{align:"right"}),o+=6,t.setDrawColor(220,220,220),t.setLineWidth(.3),t.line(s,o,l-s,o);var Y=v.querySelector("canvas"),L=50,b=l-s-L,A=o+6;if(Y){var J=Y.toDataURL("image/png");t.addImage(J,"PNG",b,A,L,L),t.setFontSize(7),t.setTextColor(150,150,150),t.text(I18n.t("pdf_scan_qr"),b+L/2,A+L+4,{align:"center"})}var P=s,V=b-s-10;o+=14;function k(Zt,te){t.setFont("helvetica","normal"),t.setFontSize(9),t.setTextColor(150,150,150),t.text(Zt,P,o),o+=5,t.setFont("helvetica","bold"),t.setFontSize(11),t.setTextColor(40,40,40);var St=t.splitTextToSize(te,V);t.text(St,P,o),o+=St.length*5+4}if(a){var m=a.toFixed(8)+" XMR";d!=="XMR"&&c&&(m+=" (~ "+c.toFixed(2)+" "+d+")"),k(I18n.t("pdf_amount"),m)}if(r&&k(I18n.t("pdf_desc"),r),f>0){var I=new Date(Date.now()+f*864e5),$=I.toLocaleDateString(I18n.getLang()==="de"?"de-CH":"en-US",{year:"numeric",month:"long",day:"numeric"});k(I18n.t("pdf_deadline"),$+" ("+I18n.t("pdf_deadline_days").replace("{d}",f)+")")}o=Math.max(o,A+L+12),t.setFont("helvetica","normal"),t.setFontSize(9),t.setTextColor(150,150,150),t.text(I18n.t("pdf_address"),s,o),o+=5,t.setFillColor(245,245,245),t.roundedRect(s,o-3.5,x,10,2,2,"F"),t.setFont("courier","normal"),t.setFontSize(8),t.setTextColor(60,60,60),t.text(n,s+3,o+2.5),o+=14;var Q=nt.textContent;if(Q){t.setFillColor(245,245,245),t.roundedRect(s,o-3.5,x,10,2,2,"F"),t.setFont("courier","normal"),t.setFontSize(6.5),t.setTextColor(100,100,100);var Z=t.splitTextToSize(Q,x-6);t.text(Z,s+3,o+2),o+=Z.length*3+10}if(C){o+=4;var B="";if(C.verified_at){var ct=new Date(C.verified_at*1e3);B=ct.toLocaleDateString(I18n.getLang()==="de"?"de-CH":"en-US",{year:"numeric",month:"long",day:"numeric"})}var lt=C.amount.toFixed(6)+" XMR \u2014 TX "+C.tx_hash.substring(0,8)+"..."+(B?" \u2014 "+B:"");t.setFillColor(76,175,80),t.roundedRect(s,o-4,x,16,2,2,"F"),t.setFont("helvetica","bold"),t.setFontSize(12),t.setTextColor(255,255,255),t.text(I18n.t("status_paid").toUpperCase(),s+x/2,o+1,{align:"center"}),t.setFont("helvetica","normal"),t.setFontSize(7.5),t.text(lt,s+x/2,o+7,{align:"center"}),o+=22}t.setDrawColor(220,220,220),t.setLineWidth(.3);var H=t.internal.pageSize.getHeight()-15;t.line(s,H,l-s,H),t.setFont("helvetica","normal"),t.setFontSize(7),t.setTextColor(180,180,180),t.text(I18n.t("pdf_footer"),l/2,H+5,{align:"center"});var X=U.value;X&&t.text(X,l/2,H+9,{align:"center"});var wt="xmrpay-"+(r?r.replace(/[^a-zA-Z0-9]/g,"-").substring(0,30):"invoice")+".pdf";t.save(wt)}function Yt(){if(q.classList.contains("open")){q.classList.remove("open");return}if(!vt&&!window.XmrCrypto){Jt().then(function(){vt=!0,q.classList.add("open"),E.focus()});return}q.classList.add("open"),E.focus()}function Jt(){return new Promise(function(e,t){if(window.XmrCrypto){e();return}const n=document.createElement("script");n.src="lib/xmr-crypto.bundle.js",n.onload=e,n.onerror=function(){t(new Error("Failed to load crypto module"))},document.head.appendChild(n)})}function K(e){return/^[0-9a-fA-F]{64}$/.test(e)}function Lt(){const e=E.value.trim(),t=O.value.trim();R.disabled=!(K(e)&&K(t))}async function Vt(){const e=E.value.trim(),t=O.value.trim(),n=u.value.trim();if(!(!K(e)||!K(t)||!N(n))){R.disabled=!0,p.className="proof-result active",p.textContent=I18n.t("proof_verifying");try{var a=await fetch("/api/node.php",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({method:"gettransactions",params:{txs_hashes:[e],decode_as_json:!0}})}),r=await a.json(),c=r.txs||[];if(c.length===0){p.className="proof-result active error",p.textContent=I18n.t("proof_tx_not_found"),R.disabled=!1;return}for(var d=c[0],l=JSON.parse(d.as_json),s=XmrCrypto.getKeysFromAddress(n),x=s.publicViewKey,o=s.publicSpendKey,st=XmrCrypto.bytesToScalar(XmrCrypto.hexToBytes(t)),Y=XmrCrypto.Point.fromHex(x),L=Y.multiply(st).multiply(8n),b=L.toBytes(),A=XmrCrypto.Point.fromHex(o),J=l.vout||[],P=l.rct_signatures&&l.rct_signatures.ecdhInfo||[],V=0n,k=!1,m=0;m<J.length;m++){var I=J[m],$=I.target&&I.target.tagged_key?I.target.tagged_key.key:I.target&&I.target.key;if($){var Q=XmrCrypto.encodeVarint(m),Z=XmrCrypto.hashToScalar(XmrCrypto.concat(b,Q)),B=XmrCrypto.bytesToScalar(Z),ct=XmrCrypto.Point.BASE.multiply(B).add(A),lt=XmrCrypto.bytesToHex(ct.toBytes());if(lt===$&&(k=!0,P[m]&&P[m].amount)){var H=XmrCrypto.decodeRctAmount(P[m].amount,b,m);V+=H}}}if(k){var X=Number(V)/1e12;p.className="proof-result active success",p.textContent=I18n.t("proof_verified").replace("{amount}",X.toFixed(6)),_&&await fetch("/api/verify.php",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({code:_,tx_hash:e,amount:X,confirmations:d.confirmations||0})}),it({amount:X,tx_hash:e})}else p.className="proof-result active error",p.textContent=I18n.t("proof_no_match")}catch{p.className="proof-result active error",p.textContent=I18n.t("proof_error")}R.disabled=!1}}function $t(e){fetch("/api/verify.php?code="+encodeURIComponent(e)).then(function(t){return t.json()}).then(function(t){t.verified&&it(t)}).catch(function(){})}function it(e){W.className="payment-status paid",v.classList.add("paid");var t=v.querySelector(".paid-stamp");if(t)t.textContent=I18n.t("status_paid");else{var n=document.createElement("div");n.className="paid-stamp",n.textContent=I18n.t("status_paid"),v.appendChild(n)}var a=v.querySelector(".qr-hint");if(a){var r="";if(e.verified_at){var c=new Date(e.verified_at*1e3);r=" \u2014 "+c.toLocaleDateString(I18n.getLang()==="de"?"de-CH":"en-US",{year:"numeric",month:"long",day:"numeric"})}a.textContent=e.amount.toFixed(6)+" XMR \u2014 TX "+e.tx_hash.substring(0,8)+"..."+r,a.className="qr-hint paid-info"}W.innerHTML="",C=e,ut.style.display="none",document.getElementById("copyAddr").style.display="none";var d=document.getElementById("proofSection");d&&(d.style.display="none"),Qt()}function Qt(){var e=document.createElement("canvas");e.width=32,e.height=32;var t=e.getContext("2d"),n=new Image;n.onload=function(){t.drawImage(n,0,0,32,32),t.beginPath(),t.arc(25,25,7,0,Math.PI*2),t.fillStyle="#fff",t.fill(),t.beginPath(),t.arc(25,25,5.5,0,Math.PI*2),t.fillStyle="#4caf50",t.fill();var a=document.getElementById("favicon");a.href=e.toDataURL("image/png")},n.src="favicon.svg"}})();
|