{"id":448,"date":"2026-07-11T18:32:09","date_gmt":"2026-07-11T16:32:09","guid":{"rendered":"https:\/\/blirtyapp.com\/?page_id=448"},"modified":"2026-07-23T23:11:36","modified_gmt":"2026-07-23T21:11:36","slug":"beort04jce593sfd54jw3h424h53b2j3h","status":"publish","type":"page","link":"https:\/\/blirtyapp.com\/index.php\/beort04jce593sfd54jw3h424h53b2j3h\/","title":{"rendered":""},"content":{"rendered":"\n<div id=\"ticket-validator-root\" style=\"max-width:520px;margin:32px auto;padding:24px;border:1px solid #eee;border-radius:12px;font-family:system-ui,-apple-system,Segoe UI,Roboto;\">\n  <h2 style=\"margin:0 0 10px;font-size:28px;line-height:34px;\">Sprawdzenie bilet\u00f3w<\/h2>\n\n  <p id=\"validator-info\" style=\"margin:0 0 18px;color:#666;\">\n    Skanuj bilety i kupony Blirty.\n  <\/p>\n\n  <div id=\"step-scanner\" style=\"display:none;margin-top:18px;\">\n    <button id=\"scan\" style=\"padding:12px 16px;border:0;background:#5b0b14;color:#fff;border-radius:8px;cursor:pointer;width:100%;font-weight:700;\">\n      Skanuj bilet \/ kupon\n    <\/button>\n\n    <button id=\"stop-scan\" style=\"display:none;margin-top:10px;padding:10px 14px;border:1px solid #ddd;background:#fff;color:#111;border-radius:8px;cursor:pointer;width:100%;\">\n      Zatrzymaj kamer\u0119\n    <\/button>\n\n    <video id=\"video\" playsinline=\"\" muted=\"\" style=\"display:none;margin-top:14px;width:100%;border-radius:12px;background:#000;\"><\/video>\n    <canvas id=\"canvas\" style=\"display:none;\"><\/canvas>\n\n    <div id=\"scan-result\" style=\"display:none;margin-top:16px;padding:14px;border-radius:10px;border:1px solid #eee;background:#fafafa;font-weight:600;\"><\/div>\n  <\/div>\n\n  <div id=\"msg\" style=\"margin-top:12px;color:#333;\"><\/div>\n<\/div>\n\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/jsqr@1.4.0\/dist\/jsQR.js\"><\/script>\n\n<script>\n(function () {\n  const VALIDATE_SCAN_URL = \"https:\/\/ktor-service-245368367879.europe-central2.run.app\/api\/partner\/validate\";\n\n  const $ = (id) => document.getElementById(id);\n\n  const validatorToken = decodeURIComponent(\n    window.location.hash.replace(\"#\", \"\").trim()\n  );\n\n  let stream = null;\n  let scanning = false;\n  let validating = false;\n  let barcodeDetector = null;\n\n  const msg = (text, color = \"#333\") => {\n    const el = $(\"msg\");\n    el.textContent = text;\n    el.style.color = color;\n  };\n\n  const showResult = (text, color = \"#333\", background = \"#fafafa\") => {\n    const el = $(\"scan-result\");\n    el.style.display = \"\";\n    el.style.color = color;\n    el.style.background = background;\n    el.textContent = text;\n  };\n\n  const parseQr = (raw) => {\n    const value = String(raw || \"\").trim();\n    if (!value) throw new Error(\"Pusty kod QR.\");\n\n    const parts = value.split(\"|\").map((x) => x.trim());\n\n    if (parts.length !== 3 || !parts[0] || !parts[1] || !parts[2]) {\n      throw new Error(\"Nieprawid\u0142owy format kodu QR.\");\n    }\n\n    return value;\n  };\n\n  const responseToMessage = async (resp) => {\n    const text = await resp.text();\n\n    try {\n      const json = JSON.parse(text);\n\n      if (typeof json === \"string\") return json;\n      if (json && json.data != null) return String(json.data);\n      if (json && json.message != null) return String(json.message);\n\n      return text;\n    } catch (_) {\n      return text;\n    }\n  };\n\n  const stopCamera = () => {\n    scanning = false;\n\n    if (stream) {\n      stream.getTracks().forEach((track) => track.stop());\n      stream = null;\n    }\n\n    $(\"video\").style.display = \"none\";\n    $(\"stop-scan\").style.display = \"none\";\n    $(\"scan\").disabled = false;\n    $(\"scan\").textContent = \"Skanuj kolejny bilet \/ kupon\";\n  };\n\n  const validateScan = async (qrText) => {\n    if (validating) return;\n\n    validating = true;\n    stopCamera();\n\n    try {\n      if (!validatorToken) {\n        showResult(\"NOK Brakuje tokenu walidatora.\", \"#d32f2f\", \"#fff5f5\");\n        return;\n      }\n\n      const qrContent = parseQr(qrText);\n\n      showResult(\"Sprawdzam\u2026\", \"#333\", \"#fafafa\");\n\n      const resp = await fetch(VALIDATE_SCAN_URL, {\n        method: \"POST\",\n        headers: {\n          \"Content-Type\": \"application\/json\"\n        },\n        body: JSON.stringify({\n          qrContent: qrContent,\n          validatorToken: validatorToken\n        })\n      });\n\n      const backendMessage = await responseToMessage(resp);\n\n      if (resp.status === 403) {\n        showResult(\"NOK Brak uprawnie\u0144 do walidacji.\", \"#d32f2f\", \"#fff5f5\");\n      } else if (backendMessage.startsWith(\"OK\")) {\n        showResult(backendMessage, \"#2e7d32\", \"#f1fff4\");\n      } else {\n        showResult(backendMessage || \"NOK Nie uda\u0142o si\u0119 sprawdzi\u0107.\", \"#d32f2f\", \"#fff5f5\");\n      }\n    } catch (e) {\n      console.error(e);\n      showResult(e && e.message ? e.message : \"NOK Nie uda\u0142o si\u0119 sprawdzi\u0107.\", \"#d32f2f\", \"#fff5f5\");\n    } finally {\n      validating = false;\n      $(\"scan\").disabled = false;\n      $(\"scan\").textContent = \"Skanuj kolejny bilet \/ kupon\";\n    }\n  };\n\n  const scanLoop = async () => {\n    if (!scanning || validating) return;\n\n    const video = $(\"video\");\n    const canvas = $(\"canvas\");\n    const ctx = canvas.getContext(\"2d\", { willReadFrequently: true });\n\n    try {\n      if (\"BarcodeDetector\" in window) {\n        if (!barcodeDetector) {\n          barcodeDetector = new BarcodeDetector({ formats: [\"qr_code\"] });\n        }\n\n        const codes = await barcodeDetector.detect(video);\n\n        if (codes.length > 0) {\n          await validateScan(codes[0].rawValue);\n          return;\n        }\n      } else {\n        if (typeof window.jsQR !== \"function\") {\n          msg(\"Ta przegl\u0105darka nie obs\u0142uguje skanowania QR. Spr\u00f3buj w Chrome lub Safari.\", \"#d32f2f\");\n          stopCamera();\n          return;\n        }\n\n        if (video.videoWidth > 0 && video.videoHeight > 0) {\n          canvas.width = video.videoWidth;\n          canvas.height = video.videoHeight;\n          ctx.drawImage(video, 0, 0, canvas.width, canvas.height);\n\n          const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);\n          const code = window.jsQR(imageData.data, imageData.width, imageData.height);\n\n          if (code && code.data) {\n            await validateScan(code.data);\n            return;\n          }\n        }\n      }\n    } catch (e) {\n      console.error(e);\n    }\n\n    if (scanning) {\n      requestAnimationFrame(scanLoop);\n    }\n  };\n\n  const startScanner = async () => {\n    if (!validatorToken) {\n      msg(\"Nieprawid\u0142owy link walidatora. Brakuje tokenu.\", \"#d32f2f\");\n      return;\n    }\n\n    try {\n      showResult(\"Uruchamiam kamer\u0119\u2026\", \"#333\", \"#fafafa\");\n\n      stream = await navigator.mediaDevices.getUserMedia({\n        video: {\n          facingMode: { ideal: \"environment\" }\n        },\n        audio: false\n      });\n\n      const video = $(\"video\");\n      video.srcObject = stream;\n      video.style.display = \"\";\n      $(\"stop-scan\").style.display = \"\";\n      $(\"scan\").disabled = true;\n      $(\"scan\").textContent = \"Skanowanie\u2026\";\n\n      await video.play();\n\n      scanning = true;\n      showResult(\"Skieruj kamer\u0119 na kod QR biletu lub kuponu.\", \"#333\", \"#fafafa\");\n      requestAnimationFrame(scanLoop);\n    } catch (e) {\n      console.error(e);\n      showResult(\"Nie uda\u0142o si\u0119 uruchomi\u0107 kamery. Sprawd\u017a uprawnienia przegl\u0105darki.\", \"#d32f2f\", \"#fff5f5\");\n      stopCamera();\n    }\n  };\n\n  $(\"scan\").addEventListener(\"click\", startScanner);\n\n  $(\"stop-scan\").addEventListener(\"click\", () => {\n    stopCamera();\n    showResult(\"Skanowanie zatrzymane.\", \"#333\", \"#fafafa\");\n  });\n\n  window.addEventListener(\"beforeunload\", stopCamera);\n\n  if (!validatorToken) {\n    $(\"step-scanner\").style.display = \"none\";\n    $(\"validator-info\").textContent = \"Ten link jest nieaktywny. Brakuje tokenu.\";\n    msg(\"Popro\u015b organizatora o poprawny link do skanera.\", \"#d32f2f\");\n  } else {\n    $(\"step-scanner\").style.display = \"\";\n    $(\"validator-info\").textContent = \"Gotowe. Mo\u017cesz skanowa\u0107 bilety i kupony.\";\n    msg(\"Link walidatora aktywny.\", \"#2e7d32\");\n  }\n})();\n<\/script>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Sprawdzenie bilet\u00f3w Skanuj bilety i kupony Blirty. Skanuj bilet \/ kupon Zatrzymaj kamer\u0119<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_uag_custom_page_level_css":"","site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"class_list":["post-448","page","type-page","status-publish","hentry"],"uagb_featured_image_src":{"full":false,"thumbnail":false,"medium":false,"medium_large":false,"large":false,"1536x1536":false,"2048x2048":false},"uagb_author_info":{"display_name":"admin-iPcaUszA","author_link":"https:\/\/blirtyapp.com\/index.php\/author\/admin-ipcausza\/"},"uagb_comment_info":0,"uagb_excerpt":"Sprawdzenie bilet\u00f3w Skanuj bilety i kupony Blirty. Skanuj bilet \/ kupon Zatrzymaj kamer\u0119","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/blirtyapp.com\/index.php\/wp-json\/wp\/v2\/pages\/448","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blirtyapp.com\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/blirtyapp.com\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/blirtyapp.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blirtyapp.com\/index.php\/wp-json\/wp\/v2\/comments?post=448"}],"version-history":[{"count":8,"href":"https:\/\/blirtyapp.com\/index.php\/wp-json\/wp\/v2\/pages\/448\/revisions"}],"predecessor-version":[{"id":483,"href":"https:\/\/blirtyapp.com\/index.php\/wp-json\/wp\/v2\/pages\/448\/revisions\/483"}],"wp:attachment":[{"href":"https:\/\/blirtyapp.com\/index.php\/wp-json\/wp\/v2\/media?parent=448"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}