CyberMax-tools's picture
Apps pro.js: offline Pro keys (Ed25519) + direct checkout map (Workers cap fix, 17 Oct 2026)
bdc2129 verified
Raw History Blame Contribute Delete
10.1 kB
/* CyberMax Pro unlock for static web apps (copied into each product's web/ folder; each product stays self-contained).
* Config before this script: window.CMX_PRO = { id, name, price, perks: [..], offer }.
* Buy: counted link https://data.cybermaxtools.com/buy/<offer>?s=app-<id> -> Stripe Payment Link (one-time).
* After checkout Stripe sends the buyer to data.cybermaxtools.com/unlock/<id>?session_id=..., which checks the
* Checkout Session with Stripe and returns here with #cmx-pro=<session id>. The app re-checks that code once
* (/unlock/<id>?session_id=..&json=1) and remembers Pro in this browser. The same code (shown on the receipt page and
* in the Pro box) unlocks Pro on another device. No account, no tracking. */
(function () {
"use strict";
var C = window.CMX_PRO || {};
if (!C.id) return;
var DATA = "https://data.cybermaxtools.com";
var KEY = "cmx-pro:" + C.id;
var SID = /^(cmxk1\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+|cs_(live|test)_[A-Za-z0-9]{10,200}|[A-Z0-9]{8}-[A-Z0-9]{8}-[A-Z0-9]{8}-[A-Z0-9]{8})$/i; // Stripe order code or Gumroad licence key
var listeners = [];
function get() { try { return JSON.parse(localStorage.getItem(KEY) || "null"); } catch (e) { return null; } }
function set(v) { try { localStorage.setItem(KEY, JSON.stringify(v)); } catch (e) { /* private mode: Pro for this visit */ } mem = v; }
var mem = get();
function isPro() { return !!(mem && mem.code); }
// Offline Pro keys (17 Oct 2026): "cmxk1.<payload>.<Ed25519 signature>" from data.cybermaxtools.com/unlock is verified
// here with WebCrypto and the public key below, so unlocking needs no Worker call. Expired, foreign-signed or unverifiable
// (no Ed25519 in this browser) keys fall back to the online check of the order code inside them, as before.
var CMX_PUB = "u0FDxbu0d9B9Sz5-yLT6AuL5IagyTlFcbBgEDqZxWtk";
function b64d(s) { s = s.replace(/-/g, "+").replace(/_/g, "/"); while (s.length % 4) s += "="; var b = atob(s), u = new Uint8Array(b.length); for (var i = 0; i < b.length; i++) u[i] = b.charCodeAt(i); return u; }
function cmxVerify(k) {
var parts = k.split("."), p = null;
try { p = JSON.parse(new TextDecoder().decode(b64d(parts[1]))); } catch (e) { return Promise.resolve({ p: null, ok: false }); }
if (!(window.crypto && crypto.subtle)) return Promise.resolve({ p: p, ok: null });
return crypto.subtle.importKey("raw", b64d(CMX_PUB), { name: "Ed25519" }, false, ["verify"])
.then(function (key) { return crypto.subtle.verify({ name: "Ed25519" }, key, b64d(parts[2]), new TextEncoder().encode(parts[0] + "." + parts[1])); })
.then(function (ok) { return { p: p, ok: !!ok }; }, function () { return { p: p, ok: null }; });
}
function check(code) {
if (!/^cmxk1\./.test(code)) return checkOnline(code);
return cmxVerify(code).then(function (r) {
var p = r.p;
if (!p || p.a !== C.id) return false;
if (r.ok && (!p.x || p.x * 1000 > Date.now())) return true;
return p.c ? checkOnline(p.c) : false;
});
}
function checkOnline(code) {
return fetch(DATA + "/unlock/" + encodeURIComponent(C.id) + "?json=1&session_id=" + encodeURIComponent(code))
.then(function (r) { return r.json(); }).then(function (j) { return !!(j && j.pro); }).catch(function () { return null; });
}
function unlock(code, from) {
return check(code).then(function (ok) {
if (ok) { set({ code: code, at: new Date().toISOString() }); apply(); toast("Pro unlocked. Thank you for supporting " + C.name + "!"); }
else if (ok === false) toast("That code is not a paid " + C.name + " Pro order.");
else toast("Could not reach the unlock check. Try again in a minute.");
return ok;
});
}
function toast(msg) {
var t = document.getElementById("toast");
if (t) { t.textContent = msg; t.hidden = false; t.classList.add("show"); setTimeout(function () { t.classList.remove("show"); }, 3500); }
var s = document.getElementById("cmx-pro-msg");
if (s) s.textContent = msg;
}
function esc(s) { return String(s).replace(/[&<>"]/g, function (c) { return { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" }[c]; }); }
function box() {
var el = document.getElementById("cmx-pro");
if (!el) {
el = document.createElement("section");
el.id = "cmx-pro";
var anchor = document.querySelector("footer.more") || document.querySelector("main footer") || document.querySelector("footer");
if (anchor && anchor.parentNode) anchor.parentNode.insertBefore(el, anchor); else (document.querySelector("main") || document.body).appendChild(el);
}
el.className = "cmx-pro";
return el;
}
function render() {
var el = box();
var buy = DATA + "/buy/" + (C.offer || C.id + "-pro") + "?s=app-" + C.id; // counted redirect to the Stripe Payment Link
if (isPro()) {
el.innerHTML = '<h2>' + esc(C.name) + ' Pro <span class="cmx-pro-badge">unlocked</span></h2><p>Thank you for supporting ' + esc(C.name) +
'. Pro is on in this browser: ' + C.perks.map(esc).join(" · ") + '.</p><p class="cmx-pro-small">Your unlock code (use it on another device): <code style="user-select:all;word-break:break-all">' + esc(mem.code) + '</code></p>';
} else {
el.innerHTML = '<h2>' + esc(C.name) + ' Pro, ' + esc(C.price) + ' once</h2><ul>' + C.perks.map(function (p) { return "<li>" + esc(p) + "</li>"; }).join("") +
'</ul><p><a class="btn" id="cmx-pro-buy" href="' + buy + '" rel="nofollow">Get Pro for ' + esc(C.price) + '</a> <span class="cmx-pro-small">One payment, no subscription, secure Stripe checkout. Everything free stays free.</span></p>' +
'<details><summary>Already bought Pro?</summary><form id="cmx-pro-form"><label>Unlock code <input id="cmx-pro-code" autocomplete="off" placeholder="cs_live_… or Gumroad licence key"></label> <button class="btn ghost" type="submit">Unlock</button></form></details><p id="cmx-pro-msg" class="cmx-pro-small" aria-live="polite"></p>';
var f = document.getElementById("cmx-pro-form");
if (f) f.onsubmit = function (e) { e.preventDefault(); var c = (document.getElementById("cmx-pro-code").value || "").trim(); if (SID.test(c)) unlock(c); else toast("Paste the code from your receipt page (it starts with cs_live_) or your Gumroad licence key."); };
}
}
function apply() {
document.documentElement.classList.toggle("cmx-pro", isPro());
render();
listeners.forEach(function (fn) { try { fn(isPro()); } catch (e) { /* app hook */ } });
}
function download(name, text, type) {
var a = document.createElement("a");
a.href = URL.createObjectURL(new Blob([text], { type: type || "text/csv;charset=utf-8" }));
a.download = name;
document.body.appendChild(a); a.click(); a.remove();
setTimeout(function () { URL.revokeObjectURL(a.href); }, 2000);
}
function cell(s) { s = String(s == null ? "" : s).replace(/\s+/g, " ").trim(); return /[",\n]/.test(s) ? '"' + s.replace(/"/g, '""') + '"' : s; }
function rows(table) {
if (table && !table.querySelector("tr")) return table.textContent.trim() ? [[table.textContent.trim()]] : []; // a text result
return Array.prototype.map.call(table ? table.querySelectorAll("tr") : [], function (tr) {
return Array.prototype.map.call(tr.querySelectorAll("th,td"), function (c) { return c.textContent; });
}).filter(function (r) { return r.length; });
}
function csv(tables) { return tables.map(function (t) { return (t.title ? cell(t.title) + "\n" : "") + rows(t.el).map(function (r) { return r.map(cell).join(","); }).join("\n"); }).join("\n\n") + "\n"; }
function markdown(table) {
var r = rows(table); if (!r.length) return "";
var md = function (x) { return "| " + x.map(function (c) { return String(c).replace(/\|/g, "\\|").replace(/\s+/g, " ").trim(); }).join(" | ") + " |"; };
return [md(r[0]), md(r[0].map(function () { return "---"; }))].concat(r.slice(1).map(md)).join("\n") + "\n";
}
window.CMXPro = { isPro: isPro, onChange: function (fn) { listeners.push(fn); fn(isPro()); }, download: download, csv: csv, markdown: markdown, rows: rows, toast: toast };
var m = /[#&]cmx-key=([^&]+)/.exec(location.hash || "") || /[#&]cmx-pro=([^&]+)/.exec(location.hash || "");
if (m && SID.test(decodeURIComponent(m[1]))) {
var code = decodeURIComponent(m[1]);
history.replaceState(null, "", location.pathname + location.search);
if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", function () { apply(); unlock(code); }); else { apply(); unlock(code); }
} else if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", apply); else apply();
})();
/* Direct checkout (17 Oct 2026, Workers cap fix): a click on a data.cybermaxtools.com/buy/<offer> link goes straight to
the Stripe/Polar checkout (map: https://cybermaxtools.com/store/buy-direct.json, a static file) and the click is counted
by a background beacon. Without the map the link works as before. */
(function () {
var MAP = null, U = "https://cybermaxtools.com/store/buy-direct.json";
try { MAP = JSON.parse(sessionStorage.getItem("cmx-bd") || "null"); } catch (e) { /* none */ }
if (!MAP && window.fetch) fetch(U).then(function (r) { return r.json(); }).then(function (j) { MAP = j; try { sessionStorage.setItem("cmx-bd", JSON.stringify(j)); } catch (e) { /* full */ } }).catch(function () {});
document.addEventListener("click", function (e) {
var a = e.target && e.target.closest && e.target.closest('a[href*="data.cybermaxtools.com/buy/"]');
if (!a || !MAP || !MAP.offers) return;
try {
var u = new URL(a.href), k = /^\/buy\/([a-z0-9-]+)\/?$/.exec(u.pathname), t = k && MAP.offers[k[1]];
if (!t) return;
var s = String(u.searchParams.get("s") || "unknown").toLowerCase().replace(/[^a-z0-9-]/g, "").slice(0, 80) || "unknown";
a.href = t.split(MAP.ph).join(s);
if (!navigator.webdriver) navigator.sendBeacon(u.href + (u.search ? "&" : "?") + "b=1");
} catch (x) { /* keep the counted link */ }
}, true);
})();