Get started
Install
Four packages, all Apache-2.0, all published to npm. None of them opens a network connection, holds a key, or needs a server.
The four packages
| Package | What it is for |
|---|---|
@payvol/core |
The data model: encode and decode the canton: URI, validate a request,
canonicalise it and hash it. A /draft02 subpath carries the newer profile,
its observation adapter and the reconciler.
|
@payvol/qr |
The carriers: QR payload text, web+canton links, and NFC NDEF records.
Drawing the QR image and writing the tag are your renderer's job, not this package's.
|
@payvol/conformance | The vectors and the runner, so an independent implementation can be checked rather than trusted. |
@payvol/cli | The same operations from a terminal, and the conformance runner as a command. |
Installing
npm install @payvol/core @payvol/qr
# the conformance suite, when you are checking an implementation
npm install --save-dev @payvol/conformance
# the CLI needs no install at all
npx @payvol/cli conformance Proving the install
Everything below ran in an empty directory against the published packages.
// Everything the four packages give you, from a clean install.
import { encode, decode, validate, toCanonical, sha256 } from '@payvol/core';
import { qrPayload, ndefUriRecord, parseNdefUriRecord } from '@payvol/qr';
import { runBundledVectors, loadAllVectors } from '@payvol/conformance/node';
import { formatSummary } from '@payvol/conformance';
const request = {
version: '0.1',
type: 'transfer',
recipient: 'payee::1220aabbcc',
instrument: 'DSO::1220be58/Amulet',
amount: '12.50',
reference: 'INV-2026-0042',
};
const uri = encode(request);
console.log('uri ', uri);
console.log('round trip ', decode(uri).reference === request.reference);
console.log('valid ', validate(request).valid);
console.log('digest ', sha256(toCanonical(request)).slice(0, 32), '…');
console.log('qr payload ', qrPayload(request) === uri);
console.log('ndef round ', parseNdefUriRecord(ndefUriRecord(request)) === uri);
console.log('vectors ', loadAllVectors().length);
console.log(formatSummary(runBundledVectors()).split('\n').pop()); uri canton:payee%3A%3A1220aabbcc?instrument=DSO%3A%3A1220be58%2FAmulet&amount=12.50&reference=INV-2026-0042
round trip true
valid true
digest e3cdfb5a334a25089851d9515b12d20a …
qr payload true
ndef round true
vectors 146
PASS: 146 passed, 0 failed, 0 skipped (146 total)
Output from @payvol/core version 0.4.0 and its sibling
packages, installed from npm, on 2026-10-18.
Node and the browser
engines.node is >=20.19.0 on all four, which is what the resolved
@noble/hashes requires. Below that an engine-strict install refuses.
@payvol/core and @payvol/qr are dependency-light and run unchanged in
a browser: no node:crypto, no filesystem. @payvol/conformance splits
deliberately — the root entry takes vectors as data and runs anywhere, while
@payvol/conformance/node carries the file loaders and needs Node.
Which version is which
The npm version and the profile version are different numbers and move independently. The
packages are on one npm version; the shipped data profile is
0.1 and the profile under development is
0.2-draft. A recent package writing a
0.1 artifact is normal.
Versioning and deprecation has the full account.