verifiabl-issuer 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +41 -0
- data/README.md +38 -11
- data/lib/verifiabl/issuer/assets/frame-horizontal-1410.vfr1 +0 -0
- data/lib/verifiabl/issuer/assets/frame-horizontal-1880.vfr1 +0 -0
- data/lib/verifiabl/issuer/assets/frame-horizontal-2820.vfr1 +0 -0
- data/lib/verifiabl/issuer/assets/frame-horizontal-940.vfr1 +0 -0
- data/lib/verifiabl/issuer/frame_assets.rb +15 -4
- data/lib/verifiabl/issuer/generated/frame_widths.rb +1 -0
- data/lib/verifiabl/issuer/generated/horizontal_frame.rb +10 -0
- data/lib/verifiabl/issuer/generated/non-pii-v2-fields.json +4 -1
- data/lib/verifiabl/issuer/generated/v2_codes.rb +15 -2
- data/lib/verifiabl/issuer/pii.rb +9 -23
- data/lib/verifiabl/issuer/rendering.rb +82 -48
- data/lib/verifiabl/issuer/validation.rb +0 -3
- data/lib/verifiabl/issuer/version.rb +1 -1
- data/lib/verifiabl/issuer.rb +12 -4
- data/sig/verifiabl-issuer.rbs +5 -3
- metadata +7 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 136cf6c7ac5d993f41d75c2f441cbea8329aef35ae57edadc70d3530dc8850dc
|
|
4
|
+
data.tar.gz: e48d77fc6c7c06e77a4b58702bdd618dac3589a15428bbbc62abf5e3f3551821
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dd7911d0a278a7998ea615cfac226b999bfd386af07bfe4ab03628330711eb9568a61fe0ec0089f638c0f2602d9f5ff69897d9ceffbee4fe54fd8330fceb8e6b
|
|
7
|
+
data.tar.gz: 1543207b85bb2e61c53625fc2a610a9b99d4c0dc1b560165e79456725ab07000b62a8e7354dc2b817a44b7e53b13815373388b81480d53fa19ca5b6e3a7f6584
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,47 @@ All notable changes to the Verifiabl Ruby SDK will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## [0.5.0] - 2026-10-09
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- AU2 `lump_sum` earnings lines accept `lump_sum_type`
|
|
12
|
+
(`PayslipCodes::Australian::LUMP_SUM_TYPES`: `a_redundancy` for STP lump sum
|
|
13
|
+
A type R, `a_other` for A type T, `b`, `d` or `e`), and a new `etp` line takes
|
|
14
|
+
`etp_type` (`PayslipCodes::Australian::ETP_TYPES`: `redundancy` for ETP code
|
|
15
|
+
R, `other` for O, `redundancy_split` for S, `other_split` for P,
|
|
16
|
+
`death_dependant` for D, `death_non_dependant` for N,
|
|
17
|
+
`death_non_dependant_split` for B or `death_trustee` for T) and
|
|
18
|
+
`etp_component` (`PayslipCodes::Australian::ETP_COMPONENTS`: `taxable` or
|
|
19
|
+
`tax_free`). Send the taxable and tax-free components as separate `etp`
|
|
20
|
+
lines; tax withheld from an ETP is part of `paygw`. Lump sum W stays
|
|
21
|
+
`return_to_work`, and lump sum U stays `paid_leave` with
|
|
22
|
+
`unused_on_termination`. The gem sends a `lump_sum` line without a type, and
|
|
23
|
+
the API accepts that for now. It will require `lump_sum_type` before
|
|
24
|
+
production, once every issuer is on an SDK release with the lump sum codes.
|
|
25
|
+
|
|
26
|
+
### Removed
|
|
27
|
+
|
|
28
|
+
- **Breaking:** removed `au.payslip.v1` and `nz.payslip.v1` registration
|
|
29
|
+
validation. The gem no longer requires `period_start` for those schemas; it
|
|
30
|
+
sends them like any other schema without a field tree, and the issuer API
|
|
31
|
+
rejects them. Send `au.payslip.v2` instead, for example with
|
|
32
|
+
`Verifiabl::Issuer.prepare_australian_v2_payslip`.
|
|
33
|
+
- **Breaking:** removed the P2 PII writer `Verifiabl::Issuer.format_pii`.
|
|
34
|
+
Verifiabl now accepts only the AU2 and NZ2 PII formats from issuers. Use
|
|
35
|
+
`prepare_australian_v2_payslip` or `prepare_new_zealand_v2_payslip`, or for
|
|
36
|
+
low-level use `format_australian_pii` or `format_new_zealand_pii` with
|
|
37
|
+
`encrypt_pii`. Payslips already issued with P1 or P2 still verify.
|
|
38
|
+
|
|
39
|
+
## [0.4.0] - 2026-10-07
|
|
40
|
+
|
|
41
|
+
- Add `layout: :horizontal` to local SVG and PNG rendering, with the QR on the
|
|
42
|
+
left, a 7-unit white gap and an opaque light-tinted brand panel on the right.
|
|
43
|
+
SVG minimum/default width is 940; PNG supports 940, 1410, 1880 and 2820 pixels
|
|
44
|
+
(default 1410). Vertical stays the default with unchanged output.
|
|
45
|
+
- Qualify horizontal SVG bytes, PNG pixels and QR metadata against Node and .NET,
|
|
46
|
+
including independent digital decoding.
|
|
47
|
+
|
|
7
48
|
## [0.3.0] - 2026-10-04
|
|
8
49
|
|
|
9
50
|
- Accept an `other` earnings line in AU2 and NZ2 payslips, for a pay code that
|
data/README.md
CHANGED
|
@@ -9,7 +9,7 @@ client, RBS signatures, and packed-gem consumer qualification.
|
|
|
9
9
|
Add the stable release to your bundle:
|
|
10
10
|
|
|
11
11
|
```ruby
|
|
12
|
-
gem "verifiabl-issuer", "0.
|
|
12
|
+
gem "verifiabl-issuer", "0.5.0"
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
Then run `bundle install`.
|
|
@@ -220,10 +220,12 @@ nz::LEAVE_BALANCE_UNITS # => ["hours", "days", "weeks"]
|
|
|
220
220
|
```
|
|
221
221
|
|
|
222
222
|
`EARNINGS_TYPES`, `PAID_LEAVE_TYPES` and `ALLOWANCE_TYPES` are available for
|
|
223
|
-
both jurisdictions.
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
223
|
+
both jurisdictions. An AU2 `lump_sum` line takes a `lump_sum_type` from
|
|
224
|
+
`LUMP_SUM_TYPES`, and an `etp` line takes an `etp_type` from `ETP_TYPES` and
|
|
225
|
+
an `etp_component` from `ETP_COMPONENTS`. Use their strings in your normal
|
|
226
|
+
payslip hash; the lists do not restrict input or add local enum/variant
|
|
227
|
+
validation. The API may accept a new code before this gem is updated.
|
|
228
|
+
Printed-text fields such as `tax_code` and `pay_cycle` are not code sets.
|
|
227
229
|
|
|
228
230
|
### Self-managed barcode flow
|
|
229
231
|
|
|
@@ -425,13 +427,16 @@ failures raise `TransportError`. Request and error messages never include the su
|
|
|
425
427
|
|
|
426
428
|
## Offline protocol primitives
|
|
427
429
|
|
|
428
|
-
|
|
430
|
+
Most integrations should use the v2 preparation helpers. To select the schema and formatter
|
|
431
|
+
yourself, format and encrypt PII locally with `format_australian_pii` (AU2, for `au.payslip.v2`) or
|
|
432
|
+
`format_new_zealand_pii` (NZ2, for `nz.payslip.v2`). The plaintext must never be logged or sent to
|
|
433
|
+
Verifiabl:
|
|
429
434
|
|
|
430
435
|
```ruby
|
|
431
|
-
plaintext = Verifiabl::Issuer.
|
|
436
|
+
plaintext = Verifiabl::Issuer.format_australian_pii(
|
|
432
437
|
employee_name: "Jane Doe",
|
|
433
438
|
employer_abn: "53004085616",
|
|
434
|
-
address: "12 Example St, Sydney NSW 2000"
|
|
439
|
+
address: {lines: ["12 Example St"], suburb: "Sydney", state_or_territory: "NSW", postcode: "2000"}
|
|
435
440
|
)
|
|
436
441
|
|
|
437
442
|
encrypted = Verifiabl::Issuer.encrypt_pii(plaintext, provider_encryption_key)
|
|
@@ -466,9 +471,31 @@ File.binwrite("barcode.png", png.png)
|
|
|
466
471
|
```
|
|
467
472
|
|
|
468
473
|
The renderers use the same official vector frame, rounded finder geometry, and QR error-correction
|
|
469
|
-
ladder as the Node and .NET SDKs.
|
|
470
|
-
|
|
471
|
-
|
|
474
|
+
ladder as the Node and .NET SDKs. Vertical is the default; pass `layout: :horizontal` to put the
|
|
475
|
+
QR on the left at full badge height, followed by a 7-unit white gap and the light-tinted
|
|
476
|
+
"Secured by Verifiabl" panel on the right. Horizontal rendering is included in `0.4.0`.
|
|
477
|
+
|
|
478
|
+
| Layout | SVG minimum/default width | PNG widths | PNG default width |
|
|
479
|
+
| --- | ---: | --- | ---: |
|
|
480
|
+
| `:vertical` | 480 | 480, 720, 960, 1440 | 720 |
|
|
481
|
+
| `:horizontal` | 940 | 940, 1410, 1880, 2820 | 1410 |
|
|
482
|
+
|
|
483
|
+
SVG widths scale continuously above the minimum. PNG frames are pre-rasterised for deterministic
|
|
484
|
+
cross-SDK output and support only the listed widths. Both layouts render the QR at the same sizes
|
|
485
|
+
at corresponding widths; the horizontal badge's total width includes its brand panel and gap.
|
|
486
|
+
For example, `build_barcode_svg(**parts, layout: :horizontal)` returns a 940×480 SVG and
|
|
487
|
+
`build_barcode_png(**parts, layout: :horizontal)` returns a 1410×720 PNG.
|
|
488
|
+
|
|
489
|
+
### Badge placement
|
|
490
|
+
|
|
491
|
+
Place the badge on a light background with a clear margin of at least four QR modules
|
|
492
|
+
(`result.module_px` at the rendered size) on every open QR side: **left, right and bottom** for
|
|
493
|
+
vertical; **left, top and bottom** for horizontal. A tenth of the QR side covers realistic records.
|
|
494
|
+
The host document supplies these quiet zones; the badge supplies only the gap facing the brand
|
|
495
|
+
frame. Size the QR, not the whole horizontal badge: a 22 mm QR means a horizontal badge about
|
|
496
|
+
43.08 mm wide and 22 mm high.
|
|
497
|
+
|
|
498
|
+
Use `max_error_correction: :q` for greater damage recovery; the default is
|
|
472
499
|
`:m`, and the renderer steps down only when necessary to preserve the three-pixel module floor.
|
|
473
500
|
Each result's `degraded` field (`svg.degraded` or `png.degraded` above) is true when the renderer
|
|
474
501
|
steps down from the requested ceiling or the modules fall below the ideal four-pixel size.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -12,17 +12,28 @@ module Verifiabl
|
|
|
12
12
|
|
|
13
13
|
module_function
|
|
14
14
|
|
|
15
|
-
def raster(width)
|
|
16
|
-
frame = load(width)
|
|
15
|
+
def raster(width, layout: :vertical)
|
|
16
|
+
frame = load(width, layout:)
|
|
17
17
|
rgba = String.new(capacity: frame.width * frame.height * 4, encoding: Encoding::BINARY)
|
|
18
18
|
entries = frame.palette.bytes.each_slice(4).map { |bytes| bytes.pack("C4") }
|
|
19
19
|
frame.indices.each_byte { |index| rgba << entries.fetch(index) }
|
|
20
20
|
[rgba, frame.width, frame.height]
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
def
|
|
23
|
+
def supported_widths(layout)
|
|
24
|
+
return SUPPORTED_WIDTHS if layout == :vertical
|
|
25
|
+
return SUPPORTED_HORIZONTAL_WIDTHS if layout == :horizontal
|
|
26
|
+
|
|
27
|
+
raise ArgumentError, "layout must be :vertical or :horizontal"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def load(width, layout: :vertical)
|
|
31
|
+
unless supported_widths(layout).include?(width)
|
|
32
|
+
raise ArgumentError, "unsupported frame width"
|
|
33
|
+
end
|
|
24
34
|
@cache ||= {}
|
|
25
|
-
|
|
35
|
+
name = (layout == :horizontal) ? "frame-horizontal-#{width}.vfr1" : "frame-#{width}.vfr1"
|
|
36
|
+
@cache[[layout, width]] ||= parse(File.binread(File.join(__dir__, "assets", name)), expected_width: width)
|
|
26
37
|
end
|
|
27
38
|
|
|
28
39
|
def parse(container, expected_width: nil)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Generated cross-SDK QR artifact. Do not edit by hand.
|
|
4
|
+
module Verifiabl
|
|
5
|
+
module Issuer
|
|
6
|
+
module RenderingAssets
|
|
7
|
+
HORIZONTAL_FRAME = "<rect x=\"0\" y=\"0\" width=\"103\" height=\"96\" fill=\"#FFFFFF\"/><g transform=\"translate(103 0)\"><path d=\"M0 0H75.4C80.70196 0 85 4.29806 85 9.6V86.4C85 91.70194 80.70196 96 75.4 96H0V0Z\" fill=\"#EDEFFF\"/><g transform=\"translate(0.5 0) scale(1.2)\" fill=\"#010A4F\"><path d=\"M9.76 32.63C9.76 32.87 9.80333 33.0733 9.89 33.24C9.97667 33.4067 10.09 33.5433 10.23 33.65C10.3767 33.75 10.5467 33.8267 10.74 33.88C10.9333 33.9267 11.1333 33.95 11.34 33.95C11.48 33.95 11.63 33.94 11.79 33.92C11.95 33.8933 12.1 33.8467 12.24 33.78C12.38 33.7133 12.4967 33.6233 12.59 33.51C12.6833 33.39 12.73 33.24 12.73 33.06C12.73 32.8667 12.6667 32.71 12.54 32.59C12.42 32.47 12.26 32.37 12.06 32.29C11.86 32.21 11.6333 32.14 11.38 32.08C11.1267 32.02 10.87 31.9533 10.61 31.88C10.3433 31.8133 10.0833 31.7333 9.83 31.64C9.57667 31.54 9.35 31.4133 9.15 31.26C8.95 31.1067 8.78667 30.9167 8.66 30.69C8.54 30.4567 8.48 30.1767 8.48 29.85C8.48 29.4833 8.55667 29.1667 8.71 28.9C8.87 28.6267 9.07667 28.4 9.33 28.22C9.58333 28.04 9.87 27.9067 10.19 27.82C10.51 27.7333 10.83 27.69 11.15 27.69C11.5233 27.69 11.88 27.7333 12.22 27.82C12.5667 27.9 12.8733 28.0333 13.14 28.22C13.4067 28.4067 13.6167 28.6467 13.77 28.94C13.93 29.2267 14.01 29.5767 14.01 29.99H12.49C12.4767 29.7767 12.43 29.6 12.35 29.46C12.2767 29.32 12.1767 29.21 12.05 29.13C11.9233 29.05 11.7767 28.9933 11.61 28.96C11.45 28.9267 11.2733 28.91 11.08 28.91C10.9533 28.91 10.8267 28.9233 10.7 28.95C10.5733 28.9767 10.4567 29.0233 10.35 29.09C10.25 29.1567 10.1667 29.24 10.1 29.34C10.0333 29.44 10 29.5667 10 29.72C10 29.86 10.0267 29.9733 10.08 30.06C10.1333 30.1467 10.2367 30.2267 10.39 30.3C10.55 30.3733 10.7667 30.4467 11.04 30.52C11.32 30.5933 11.6833 30.6867 12.13 30.8C12.2633 30.8267 12.4467 30.8767 12.68 30.95C12.92 31.0167 13.1567 31.1267 13.39 31.28C13.6233 31.4333 13.8233 31.64 13.99 31.9C14.1633 32.1533 14.25 32.48 14.25 32.88C14.25 33.2067 14.1867 33.51 14.06 33.79C13.9333 34.07 13.7433 34.3133 13.49 34.52C13.2433 34.72 12.9333 34.8767 12.56 34.99C12.1933 35.1033 11.7667 35.16 11.28 35.16C10.8867 35.16 10.5033 35.11 10.13 35.01C9.76333 34.9167 9.43667 34.7667 9.15 34.56C8.87 34.3533 8.64667 34.09 8.48 33.77C8.31333 33.45 8.23333 33.07 8.24 32.63H9.76ZM18.5141 31.86C18.4475 31.5 18.3275 31.2267 18.1541 31.04C17.9875 30.8533 17.7308 30.76 17.3841 30.76C17.1575 30.76 16.9675 30.8 16.8141 30.88C16.6675 30.9533 16.5475 31.0467 16.4541 31.16C16.3675 31.2733 16.3041 31.3933 16.2641 31.52C16.2308 31.6467 16.2108 31.76 16.2041 31.86H18.5141ZM16.2041 32.76C16.2241 33.22 16.3408 33.5533 16.5541 33.76C16.7675 33.9667 17.0741 34.07 17.4741 34.07C17.7608 34.07 18.0075 34 18.2141 33.86C18.4208 33.7133 18.5475 33.56 18.5941 33.4H19.8441C19.6441 34.02 19.3375 34.4633 18.9241 34.73C18.5108 34.9967 18.0108 35.13 17.4241 35.13C17.0175 35.13 16.6508 35.0667 16.3241 34.94C15.9975 34.8067 15.7208 34.62 15.4941 34.38C15.2675 34.14 15.0908 33.8533 14.9641 33.52C14.8441 33.1867 14.7841 32.82 14.7841 32.42C14.7841 32.0333 14.8475 31.6733 14.9741 31.34C15.1008 31.0067 15.2808 30.72 15.5141 30.48C15.7475 30.2333 16.0241 30.04 16.3441 29.9C16.6708 29.76 17.0308 29.69 17.4241 29.69C17.8641 29.69 18.2475 29.7767 18.5741 29.95C18.9008 30.1167 19.1675 30.3433 19.3741 30.63C19.5875 30.9167 19.7408 31.2433 19.8341 31.61C19.9275 31.9767 19.9608 32.36 19.9341 32.76H16.2041ZM24.2963 31.65C24.203 31.0567 23.853 30.76 23.2463 30.76C23.0197 30.76 22.8297 30.8133 22.6763 30.92C22.523 31.02 22.3963 31.1533 22.2963 31.32C22.203 31.48 22.1363 31.66 22.0963 31.86C22.0563 32.0533 22.0363 32.2467 22.0363 32.44C22.0363 32.6267 22.0563 32.8167 22.0963 33.01C22.1363 33.2033 22.1997 33.38 22.2863 33.54C22.3797 33.6933 22.503 33.82 22.6563 33.92C22.8097 34.02 22.9963 34.07 23.2163 34.07C23.5563 34.07 23.8163 33.9767 23.9963 33.79C24.183 33.5967 24.2997 33.34 24.3463 33.02H25.7163C25.623 33.7067 25.3563 34.23 24.9163 34.59C24.4763 34.95 23.913 35.13 23.2263 35.13C22.8397 35.13 22.483 35.0667 22.1563 34.94C21.8363 34.8067 21.563 34.6233 21.3363 34.39C21.1097 34.1567 20.933 33.88 20.8063 33.56C20.6797 33.2333 20.6163 32.8767 20.6163 32.49C20.6163 32.09 20.673 31.72 20.7863 31.38C20.9063 31.0333 21.0797 30.7367 21.3063 30.49C21.533 30.2367 21.8097 30.04 22.1363 29.9C22.463 29.76 22.8363 29.69 23.2563 29.69C23.563 29.69 23.8563 29.73 24.1363 29.81C24.423 29.89 24.6763 30.0133 24.8963 30.18C25.123 30.34 25.3063 30.5433 25.4463 30.79C25.5863 31.03 25.6663 31.3167 25.6863 31.65H24.2963ZM31.3685 35H30.0185V34.28H29.9885C29.8085 34.58 29.5752 34.7967 29.2885 34.93C29.0018 35.0633 28.7085 35.13 28.4085 35.13C28.0285 35.13 27.7152 35.08 27.4685 34.98C27.2285 34.88 27.0385 34.74 26.8985 34.56C26.7585 34.3733 26.6585 34.15 26.5985 33.89C26.5452 33.6233 26.5185 33.33 26.5185 33.01V29.83H27.9385V32.75C27.9385 33.1767 28.0052 33.4967 28.1385 33.71C28.2718 33.9167 28.5085 34.02 28.8485 34.02C29.2352 34.02 29.5152 33.9067 29.6885 33.68C29.8618 33.4467 29.9485 33.0667 29.9485 32.54V29.83H31.3685V35ZM32.4463 29.83H33.7963V30.79H33.8163C33.8829 30.63 33.9729 30.4833 34.0863 30.35C34.1996 30.21 34.3296 30.0933 34.4762 30C34.6229 29.9 34.7796 29.8233 34.9463 29.77C35.1129 29.7167 35.2863 29.69 35.4663 29.69C35.5596 29.69 35.6629 29.7067 35.7763 29.74V31.06C35.7096 31.0467 35.6296 31.0367 35.5363 31.03C35.4429 31.0167 35.3529 31.01 35.2663 31.01C35.0063 31.01 34.7863 31.0533 34.6063 31.14C34.4263 31.2267 34.2796 31.3467 34.1663 31.5C34.0596 31.6467 33.9829 31.82 33.9363 32.02C33.8896 32.22 33.8663 32.4367 33.8663 32.67V35H32.4463V29.83ZM39.6372 31.86C39.5705 31.5 39.4505 31.2267 39.2772 31.04C39.1105 30.8533 38.8539 30.76 38.5072 30.76C38.2805 30.76 38.0905 30.8 37.9372 30.88C37.7905 30.9533 37.6705 31.0467 37.5772 31.16C37.4905 31.2733 37.4272 31.3933 37.3872 31.52C37.3539 31.6467 37.3339 31.76 37.3272 31.86H39.6372ZM37.3272 32.76C37.3472 33.22 37.4639 33.5533 37.6772 33.76C37.8905 33.9667 38.1972 34.07 38.5972 34.07C38.8839 34.07 39.1305 34 39.3372 33.86C39.5439 33.7133 39.6705 33.56 39.7172 33.4H40.9672C40.7672 34.02 40.4605 34.4633 40.0472 34.73C39.6339 34.9967 39.1339 35.13 38.5472 35.13C38.1405 35.13 37.7739 35.0667 37.4472 34.94C37.1205 34.8067 36.8439 34.62 36.6172 34.38C36.3905 34.14 36.2139 33.8533 36.0872 33.52C35.9672 33.1867 35.9072 32.82 35.9072 32.42C35.9072 32.0333 35.9705 31.6733 36.0972 31.34C36.2239 31.0067 36.4039 30.72 36.6372 30.48C36.8705 30.2333 37.1472 30.04 37.4672 29.9C37.7939 29.76 38.1539 29.69 38.5472 29.69C38.9872 29.69 39.3705 29.7767 39.6972 29.95C40.0239 30.1167 40.2905 30.3433 40.4972 30.63C40.7105 30.9167 40.8639 31.2433 40.9572 31.61C41.0505 31.9767 41.0839 32.36 41.0572 32.76H37.3272ZM45.5694 32.4C45.5694 32.1867 45.5494 31.9833 45.5094 31.79C45.4694 31.59 45.3994 31.4167 45.2994 31.27C45.206 31.1167 45.0827 30.9933 44.9294 30.9C44.776 30.8067 44.5827 30.76 44.3494 30.76C44.116 30.76 43.9194 30.8067 43.7594 30.9C43.5994 30.9933 43.4694 31.1167 43.3694 31.27C43.276 31.4233 43.206 31.6 43.1594 31.8C43.1194 31.9933 43.0994 32.1967 43.0994 32.41C43.0994 32.61 43.1227 32.81 43.1694 33.01C43.216 33.21 43.2894 33.39 43.3894 33.55C43.496 33.7033 43.626 33.83 43.7794 33.93C43.9394 34.0233 44.1294 34.07 44.3494 34.07C44.5827 34.07 44.776 34.0233 44.9294 33.93C45.0894 33.8367 45.216 33.7133 45.3094 33.56C45.4027 33.4 45.4694 33.22 45.5094 33.02C45.5494 32.82 45.5694 32.6133 45.5694 32.4ZM45.5894 34.34H45.5694C45.4027 34.62 45.1827 34.8233 44.9094 34.95C44.6427 35.07 44.3394 35.13 43.9994 35.13C43.6127 35.13 43.2727 35.0567 42.9794 34.91C42.686 34.7567 42.4427 34.5533 42.2494 34.3C42.0627 34.04 41.9194 33.7433 41.8194 33.41C41.726 33.0767 41.6794 32.73 41.6794 32.37C41.6794 32.0233 41.726 31.69 41.8194 31.37C41.9194 31.0433 42.0627 30.7567 42.2494 30.51C42.4427 30.2633 42.6827 30.0667 42.9694 29.92C43.256 29.7667 43.5894 29.69 43.9694 29.69C44.276 29.69 44.566 29.7567 44.8394 29.89C45.1194 30.0167 45.3394 30.2067 45.4994 30.46H45.5194V27.86H46.9394V35H45.5894V34.34ZM54.5859 32.42C54.5859 32.2 54.5625 31.99 54.5159 31.79C54.4692 31.59 54.3959 31.4133 54.2959 31.26C54.1959 31.1067 54.0692 30.9867 53.9159 30.9C53.7692 30.8067 53.5892 30.76 53.3759 30.76C53.1692 30.76 52.9892 30.8067 52.8359 30.9C52.6825 30.9867 52.5559 31.1067 52.4559 31.26C52.3559 31.4133 52.2825 31.59 52.2359 31.79C52.1892 31.99 52.1659 32.2 52.1659 32.42C52.1659 32.6333 52.1892 32.84 52.2359 33.04C52.2825 33.24 52.3559 33.4167 52.4559 33.57C52.5559 33.7233 52.6825 33.8467 52.8359 33.94C52.9892 34.0267 53.1692 34.07 53.3759 34.07C53.5892 34.07 53.7692 34.0267 53.9159 33.94C54.0692 33.8467 54.1959 33.7233 54.2959 33.57C54.3959 33.4167 54.4692 33.24 54.5159 33.04C54.5625 32.84 54.5859 32.6333 54.5859 32.42ZM50.7959 27.86H52.2159V30.46H52.2359C52.4092 30.1933 52.6425 30 52.9359 29.88C53.2359 29.7533 53.5459 29.69 53.8659 29.69C54.1259 29.69 54.3825 29.7433 54.6359 29.85C54.8892 29.9567 55.1159 30.12 55.3159 30.34C55.5225 30.56 55.6892 30.8433 55.8159 31.19C55.9425 31.53 56.0059 31.9367 56.0059 32.41C56.0059 32.8833 55.9425 33.2933 55.8159 33.64C55.6892 33.98 55.5225 34.26 55.3159 34.48C55.1159 34.7 54.8892 34.8633 54.6359 34.97C54.3825 35.0767 54.1259 35.13 53.8659 35.13C53.4859 35.13 53.1459 35.07 52.8459 34.95C52.5459 34.83 52.3192 34.6267 52.1659 34.34H52.1459V35H50.7959V27.86ZM59.4591 35.64C59.3058 36.06 59.0925 36.36 58.8191 36.54C58.5458 36.72 58.1658 36.81 57.6791 36.81C57.5325 36.81 57.3858 36.8033 57.2391 36.79C57.0991 36.7833 56.9558 36.7733 56.8091 36.76V35.59C56.9425 35.6033 57.0791 35.6167 57.2191 35.63C57.3591 35.6433 57.4991 35.6467 57.6391 35.64C57.8258 35.62 57.9625 35.5467 58.0491 35.42C58.1425 35.2933 58.1891 35.1533 58.1891 35C58.1891 34.8867 58.1691 34.78 58.1291 34.68L56.3191 29.83H57.8291L58.9991 33.37H59.0191L60.1491 29.83H61.6191L59.4591 35.64Z\"/><path d=\"M18.5312 47.6154C18.5312 45.6882 20.0523 44.2301 22.0626 44.2301C24.073 44.2301 25.5905 45.6882 25.5905 47.6401C25.593 47.8232 25.5757 48.006 25.5388 48.1855H20.3224C20.5416 49.0227 21.225 49.5673 22.1268 49.5673C22.8095 49.5673 23.3763 49.2631 23.673 48.8448H25.4879C24.9853 50.125 23.6835 51 22.0857 51C20.0523 51 18.5312 49.5419 18.5312 47.6154ZM20.3357 46.9808H23.8021C23.7088 46.5987 23.4853 46.2595 23.1688 46.0197C22.8523 45.7799 22.462 45.654 22.0626 45.6628C21.2117 45.6628 20.5646 46.182 20.3357 46.9808Z\"/><path d=\"M26.4435 44.3819H28.1188V45.3585C28.6346 44.6861 29.3431 44.2802 30.2066 44.2802H30.4509V45.9794H30.2066C28.8664 45.9794 28.1188 46.6765 28.1188 47.7651V50.8461H26.4435V44.3819Z\"/><path d=\"M30.9152 42.4423C30.9152 42.1665 31.0265 41.902 31.2247 41.707C31.4229 41.512 31.6917 41.4025 31.972 41.4025C32.2523 41.4025 32.5211 41.512 32.7193 41.707C32.9175 41.902 33.0288 42.1665 33.0288 42.4423C33.0288 42.7181 32.9175 42.9826 32.7193 43.1776C32.5211 43.3726 32.2523 43.4821 31.972 43.4821C31.6917 43.4821 31.4229 43.3726 31.2247 43.1776C31.0265 42.9826 30.9152 42.7181 30.9152 42.4423ZM31.1343 44.3819H32.8096V50.8482H31.1343V44.3819Z\"/><path d=\"M34.5736 45.8908H33.5426V44.3798H34.5736V43.862C34.5736 42.4197 35.4371 41.6436 37.0474 41.6436H37.8593V43.1545H37.215C36.6098 43.1545 36.2489 43.4842 36.2489 44.0543V44.3839H37.7692V45.8949H36.2489V50.8523H34.5736V45.8908Z\"/><path d=\"M38.5244 42.4423C38.5315 42.1712 38.6459 41.9136 38.8433 41.7244C39.0406 41.5351 39.3053 41.4292 39.5809 41.4292C39.8565 41.4292 40.1212 41.5351 40.3185 41.7244C40.5159 41.9136 40.6303 42.1712 40.6374 42.4423C40.6303 42.7134 40.5159 42.971 40.3185 43.1603C40.1212 43.3495 39.8565 43.4554 39.5809 43.4554C39.3053 43.4554 39.0406 43.3495 38.8433 43.1603C38.6459 42.971 38.5315 42.7134 38.5244 42.4423Z\"/><path d=\"M40.4182 44.3819H38.7429V50.8482H40.4182V44.3819Z\"/><path d=\"M41.3467 47.6154C41.3467 45.6882 42.7253 44.2301 44.5576 44.2301C44.9597 44.2237 45.3586 44.3019 45.7275 44.4593C46.0965 44.6167 46.427 44.8497 46.6971 45.1429V44.3819H48.3724V50.8482H46.6971V50.0872C46.427 50.3804 46.0965 50.6134 45.7275 50.7709C45.3586 50.9283 44.9597 51.0064 44.5576 51C42.7253 51 41.3467 49.5419 41.3467 47.6154ZM46.6943 47.6154C46.6943 46.601 45.9209 45.8297 44.8794 45.8297C43.8484 45.8297 43.0757 46.603 43.0757 47.6154C43.0757 48.6278 43.8484 49.4011 44.8794 49.4011C45.9209 49.4025 46.6943 48.6291 46.6943 47.6154Z\"/><path d=\"M49.4634 42.0989H51.1387V45.1428C51.4083 44.8496 51.7385 44.6163 52.1071 44.4585C52.4756 44.3008 52.8742 44.2222 53.2761 44.228C55.1056 44.228 56.4717 45.6861 56.4717 47.6133C56.4717 49.5405 55.1056 50.9979 53.2761 50.9979C52.8745 51.0043 52.4761 50.9263 52.1076 50.7693C51.739 50.6122 51.4087 50.3797 51.1387 50.0872V50.8482H49.4634V42.0989ZM54.7461 47.6147C54.7461 46.6003 53.986 45.829 52.9424 45.829C51.9114 45.829 51.138 46.6023 51.138 47.6147C51.138 48.627 51.9114 49.4004 52.9424 49.4004C53.9846 49.4024 54.7447 48.6291 54.7447 47.6154L54.7461 47.6147Z\"/><path d=\"M57.3247 42.0989H59V50.8482H57.3247V42.0989Z\"/><path d=\"M19.702 41.2644L16.498 49.0082L16.1287 49.1676C15.8062 49.3049 15.4488 49.1346 15.2966 48.7665L14.422 46.6573L16.4463 41.7637C16.6411 41.2953 17.0403 41 17.4759 41H19.5665C19.6796 41 19.7522 41.1429 19.702 41.2644Z\"/><path d=\"M10.7859 47.2644H8.29596C8.07678 47.2644 7.93368 46.9897 8.03141 46.7548L9.30393 43.6786L10.7859 47.2644Z\"/><path d=\"M16.257 49.592L16.0162 50.1745C15.9403 50.3624 15.8165 50.528 15.6567 50.6552C15.5012 50.7791 15.3075 50.8474 15.1074 50.8489H13.3176C12.9323 50.8489 12.5812 50.5879 12.4102 50.1751L11.2109 47.2644L11.0399 46.8523L9.51331 43.171H11.8482C12.2845 43.171 12.6824 43.467 12.8771 43.9348L14.2125 47.1642L14.9594 48.967C15.1953 49.5398 15.7552 49.8084 16.257 49.592Z\"/></g></g>"
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -7,8 +7,8 @@ module Verifiabl
|
|
|
7
7
|
module PayslipCodes
|
|
8
8
|
module Australian
|
|
9
9
|
EARNINGS_TYPES = %w[
|
|
10
|
-
paid_leave allowance ordinary overtime
|
|
11
|
-
|
|
10
|
+
paid_leave allowance lump_sum etp ordinary overtime
|
|
11
|
+
bonus_commission directors_fees return_to_work other
|
|
12
12
|
].map!(&:freeze).freeze
|
|
13
13
|
|
|
14
14
|
PAID_LEAVE_TYPES = %w[
|
|
@@ -24,6 +24,19 @@ module Verifiabl
|
|
|
24
24
|
home_office non_deductible transport_fares uniform private_vehicle general
|
|
25
25
|
].map!(&:freeze).freeze
|
|
26
26
|
|
|
27
|
+
LUMP_SUM_TYPES = %w[
|
|
28
|
+
a_redundancy a_other b d e
|
|
29
|
+
].map!(&:freeze).freeze
|
|
30
|
+
|
|
31
|
+
ETP_TYPES = %w[
|
|
32
|
+
redundancy other redundancy_split other_split death_dependant death_non_dependant
|
|
33
|
+
death_non_dependant_split death_trustee
|
|
34
|
+
].map!(&:freeze).freeze
|
|
35
|
+
|
|
36
|
+
ETP_COMPONENTS = %w[
|
|
37
|
+
taxable tax_free
|
|
38
|
+
].map!(&:freeze).freeze
|
|
39
|
+
|
|
27
40
|
PAY_FREQUENCIES = %w[
|
|
28
41
|
weekly fortnightly monthly quarterly four_weekly semi_monthly
|
|
29
42
|
].map!(&:freeze).freeze
|
data/lib/verifiabl/issuer/pii.rb
CHANGED
|
@@ -6,16 +6,12 @@ require_relative "generated/jurisdiction_pii_profiles"
|
|
|
6
6
|
module Verifiabl
|
|
7
7
|
module Issuer
|
|
8
8
|
module Pii
|
|
9
|
-
FIELD_ORDER = %i[
|
|
10
|
-
employee_name position department employer_abn bsb account_number account_name address
|
|
11
|
-
].freeze
|
|
12
9
|
AUSTRALIAN_FIELD_ORDER = JurisdictionPiiProfiles::AUSTRALIAN_FIELD_ORDER
|
|
13
10
|
NEW_ZEALAND_FIELD_ORDER = JurisdictionPiiProfiles::NEW_ZEALAND_FIELD_ORDER
|
|
14
|
-
PROFILE_ID = PiiTextProfile::PROFILE_ID
|
|
15
11
|
AUSTRALIAN_PROFILE_ID = JurisdictionPiiProfiles::AUSTRALIAN_PROFILE_ID
|
|
16
12
|
NEW_ZEALAND_PROFILE_ID = JurisdictionPiiProfiles::NEW_ZEALAND_PROFILE_ID
|
|
17
13
|
PROFILE_UNICODE_VERSION = PiiTextProfile::UNICODE_VERSION
|
|
18
|
-
PAYLOAD_MAX_BYTES =
|
|
14
|
+
PAYLOAD_MAX_BYTES = JurisdictionPiiProfiles::PAYLOAD_MAX_BYTES
|
|
19
15
|
FORMAT_CHARACTER_RANGES = PiiTextProfile::FORMAT_CHARACTER_RANGES
|
|
20
16
|
|
|
21
17
|
Violation = Data.define(:field, :reason)
|
|
@@ -43,16 +39,8 @@ module Verifiabl
|
|
|
43
39
|
|
|
44
40
|
module_function
|
|
45
41
|
|
|
46
|
-
# Formats employee PII
|
|
47
|
-
#
|
|
48
|
-
def format(fields)
|
|
49
|
-
validate_fields!(fields)
|
|
50
|
-
values, violations = normalize_values(fields)
|
|
51
|
-
raise ValidationError, violations unless violations.empty?
|
|
52
|
-
|
|
53
|
-
format_profile("P2", FIELD_ORDER.map { |field| values.fetch(field) })
|
|
54
|
-
end
|
|
55
|
-
|
|
42
|
+
# Formats Australian employee PII as AU2 plaintext. The returned value
|
|
43
|
+
# must be encrypted before it is persisted or embedded.
|
|
56
44
|
def format_australian(fields)
|
|
57
45
|
allowed = %i[
|
|
58
46
|
employee_name position department employer_name employer_abn bsb account_number account_name address
|
|
@@ -69,6 +57,8 @@ module Verifiabl
|
|
|
69
57
|
format_profile(JurisdictionPiiProfiles::AUSTRALIAN_MARKER, AUSTRALIAN_FIELD_ORDER.map { |field| wire_values.fetch(field) })
|
|
70
58
|
end
|
|
71
59
|
|
|
60
|
+
# Formats New Zealand employee PII as NZ2 plaintext. The returned value
|
|
61
|
+
# must be encrypted before it is persisted or embedded.
|
|
72
62
|
def format_new_zealand(fields)
|
|
73
63
|
allowed = %i[
|
|
74
64
|
employee_name ird_number position department employer_name account_number account_name address
|
|
@@ -83,7 +73,7 @@ module Verifiabl
|
|
|
83
73
|
format_profile(JurisdictionPiiProfiles::NEW_ZEALAND_MARKER, NEW_ZEALAND_FIELD_ORDER.map { |field| wire_values.fetch(field) })
|
|
84
74
|
end
|
|
85
75
|
|
|
86
|
-
def validate_fields!(fields, allowed
|
|
76
|
+
def validate_fields!(fields, allowed)
|
|
87
77
|
raise ArgumentError, "fields must be a Hash" unless fields.is_a?(Hash)
|
|
88
78
|
|
|
89
79
|
normalized_keys = fields.keys.map do |key|
|
|
@@ -95,11 +85,6 @@ module Verifiabl
|
|
|
95
85
|
end
|
|
96
86
|
private_class_method :validate_fields!
|
|
97
87
|
|
|
98
|
-
def normalize_values(fields)
|
|
99
|
-
normalize_named_values(fields, FIELD_ORDER)
|
|
100
|
-
end
|
|
101
|
-
private_class_method :normalize_values
|
|
102
|
-
|
|
103
88
|
def normalize_named_values(fields, names)
|
|
104
89
|
names.each_with_object([{}, []]) do |field, (values, violations)|
|
|
105
90
|
value, violation = normalize_value(field, field_value(fields, field))
|
|
@@ -155,8 +140,9 @@ module Verifiabl
|
|
|
155
140
|
|
|
156
141
|
def format_profile(version, segments)
|
|
157
142
|
plaintext = "#{version}|#{segments.join("|")}"
|
|
158
|
-
|
|
159
|
-
|
|
143
|
+
if plaintext.bytesize > PAYLOAD_MAX_BYTES
|
|
144
|
+
raise RangeError, "#{version} plaintext exceeds #{PAYLOAD_MAX_BYTES} UTF-8 bytes"
|
|
145
|
+
end
|
|
160
146
|
|
|
161
147
|
plaintext
|
|
162
148
|
end
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require_relative "frame_assets"
|
|
4
4
|
require_relative "png_encoder"
|
|
5
5
|
require_relative "rendering_assets"
|
|
6
|
+
require_relative "generated/horizontal_frame"
|
|
6
7
|
|
|
7
8
|
module Verifiabl
|
|
8
9
|
module Issuer
|
|
@@ -24,52 +25,66 @@ module Verifiabl
|
|
|
24
25
|
SvgResult = Data.define(:svg, :width, :height, :content, :error_correction_level, :qr_version, :module_px, :degraded)
|
|
25
26
|
PngResult = Data.define(:png, :width, :height, :content, :error_correction_level, :qr_version, :module_px, :degraded)
|
|
26
27
|
Raster = Data.define(:rgba, :width, :height, :content, :error_correction_level, :qr_version, :module_px, :degraded)
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
Geometry = Data.define(:viewbox_width, :height_units, :qr_x, :qr_y, :qr_units, :min_width, :png_width)
|
|
29
|
+
VERTICAL_GEOMETRY = Geometry.new(VIEWBOX_WIDTH, HEIGHT_UNITS, 0, QR_Y, QR_UNITS, MIN_WIDTH, 720)
|
|
30
|
+
# 96-unit QR, the same 7-unit gap as vertical, then an 85-unit panel.
|
|
31
|
+
# Corresponding widths give the same QR size in both layouts.
|
|
32
|
+
HORIZONTAL_GEOMETRY = Geometry.new(188, 96, 0, 0, 96, 940, 1410)
|
|
33
|
+
RasterContext = Data.define(:rgba, :width, :denominator, :num_x, :num_y, :pixel_width, :geometry)
|
|
34
|
+
SvgRequest = Data.define(:verifiabl_reference, :encrypted_pii, :width, :layout, :environment, :scan_base_url, :max_error_correction, :error_correction_level)
|
|
29
35
|
PngRequest = Data.define(*SvgRequest.members, :compression_level)
|
|
30
|
-
SVG_DEFAULTS = {width: MIN_WIDTH, environment: :production, scan_base_url: nil, max_error_correction: nil, error_correction_level: nil}.freeze
|
|
36
|
+
SVG_DEFAULTS = {width: MIN_WIDTH, layout: :vertical, environment: :production, scan_base_url: nil, max_error_correction: nil, error_correction_level: nil}.freeze
|
|
31
37
|
PNG_DEFAULTS = SVG_DEFAULTS.merge(width: 720, compression_level: 6).freeze
|
|
32
|
-
private_constant :Raster, :RasterContext, :SvgRequest, :PngRequest, :SVG_DEFAULTS, :PNG_DEFAULTS
|
|
38
|
+
private_constant :Raster, :RasterContext, :SvgRequest, :PngRequest, :SVG_DEFAULTS, :PNG_DEFAULTS, :Geometry, :VERTICAL_GEOMETRY, :HORIZONTAL_GEOMETRY
|
|
33
39
|
|
|
34
40
|
module_function
|
|
35
41
|
|
|
36
42
|
def svg(**options)
|
|
37
|
-
|
|
38
|
-
|
|
43
|
+
geometry = geometry_for(options.fetch(:layout, :vertical))
|
|
44
|
+
request = SvgRequest.new(**SVG_DEFAULTS.merge(width: geometry.min_width).merge(options))
|
|
45
|
+
width = validate_svg_width(request.width, geometry.min_width)
|
|
39
46
|
ladder = error_correction_ladder(request.max_error_correction, request.error_correction_level)
|
|
40
|
-
qr = select_qr(request, width, ladder)
|
|
41
|
-
|
|
42
|
-
module_px = module_size * width / VIEWBOX_WIDTH
|
|
43
|
-
build_svg_result(qr, ladder, width, module_size, module_px)
|
|
47
|
+
qr = select_qr(request, width, ladder, geometry)
|
|
48
|
+
build_svg_result(qr, ladder, width, geometry)
|
|
44
49
|
end
|
|
45
50
|
|
|
46
|
-
def build_svg_result(qr, ladder, width,
|
|
47
|
-
|
|
51
|
+
def build_svg_result(qr, ladder, width, geometry)
|
|
52
|
+
module_size = geometry.qr_units.to_f / qr.modules.length
|
|
53
|
+
module_px = module_size * width / geometry.viewbox_width
|
|
54
|
+
height = round(width * geometry.height_units / geometry.viewbox_width.to_f)
|
|
48
55
|
SvgResult.new(
|
|
49
|
-
svg: svg_document(qr.modules, width, height, module_size).freeze,
|
|
56
|
+
svg: svg_document(qr.modules, [width, height], module_size, geometry).freeze,
|
|
50
57
|
width:,
|
|
51
58
|
height:,
|
|
59
|
+
**qr_metadata(qr, ladder, module_px)
|
|
60
|
+
)
|
|
61
|
+
end
|
|
62
|
+
private_class_method :build_svg_result
|
|
63
|
+
|
|
64
|
+
def qr_metadata(qr, ladder, module_px)
|
|
65
|
+
{
|
|
52
66
|
content: qr.content,
|
|
53
67
|
error_correction_level: qr.error_correction_level,
|
|
54
68
|
qr_version: qr.version,
|
|
55
69
|
module_px: round(module_px),
|
|
56
70
|
degraded: degraded?(qr.error_correction_level, ladder, module_px)
|
|
57
|
-
|
|
71
|
+
}
|
|
58
72
|
end
|
|
59
|
-
private_class_method :
|
|
73
|
+
private_class_method :qr_metadata
|
|
60
74
|
|
|
61
|
-
def svg_document(modules,
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
%(<g transform="translate(
|
|
75
|
+
def svg_document(modules, dimensions, module_size, geometry)
|
|
76
|
+
width, height = dimensions
|
|
77
|
+
frame = (geometry == HORIZONTAL_GEOMETRY) ? RenderingAssets::HORIZONTAL_FRAME : render_vertical_frame
|
|
78
|
+
%(<svg xmlns="http://www.w3.org/2000/svg" width="#{number(width)}" height="#{number(height)}" viewBox="0 0 #{geometry.viewbox_width} #{geometry.height_units}" role="img" aria-label="Secured by Verifiabl verification barcode">) + frame +
|
|
79
|
+
%(<g transform="translate(#{geometry.qr_x} #{geometry.qr_y})"><g shape-rendering="crispEdges">) + render_modules(modules, module_size) +
|
|
66
80
|
%(</g>) + render_finders(modules.length, module_size) + %(</g></svg>)
|
|
67
81
|
end
|
|
68
82
|
private_class_method :svg_document
|
|
69
83
|
|
|
70
84
|
def png(**options)
|
|
71
|
-
|
|
72
|
-
|
|
85
|
+
geometry = geometry_for(options.fetch(:layout, :vertical))
|
|
86
|
+
request = PngRequest.new(**PNG_DEFAULTS.merge(width: geometry.png_width).merge(options))
|
|
87
|
+
validate_png_width(request.width, request.layout)
|
|
73
88
|
ladder = error_correction_ladder(request.max_error_correction, request.error_correction_level)
|
|
74
89
|
build_png_result(compose(request:, ladder:), request.compression_level)
|
|
75
90
|
end
|
|
@@ -91,28 +106,30 @@ module Verifiabl
|
|
|
91
106
|
def compose(**options)
|
|
92
107
|
ladder = options.delete(:ladder)
|
|
93
108
|
request = options.delete(:request) || PngRequest.new(**PNG_DEFAULTS.merge(options))
|
|
94
|
-
|
|
95
|
-
rgba, raster_width, height = FrameAssets.raster(request.width)
|
|
96
|
-
blit_qr(rgba, raster_width, qr.modules, request.width)
|
|
97
|
-
build_raster(rgba, [raster_width, height], qr, ladder, request.width.to_f / qr.modules.length)
|
|
109
|
+
compose_qr(request, ladder, geometry_for(request.layout))
|
|
98
110
|
end
|
|
99
111
|
private_class_method :compose
|
|
100
112
|
|
|
113
|
+
def compose_qr(request, ladder, geometry)
|
|
114
|
+
qr = select_qr(request, request.width, ladder, geometry)
|
|
115
|
+
rgba, raster_width, height = FrameAssets.raster(request.width, layout: request.layout)
|
|
116
|
+
blit_qr(rgba, raster_width, qr.modules, request.width, geometry)
|
|
117
|
+
module_px = geometry.qr_units.to_f * request.width / (geometry.viewbox_width * qr.modules.length)
|
|
118
|
+
build_raster(rgba, [raster_width, height], qr, ladder, module_px)
|
|
119
|
+
end
|
|
120
|
+
private_class_method :compose_qr
|
|
121
|
+
|
|
101
122
|
def build_raster(rgba, dimensions, qr, ladder, module_px)
|
|
102
123
|
Raster.new(
|
|
103
124
|
rgba: rgba.freeze,
|
|
104
125
|
width: dimensions.fetch(0),
|
|
105
126
|
height: dimensions.fetch(1),
|
|
106
|
-
|
|
107
|
-
error_correction_level: qr.error_correction_level,
|
|
108
|
-
qr_version: qr.version,
|
|
109
|
-
module_px: round(module_px),
|
|
110
|
-
degraded: degraded?(qr.error_correction_level, ladder, module_px)
|
|
127
|
+
**qr_metadata(qr, ladder, module_px)
|
|
111
128
|
)
|
|
112
129
|
end
|
|
113
130
|
private_class_method :build_raster
|
|
114
131
|
|
|
115
|
-
def select_qr(request, width, ladder)
|
|
132
|
+
def select_qr(request, width, ladder, geometry)
|
|
116
133
|
ladder.each do |level|
|
|
117
134
|
begin
|
|
118
135
|
qr = Qr.encode_scan_url(
|
|
@@ -125,7 +142,7 @@ module Verifiabl
|
|
|
125
142
|
rescue Qr::CapacityError
|
|
126
143
|
next
|
|
127
144
|
end
|
|
128
|
-
return qr if
|
|
145
|
+
return qr if geometry.qr_units.to_f * width / (geometry.viewbox_width * qr.modules.length) >= MIN_MODULE_PX
|
|
129
146
|
end
|
|
130
147
|
raise Qr::CapacityError, "The PII is too long to render a scannable barcode in the branded frame at width #{number(width)}, even at the lowest error correction"
|
|
131
148
|
end
|
|
@@ -237,6 +254,12 @@ module Verifiabl
|
|
|
237
254
|
end
|
|
238
255
|
private_class_method :rounded_rect_left
|
|
239
256
|
|
|
257
|
+
def render_vertical_frame
|
|
258
|
+
header = %(<path d="M0 8C0 3.58172 3.58172 0 8 0H88C92.4183 0 96 3.58172 96 8V47H0V8Z" fill="#{NAVY}"/>) + render_header
|
|
259
|
+
%(<rect x="0" y="39" width="96" height="111" fill="#FFFFFF"/>) + header
|
|
260
|
+
end
|
|
261
|
+
private_class_method :render_vertical_frame
|
|
262
|
+
|
|
240
263
|
def render_header
|
|
241
264
|
secured_by = RenderingAssets::SECURED_BY_PATH.sub("{color}", "#FFFFFF")
|
|
242
265
|
wordmark = %(<g transform="translate(8 23) scale(1)" fill="#FFFFFF">#{RenderingAssets::WORDMARK_PATHS}</g>)
|
|
@@ -244,8 +267,8 @@ module Verifiabl
|
|
|
244
267
|
end
|
|
245
268
|
private_class_method :render_header
|
|
246
269
|
|
|
247
|
-
def blit_qr(rgba, raster_width, modules, pixel_width)
|
|
248
|
-
context = raster_context(rgba, raster_width, modules.length, pixel_width)
|
|
270
|
+
def blit_qr(rgba, raster_width, modules, pixel_width, geometry)
|
|
271
|
+
context = raster_context(rgba, raster_width, modules.length, pixel_width, geometry)
|
|
249
272
|
edges_x, edges_y = raster_edges(context, modules.length)
|
|
250
273
|
draw_raster_modules(context, modules, edges_x, edges_y)
|
|
251
274
|
|
|
@@ -256,11 +279,13 @@ module Verifiabl
|
|
|
256
279
|
end
|
|
257
280
|
private_class_method :blit_qr
|
|
258
281
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
282
|
+
# Keep module edges and finder coverage in exact rational/integer units.
|
|
283
|
+
# Floating-point raster geometry would break cross-SDK pixel parity.
|
|
284
|
+
def raster_context(rgba, width, size, pixel_width, geometry)
|
|
285
|
+
denominator = geometry.viewbox_width * size
|
|
286
|
+
num_x = ->(position) { pixel_width * (geometry.qr_x * size + geometry.qr_units * position) }
|
|
287
|
+
num_y = ->(position) { pixel_width * (geometry.qr_y * size + geometry.qr_units * position) }
|
|
288
|
+
RasterContext.new(rgba:, width:, denominator:, num_x:, num_y:, pixel_width:, geometry:)
|
|
264
289
|
end
|
|
265
290
|
private_class_method :raster_context
|
|
266
291
|
|
|
@@ -295,8 +320,8 @@ module Verifiabl
|
|
|
295
320
|
private_class_method :render_raster_finder
|
|
296
321
|
|
|
297
322
|
def raster_finder_shapes(context, module_x, module_y)
|
|
298
|
-
module_q =
|
|
299
|
-
radius_unit =
|
|
323
|
+
module_q = context.geometry.qr_units * context.pixel_width * 80
|
|
324
|
+
radius_unit = context.geometry.qr_units * context.pixel_width
|
|
300
325
|
outer = raster_finder_outer(context, module_x, module_y, module_q, radius_unit)
|
|
301
326
|
{outer:, inner: inset_finder_shape(outer, module_q, 1, 80 * radius_unit), dot: inset_finder_shape(outer, module_q, 2, 52 * radius_unit)}
|
|
302
327
|
end
|
|
@@ -385,18 +410,27 @@ module Verifiabl
|
|
|
385
410
|
end
|
|
386
411
|
private_class_method :fill_black
|
|
387
412
|
|
|
388
|
-
def
|
|
389
|
-
|
|
390
|
-
|
|
413
|
+
def geometry_for(layout)
|
|
414
|
+
return VERTICAL_GEOMETRY if layout == :vertical
|
|
415
|
+
return HORIZONTAL_GEOMETRY if layout == :horizontal
|
|
416
|
+
|
|
417
|
+
raise ArgumentError, "layout must be :vertical or :horizontal"
|
|
418
|
+
end
|
|
419
|
+
private_class_method :geometry_for
|
|
420
|
+
|
|
421
|
+
def validate_svg_width(value, minimum)
|
|
422
|
+
unless value.is_a?(Numeric) && value.finite? && value >= minimum
|
|
423
|
+
raise ArgumentError, "width must be at least #{minimum}"
|
|
391
424
|
end
|
|
392
425
|
value
|
|
393
426
|
end
|
|
394
427
|
private_class_method :validate_svg_width
|
|
395
428
|
|
|
396
|
-
def validate_png_width(value)
|
|
397
|
-
|
|
429
|
+
def validate_png_width(value, layout)
|
|
430
|
+
widths = FrameAssets.supported_widths(layout)
|
|
431
|
+
return value if value.is_a?(Integer) && widths.include?(value)
|
|
398
432
|
|
|
399
|
-
raise ArgumentError, "width must be one of #{
|
|
433
|
+
raise ArgumentError, "width must be one of #{widths.join(", ")}"
|
|
400
434
|
end
|
|
401
435
|
private_class_method :validate_png_width
|
|
402
436
|
|
|
@@ -57,9 +57,6 @@ module Verifiabl
|
|
|
57
57
|
|
|
58
58
|
def validate_payslip_non_pii!(schema, payslip_non_pii)
|
|
59
59
|
raise ArgumentError, "payslip_non_pii must be a Hash" unless payslip_non_pii.is_a?(Hash)
|
|
60
|
-
if %w[au.payslip.v1 nz.payslip.v1].include?(schema) && !key?(payslip_non_pii, :period_start)
|
|
61
|
-
raise ArgumentError, "period_start is required for #{schema}"
|
|
62
|
-
end
|
|
63
60
|
validate_iso_date!(value(payslip_non_pii, :period_start), "period_start") if key?(payslip_non_pii, :period_start)
|
|
64
61
|
validate_iso_date!(value(payslip_non_pii, :period_end), "period_end") if key?(payslip_non_pii, :period_end)
|
|
65
62
|
validate_v2_currency!(payslip_non_pii) if [AUSTRALIAN_PAYSLIP_V2_SCHEMA, NEW_ZEALAND_PAYSLIP_V2_SCHEMA].include?(schema)
|
data/lib/verifiabl/issuer.rb
CHANGED
|
@@ -21,10 +21,6 @@ require_relative "issuer/version"
|
|
|
21
21
|
module Verifiabl
|
|
22
22
|
module Issuer
|
|
23
23
|
class << self
|
|
24
|
-
def format_pii(fields)
|
|
25
|
-
Pii.format(fields)
|
|
26
|
-
end
|
|
27
|
-
|
|
28
24
|
def format_australian_pii(fields)
|
|
29
25
|
Pii.format_australian(fields)
|
|
30
26
|
end
|
|
@@ -59,10 +55,22 @@ module Verifiabl
|
|
|
59
55
|
Payload.scan_url(**options)
|
|
60
56
|
end
|
|
61
57
|
|
|
58
|
+
# Render a branded SVG. Vertical is the default; horizontal puts the QR
|
|
59
|
+
# on the left and the light-tinted brand panel on the right.
|
|
60
|
+
# @param options [Hash] rendering and scan-URL options
|
|
61
|
+
# @option options [Symbol] :layout (:vertical) :vertical or :horizontal
|
|
62
|
+
# @option options [Numeric] :width minimum/default 480 vertical, 940 horizontal
|
|
63
|
+
# @return [Rendering::SvgResult] deterministic SVG and QR metadata
|
|
62
64
|
def build_barcode_svg(**options)
|
|
63
65
|
Rendering.svg(**options)
|
|
64
66
|
end
|
|
65
67
|
|
|
68
|
+
# Render a deterministic branded PNG from a pre-rasterised frame.
|
|
69
|
+
# @param options [Hash] rendering and scan-URL options
|
|
70
|
+
# @option options [Symbol] :layout (:vertical) :vertical or :horizontal
|
|
71
|
+
# @option options [Integer] :width default 720 vertical, 1410 horizontal;
|
|
72
|
+
# supported widths are 480/720/960/1440 and 940/1410/1880/2820 respectively
|
|
73
|
+
# @return [Rendering::PngResult] deterministic PNG and QR metadata
|
|
66
74
|
def build_barcode_png(**options)
|
|
67
75
|
Rendering.png(**options)
|
|
68
76
|
end
|
data/sig/verifiabl-issuer.rbs
CHANGED
|
@@ -11,6 +11,9 @@ module Verifiabl
|
|
|
11
11
|
PAID_LEAVE_TYPES: Array[String]
|
|
12
12
|
ALLOWANCE_TYPES: Array[String]
|
|
13
13
|
OTHER_ALLOWANCE_CATEGORIES: Array[String]
|
|
14
|
+
LUMP_SUM_TYPES: Array[String]
|
|
15
|
+
ETP_TYPES: Array[String]
|
|
16
|
+
ETP_COMPONENTS: Array[String]
|
|
14
17
|
PAY_FREQUENCIES: Array[String]
|
|
15
18
|
end
|
|
16
19
|
|
|
@@ -57,7 +60,6 @@ module Verifiabl
|
|
|
57
60
|
?address: new_zealand_address?
|
|
58
61
|
}
|
|
59
62
|
|
|
60
|
-
def self.format_pii: (Hash[Symbol | String, String?]) -> String
|
|
61
63
|
def self.format_australian_pii: (australian_pii_fields) -> String
|
|
62
64
|
def self.format_new_zealand_pii: (new_zealand_pii_fields) -> String
|
|
63
65
|
def self.prepare_australian_v2_payslip: (pii: australian_pii_fields, payslip_non_pii: Hash[Symbol | String, untyped], issued_at: String | Time, key: binary, ?verifiabl_reference: String) -> PreparedV2Payslip
|
|
@@ -66,8 +68,8 @@ module Verifiabl
|
|
|
66
68
|
def self.generate_verifiabl_reference: () -> String
|
|
67
69
|
def self.build_barcode_payload: (verifiabl_reference: String, encrypted_pii: binary) -> String
|
|
68
70
|
def self.build_scan_url: (verifiabl_reference: String, encrypted_pii: binary, ?environment: Symbol | String, ?scan_base_url: String?) -> String
|
|
69
|
-
def self.build_barcode_svg: (verifiabl_reference: String, encrypted_pii: binary, ?width: Numeric, ?environment: Symbol | String, ?scan_base_url: String?, ?max_error_correction: Symbol | String, ?error_correction_level: Symbol | String) -> Rendering::SvgResult
|
|
70
|
-
def self.build_barcode_png: (verifiabl_reference: String, encrypted_pii: binary, ?width: Integer, ?environment: Symbol | String, ?scan_base_url: String?, ?max_error_correction: Symbol | String, ?error_correction_level: Symbol | String, ?compression_level: Integer) -> Rendering::PngResult
|
|
71
|
+
def self.build_barcode_svg: (verifiabl_reference: String, encrypted_pii: binary, ?width: Numeric, ?layout: :vertical | :horizontal, ?environment: Symbol | String, ?scan_base_url: String?, ?max_error_correction: Symbol | String, ?error_correction_level: Symbol | String) -> Rendering::SvgResult
|
|
72
|
+
def self.build_barcode_png: (verifiabl_reference: String, encrypted_pii: binary, ?width: Integer, ?layout: :vertical | :horizontal, ?environment: Symbol | String, ?scan_base_url: String?, ?max_error_correction: Symbol | String, ?error_correction_level: Symbol | String, ?compression_level: Integer) -> Rendering::PngResult
|
|
71
73
|
|
|
72
74
|
class PreparedV2Payslip
|
|
73
75
|
attr_reader verifiabl_reference: String
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: verifiabl-issuer
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Verifiabl
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-09 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: base64
|
|
@@ -167,6 +167,10 @@ files:
|
|
|
167
167
|
- lib/verifiabl/issuer/assets/frame-480.vfr1
|
|
168
168
|
- lib/verifiabl/issuer/assets/frame-720.vfr1
|
|
169
169
|
- lib/verifiabl/issuer/assets/frame-960.vfr1
|
|
170
|
+
- lib/verifiabl/issuer/assets/frame-horizontal-1410.vfr1
|
|
171
|
+
- lib/verifiabl/issuer/assets/frame-horizontal-1880.vfr1
|
|
172
|
+
- lib/verifiabl/issuer/assets/frame-horizontal-2820.vfr1
|
|
173
|
+
- lib/verifiabl/issuer/assets/frame-horizontal-940.vfr1
|
|
170
174
|
- lib/verifiabl/issuer/base32.rb
|
|
171
175
|
- lib/verifiabl/issuer/client.rb
|
|
172
176
|
- lib/verifiabl/issuer/configuration.rb
|
|
@@ -174,6 +178,7 @@ files:
|
|
|
174
178
|
- lib/verifiabl/issuer/errors.rb
|
|
175
179
|
- lib/verifiabl/issuer/frame_assets.rb
|
|
176
180
|
- lib/verifiabl/issuer/generated/frame_widths.rb
|
|
181
|
+
- lib/verifiabl/issuer/generated/horizontal_frame.rb
|
|
177
182
|
- lib/verifiabl/issuer/generated/jurisdiction_pii_profiles.rb
|
|
178
183
|
- lib/verifiabl/issuer/generated/non-pii-v2-fields.json
|
|
179
184
|
- lib/verifiabl/issuer/generated/pii_text_profile.rb
|