Skip to content

Get the QR code from Verifiabl

In this integration, Verifiabl makes the QR code. You prepare the payslip with the SDK helper for its country and send it to Verifiabl. The API returns a PNG of the QR code.

Recommended: make the QR code in your system. Refer to Make the QR code in your system.

To call the HTTP API without the SDK, follow Use the Issuer API. For client options, retries and errors, refer to the Issuer SDK guide.

npm install @verifiabl/issuer@1.0.0
dotnet add package Verifiabl.Issuer --version 1.0.0
gem install verifiabl-issuer --version 1.0.0

Ruby requires version 3.3 or newer. Refer to the complete example on GitHub.

Create a client with your client ID and client secret. The SDK gets an access token and renews it.

import { VerifiablClient } from "@verifiabl/issuer";

const clientId = process.env.VERIFIABL_CLIENT_ID?.trim();
const clientSecret = process.env.VERIFIABL_CLIENT_SECRET?.trim();

if (!clientId || !clientSecret) {
  throw new Error("Missing VERIFIABL_CLIENT_ID or VERIFIABL_CLIENT_SECRET environment variable");
}

const client = new VerifiablClient({
  environment: "sandbox",
  auth: { clientId, clientSecret },
});
using Verifiabl;
using Verifiabl.Client;

string clientId = Environment.GetEnvironmentVariable("VERIFIABL_CLIENT_ID")?.Trim()
    ?? throw new InvalidOperationException("Missing VERIFIABL_CLIENT_ID environment variable");
string clientSecret = Environment.GetEnvironmentVariable("VERIFIABL_CLIENT_SECRET")?.Trim()
    ?? throw new InvalidOperationException("Missing VERIFIABL_CLIENT_SECRET environment variable");

if (clientId.Length == 0 || clientSecret.Length == 0)
{
    throw new InvalidOperationException("Missing VERIFIABL_CLIENT_ID or VERIFIABL_CLIENT_SECRET environment variable");
}

var client = new VerifiablClient(new VerifiablClientOptions
{
    Environment = VerifiablEnvironment.Sandbox,
    Auth = VerifiablAuth.ClientCredentials(clientId, clientSecret),
});
require "verifiabl/issuer"

issuer = Verifiabl::Issuer::Client.new(
  Verifiabl::Issuer::Configuration.new(
    environment: :sandbox, # or :production
    client_id: ENV.fetch("VERIFIABL_CLIENT_ID"),
    client_secret: ENV.fetch("VERIFIABL_CLIENT_SECRET")
  )
)

Prepare the payslip with the helper for its country. The helper validates the non-PII data and encrypts the PII with your encryption key. Use the prepared payslip in the next steps.

import { prepareAustralianV2Payslip } from "@verifiabl/issuer";

const encodedKey = process.env.VERIFIABL_ENCRYPTION_KEY_BASE64;
if (!encodedKey) throw new Error("Missing VERIFIABL_ENCRYPTION_KEY_BASE64 environment variable");
const key = Buffer.from(encodedKey, "base64");

const prepared = prepareAustralianV2Payslip({
  pii: {
    employeeName: "Ana Leota", employerName: "Example Software Pty Ltd",
    employerAbn: "12 345 678 901",
    address: { lines: ["12 Example St"], suburb: "Sydney", stateOrTerritory: "NSW", postcode: "2000" },
  },
  payslipNonPii: {
    periodEnd: "2026-08-31", paymentDate: "2026-09-04", currency: "AUD",
    gross: "9000.00", paygw: "2250.00", net: "6750.00",
  },
  issuedAt: new Date().toISOString(), key,
});
using Verifiabl;
using Verifiabl.Client;

byte[] key = Convert.FromBase64String(
    Environment.GetEnvironmentVariable("VERIFIABL_ENCRYPTION_KEY_BASE64")!);

PreparedV2Payslip prepared = V2Issuance.PrepareAustralian(
    pii: new AustralianPiiFields
    {
        EmployeeName = "Ana Leota", EmployerName = "Example Software Pty Ltd",
        EmployerAbn = "12 345 678 901",
    },
    payslip: new AustralianPayslipV2
    {
        PeriodEnd = new DateOnly(2026, 8, 31), PaymentDate = new DateOnly(2026, 9, 4),
        Currency = PayslipCurrencies.Aud,
        Gross = 9000.00m, Paygw = 2250.00m, Net = 6750.00m,
    },
    issuedAt: DateTimeOffset.UtcNow, key: key);
require "base64"

key = Base64.strict_decode64(ENV.fetch("VERIFIABL_ENCRYPTION_KEY_BASE64"))

prepared = Verifiabl::Issuer.prepare_australian_v2_payslip(
  pii: {
    employee_name: "Ana Leota", employer_name: "Example Software Pty Ltd",
    employer_abn: "12 345 678 901",
    address: {lines: ["12 Example St"], suburb: "Sydney", state_or_territory: "NSW", postcode: "2000"}
  },
  payslip_non_pii: {
    period_end: "2026-08-31", payment_date: "2026-09-04", currency: "AUD",
    gross: "9000.00", paygw: "2250.00", net: "6750.00"
  },
  issued_at: Time.now.utc, key: key
)

The example uses DateOnly on .NET 8 or newer. On .NET Framework 4.7.2, use YYYY-MM-DD strings for payslip dates.

Send prepared.apiManagedRegistrationprepared.ApiManagedRegistrationprepared.api_managed_registration to registerAndBuildBarcode. The request includes the non-PII data, the encryption metadata and the ciphertext. Verifiabl returns a PNG of the QR code and the reference for the payslip.

const result = await client.registerAndBuildBarcode(prepared.apiManagedRegistration);
// result.barcode.data is a base64 PNG.
RegisterAndBuildBarcodeResponse result = await client.RegisterAndBuildBarcodeAsync(
    prepared.ApiManagedRegistration);
// result.Barcode.Data is a base64 PNG.
result = issuer.register_and_build_barcode(**prepared.api_managed_registration)
# result.barcode.data is a base64 PNG.

Decode the PNG from barcode.dataBarcode.Databarcode.data.

Put the QR code on the payslip PDF, usually in the bottom-right corner.

Keep a light margin around the QR code. Scanners need this margin. The image has a margin only on the side next to the “Secured by Verifiabl” frame. On the other sides, make the margin at least one tenth of the QR code width:

  • For the vertical layout, these sides are the left, the right and the bottom.
  • For the horizontal layout, these sides are the left, the top and the bottom.

One tenth of the QR code width is more than four QR modules (the small squares) for all payslips. Four modules is the minimum.

Add the payload 2|<verifiablReference>|<BASE32> to the XMP metadata of the PDF. A verifier’s system reads the metadata copy first, because it does not need to decode the QR code. A copy made with print to PDF, or a photograph of the payslip, does not keep the metadata. These copies need the QR code.

import { buildBarcodePayload } from "@verifiabl/issuer";

const xmpPayload = buildBarcodePayload(prepared.barcodeParts(result.verifiablReference));
string xmpPayload = VerifiablBarcode.BuildPayload(
    prepared.BarcodeParts(result.VerifiablReference));
xmp_payload = Verifiabl::Issuer.build_barcode_payload(
  **prepared.barcode_parts(result.verifiabl_reference)
)

For the XMP namespace and property name, refer to PDF metadata copy.

For the request fields, response fields, status codes and a cURL example, refer to registerAndBuildBarcode.