Files
upn-qr/views/index.ejs
2023-10-23 22:54:08 +02:00

365 lines
16 KiB
Plaintext

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>UPN-QR | Open generator for UPN QR codes</title>
<link rel="stylesheet" href="/public/style.css">
</head>
<style>
section div {
padding-left: 25px;
display: grid;
}
.credits {
display: grid;
align-items: center;
justify-content: center;
text-align: center;
}
.maker {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
row-gap: .5rem;
column-gap: .5rem;
}
.code {
grid-column: 1;
background-size: contain;
background-position: top center;
background-repeat: no-repeat;
background-image: url("/public/invalid-content.png");
min-height: 210px;
min-width: 210px;
}
.maker button,
.maker input[type="submit"] {
margin-bottom: auto;
}
</style>
<body>
<section>
<h1>About</h1>
<p>
This API was developed for personal use, but modified and later released for public use.
It is an unofficial application with the sole purpose of simplifying QR code generation for universal payment ordes (<a href="https://upn-qr.si">https://upn-qr.si</a>).
You can read the specification <a href="/public/NavodilaZaProgramerjeUPNQR.pdf">here</a>.
The application does not keep any long-term logs.
</p>
</section>
<section>
<h1 id="maker"><a href="#maker">&#128279;</a> Form maker</h1>
<span>Fill in the fields with reciever's bank information and leave empty the ones that user has to fill out.</span>
<form action="/form" method="get" class="maker">
<div>
<label for="title">Form title</label>
<input type="text" placeholder="First half of the bike" name="title">
</div>
<div>
<label for="client-name">Client name</label>
<input type="text" placeholder="Peter Novak" name="client-name">
</div>
<div>
<label for="client-address">Client address</label>
<input type="text" placeholder="Ravna ulica 13 a" name="client-address">
</div>
<div>
<label for="client-city">Client city</label>
<input type="text" placeholder="1000 Ljubljana" name="client-city">
</div>
<div>
<label for="amount">Amount</label>
<input type="number" placeholder="00000001132" min="0" max="99999999999" name="amount">
</div>
<div>
<label for="deadline">Deadline</label>
<input type="text" placeholder="01.02.2034" min=0 max=10 name="deadline">
</div>
<div>
<label for="code">Purpose code</label>
<input type="text" placeholder="OTHR" name="code">
</div>
<div>
<label for="purpose">Payment purpose</label>
<input type="text" placeholder="Moutain bike first half" name="purpose">
</div>
<div>
<label for="iban">IBAN</label>
<input type="text" placeholder="SI56047500000280672" name="iban">
</div>
<div>
<label for="reference">Reference</label>
<input type="text" placeholder="SI121234567890120" name="reference">
</div>
<div>
<label for="issuer-name">Issuer name</label>
<input type="text" placeholder="Kolesarstvo Hrib" name="issuer-name">
</div>
<div>
<label for="issuer-address">Issuer address</label>
<input type="text" placeholder="Za deveto smreko 15 k" name="issuer-address">
</div>
<div>
<label for="issuer-city">Issuer city</label>
<input type="text" placeholder="1000 Ljubljana" name="issuer-city">
</div>
<div class="code" id="qrcode"></div>
<div>
<button id="copyurl" type="button">Copy image URL</button>
</div>
<div>
<input type="submit" value="Create form">
</div>
</div>
</section>
<script>
function val (name) { return encodeURIComponent(document.getElementsByName(name)?.[0]?.value) || "" }
function getNewUrl () {
const qstring = [
["client_name", val("client-name")],
["client_address", val("client-address")],
["client_city", val("client-city")],
["amount", val("amount").padStart(11, "0")],
["purpose_code", val("code")],
["payment_purpose", val("purpose")],
["iban", val("iban")],
["reference", val("reference")],
["issuer_name", val("issuer-name")],
["issuer_address", val("issuer-address")],
["issuer_city", val("issuer-city")],
["deadline", val("deadline")],
].map(v => `${v[0]}=${v[1]}`).join("&")
return encodeURI(`${window.location.origin}/api/qrcode?${qstring}`)
}
const copyBtn = document.getElementById("copyurl")
copyBtn.addEventListener("click", () => copyUrl())
function copyUrl () {
try {
const dummy = document.createElement('input')
document.body.appendChild(dummy)
dummy.value = getNewUrl()
dummy.select()
document.execCommand('copy')
document.body.removeChild(dummy)
copyBtn.innerText = "Copied!"
copyBtn.classList.add("success")
setTimeout(() => {
copyBtn.innerText = "Copy image URL"
copyBtn.classList.remove("success")
}, 750)
} catch (error) {
console.error(error)
copyBtn.innerText = "Failed!"
copyBtn.classList.add("error")
setTimeout(() => {
copyBtn.innerText = "Copy image URL"
copyBtn.classList.remove("error")
}, 750)
}
}
const qrcode = document.getElementById("qrcode")
function val (name) { return document.getElementsByName(name)?.[0]?.value || "" }
let debounce = null
function updateQRdeb () {
clearTimeout(debounce)
debounce = setTimeout(() => {
clearTimeout(debounce)
updateQR()
}, 500);
}
function updateQR (e) {
try {
const preloadImg = new Image();
const newurl = getNewUrl()
preloadImg.addEventListener("load", () => qrcode.style.setProperty('background-image', `url("${newurl}"), url("/public/invalid-content.png")`))
preloadImg.addEventListener("error", () => qrcode.style.setProperty('background-image', `url("/public/invalid-content.png")`))
preloadImg.src=newurl;
} catch (error) {
setImgUrl(`url("/public/invalid-content.png")`)
}
}
// Add input listener to all inputs
for (el of document.getElementsByTagName("input")) {
el.addEventListener("input", updateQRdeb)
}
updateQR()
</script>
<section>
<h1 id="api"><a href="#api">&#128279;</a> API</h1>
<h4 id="api-qrcode"><a href="#api-qrcode">&#128279;</a> <code>GET /api/qrcode</code></h4>
<div>
<span>Returns a <code>image/png</code> image if successful (OK 200), else returns the following JSON <code>{ ok: false, errors: String[] }</code> where <code>errors</code> is an array of descriptive strings.</span>
<br>
<pre>
&#x3C;!-- Meant to be used as direct image source, for example --&#x3E;
&#x3C;img src="https://upn-qr.gitapp.si/api/qrcode?client_name=Dobri človek&client_address=Kristanova ulica 1&client_city=1000 Ljubljana&amount=00000001000&deadline=01.02.2034&payment_purpose=Donacija&iban=SI56021400015556761&reference=SI99&issuer_name=Slovenska Karitas&issuer_address=Kristanova ulica 1&issuer_city=1000 Ljubljana"&#x3E;
</pre>
<img src="/api/qrcode?client_name=Dobri človek&client_address=Kristanova ulica 1&client_city=1000 Ljubljana&amount=00000001000&deadline=01.02.2034&payment_purpose=Donacija&iban=SI56021400015556761&reference=SI99&issuer_name=Slovenska Karitas&issuer_address=Kristanova ulica 1&issuer_city=1000 Ljubljana">
</div>
<br>
<div>
<b>Query parameters:</b>
<p>Following the specification from official documentation - <a href="https://www.upn-qr.si/uploads/files/NavodilaZaProgramerjeUPNQR.pdf">NavodilaZaProgramerjeUPNQR.pdf</a>, section <code>4. Vsebina kode QR</code>.</p>
<h5 id="api-qrcode-client_name"><a href="#api-qrcode-client_name">&#128279;</a> <code>client_name</code></h5>
<div>
<span>Regex: <code>^[a-zA-Z0-9ČŠŽĐ'](?:[A-Z0-9 ČŠŽĐ']{0,31}[A-Z0-9ČŠŽĐ'])?$/i</code></span>
<br>
<span>Demo: <a href="https://regex101.com/r/pNmOI0/1">regex101.com/r/pNmOI0/1</a></span>
<br>
<span>Description: Name and surname of the client. Max length 33 characters including spaces and numbers.</span>
<br>
<span>Example: Peter Novak</span>
</div>
<h5 id="api-qrcode-client_address"><a href="#api-qrcode-client_address">&#128279;</a> <code>client_address</code></h5>
<div>
<span>Regex: <code>^[a-zA-Z0-9ČŠŽĐ](?:[A-Z0-9 ČŠŽĐ]{0,31}[A-Z0-9ČŠŽĐ])?$/i</code></span>
<br>
<span>Demo: <a href="https://regex101.com/r/JA4wmM/1">regex101.com/r/JA4wmM/1</a></span>
<br>
<span>Description: Full client address in long form. Max length 33 characters including spaces and numbers.</span>
<br>
<span>Example: Ravna ulica 13 a</span>
</div>
<h5 id="api-qrcode-client_city"><a href="#api-qrcode-client_city">&#128279;</a> <code>client_city</code></h5>
<div>
<span>Regex: <code>^[a-zA-Z0-9ČŠŽĐ](?:[A-Z0-9 ČŠŽĐ]{0,31}[A-Z0-9ČŠŽĐ])?$/i</code></span>
<br>
<span>Demo: <a href="https://regex101.com/r/pK3oEm/1">regex101.com/r/5QMpTn/1</a></span>
<br>
<span>Description: Client's city including postal code</span>
<br>
<span>Example: 1000 Ljubljana</span>
</div>
<h5 id="api-qrcode-amount"><a href="#api-qrcode-amount">&#128279;</a> <code>amount</code></h5>
<div>
<span>Regex: <code>^(?=.{11}$)[0]{1,11}[0-9]{0,11}$</code></span>
<br>
<span>Demo: <a href="https://regex101.com/r/Tyq5S1/1">regex101.com/r/Tyq5S1/1</a></span>
<br>
<span>Description: Total amount to pay. Must contain 11 numbers. Last two numbers are decimal places (cents).</span>
<br>
<span>Example (11,32€): 00000001132</span>
</div>
<h5 id="api-qrcode-deadline"><a href="#api-qrcode-deadline">&#128279;</a> <code>deadline</code></h5>
<div>
<span>Regex: <code>^[0-9]{2}\.[0-9]{2}\.[0-9]{4}$</code></span>
<br>
<span>Demo: <a href="https://regex101.com/r/JDZT9P/1">regex101.com/r/JDZT9P/1</a></span>
<br>
<span>Description: Payment deadline. Field is optional. Format DD.MM.YYYY</span>
<br>
<span>Example: 01.02.2034</span>
</div>
<h5 id="api-qrcode-purpose_code"><a href="#api-qrcode-purpose_code">&#128279;</a> <code>purpose_code</code></h5>
<div>
<span>Regex: <code>^[A-Z]{4}$</code></span>
<br>
<span>Demo: <a href="https://regex101.com/r/TsiZQJ/1">regex101.com/r/TsiZQJ/1</a></span>
<br>
<span>Default: OTHR</span>
<br>
<span>Description: Must contain 4 uppercase characters compliant with public purpose code library <a href="https://www.nlb.si/sepa-koda-namena-prebivalstvo">www.nlb.si/sepa-koda-namena-prebivalstvo</a></span>
<br>
<span>Example: OTHR</span>
</div>
<h5 id="api-qrcode-payment_purpose"><a href="#api-qrcode-payment_purpose">&#128279;</a> <code>payment_purpose</code></h5>
<div>
<span>Regex: <code>^[A-Z0-9ČŠŽĐ](?:[A-Z0-9 ČŠŽĐ\-:;_'"]{0,40}[A-Z0-9ČŠŽĐ])?$</code></span>
<br>
<span>Demo: <a href="https://regex101.com/r/egl24t/1">regex101.com/r/egl24t/1</a></span>
<br>
<span>Description: Can contain any character including ŠČŽĐ and space. Max length 33 characters including space.</span>
<br>
<span>Example: Moutain bike first half</span>
</div>
<h5 id="api-qrcode-iban"><a href="#api-qrcode-iban">&#128279;</a> <code>iban</code></h5>
<div>
<span>Regex: <code>^[A-Z]{2}\d{17}$</code></span>
<br>
<span>Demo: <a href="https://regex101.com/r/8bXDvh/1">regex101.com/r/8bXDvh/1</a></span>
<br>
<span>Description: Must contain country code (exp. SI56). No spaces allowed. Max length 34 characters</span>
<br>
<span>Example: SI56047500000280672</span>
</div>
<h5 id="api-qrcode-reference"><a href="#api-qrcode-reference">&#128279;</a> <code>reference</code></h5>
<div>
<span>Regex: <code>^[A-Z]{2}[0-9\-]{1,24}$</code></span>
<br>
<span>Demo: <a href="https://regex101.com/r/2tSYMw/1">regex101.com/r/2tSYMw/1</a></span>
<br>
<span>Description: Must contain reference model (exp. SI00) and reference (exp. 1234-2002). Max length 4+22 numbers and minus symbols. No spaces allowed. Reference model and structure must comply with <a href="https://www.nlb.si/navodila-upn">standard reference model.</a></span>
<br>
<span>Example: SI121234567890120</span>
</div>
<h5 id="api-qrcode-issuer_name"><a href="#api-qrcode-issuer_name">&#128279;</a> <code>issuer_name</code></h5>
<div>
<span>Regex: <code>^[a-zA-Z0-9ČŠŽĐ.'](?:[A-Z0-9 ČŠŽĐ.'\-]{0,31}[A-Z0-9ČŠŽĐ.'])?$/i</code></span>
<br>
<span>Demo: <a href="https://regex101.com/r/ND8T1c/1">regex101.com/r/ubDgZL/1</a></span>
<br>
<span>Description: Name and surname of the issuer or company name. Max length 33 characters including spaces and numbers.</span>
<br>
<span>Example: Kolesarstvo Hrib</span>
</div>
<h5 id="api-qrcode-issuer_address"><a href="#api-qrcode-issuer_address">&#128279;</a> <code>issuer_address</code></h5>
<div>
<span>Regex: <code>^[a-zA-Z0-9ČŠŽĐ](?:[A-Z0-9 ČŠŽĐ\-.]{0,31}[A-Z0-9ČŠŽĐ])?$/i</code></span>
<br>
<span>Demo: <a href="https://regex101.com/r/qgkRMO/1">regex101.com/r/5oKk0T/1</a></span>
<br>
<span>Description: Full issuer address in long form. Max length 33 characters including spaces, numbers, minus and dot.</span>
<br>
<span>Example: Za deveto smreko 15-k p.p. 15</span>
</div>
<h5 id="api-qrcode-issuer_city"><a href="#api-qrcode-issuer_city">&#128279;</a> <code>issuer_city</code></h5>
<div>
<span>Regex: <code>^[a-zA-Z0-9ČŠŽĐ](?:[A-Z0-9 ČŠŽĐ]{0,31}[A-Z0-9ČŠŽĐ])?$/i</code></span>
<br>
<span>Demo: <a href="https://regex101.com/r/JfuNU1/1">regex101.com/r/JfuNU1/1</a></span>
<br>
<span>Description: Issuer city including postal code</span>
<br>
<span>Example: 1000 Ljubljana</span>
</div>
<!-- <h5><code>query</code></h5>
<div>
Regex: <code></code>
<br>
Demo: <a href=""></a>
</div> -->
</div>
<div class="credits">
<span>-- - --</span>
Development and hosting: <a href="https://aljaxus.eu">Aljaž Starc</a>
Initial insight: <a href="http://www.valentincic.eu/">Tjaž Valentinčič</a>
<span>-</span>
<a href="https://gitplac.si/aljaxus/upn-qr">gitplac.si/aljaxus/upn-qr</a>
<span>-</span>
</div>
</section>
</body>
</html>