![JAR search and dependency download from the Maven repository](/logo.png)
com.github.jlangch.venice.qrref.venice Maven / Gradle / Ivy
;;;; __ __ _
;;;; \ \ / /__ _ __ (_) ___ ___
;;;; \ \/ / _ \ '_ \| |/ __/ _ \
;;;; \ / __/ | | | | (_| __/
;;;; \/ \___|_| |_|_|\___\___|
;;;;
;;;;
;;;; Copyright 2017-2024 Venice
;;;;
;;;; Licensed under the Apache License, Version 2.0 (the "License");
;;;; you may not use this file except in compliance with the License.
;;;; You may obtain a copy of the License at
;;;;
;;;; http://www.apache.org/licenses/LICENSE-2.0
;;;;
;;;; Unless required by applicable law or agreed to in writing, software
;;;; distributed under the License is distributed on an "AS IS" BASIS,
;;;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;;;; See the License for the specific language governing permissions and
;;;; limitations under the License.
;;;; Venice QR Reference functions
(ns qrref)
(def bill-types { :bill 0
:reminder-1 1
:reminder-2 2
:reminder-3 3 })
;; the number of meta digits in the QR reference:
;; - bill type digit
;; - version digit
;; - checksum digit
(def- num-meta-chars 3)
(def- qr-ref-len 27)
(def- checksum-table [0, 9, 4, 6, 8, 2, 7, 1, 3, 5])
(defn- digit->long [c] (- (long c) (long #\0)))
(defn- long->bill-type [n]
(if-let [t (get (map-invert bill-types) n)]
t
(throw (ex :VncException (str "Invalid bill type index '" n "'!")))))
(defn- bill-type->long [t]
(if-let [n (get bill-types t)]
n
(throw (ex :VncException (str "Invalid bill type '" (pr-str t) "'!")))))
(defn- remove-leading-zeroes [s]
(loop [s s]
(if (and (> (count s) 1) (str/starts-with? s "0"))
(recur (str/rest s))
s)))
(defn mod-10-checksum [s]
(if-not (match? s #"[ 0-9]+")
(throw (ex :VncException "The string must only contain spaces and digits!"))
(reduce (fn [carry d] (get checksum-table (mod (+ carry (digit->long d)) 10)))
0
(filter str/digit? (seq s)))))
(defn- qr-ref-raw [version bill-type bill-nr]
(let [padding-zeros (- qr-ref-len num-meta-chars (count bill-nr))]
(if (neg? padding-zeros)
(throw (ex :VncException "The QR-Reference bill number is too long!"))
(str (str/repeat "0" padding-zeros)
bill-nr
(bill-type->long bill-type)
version))))
(defn
^{ :arglists '("(format s)")
:doc "Format a QR reference."
:examples '(
"""
(do
(load-module :qrref ['qrref :as 'qr])
(qr/format "000000000000000000001234011"))
""",
"""
(do
(load-module :qrref ['qrref :as 'qr])
(qr/format (qr/qr-ref 1 :bill "1234")))
""" )
:see-also '("qrref/qr-ref", "qrref/parse") }
format [s]
(->> (seq s)
(reverse)
(partition-all 5)
(map #(apply str (reverse %)))
(reverse)
(str/join " ")))
(defn
^{ :arglists '("(qr-ref version bill-type bill-nr)")
:doc """
Creates a QR reference according to the Swiss payment standards.
- *version*, an integer [1..9]¶
- *bill-type*, one of {:bill, :reminder-1, :reminder-2, :reminder-3}¶
- *bill-nr*, a string with up to 24 digits '0'..'9'
[Swiss Payment Standards / de](https://www.paymentstandards.ch/dam/downloads/ig-qr-bill-de.pdf)
[Swiss Payment Standards / en](https://www.paymentstandards.ch/dam/downloads/ig-qr-bill-en.pdf)
"""
:examples '(
"""
(do
(load-module :qrref ['qrref :as 'qr])
(qr/qr-ref 1 :bill "1234"))
""" )
:see-also '("qrref/parse", "qrref/format") }
qr-ref [version bill-type bill-nr]
(when-not (< 0 version 10)
(throw (ex :VncException
(str/format "Unsupported QR-Reference version %d! Must be [1..9]."
version))))
(let [ref (qr-ref-raw version bill-type bill-nr)]
(str ref (mod-10-checksum ref))))
(defn
^{ :arglists '("(parse ref)")
:doc "Parse a QR reference. The reference may be formatted."
:examples '(
"""
(do
(load-module :qrref ['qrref :as 'qr])
(qr/parse (qr/qr-ref 1 :bill "1234")))
""",
"""
(do
(load-module :qrref ['qrref :as 'qr])
(qr/parse "000000000000000000001234011"))
""",
"""
(do
(load-module :qrref ['qrref :as 'qr])
(qr/parse "00 00000 00000 00000 00012 34011"))
""" )
:see-also '("qrref/qr-ref", "qrref/format") }
parse [ref]
(when (str/blank? ref)
(throw (ex :VncException "A QR-Reference must not be blank!")))
(when-not (match? ref #"[ 0-9]+")
(throw (ex :VncException
"A QR-Reference must be built from spaces and digits only!")))
(let [ref-norm (str/replace-all ref " " "")]
(when (< (count ref-norm) 10)
(throw (ex :VncException "A QR-Reference must have more than 10 digits!")))
(let [meta (str/nlast ref-norm 3)
type (digit->long (first meta))
version (digit->long (second meta))
check (digit->long (third meta))
raw-ref (str/butlast ref-norm)
check-eff (mod-10-checksum raw-ref)
bill-nr (str/butnlast ref-norm 3)]
(when-not (= check check-eff)
(throw (ex :VncException
(str/format (str "Invalid QR-Reference checksum '%d' for ref "
"'%s'. The effective checksum is '%d'!")
check
raw-ref
check-eff))))
{ :version version
:bill-typ (long->bill-type type)
:bill-nr (remove-leading-zeroes bill-nr) })))
© 2015 - 2025 Weber Informatics LLC | Privacy Policy