> ## Documentation Index
> Fetch the complete documentation index at: https://devzone.nayax.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Interactive Authentication Tool

export const SparkAuthTool = ({legacy = false}) => {
  const CSS = "\n:host{\n  --purple:#6352E0;\n  --purple-light:#EEECfb;\n  --purple-mid:#8B7EE8;\n  --yellow:#FCC705;\n  --yellow-bg:#FFFCE6;\n  --text:#0e0d0b;\n  --text-2:#52525b;\n  --text-3:#71717a;\n  --border:#e4e4e7;\n  --border-2:#d4d4d8;\n  --bg:#ffffff;\n  --bg-2:#fafafa;\n  --bg-3:#f4f4f5;\n  --green:#16a34a;\n  --green-bg:#f0fdf4;\n  --green-border:#bbf7d0;\n  --orange:#ea580c;\n  --orange-bg:#fff7ed;\n  --orange-border:#fed7aa;\n  --red:#dc2626;\n  --r:6px;\n  --r2:8px;\n  --r3:10px;\n}\n*{box-sizing:border-box;margin:0;padding:0}\n:host{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--text);font-size:13.5px;line-height:1.6;-webkit-font-smoothing:antialiased}\n\n/* ── Header ── */\n.hdr{height:56px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;padding:0 24px;background:var(--bg);position:sticky;top:0;z-index:50}\n.hdr-left{display:flex;align-items:center;gap:10px}\n.hdr-wordmark{display:flex;align-items:center;gap:8px}\n.hdr-wordmark span{font-size:15px;font-weight:600;color:var(--text);letter-spacing:-.01em}\n.devzone-badge{\n  font-size:11px;\n  font-weight:700;\n  background:#FCC705;\n  color:#0e0d0b;\n  padding:2px 7px;\n  border-radius:4px;\n  letter-spacing:.01em;\n  font-family:'Inter',sans-serif;\n  line-height:1.6;\n}\n.hdr-divider{width:1px;height:20px;background:var(--border);margin:0 4px}\n.hdr-tool{font-size:13px;font-weight:500;color:var(--text-2)}\n.hdr-right{display:flex;align-items:center;gap:8px}\n.hdr-link{font-size:12px;color:var(--text-3);text-decoration:none;display:flex;align-items:center;gap:4px;padding:5px 8px;border-radius:var(--r);transition:background .15s}\n.hdr-link:hover{background:var(--bg-3);color:var(--text)}\n.hdr-link svg{width:13px;height:13px;opacity:.6}\n\n/* ── Nav tabs ── */\n.nav{border-bottom:1px solid var(--border);padding:0 24px;display:flex;gap:0;background:var(--bg)}\n.nav-btn{padding:11px 14px;border:none;background:none;color:var(--text-3);cursor:pointer;font-size:12.5px;font-weight:500;font-family:inherit;border-bottom:2px solid transparent;margin-bottom:-1px;white-space:nowrap;transition:color .15s}\n.nav-btn:hover{color:var(--text)}\n.nav-btn.on{color:var(--purple);border-bottom-color:var(--purple);font-weight:600}\n\n/* ── Layout ── */\n.page{display:block;min-height:calc(100vh - 57px)}\n\n.main{margin:0 auto;padding:32px 40px;max-width:760px;min-width:0}\n\n/* ── Page header ── */\n.page-eyebrow{font-size:11px;font-weight:600;color:var(--purple);text-transform:uppercase;letter-spacing:.07em;margin-bottom:6px}\n.page-title{font-size:22px;font-weight:600;color:var(--text);letter-spacing:-.02em;margin-bottom:8px}\n.page-desc{font-size:13.5px;color:var(--text-2);line-height:1.65;margin-bottom:24px;border-bottom:1px solid var(--border);padding-bottom:20px}\n\n/* ── Sub tabs (New/Legacy) ── */\n.stabs{display:inline-flex;border:1px solid var(--border);border-radius:var(--r2);overflow:hidden;margin-bottom:20px;background:var(--bg-3)}\n.stab{padding:6px 16px;border:none;background:none;color:var(--text-3);cursor:pointer;font-size:12.5px;font-weight:500;font-family:inherit;transition:all .15s}\n.stab+.stab{border-left:1px solid var(--border)}\n.stab.on{background:var(--bg);color:var(--text);font-weight:600;box-shadow:0 1px 3px rgba(0,0,0,.08)}\n.sub{display:none}.sub.on{display:block}\n.tab-content{display:none}.tab-content.on{display:block}\n\n/* ── Callout / notice ── */\n.callout{border:1px solid var(--purple-light);border-left:3px solid var(--purple);background:var(--purple-light);border-radius:var(--r2);padding:12px 14px;font-size:12.5px;line-height:1.65;margin-bottom:20px;color:#3d3080}\n.callout.warn{border-color:var(--orange-border);border-left-color:var(--orange);background:var(--orange-bg);color:#7c2d12}\n.callout b{font-weight:600}\n.callout code{font-family:'Courier New',monospace;font-size:11.5px;background:rgba(0,0,0,.06);padding:1px 4px;border-radius:3px}\n\n/* ── How-to steps ── */\n.steps{margin-bottom:20px;display:flex;flex-direction:column;gap:8px}\n.step{display:flex;align-items:flex-start;gap:10px;font-size:12.5px;color:var(--text-2);line-height:1.6}\n.step-n{min-width:20px;height:20px;border-radius:50%;background:var(--purple);color:#fff;font-size:10px;font-weight:600;display:inline-flex;align-items:center;justify-content:center;margin-top:1px}\n.step b{color:var(--text);font-weight:600}\n\n/* ── Op selector ── */\n.op-row{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:20px}\n.op-card{padding:12px 14px;border:1px solid var(--border);background:var(--bg);border-radius:var(--r2);cursor:pointer;display:flex;align-items:center;gap:10px;transition:all .15s}\n.op-card:hover{border-color:var(--purple-mid);background:var(--purple-light)}\n.op-card.enc-on{border-color:var(--green);background:var(--green-bg)}\n.op-card.dec-on{border-color:var(--orange);background:var(--orange-bg)}\n.op-icon{width:28px;height:28px;border-radius:var(--r);display:flex;align-items:center;justify-content:center;font-size:13px;flex-shrink:0}\n.op-icon.e{background:#dcfce7;color:#15803d}\n.op-icon.d{background:#ffedd5;color:#c2410c}\n.op-card.enc-on .op-icon.e{background:#bbf7d0}\n.op-card.dec-on .op-icon.d{background:#fed7aa}\n.op-label{font-size:13px;font-weight:600;color:var(--text)}\n.op-sub{font-size:11.5px;color:var(--text-3);margin-top:1px}\n\n/* ── Section header ── */\n.sec-hd{display:flex;align-items:center;gap:8px;margin:20px 0 8px;font-size:11px;font-weight:600;color:var(--text-3);text-transform:uppercase;letter-spacing:.07em}\n.sec-hd::after{content:'';flex:1;height:1px;background:var(--border)}\n.sec-n{min-width:18px;height:18px;border-radius:50%;background:var(--purple);color:#fff;font-size:9px;font-weight:600;display:inline-flex;align-items:center;justify-content:center}\n\n/* ── Card / fields ── */\n.card{border:1px solid var(--border);border-radius:var(--r3);padding:16px;margin-bottom:12px;background:var(--bg)}\n.field{margin-bottom:12px;position:relative}.field:last-child{margin-bottom:0}\n.fl{display:flex;align-items:center;gap:5px;margin-bottom:5px}\n.fl label{font-size:12px;font-weight:500;color:var(--text-2)}\n.req{color:var(--red);margin-left:1px}\n.hint-txt{font-size:11px;font-weight:400;color:var(--text-3);margin-left:2px}\n.r2{display:grid;grid-template-columns:1fr 1fr;gap:12px}\n\n/* ── Tooltip ── */\n.tip{position:relative;display:inline-flex}\n.tip-btn{width:15px;height:15px;border-radius:50%;background:var(--bg-3);border:1px solid var(--border-2);color:var(--text-3);font-size:9px;font-weight:600;display:inline-flex;align-items:center;justify-content:center;cursor:default;line-height:1}\n.tip-btn:hover+.tip-pop,.tip-btn:focus+.tip-pop{display:block}\n.tip-pop{display:none;position:absolute;left:20px;top:-2px;width:230px;background:#18181b;color:#f4f4f5;font-size:11.5px;line-height:1.55;padding:9px 11px;border-radius:var(--r2);z-index:200;font-weight:400;letter-spacing:0;text-transform:none;pointer-events:none;box-shadow:0 4px 20px rgba(0,0,0,.3)}\n.tip-pop::before{content:'';position:absolute;left:-4px;top:9px;border:4px solid transparent;border-right-color:#18181b;border-left:0}\n\n/* ── Inputs ── */\ninput,textarea,select{width:100%;background:var(--bg);border:1px solid var(--border-2);color:var(--text);padding:7px 10px;border-radius:var(--r);font-size:13px;font-family:'Inter',sans-serif;outline:none;transition:border-color .15s,box-shadow .15s}\ninput:hover,textarea:hover,select:hover{border-color:var(--border-2)}\ninput:focus,textarea:focus,select:focus{border-color:var(--purple);box-shadow:0 0 0 3px rgba(99,82,224,.12)}\ntextarea{resize:vertical;min-height:72px;font-family:'Courier New',monospace;font-size:11.5px;line-height:1.55}\nselect{cursor:pointer;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2371717a' stroke-width='2'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E\");background-repeat:no-repeat;background-position:right 9px center;padding-right:28px;appearance:none}\n.irow{display:flex;gap:6px}.irow input{flex:1}\n.gbtn{padding:7px 12px;background:var(--bg);border:1px solid var(--border-2);color:var(--text-2);border-radius:var(--r);cursor:pointer;font-size:12px;font-weight:500;font-family:inherit;white-space:nowrap;flex-shrink:0;transition:all .15s}\n.gbtn:hover{border-color:var(--purple);color:var(--purple);background:var(--purple-light)}\n\n/* ── Buttons ── */\n.cta{width:100%;padding:10px 16px;border:none;border-radius:var(--r2);font-size:13px;font-weight:600;cursor:pointer;margin-top:8px;font-family:inherit;display:flex;align-items:center;justify-content:center;gap:7px;transition:opacity .15s,transform .1s;letter-spacing:-.01em}\n.cta:hover{opacity:.9}.cta:active{transform:scale(.99)}\n.cta.enc{background:var(--purple);color:#fff}\n.cta.dec{background:var(--text);color:#fff}\n.cta-sub{text-align:center;font-size:11.5px;color:var(--text-3);margin-top:6px}\n.ex-btn{display:inline-flex;align-items:center;gap:5px;padding:5px 12px;margin-bottom:16px;background:var(--bg);color:var(--text-2);border:1px solid var(--border-2);border-radius:var(--r);font-size:12px;font-weight:500;cursor:pointer;font-family:inherit;transition:all .15s}\n.ex-btn:hover{border-color:var(--purple);color:var(--purple);background:var(--purple-light)}\n.ex-btn span.ico{display:inline-flex;align-items:center;color:var(--purple)}\n.cta svg,.ex-btn svg{flex-shrink:0}\n\n/* ── Results ── */\n.rbox{display:none;border-radius:var(--r2);padding:16px;margin-top:12px}\n.rbox.show{display:block}\n.rbox.ok{background:var(--green-bg);border:1px solid var(--green-border)}\n.rbox.err{background:var(--orange-bg);border:1px solid var(--orange-border)}\n.rbox.spin{background:var(--purple-light);border:1px solid #c4bdf0}\n.r-ttl{font-size:11px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;margin-bottom:12px;display:flex;align-items:center;gap:6px}\n.r-ttl.ok{color:var(--green)}.r-ttl.err{color:var(--orange)}.r-ttl.spin{color:var(--purple)}\n.r-row{margin-bottom:8px}\n.r-lbl-row{display:flex;align-items:center;gap:5px;margin-bottom:3px}\n.r-lbl{font-size:10.5px;font-weight:600;color:var(--text-3);text-transform:uppercase;letter-spacing:.05em}\n.r-inner{display:flex;align-items:flex-start;gap:8px;background:var(--bg);border:1px solid var(--border);border-radius:var(--r);padding:8px 10px}\n.r-val{flex:1;font-family:'Courier New',monospace;font-size:12px;word-break:break-all;line-height:1.5;color:var(--text)}\n.r-val.pre{white-space:pre-wrap}\n.cp-btn{padding:3px 9px;background:var(--bg-3);border:1px solid var(--border);border-radius:4px;font-size:11px;font-weight:500;cursor:pointer;color:var(--text-2);font-family:inherit;flex-shrink:0;white-space:nowrap;margin-top:1px;transition:all .15s}\n.cp-btn:hover{background:var(--purple-light);border-color:var(--purple);color:var(--purple)}\n.cp-btn.done{background:var(--green-bg);border-color:var(--green-border);color:var(--green)}\n.r-div{height:1px;background:var(--border);margin:8px 0}\n@keyframes spin{to{transform:rotate(360deg)}}\n.loader{width:13px;height:13px;border:1.5px solid rgba(99,82,224,.2);border-top:1.5px solid var(--purple);border-radius:50%;animation:spin .7s linear infinite;display:inline-block;vertical-align:middle;margin-right:6px}\n\n/* ── Embedded mode (inside docs iframe): hide own chrome ── */\nhtml.embedded .hdr{display:none}\nhtml.embedded .page{min-height:0}\nhtml.embedded .main{padding-top:20px}\n\n.page{min-height:0}\n";
  const BODY = "<!-- Nav -->\n<nav class=\"nav\">\n  <button class=\"nav-btn on\" onclick=\"showTab('auth',this)\">StartAuthentication</button>\n  <button class=\"nav-btn\" onclick=\"showTab('session',this)\">StartSession</button>\n  <button class=\"nav-btn\" onclick=\"showTab('headers',this)\">Header Signature</button>\n</nav>\n\n<div class=\"page\">\n\n<!-- Main -->\n<main class=\"main\">\n\n<!-- ══ AUTH ══ -->\n<div id=\"tab-auth\" class=\"tab-content on\">\n  <div class=\"page-eyebrow\">Security &amp; Authentication</div>\n  <h1 class=\"page-title\">StartAuthentication</h1>\n  <p class=\"page-desc\">Encrypt or decrypt the cipher used in the <code style=\"font-family:monospace;font-size:12px;background:var(--bg-3);padding:2px 5px;border-radius:4px;border:1px solid var(--border)\">StartAuthentication</code> method. Choose between the current v2 flow (with timestamp) and the legacy v1 flow.</p>\n\n  <div class=\"stabs\">\n    <button class=\"stab on\" onclick=\"showSub('auth','new',this)\">Auth</button>\n    <button class=\"stab\" onclick=\"showSub('auth','leg',this)\">Legacy Auth (v1)</button>\n  </div>\n\n  <!-- NEW -->\n  <div id=\"auth-new\" class=\"sub on\">\n    <div class=\"callout\">\n      <b>New authentication (v2)</b> — Cipher plaintext: <code>TxId(36) = Random(17) + Timestamp(YYMMDDhhmm)</code>. Encrypted with AES-256-ECB using the last 32 chars of your token. Header: <code>TransactionSignature: SHA256(TxId;SignKey)</code>.\n    </div>\n\n    <div class=\"steps\">\n      <div class=\"step\"><span class=\"step-n\">1</span><span>Select <b>Encrypt</b> to generate a new cipher, or <b>Decrypt</b> to inspect an existing one.</span></div>\n      <div class=\"step\"><span class=\"step-n\">2</span><span>Enter your <b>token, token ID and sign key</b>. Hover <b>?</b> icons for field details.</span></div>\n      <div class=\"step\"><span class=\"step-n\">3</span><span>Fill in the <b>transaction details</b>. Use Generate to create the GUID and random string.</span></div>\n      <div class=\"step\"><span class=\"step-n\">4</span><span>Click <b>Generate cipher + headers</b> and copy the results.</span></div>\n    </div>\n\n    <button class=\"ex-btn\" onclick=\"loadEx('new')\"><svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"var(--purple)\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polygon points=\"13 2 3 14 12 14 11 22 21 10 12 10 13 2\"/></svg> Load example data</button>\n\n    <div class=\"op-row\">\n      <div class=\"op-card enc-on\" id=\"an-ec\" onclick=\"showOp('an','enc')\"><div class=\"op-icon e\"><svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\"/><path d=\"M7 11V7a5 5 0 0 1 10 0v4\"/></svg></div><div><div class=\"op-label\">Encrypt cipher</div><div class=\"op-sub\">Build cipher + headers</div></div></div>\n      <div class=\"op-card\" id=\"an-dc\" onclick=\"showOp('an','dec')\"><div class=\"op-icon d\"><svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\"/><path d=\"M7 11V7a5 5 0 0 1 9.9-1\"/></svg></div><div><div class=\"op-label\">Decrypt cipher</div><div class=\"op-sub\">Inspect existing cipher</div></div></div>\n    </div>\n\n    <div id=\"an-ef\">\n      <div class=\"sec-hd\"><span class=\"sec-n\">1</span>Token &amp; credentials</div>\n      <div class=\"card\">\n        <div class=\"r2\">\n          <div class=\"field\">\n            <div class=\"fl\"><label>Secret token<span class=\"req\">*</span></label><span class=\"tip\"><button class=\"tip-btn\" tabindex=\"-1\">?</button><span class=\"tip-pop\">Full secret token from the Nayax portal (usually 66 chars). The last 32 characters are used as the AES-256 encryption key.</span></span></div>\n            <input id=\"an-tok\" placeholder=\"mrV3U3nsgGFrE3w5…\">\n          </div>\n          <div class=\"field\">\n            <div class=\"fl\"><label>Token ID<span class=\"req\">*</span></label><span class=\"tip\"><button class=\"tip-btn\" tabindex=\"-1\">?</button><span class=\"tip-pop\">Numeric ID of your token. Sent as the IntegratorId HTTP header with every request.</span></span></div>\n            <input id=\"an-tid\" placeholder=\"116383\">\n          </div>\n        </div>\n        <div class=\"field\">\n          <div class=\"fl\"><label>Sign key<span class=\"req\">*</span><span class=\"hint-txt\">16 chars</span></label><span class=\"tip\"><button class=\"tip-btn\" tabindex=\"-1\">?</button><span class=\"tip-pop\">16-character secret shared by Nayax. Used to compute the TransactionSignature header: SHA256(SparkTransactionId;SignKey).</span></span></div>\n          <input id=\"an-sk\" placeholder=\"RbtdDsiVNjkAeRty\">\n        </div>\n      </div>\n      <div class=\"sec-hd\"><span class=\"sec-n\">2</span>Transaction details</div>\n      <div class=\"card\">\n        <div class=\"r2\">\n          <div class=\"field\">\n            <div class=\"fl\"><label>Terminal ID<span class=\"req\">*</span></label><span class=\"tip\"><button class=\"tip-btn\" tabindex=\"-1\">?</button><span class=\"tip-pop\">Identifier of the Nayax terminal. Format depends on the Terminal ID Type selected.</span></span></div>\n            <input id=\"an-term\" placeholder=\"0434334921100366\">\n          </div>\n          <div class=\"field\">\n            <div class=\"fl\"><label>Terminal ID type<span class=\"req\">*</span></label><span class=\"tip\"><button class=\"tip-btn\" tabindex=\"-1\">?</button><span class=\"tip-pop\">Type 1 = Hardware serial number. Type 2 = Nayax internal machine ID (MachineId).</span></span></div>\n            <select id=\"an-ttype\"><option value=\"1\">1 — HW Serial</option><option value=\"2\">2 — Nayax Machine ID</option></select>\n          </div>\n        </div>\n        <div class=\"field\">\n          <div class=\"fl\"><label>Spark transaction ID<span class=\"req\">*</span><span class=\"hint-txt\">UUID v4, 36 chars</span></label><span class=\"tip\"><button class=\"tip-btn\" tabindex=\"-1\">?</button><span class=\"tip-pop\">A UUID v4 generated by you for each transaction. Uniquely identifies this auth attempt. Format: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx.</span></span></div>\n          <div class=\"irow\"><input id=\"an-txid\" placeholder=\"12c7cec2-c690-4425-9a1f-db0db60e2d8c\"><button class=\"gbtn\" onclick=\"fill('an-txid',mkGuid())\">Generate</button></div>\n        </div>\n        <div class=\"field\">\n          <div class=\"fl\"><label>Random string<span class=\"req\">*</span><span class=\"hint-txt\">exactly 17 chars</span></label><span class=\"tip\"><button class=\"tip-btn\" tabindex=\"-1\">?</button><span class=\"tip-pop\">A random 17-character alphanumeric string you generate per request. Included in the cipher plaintext to prevent replay attacks.</span></span></div>\n          <div class=\"irow\"><input id=\"an-rand\" placeholder=\"123456789qwertyui\" maxlength=\"17\"><button class=\"gbtn\" onclick=\"fill('an-rand',mkRand(17))\">Generate</button></div>\n        </div>\n      </div>\n      <button class=\"cta enc\" onclick=\"doAnEnc()\"><svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\"/><path d=\"M7 11V7a5 5 0 0 1 10 0v4\"/></svg>Generate cipher + headers</button>\n      <p class=\"cta-sub\">Fill in all fields above, then click to encrypt and get your ready-to-send request body and headers.</p>\n      <div id=\"an-er\" class=\"rbox\"></div>\n    </div>\n\n    <div id=\"an-df\" style=\"display:none\">\n      <div class=\"card\">\n        <div class=\"field\">\n          <div class=\"fl\"><label>Secret token<span class=\"req\">*</span></label><span class=\"tip\"><button class=\"tip-btn\" tabindex=\"-1\">?</button><span class=\"tip-pop\">The token used when this cipher was encrypted. Last 32 chars = AES-256 key.</span></span></div>\n          <input id=\"and-tok\" placeholder=\"mrV3U3nsgGFrE3w5…\">\n        </div>\n        <div class=\"field\">\n          <div class=\"fl\"><label>Cipher (Base64)<span class=\"req\">*</span></label><span class=\"tip\"><button class=\"tip-btn\" tabindex=\"-1\">?</button><span class=\"tip-pop\">The Base64 AES-256-ECB cipher from the request body. Decrypts to: TxId(36)=Random(17)+Timestamp(10).</span></span></div>\n          <textarea id=\"and-cip\" rows=\"3\" placeholder=\"X305dITNTAw2vHsxE+taVcn6UvgBC3fd…\"></textarea>\n        </div>\n      </div>\n      <button class=\"cta dec\" onclick=\"doAnDec()\"><svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\"/><path d=\"M7 11V7a5 5 0 0 1 9.9-1\"/></svg>Decrypt cipher</button>\n      <p class=\"cta-sub\">Result shows the transaction ID, random string and timestamp extracted from the cipher.</p>\n      <div id=\"an-dr\" class=\"rbox\"></div>\n    </div>\n  </div>\n\n  <!-- LEGACY -->\n  <div id=\"auth-leg\" class=\"sub\">\n    <div class=\"callout warn\">\n      <b>Legacy authentication (v1)</b> — For existing integrations only. Cipher: <code>TxId(36)=Random(17)</code> — no timestamp. Header: <code>Signature: SHA256(minifiedJSON;HeaderHashKey)</code>.\n    </div>\n    <div class=\"steps\">\n      <div class=\"step\"><span class=\"step-n\">1</span><span>Enter your <b>token, integrator ID and header hash key</b> from the Nayax portal.</span></div>\n      <div class=\"step\"><span class=\"step-n\">2</span><span>Fill in the <b>transaction fields</b> and use Generate for the GUID and random string.</span></div>\n      <div class=\"step\"><span class=\"step-n\">3</span><span>Click <b>Build cipher + legacy signature</b> to get the cipher, minified body and Signature header.</span></div>\n    </div>\n    <button class=\"ex-btn\" onclick=\"loadEx('leg')\"><svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"var(--purple)\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polygon points=\"13 2 3 14 12 14 11 22 21 10 12 10 13 2\"/></svg> Load example data</button>\n    <div class=\"op-row\">\n      <div class=\"op-card enc-on\" id=\"al-ec\" onclick=\"showOp('al','enc')\"><div class=\"op-icon e\">🔒</div><div><div class=\"op-label\">Build cipher + signature</div><div class=\"op-sub\">Legacy encrypt flow</div></div></div>\n      <div class=\"op-card\" id=\"al-dc\" onclick=\"showOp('al','dec')\"><div class=\"op-icon d\">🔓</div><div><div class=\"op-label\">Decrypt cipher</div><div class=\"op-sub\">Inspect legacy cipher</div></div></div>\n    </div>\n    <div id=\"al-ef\">\n      <div class=\"card\">\n        <div class=\"r2\">\n          <div class=\"field\"><div class=\"fl\"><label>Secret token<span class=\"req\">*</span></label><span class=\"tip\"><button class=\"tip-btn\" tabindex=\"-1\">?</button><span class=\"tip-pop\">Your full secret token. Last 32 chars = AES-256 key.</span></span></div><input id=\"al-tok\" placeholder=\"mrV3U3nsgGFrE3w5…\"></div>\n          <div class=\"field\"><div class=\"fl\"><label>Integrator ID<span class=\"req\">*</span></label><span class=\"tip\"><button class=\"tip-btn\" tabindex=\"-1\">?</button><span class=\"tip-pop\">Your numeric integrator ID. Sent as the IntegratorId HTTP header.</span></span></div><input id=\"al-int\" placeholder=\"927\"></div>\n        </div>\n        <div class=\"field\"><div class=\"fl\"><label>Header hash key<span class=\"req\">*</span><span class=\"hint-txt\">16 chars</span></label><span class=\"tip\"><button class=\"tip-btn\" tabindex=\"-1\">?</button><span class=\"tip-pop\">Legacy 16-char key for computing: Signature = SHA256(minifiedRequestBodyJSON;HeaderHashKey).</span></span></div><input id=\"al-hk\" placeholder=\"RbtdDsiVNjkAeRty\"></div>\n      </div>\n      <div class=\"card\">\n        <div class=\"r2\">\n          <div class=\"field\"><div class=\"fl\"><label>Token ID<span class=\"req\">*</span></label><span class=\"tip\"><button class=\"tip-btn\" tabindex=\"-1\">?</button><span class=\"tip-pop\">Numeric token ID. Included in the request body JSON.</span></span></div><input id=\"al-tid\" placeholder=\"116383\"></div>\n          <div class=\"field\"><div class=\"fl\"><label>Terminal ID<span class=\"req\">*</span></label><span class=\"tip\"><button class=\"tip-btn\" tabindex=\"-1\">?</button><span class=\"tip-pop\">The vending machine terminal identifier.</span></span></div><input id=\"al-term\" placeholder=\"0434334921100366\"></div>\n        </div>\n        <div class=\"r2\">\n          <div class=\"field\"><div class=\"fl\"><label>Terminal ID type</label><span class=\"tip\"><button class=\"tip-btn\" tabindex=\"-1\">?</button><span class=\"tip-pop\">1 = Hardware serial. 2 = Nayax internal Machine ID.</span></span></div><select id=\"al-ttype\"><option value=\"1\">1 — HW Serial</option><option value=\"2\">2 — Nayax Machine ID</option></select></div>\n          <div class=\"field\"><div class=\"fl\"><label>Spark transaction ID<span class=\"req\">*</span></label><span class=\"tip\"><button class=\"tip-btn\" tabindex=\"-1\">?</button><span class=\"tip-pop\">UUID v4 generated by you. 36 chars including hyphens.</span></span></div><div class=\"irow\"><input id=\"al-txid\" placeholder=\"12c7cec2…\"><button class=\"gbtn\" onclick=\"fill('al-txid',mkGuid())\">Gen</button></div></div>\n        </div>\n        <div class=\"field\"><div class=\"fl\"><label>Random string<span class=\"req\">*</span><span class=\"hint-txt\">17 chars</span></label><span class=\"tip\"><button class=\"tip-btn\" tabindex=\"-1\">?</button><span class=\"tip-pop\">17-char random alphanumeric. Legacy cipher = TxId=Random (no timestamp).</span></span></div><div class=\"irow\"><input id=\"al-rand\" placeholder=\"123456789qwertyui\" maxlength=\"17\"><button class=\"gbtn\" onclick=\"fill('al-rand',mkRand(17))\">Generate</button></div></div>\n      </div>\n      <button class=\"cta enc\" onclick=\"doAlEnc()\"><svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\"/><path d=\"M7 11V7a5 5 0 0 1 10 0v4\"/></svg>Generate cipher + legacy signature</button>\n      <p class=\"cta-sub\">Returns the cipher, minified request body, and Signature header value.</p>\n      <div id=\"al-er\" class=\"rbox\"></div>\n    </div>\n    <div id=\"al-df\" style=\"display:none\">\n      <div class=\"card\">\n        <div class=\"field\"><div class=\"fl\"><label>Secret token<span class=\"req\">*</span></label><span class=\"tip\"><button class=\"tip-btn\" tabindex=\"-1\">?</button><span class=\"tip-pop\">Token used to encrypt. Last 32 chars = AES-256 key.</span></span></div><input id=\"ald-tok\" placeholder=\"mrV3U3nsgGFrE3w5…\"></div>\n        <div class=\"field\"><div class=\"fl\"><label>Cipher (Base64)<span class=\"req\">*</span></label><span class=\"tip\"><button class=\"tip-btn\" tabindex=\"-1\">?</button><span class=\"tip-pop\">Legacy cipher decrypts to TxId(36)=Random(17) — no timestamp.</span></span></div><textarea id=\"ald-cip\" rows=\"3\" placeholder=\"X305dITNTAw2…\"></textarea></div>\n      </div>\n      <button class=\"cta dec\" onclick=\"doAlDec()\"><svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\"/><path d=\"M7 11V7a5 5 0 0 1 9.9-1\"/></svg>Decrypt cipher</button>\n      <p class=\"cta-sub\">Legacy ciphers contain Transaction ID and Random string only — no timestamp.</p>\n      <div id=\"al-dr\" class=\"rbox\"></div>\n    </div>\n  </div>\n</div>\n\n<!-- ══ SESSION ══ -->\n<div id=\"tab-session\" class=\"tab-content\">\n  <div class=\"page-eyebrow\">Security &amp; Authentication</div>\n  <h1 class=\"page-title\">StartSession</h1>\n  <p class=\"page-desc\">Build or inspect the cipher for the <code style=\"font-family:monospace;font-size:12px;background:var(--bg-3);padding:2px 5px;border-radius:4px;border:1px solid var(--border)\">StartSession</code> callback. Nayax calls your endpoint — you respond with an encrypted cipher.</p>\n  <div class=\"callout\">\n    <b>Flow:</b> Nayax POSTs to your endpoint with HwSerial, MachineId, TokenId and a 27-char Random. You respond with a cipher: <code>TxId(36)=Random(27)</code> → AES-256-ECB. No timestamp in StartSession cipher.\n  </div>\n  <div class=\"steps\">\n    <div class=\"step\"><span class=\"step-n\">1</span><span><b>Nayax calls your endpoint.</b> Enter the values they send (HwSerial, MachineId, TokenId, Random) in section 1.</span></div>\n    <div class=\"step\"><span class=\"step-n\">2</span><span>In section 2, enter your <b>secret token</b> and generate a <b>36-digit transaction ID</b>.</span></div>\n    <div class=\"step\"><span class=\"step-n\">3</span><span>Click <b>Generate StartSession cipher</b>. Return the response body JSON to Nayax.</span></div>\n  </div>\n  <button class=\"ex-btn\" onclick=\"loadEx('ss')\"><svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"var(--purple)\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polygon points=\"13 2 3 14 12 14 11 22 21 10 12 10 13 2\"/></svg> Load example data</button>\n  <div class=\"op-row\">\n    <div class=\"op-card enc-on\" id=\"ss-ec\" onclick=\"showOp('ss','enc')\"><div class=\"op-icon e\"><svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\"/><path d=\"M7 11V7a5 5 0 0 1 10 0v4\"/></svg></div><div><div class=\"op-label\">Build response cipher</div><div class=\"op-sub\">Respond to Nayax call</div></div></div>\n    <div class=\"op-card\" id=\"ss-dc\" onclick=\"showOp('ss','dec')\"><div class=\"op-icon d\"><svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\"/><path d=\"M7 11V7a5 5 0 0 1 9.9-1\"/></svg></div><div><div class=\"op-label\">Decrypt cipher</div><div class=\"op-sub\">Inspect StartSession cipher</div></div></div>\n  </div>\n  <div id=\"ss-ef\">\n    <div class=\"sec-hd\"><span class=\"sec-n\">1</span>Nayax sends you</div>\n    <div class=\"card\">\n      <div class=\"r2\">\n        <div class=\"field\"><div class=\"fl\"><label>HwSerial</label><span class=\"tip\"><button class=\"tip-btn\" tabindex=\"-1\">?</button><span class=\"tip-pop\">Hardware serial number of the Nayax device, sent in the StartSession request.</span></span></div><input id=\"ss-hw\" placeholder=\"0434334921100366\"></div>\n        <div class=\"field\"><div class=\"fl\"><label>MachineId</label><span class=\"tip\"><button class=\"tip-btn\" tabindex=\"-1\">?</button><span class=\"tip-pop\">Nayax internal numeric machine ID, sent in the StartSession call.</span></span></div><input id=\"ss-mid\" placeholder=\"71234996\"></div>\n      </div>\n      <div class=\"r2\">\n        <div class=\"field\"><div class=\"fl\"><label>TokenId<span class=\"req\">*</span></label><span class=\"tip\"><button class=\"tip-btn\" tabindex=\"-1\">?</button><span class=\"tip-pop\">Numeric token ID sent by Nayax so you know which key to use in your cipher response.</span></span></div><input id=\"ss-tid\" placeholder=\"116383\"></div>\n        <div class=\"field\"><div class=\"fl\"><label>Random from Nayax<span class=\"req\">*</span><span class=\"hint-txt\">27 chars</span></label><span class=\"tip\"><button class=\"tip-btn\" tabindex=\"-1\">?</button><span class=\"tip-pop\">27-char random string sent by Nayax. Must appear in your cipher response: TxId=Random(27).</span></span></div><div class=\"irow\"><input id=\"ss-rand\" placeholder=\"123456789qwertyuioasdfghjkl\" maxlength=\"27\"><button class=\"gbtn\" onclick=\"fill('ss-rand',mkRand(27))\">Gen</button></div></div>\n      </div>\n    </div>\n    <div class=\"sec-hd\"><span class=\"sec-n\">2</span>Your response</div>\n    <div class=\"card\">\n      <div class=\"field\"><div class=\"fl\"><label>Secret token<span class=\"req\">*</span></label><span class=\"tip\"><button class=\"tip-btn\" tabindex=\"-1\">?</button><span class=\"tip-pop\">Your secret token for the TokenId Nayax sent. Last 32 chars = AES-256 key.</span></span></div><input id=\"ss-tok\" placeholder=\"mrV3U3nsgGFrE3w5…\"></div>\n      <div class=\"field\"><div class=\"fl\"><label>Spark transaction ID<span class=\"req\">*</span><span class=\"hint-txt\">36 numeric chars</span></label><span class=\"tip\"><button class=\"tip-btn\" tabindex=\"-1\">?</button><span class=\"tip-pop\">A 36-digit numeric ID you generate for this transaction. All digits, not a GUID. Included in your cipher and returned to Nayax.</span></span></div><div class=\"irow\"><input id=\"ss-txid\" placeholder=\"123456789012345678901234567890123456\" maxlength=\"36\"><button class=\"gbtn\" onclick=\"fill('ss-txid',mkNumId())\">Generate</button></div></div>\n    </div>\n    <button class=\"cta enc\" onclick=\"doSsEnc()\"><svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\"/><path d=\"M7 11V7a5 5 0 0 1 10 0v4\"/></svg>Generate StartSession cipher</button>\n    <p class=\"cta-sub\">Returns the Cipher and full JSON response body to send back to Nayax.</p>\n    <div id=\"ss-er\" class=\"rbox\"></div>\n  </div>\n  <div id=\"ss-df\" style=\"display:none\">\n    <div class=\"card\">\n      <div class=\"field\"><div class=\"fl\"><label>Secret token<span class=\"req\">*</span></label><span class=\"tip\"><button class=\"tip-btn\" tabindex=\"-1\">?</button><span class=\"tip-pop\">The secret token used to encrypt this cipher. Last 32 chars = AES-256 key.</span></span></div><input id=\"ssd-tok\" placeholder=\"mrV3U3nsgGFrE3w5…\"></div>\n      <div class=\"field\"><div class=\"fl\"><label>Cipher (Base64)<span class=\"req\">*</span></label><span class=\"tip\"><button class=\"tip-btn\" tabindex=\"-1\">?</button><span class=\"tip-pop\">The StartSession cipher. Decrypts to: TxId(36)=Random(27).</span></span></div><textarea id=\"ssd-cip\" rows=\"3\" placeholder=\"a0Qnxm4fWMskzFXi…\"></textarea></div>\n    </div>\n    <button class=\"cta dec\" onclick=\"doSsDec()\"><svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\"/><path d=\"M7 11V7a5 5 0 0 1 9.9-1\"/></svg>Decrypt cipher</button>\n    <div id=\"ss-dr\" class=\"rbox\"></div>\n  </div>\n</div>\n\n<!-- ══ HEADERS ══ -->\n<div id=\"tab-headers\" class=\"tab-content\">\n  <div class=\"page-eyebrow\">Security &amp; Authentication</div>\n  <h1 class=\"page-title\">Header Signature</h1>\n  <p class=\"page-desc\">Calculate the HTTP authentication headers for StartAuthentication requests. Choose between the current v2 TransactionSignature and the legacy v1 Signature flow.</p>\n  <div class=\"stabs\">\n    <button class=\"stab on\" onclick=\"showSub('hdr','new',this)\">New auth header</button>\n    <button class=\"stab\" onclick=\"showSub('hdr','leg',this)\">Legacy signature</button>\n  </div>\n  <div id=\"hdr-new\" class=\"sub on\">\n    <div class=\"callout\">Computes <code>TransactionSignature</code> for New Auth (v2): <code>SHA256(SparkTransactionId;SignKey)</code></div>\n    <div class=\"steps\">\n      <div class=\"step\"><span class=\"step-n\">1</span><span>Enter the <b>Spark transaction ID</b> (same GUID used in the request body) and your <b>sign key</b>.</span></div>\n      <div class=\"step\"><span class=\"step-n\">2</span><span>Click <b>Calculate headers</b> to get the IntegratorId and TransactionSignature values.</span></div>\n    </div>\n    <button class=\"ex-btn\" onclick=\"loadEx('hdr-new')\"><svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"var(--purple)\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polygon points=\"13 2 3 14 12 14 11 22 21 10 12 10 13 2\"/></svg> Load example data</button>\n    <div class=\"card\">\n      <div class=\"field\"><div class=\"fl\"><label>Spark transaction ID<span class=\"req\">*</span></label><span class=\"tip\"><button class=\"tip-btn\" tabindex=\"-1\">?</button><span class=\"tip-pop\">The same UUID v4 used in the request body. Signature = SHA256(this;SignKey).</span></span></div><input id=\"hn-txid\" placeholder=\"12c7cec2-c690-4425-9a1f-db0db60e2d8c\"></div>\n      <div class=\"r2\">\n        <div class=\"field\"><div class=\"fl\"><label>Sign key<span class=\"req\">*</span></label><span class=\"tip\"><button class=\"tip-btn\" tabindex=\"-1\">?</button><span class=\"tip-pop\">16-char secret key from Nayax. Combined with TxId to produce the TransactionSignature header.</span></span></div><input id=\"hn-sk\" placeholder=\"RbtdDsiVNjkAeRty\"></div>\n        <div class=\"field\"><div class=\"fl\"><label>Integrator ID<span class=\"req\">*</span></label><span class=\"tip\"><button class=\"tip-btn\" tabindex=\"-1\">?</button><span class=\"tip-pop\">Your numeric integrator ID. Sent as the IntegratorId HTTP header.</span></span></div><input id=\"hn-int\" placeholder=\"927\"></div>\n      </div>\n    </div>\n    <button class=\"cta enc\" onclick=\"doHdrNew()\"><svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"22 12 18 12 15 21 9 3 6 12 2 12\"/></svg>Calculate headers</button>\n    <p class=\"cta-sub\">Returns both HTTP headers ready to attach to your request.</p>\n    <div id=\"hdr-new-r\" class=\"rbox\"></div>\n  </div>\n  <div id=\"hdr-leg\" class=\"sub\">\n    <div class=\"callout warn\">Computes <code>Signature</code> for Legacy Auth (v1): <code>SHA256(minifiedJSON;HeaderHashKey)</code></div>\n    <div class=\"steps\">\n      <div class=\"step\"><span class=\"step-n\">1</span><span>Paste the <b>full request body JSON</b> — it's minified automatically before hashing.</span></div>\n      <div class=\"step\"><span class=\"step-n\">2</span><span>Enter the <b>header hash key</b> and integrator ID, then click <b>Calculate legacy signature</b>.</span></div>\n    </div>\n    <button class=\"ex-btn\" onclick=\"loadEx('hdr-leg')\"><svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"var(--purple)\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polygon points=\"13 2 3 14 12 14 11 22 21 10 12 10 13 2\"/></svg> Load example data</button>\n    <div class=\"card\">\n      <div class=\"field\"><div class=\"fl\"><label>Request body JSON<span class=\"req\">*</span></label><span class=\"tip\"><button class=\"tip-btn\" tabindex=\"-1\">?</button><span class=\"tip-pop\">Full request body you're sending. Auto-minified via JSON.stringify() before hashing. Must exactly match the HTTP body.</span></span></div><textarea id=\"hl-body\" rows=\"4\" placeholder='{\"TokenId\":116383,\"TerminalId\":\"0434330725173725\",\"TerminalIdType\":2,\"Random\":\"21gVJ9wihLpFaU25B\",\"Cipher\":\"637A0+...\"}'></textarea></div>\n      <div class=\"r2\">\n        <div class=\"field\"><div class=\"fl\"><label>Header hash key<span class=\"req\">*</span></label><span class=\"tip\"><button class=\"tip-btn\" tabindex=\"-1\">?</button><span class=\"tip-pop\">16-char key from Nayax for legacy auth. Used to compute: SHA256(minifiedBody;HeaderHashKey).</span></span></div><input id=\"hl-hk\" placeholder=\"RbtdDsiVNjkAeRty\"></div>\n        <div class=\"field\"><div class=\"fl\"><label>Integrator ID<span class=\"req\">*</span></label><span class=\"tip\"><button class=\"tip-btn\" tabindex=\"-1\">?</button><span class=\"tip-pop\">Your numeric integrator ID sent as IntegratorId HTTP header.</span></span></div><input id=\"hl-int\" placeholder=\"927\"></div>\n      </div>\n    </div>\n    <button class=\"cta dec\" onclick=\"doHdrLeg()\"><svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"22 12 18 12 15 21 9 3 6 12 2 12\"/></svg>Calculate legacy signature</button>\n    <p class=\"cta-sub\">Returns the minified JSON and Signature header value.</p>\n    <div id=\"hdr-leg-r\" class=\"rbox\"></div>\n  </div>\n</div>\n\n</main>\n</div><!-- /page -->";
  const SCRIPT = "\nconst LOCK_ENC='<svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\"/><path d=\"M7 11V7a5 5 0 0 1 10 0v4\"/></svg>';\nconst LOCK_DEC='<svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\"/><path d=\"M7 11V7a5 5 0 0 1 9.9-1\"/></svg>';\n\nconst TE=new TextEncoder(),TD=new TextDecoder();\nfunction toB64(u8){let s='';for(let i=0;i<u8.length;i++)s+=String.fromCharCode(u8[i]);return btoa(s);}\nfunction frB64(s){const b=atob(s),u=new Uint8Array(b.length);for(let i=0;i<b.length;i++)u[i]=b.charCodeAt(i);return u;}\nfunction pkpad(u8){const p=16-(u8.length%16),o=new Uint8Array(u8.length+p);o.set(u8);o.fill(p,u8.length);return o;}\nfunction pkunpad(u8){const p=u8[u8.length-1];if(p<1||p>16)throw new Error('Bad PKCS7 padding — wrong token?');return u8.slice(0,u8.length-p);}\nasync function aesEnc(plain,token){\n  if(token.length<32)throw new Error('Token needs ≥32 chars.');\n  const key=await crypto.subtle.importKey('raw',TE.encode(token.slice(-32)),{name:'AES-CBC'},false,['encrypt']);\n  const iv=new Uint8Array(16),src=pkpad(TE.encode(plain)),out=new Uint8Array(src.length);\n  for(let i=0;i<src.length;i+=16){const ct=await crypto.subtle.encrypt({name:'AES-CBC',iv},key,src.slice(i,i+16));out.set(new Uint8Array(ct).slice(0,16),i);}\n  return toB64(out);\n}\nasync function aesDec(b64,token){\n  if(token.length<32)throw new Error('Token needs ≥32 chars.');\n  const kb=TE.encode(token.slice(-32));\n  const eKey=await crypto.subtle.importKey('raw',kb,{name:'AES-CBC'},false,['encrypt']);\n  const dKey=await crypto.subtle.importKey('raw',kb,{name:'AES-CBC'},false,['decrypt']);\n  const iv=new Uint8Array(16),src=frB64(b64);\n  if(src.length%16!==0)throw new Error('Cipher length not multiple of 16.');\n  const out=new Uint8Array(src.length);\n  for(let i=0;i<src.length;i+=16){\n    const C=src.slice(i,i+16),p16=new Uint8Array(16);p16.fill(0x10);\n    for(let j=0;j<16;j++)p16[j]^=C[j];\n    const dum=new Uint8Array(await crypto.subtle.encrypt({name:'AES-CBC',iv},eKey,p16)).slice(0,16);\n    const two=new Uint8Array(32);two.set(C,0);two.set(dum,16);\n    out.set(new Uint8Array(await crypto.subtle.decrypt({name:'AES-CBC',iv},dKey,two)).slice(0,16),i);\n  }\n  return TD.decode(pkunpad(out));\n}\nasync function sha256(s){const h=await crypto.subtle.digest('SHA-256',TE.encode(s));return Array.from(new Uint8Array(h)).map(b=>b.toString(16).padStart(2,'0')).join('');}\n\n// ═══════════════════════════════\n// HELPERS\n// ═══════════════════════════════\nconst EX={tok:'ducBfFglprWYPUvzbSB2RBqtS8KepkYQG7JjGT2_Wg1C-fpAM4QyH8np1y8q-JA00',tid:'125873',sk:'Tis73hdl39dyrh48',term:'0434330725173725',ttype:'2',txid:'1baeb893-7917-4a33-b3df-8c2b59cab98c',r17:'21gVJ9wihLpFaU25B',r27:'21gVJ9wihLpFaU25Babcdefghij',int:'927',nid:'123456789012345678901234567890123456'};\nfunction utcTs(){const n=new Date(),p=v=>String(v).padStart(2,'0');return String(n.getUTCFullYear()).slice(-2)+p(n.getUTCMonth()+1)+p(n.getUTCDate())+p(n.getUTCHours())+p(n.getUTCMinutes());}\nfunction mkGuid(){return'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g,c=>{const r=Math.random()*16|0;return(c==='x'?r:(r&3|8)).toString(16);});}\nfunction mkRand(n){const c='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';return Array.from({length:n},()=>c[~~(Math.random()*c.length)]).join('');}\nfunction mkNumId(){return Array.from({length:36},()=>~~(Math.random()*10)).join('');}\nfunction g(id){return (window.__satRoot||document).getElementById(id)||{style:{},classList:{add:function(){},remove:function(){}},value:\"\"};}\nfunction v(id){return g(id).value.trim();}\nfunction fill(id,val){g(id).value=val;}\nfunction esc(s){return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');}\nfunction loadEx(w){\n  if(w==='new'){fill('an-tok',EX.tok);fill('an-tid',EX.tid);fill('an-sk',EX.sk);fill('an-term',EX.term);g('an-ttype').value=EX.ttype;fill('an-txid',EX.txid);fill('an-rand',EX.r17);}\n  else if(w==='leg'){fill('al-tok',EX.tok);fill('al-int',EX.int);fill('al-hk',EX.sk);fill('al-tid',EX.tid);fill('al-term',EX.term);g('al-ttype').value=EX.ttype;fill('al-txid',EX.txid);fill('al-rand',EX.r17);}\n  else if(w==='ss'){fill('ss-hw',EX.term);fill('ss-mid','71234996');fill('ss-tid',EX.tid);fill('ss-rand',EX.r27);fill('ss-tok',EX.tok);fill('ss-txid',EX.nid);}\n  else if(w==='hdr-new'){fill('hn-txid',EX.txid);fill('hn-sk',EX.sk);fill('hn-int',EX.int);}\n  else if(w==='hdr-leg'){fill('hl-body',JSON.stringify({TokenId:125873,TerminalId:EX.term,TerminalIdType:2,Random:EX.r17,Cipher:'637A0+D5EWvqZzbRL07O3DykMknT/gcj6BD1wywMc8e+lBqAu0Kht0f05SU9Lk5TeeQQKfJEoAYF4wUHVi9dRA=='}));fill('hl-hk',EX.sk);fill('hl-int',EX.int);}\n}\n\n// ═══════════════════════════════\n// UI\n// ═══════════════════════════════\nfunction showTab(name,btn){\n  (window.__satRoot||document).querySelectorAll('.nav-btn').forEach(b=>b.classList.remove('on'));\n  (window.__satRoot||document).querySelectorAll('.tab-content').forEach(t=>t.classList.remove('on'));\n  btn.classList.add('on');g('tab-'+name).classList.add('on');\n  g('sb-session-group').style.display=name==='session'?'':'none';\n  g('sb-hdr-group').style.display=name==='headers'?'':'none';\n  (window.__satRoot||document).querySelectorAll('.sidebar-item').forEach(b=>b.classList.remove('on'));\n  if(name==='auth'){g('sb-new').classList.add('on');}\n  else if(name==='session'){g('sb-ss-enc').classList.add('on');}\n  else if(name==='headers'){g('sb-hdr-new').classList.add('on');}\n}\nfunction showSub(grp,name,btn){\n  if(btn){btn.closest('.stabs').querySelectorAll('.stab').forEach(b=>b.classList.remove('on'));btn.classList.add('on');}\n  const parent=grp==='hdr'?g('tab-headers'):g('tab-auth');\n  parent.querySelectorAll('.sub').forEach(s=>s.classList.remove('on'));\n  g(grp+'-'+name).classList.add('on');\n  if(grp==='auth'){(window.__satRoot||document).querySelectorAll('.sidebar-item').forEach(b=>b.classList.remove('on'));g('sb-'+name).classList.add('on');}\n}\nfunction showOp(pre,mode){\n  const ec=g(pre+'-ec'),dc=g(pre+'-dc'),ef=g(pre+'-ef'),df=g(pre+'-df');\n  if(mode==='enc'){ec.className='op-card enc-on';dc.className='op-card';ef.style.display='';df.style.display='none';}\n  else{dc.className='op-card dec-on';ec.className='op-card';df.style.display='';ef.style.display='none';}\n}\nfunction sideNav(name){showSub('auth',name,null);(window.__satRoot||document).querySelectorAll('.sidebar-item').forEach(b=>b.classList.remove('on'));g('sb-'+name).classList.add('on');}\nfunction sideNavSS(mode){showOp('ss',mode);(window.__satRoot||document).querySelectorAll('#sb-session-group .sidebar-item').forEach(b=>b.classList.remove('on'));g('sb-ss-'+mode).classList.add('on');}\nfunction sideNavHdr(name){showSub('hdr',name,null);(window.__satRoot||document).querySelectorAll('#sb-hdr-group .sidebar-item').forEach(b=>b.classList.remove('on'));g('sb-hdr-'+name).classList.add('on');}\n\n// Results\nlet cpIdx=0;const cpStore={};\nfunction spin(id){const el=g(id);el.className='rbox show spin';el.innerHTML='<div class=\"r-ttl spin\"><span class=\"loader\"></span>Processing…</div>';}\nfunction errBox(id,msg){const el=g(id);el.className='rbox show err';el.innerHTML='<div class=\"r-ttl err\">Error</div><div style=\"font-size:12.5px;color:var(--orange);word-break:break-word\">'+esc(msg)+'</div>';}\nfunction rRow(label,val,pre,tip){\n  const k='cp'+(cpIdx++);cpStore[k]=val;\n  const tipHtml=tip?'<span class=\"tip\"><button class=\"tip-btn\" tabindex=\"-1\">?</button><span class=\"tip-pop\">'+tip+'</span></span>':'';\n  return'<div class=\"r-row\"><div class=\"r-lbl-row\"><span class=\"r-lbl\">'+esc(label)+'</span>'+tipHtml+'</div>'\n    +'<div class=\"r-inner\"><span class=\"r-val'+(pre?' pre':'')+'\">'+(pre?'<span style=\"font-size:11.5px\">'+esc(val)+'</span>':esc(val))+'</span>'\n    +'<button class=\"cp-btn\" id=\"'+k+'\" onclick=\"doCopy(\\''+k+'\\')\">Copy</button></div></div>';\n}\nfunction rDiv(){return'<div class=\"r-div\"></div>';}\nfunction okBox(id,rows){\n  const el=g(id);el.className='rbox show ok';\n  let h='<div class=\"r-ttl ok\"><svg width=\"13\" height=\"13\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\" style=\"margin-right:4px;vertical-align:middle\"><path d=\"M20 6L9 17l-5-5\"/></svg>Result</div>';\n  rows.forEach(r=>{h+=r.div?rDiv():rRow(r.l,r.v,r.pre,r.tip);});\n  el.innerHTML=h;\n}\nfunction doCopy(k){\n  const val=cpStore[k],btn=g(k);\n  const ta=document.createElement('textarea');ta.value=val;ta.style.cssText='position:fixed;left:-9999px;opacity:0';\n  document.body.appendChild(ta);ta.focus();ta.select();\n  try{document.execCommand('copy');}catch(e){}\n  document.body.removeChild(ta);\n  if(navigator.clipboard)navigator.clipboard.writeText(val).catch(()=>{});\n  btn.textContent='Copied';btn.classList.add('done');\n  setTimeout(()=>{btn.textContent='Copy';btn.classList.remove('done');},1500);\n}\n\n// ═══════════════════════════════\n// ACTIONS\n// ═══════════════════════════════\nasync function doAnEnc(){\n  spin('an-er');\n  try{\n    const tok=v('an-tok'),tid=v('an-tid'),sk=v('an-sk'),term=v('an-term'),ttype=v('an-ttype'),txid=v('an-txid'),rand=v('an-rand');\n    if(!tok||!tid||!sk||!term||!txid||!rand)throw new Error('Please fill all required fields.');\n    if(txid.length!==36)throw new Error('Transaction ID must be 36 chars (got '+txid.length+').');\n    if(rand.length!==17)throw new Error('Random must be exactly 17 chars (got '+rand.length+').');\n    const stamp=utcTs(),plain=txid+'='+rand+stamp,cipher=await aesEnc(plain,tok);\n    const sig=await sha256(txid+';'+sk),hashed=await sha256(txid);\n    const body={TokenId:parseInt(tid),TerminalId:term,TerminalIdType:parseInt(ttype),Random:rand,Cipher:cipher,SparkTransactionId:txid};\n    okBox('an-er',[\n      {l:'Encryption key (last 32 of token)',v:tok.slice(-32),tip:'AES-256 key derived from your token.'},\n      {l:'Plaintext (before encryption)',v:plain,tip:'Exact string encrypted. Format: TxId=Random+Timestamp.'},\n      {l:'Timestamp (UTC YYMMDDhhmm)',v:stamp,tip:'UTC time appended to cipher plaintext. Nayax validates this is recent.'},\n      {l:'Cipher (Base64)',v:cipher,tip:'Put this in the Cipher field of your request body.'},\n      {div:1},\n      {l:'IntegratorId header',v:tid,tip:'HTTP header: IntegratorId: <value>'},\n      {l:'TransactionSignature header',v:sig,tip:'HTTP header: TransactionSignature: <value>. SHA256(SparkTransactionId;SignKey).'},\n      {div:1},\n      {l:'Full request body',v:JSON.stringify(body,null,2),pre:1,tip:'Complete JSON body for your POST to StartAuthentication.'},\n      {l:'Expected HashedSparkTransactionId (in Nayax response)',v:hashed,tip:'Verify Nayax response authenticity: SHA256(SparkTransactionId).'},\n    ]);\n  }catch(e){errBox('an-er',e.message);}\n}\nasync function doAnDec(){\n  spin('an-dr');\n  try{\n    const tok=v('and-tok'),cip=v('and-cip');\n    if(!tok||!cip)throw new Error('Token and cipher are required.');\n    const p=await aesDec(cip,tok);\n    const rows=[{l:'Decrypted plaintext',v:p,tip:'Full decrypted string: TxId(36)=Random(17)+Timestamp(10).'}];\n    if(p.length>=54&&p[36]==='='){\n      const txid=p.slice(0,36),rand=p.slice(37,54),stamp=p.slice(54),hashed=await sha256(txid);\n      rows.push({div:1},{l:'Spark transaction ID',v:txid,tip:'UUID v4 generated by the integrator.'},{l:'Random string',v:rand,tip:'17-char random value for replay prevention.'});\n      if(stamp)rows.push({l:'Timestamp (YYMMDDhhmm UTC)',v:stamp,tip:'UTC timestamp. Nayax validates it is within a time window.'});\n      rows.push({l:'Expected HashedSparkTransactionId',v:hashed,tip:'Should match HashedSparkTransactionId in the Nayax response.'});\n    }\n    okBox('an-dr',rows);\n  }catch(e){errBox('an-dr',e.message);}\n}\nasync function doAlEnc(){\n  spin('al-er');\n  try{\n    const tok=v('al-tok'),int=v('al-int'),hk=v('al-hk'),tid=v('al-tid'),term=v('al-term'),ttype=v('al-ttype'),txid=v('al-txid'),rand=v('al-rand');\n    if(!tok||!int||!hk||!tid||!term||!txid||!rand)throw new Error('Fill all required fields.');\n    if(rand.length!==17)throw new Error('Random must be 17 chars (got '+rand.length+').');\n    if(txid.length!==36)throw new Error('Transaction ID must be 36 chars (got '+txid.length+').');\n    const plain=txid+'='+rand,cipher=await aesEnc(plain,tok);\n    const body={TokenId:parseInt(tid),TerminalId:term,TerminalIdType:parseInt(ttype),Random:rand,Cipher:cipher};\n    const mini=JSON.stringify(body),sig=await sha256(mini+';'+hk);\n    okBox('al-er',[\n      {l:'Plaintext (no timestamp — legacy)',v:plain,tip:'Legacy cipher: TxId=Random only, no timestamp.'},\n      {l:'Cipher (Base64)',v:cipher,tip:'AES-256-ECB cipher for the Cipher field.'},\n      {div:1},\n      {l:'Minified request body',v:mini,pre:1,tip:'JSON.stringify() output. Signature = SHA256(this;HeaderHashKey).'},\n      {l:'IntegratorId header',v:int,tip:'HTTP header: IntegratorId: <value>'},\n      {l:'Signature header (legacy)',v:sig,tip:'HTTP header: Signature: <value>. SHA256(minifiedBody;HeaderHashKey).'},\n    ]);\n  }catch(e){errBox('al-er',e.message);}\n}\nasync function doAlDec(){\n  spin('al-dr');\n  try{\n    const tok=v('ald-tok'),cip=v('ald-cip');\n    if(!tok||!cip)throw new Error('Required fields missing.');\n    const p=await aesDec(cip,tok);\n    const rows=[{l:'Decrypted plaintext',v:p,tip:'Legacy: TxId(36)=Random(17), no timestamp.'}];\n    const sep=p.indexOf('=');\n    if(sep>-1)rows.push({div:1},{l:'Spark transaction ID',v:p.slice(0,sep)},{l:'Random string',v:p.slice(sep+1),tip:'No timestamp in legacy mode.'});\n    okBox('al-dr',rows);\n  }catch(e){errBox('al-dr',e.message);}\n}\nasync function doSsEnc(){\n  spin('ss-er');\n  try{\n    const tok=v('ss-tok'),txid=v('ss-txid'),rand=v('ss-rand');\n    if(!tok||!txid||!rand)throw new Error('Token, transaction ID and random required.');\n    if(txid.length!==36)throw new Error('Transaction ID must be 36 chars (got '+txid.length+').');\n    if(rand.length!==27)throw new Error('Random must be 27 chars (got '+rand.length+').');\n    const plain=txid+'='+rand,cipher=await aesEnc(plain,tok);\n    const resp={Cipher:cipher,SparkTransactionId:txid,Status:{Verdict:'Approved'}};\n    okBox('ss-er',[\n      {l:'Plaintext (TxId=Random)',v:plain,tip:'Encrypted string: your TxId + Nayax\\'s 27-char random.'},\n      {l:'Key (last 32 of token)',v:tok.slice(-32),tip:'AES-256 key used for encryption.'},\n      {l:'Cipher (Base64)',v:cipher,tip:'Return this in your HTTP response to Nayax\\'s StartSession call.'},\n      {div:1},\n      {l:'Response body (return this to Nayax)',v:JSON.stringify(resp,null,2),pre:1,tip:'Full JSON body to return in your HTTP response.'},\n    ]);\n  }catch(e){errBox('ss-er',e.message);}\n}\nasync function doSsDec(){\n  spin('ss-dr');\n  try{\n    const tok=v('ssd-tok'),cip=v('ssd-cip');\n    if(!tok||!cip)throw new Error('Required fields missing.');\n    const p=await aesDec(cip,tok);\n    const rows=[{l:'Decrypted plaintext',v:p,tip:'StartSession cipher: TxId(36)=Random(27).'}];\n    const sep=p.indexOf('=');\n    if(sep>-1)rows.push({div:1},{l:'Transaction ID',v:p.slice(0,sep),tip:'36-char numeric ID generated by the integrator.'},{l:'Random (from Nayax)',v:p.slice(sep+1),tip:'Original 27-char random sent by Nayax.'});\n    okBox('ss-dr',rows);\n  }catch(e){errBox('ss-dr',e.message);}\n}\nasync function doHdrNew(){\n  spin('hdr-new-r');\n  try{\n    const txid=v('hn-txid'),sk=v('hn-sk'),int=v('hn-int');\n    if(!txid||!sk||!int)throw new Error('Fill all fields.');\n    const sig=await sha256(txid+';'+sk);\n    okBox('hdr-new-r',[{l:'IntegratorId header',v:int,tip:'HTTP header: IntegratorId: <value>'},{l:'TransactionSignature header',v:sig,tip:'HTTP header: TransactionSignature: <value>'}]);\n  }catch(e){errBox('hdr-new-r',e.message);}\n}\nasync function doHdrLeg(){\n  spin('hdr-leg-r');\n  try{\n    const body=v('hl-body'),hk=v('hl-hk'),int=v('hl-int');\n    if(!body||!hk||!int)throw new Error('Fill all fields.');\n    const mini=JSON.stringify(JSON.parse(body)),sig=await sha256(mini+';'+hk);\n    okBox('hdr-leg-r',[\n      {l:'Minified JSON (hashed)',v:mini,pre:1,tip:'Exact string used for hashing.'},\n      {l:'IntegratorId header',v:int,tip:'HTTP header: IntegratorId: <value>'},\n      {l:'Signature header (legacy)',v:sig,tip:'HTTP header: Signature: <value>'},\n    ]);\n  }catch(e){errBox('hdr-leg-r',e.message.includes('JSON')?'Invalid JSON: '+e.message:e.message);}\n}\n\n// When loaded inside a docs iframe, hide the tool's own header chrome.\nif(window.self!==window.top)document.documentElement.classList.add('embedded');\n// Deep-link: open a specific auth variant on load, e.g. ?auth=legacy\ntry{\n  const _a=new URLSearchParams(location.search).get('auth');\n  if(_a==='legacy'||_a==='leg'){\n    const _b=(window.__satRoot||document).querySelectorAll('#tab-auth .stabs .stab')[1];\n    showSub('auth','leg',_b||null);\n  }\n}catch(e){}\n";
  const mount = host => {
    if (!host || typeof window === "undefined") return;
    if (!host.shadowRoot) {
      const root = host.attachShadow({
        mode: "open"
      });
      root.innerHTML = "<style>" + CSS + "</style>" + BODY;
      window.__satRoot = root;
      if (!window.__satLoaded) {
        window.__satLoaded = true;
        const s = document.createElement("script");
        s.textContent = SCRIPT;
        document.body.appendChild(s);
      }
    } else {
      window.__satRoot = host.shadowRoot;
    }
    setTimeout(function () {
      try {
        const R = window.__satRoot;
        if (!R) return;
        if (legacy) {
          const b = R.querySelector("#tab-auth .stabs .stab:last-child");
          if (window.showSub) window.showSub("auth", "leg", b);
          return;
        }
        const h = (typeof location !== "undefined" ? location.hash : "").toLowerCase().replace("#", "");
        const navs = R.querySelectorAll(".nav-btn");
        if ((h === "startsession" || h === "session") && window.showTab) window.showTab("session", navs[1]); else if ((h === "headersignature" || h === "header-signature" || h === "headers") && window.showTab) window.showTab("headers", navs[2]);
      } catch (e) {}
    }, 0);
  };
  return <div ref={mount} style={{
    width: "100%"
  }} />;
};

Use this tool to handle Spark authentication without writing any code. Everything runs in your browser, so your token and sign key never leave your machine.

The tabs at the top cover the three operations the tool performs:

* **StartAuthentication**: build or inspect the cipher and the
  `TransactionSignature` header for the `StartAuthentication` call, the
  integrator's first request to open a transaction. The **Auth** and **Legacy
  Auth (v1)** sub-tabs cover the current and legacy signature flows.
* **StartSession**: build or inspect the `Cipher` your server returns when
  Nayax sends a `StartSession` request to open a session.
* **Header Signature**: generate the `IntegratorId` and `TransactionSignature`
  headers that authenticate a Spark API request.

<SparkAuthTool />

## See Also

<Columns cols={2}>
  <Card icon="link" title="Authentication" href="/docs/integrate-pos-device/spark/security-authentication/spark-authentication">
    The current standard for all new integrations. Header-based signature.
  </Card>

  <Card icon="link" title="Legacy Authentication" href="/docs/integrate-pos-device/spark/security-authentication/spark-legacy-authentication">
    For maintaining existing integrations. HMAC signature over the JSON body.
  </Card>
</Columns>
