···
1
1
+
{ ... }:
2
2
+
{
3
3
+
perSystem =
4
4
+
{ pkgs, ... }:
5
5
+
{
6
6
+
packages.payment-qr = pkgs.writeShellApplication {
7
7
+
name = "payment-qr";
8
8
+
runtimeInputs = [
9
9
+
pkgs.qrencode
10
10
+
];
11
11
+
text = builtins.readFile ./payment-qr.sh;
12
12
+
};
13
13
+
};
14
14
+
}
···
1
1
+
#!/usr/bin/env bash
2
2
+
set -euo pipefail
3
3
+
4
4
+
# SEPA Payment QR Code Generator
5
5
+
# Generates EPC QR codes for SEPA payments according to EPC069-12 standard
6
6
+
7
7
+
usage() {
8
8
+
cat >&2 <<EOF
9
9
+
Usage: payment-qr [OPTIONS]
10
10
+
11
11
+
Generate SEPA payment QR code (EPC QR code format)
12
12
+
13
13
+
OPTIONS:
14
14
+
-n, --name NAME Beneficiary name (required)
15
15
+
-i, --iban IBAN IBAN (required)
16
16
+
-b, --bic BIC BIC (optional, auto-detected from IBAN if not provided)
17
17
+
-a, --amount AMOUNT Amount in EUR (optional, e.g., 123.45)
18
18
+
-d, --desc DESCRIPTION Payment description/reference (optional)
19
19
+
-h, --help Show this help message
20
20
+
21
21
+
EXAMPLES:
22
22
+
payment-qr -n "John Doe" -i DE89370400440532013000 -a 50.00 -d "Invoice 123"
23
23
+
payment-qr --name "Acme Corp" --iban DE89370400440532013000 --bic COBADEFFXXX --amount 100
24
24
+
25
25
+
OUTPUT:
26
26
+
Displays the QR code in terminal using UTF-8 characters
27
27
+
EOF
28
28
+
exit "${1:-0}"
29
29
+
}
30
30
+
31
31
+
# BIC lookup from IBAN (simplified - maps country code to common banks)
32
32
+
# In production, this would use a proper BIC database/API
33
33
+
get_bic_from_iban() {
34
34
+
local iban="$1"
35
35
+
# Remove spaces and convert to uppercase
36
36
+
iban="${iban// /}"
37
37
+
iban="${iban^^}"
38
38
+
39
39
+
# Extract country code (first 2 characters)
40
40
+
local country="${iban:0:2}"
41
41
+
42
42
+
# For now, we'll use NOTPROVIDED as fallback
43
43
+
# A real implementation would query a BIC database
44
44
+
echo "NOTPROVIDED"
45
45
+
}
46
46
+
47
47
+
# Validate IBAN format (basic check)
48
48
+
validate_iban() {
49
49
+
local iban="$1"
50
50
+
iban="${iban// /}"
51
51
+
iban="${iban^^}"
52
52
+
53
53
+
# Basic validation: starts with 2 letters, followed by 2 digits, then alphanumeric
54
54
+
if [[ ! "$iban" =~ ^[A-Z]{2}[0-9]{2}[A-Z0-9]+$ ]]; then
55
55
+
echo "Error: Invalid IBAN format" >&2
56
56
+
return 1
57
57
+
fi
58
58
+
59
59
+
# Length should be between 15 and 34 characters
60
60
+
if [ "${#iban}" -lt 15 ] || [ "${#iban}" -gt 34 ]; then
61
61
+
echo "Error: IBAN length must be between 15 and 34 characters" >&2
62
62
+
return 1
63
63
+
fi
64
64
+
65
65
+
echo "$iban"
66
66
+
}
67
67
+
68
68
+
# Parse command line arguments
69
69
+
NAME=""
70
70
+
IBAN=""
71
71
+
BIC=""
72
72
+
AMOUNT=""
73
73
+
DESCRIPTION=""
74
74
+
75
75
+
while [[ $# -gt 0 ]]; do
76
76
+
case $1 in
77
77
+
-n|--name)
78
78
+
NAME="$2"
79
79
+
shift 2
80
80
+
;;
81
81
+
-i|--iban)
82
82
+
IBAN="$2"
83
83
+
shift 2
84
84
+
;;
85
85
+
-b|--bic)
86
86
+
BIC="$2"
87
87
+
shift 2
88
88
+
;;
89
89
+
-a|--amount)
90
90
+
AMOUNT="$2"
91
91
+
shift 2
92
92
+
;;
93
93
+
-d|--desc|--description)
94
94
+
DESCRIPTION="$2"
95
95
+
shift 2
96
96
+
;;
97
97
+
-h|--help)
98
98
+
usage 0
99
99
+
;;
100
100
+
*)
101
101
+
echo "Error: Unknown option: $1" >&2
102
102
+
usage 1
103
103
+
;;
104
104
+
esac
105
105
+
done
106
106
+
107
107
+
# Validate required parameters
108
108
+
if [ -z "$NAME" ]; then
109
109
+
echo "Error: Beneficiary name is required (-n/--name)" >&2
110
110
+
usage 1
111
111
+
fi
112
112
+
113
113
+
if [ -z "$IBAN" ]; then
114
114
+
echo "Error: IBAN is required (-i/--iban)" >&2
115
115
+
usage 1
116
116
+
fi
117
117
+
118
118
+
# Validate and normalize IBAN
119
119
+
IBAN=$(validate_iban "$IBAN") || exit 1
120
120
+
121
121
+
# Auto-detect BIC if not provided
122
122
+
if [ -z "$BIC" ]; then
123
123
+
BIC=$(get_bic_from_iban "$IBAN")
124
124
+
echo "BIC not provided, using: $BIC" >&2
125
125
+
fi
126
126
+
127
127
+
# Validate amount format if provided
128
128
+
if [ -n "$AMOUNT" ]; then
129
129
+
if [[ ! "$AMOUNT" =~ ^[0-9]+(\.[0-9]{1,2})?$ ]]; then
130
130
+
echo "Error: Invalid amount format. Use format: 123.45" >&2
131
131
+
exit 1
132
132
+
fi
133
133
+
# Format with exactly 2 decimal places
134
134
+
AMOUNT=$(printf "%.2f" "$AMOUNT")
135
135
+
fi
136
136
+
137
137
+
# Build EPC QR code data according to EPC069-12 standard
138
138
+
# Format:
139
139
+
# Line 1: BCD (Service Tag)
140
140
+
# Line 2: 002 (Version)
141
141
+
# Line 3: 1 (Character set: UTF-8)
142
142
+
# Line 4: SCT (Identification: SEPA Credit Transfer)
143
143
+
# Line 5: BIC
144
144
+
# Line 6: Beneficiary Name
145
145
+
# Line 7: Beneficiary Account (IBAN)
146
146
+
# Line 8: Amount (EUR123.45 or empty)
147
147
+
# Line 9: Purpose (empty for general)
148
148
+
# Line 10: Structured Reference (empty)
149
149
+
# Line 11: Unstructured Remittance (description)
150
150
+
# Line 12: Beneficiary to Originator Information (empty)
151
151
+
152
152
+
EPC_DATA=""
153
153
+
EPC_DATA+="BCD"$'\n' # Service Tag
154
154
+
EPC_DATA+="002"$'\n' # Version
155
155
+
EPC_DATA+="1"$'\n' # Character set (UTF-8)
156
156
+
EPC_DATA+="SCT"$'\n' # Identification
157
157
+
EPC_DATA+="${BIC}"$'\n' # BIC
158
158
+
EPC_DATA+="${NAME}"$'\n' # Beneficiary name
159
159
+
EPC_DATA+="${IBAN}"$'\n' # IBAN
160
160
+
161
161
+
if [ -n "$AMOUNT" ]; then
162
162
+
EPC_DATA+="EUR${AMOUNT}"$'\n' # Amount
163
163
+
else
164
164
+
EPC_DATA+=$'\n' # Empty amount
165
165
+
fi
166
166
+
167
167
+
EPC_DATA+=$'\n' # Purpose (empty)
168
168
+
EPC_DATA+=$'\n' # Structured reference (empty)
169
169
+
EPC_DATA+="${DESCRIPTION}"$'\n' # Unstructured remittance
170
170
+
EPC_DATA+=$'\n' # Beneficiary to originator info (empty)
171
171
+
172
172
+
# Generate QR code using qrencode
173
173
+
# -t utf8: Output as UTF-8 text
174
174
+
# -l M: Error correction level Medium
175
175
+
echo "$EPC_DATA" | qrencode -t utf8 -l M