Skip to main content

Node SDK: @oid4pay/oid4ac-merchant

The Node SDK is a verify-only merchant library. It verifies SD-JWT VC mandates, RFC 9421 signed offers, and signed catalogs. It never settles a charge: settlement is performed by the Authorization Server over HTTP, not through an SDK call.

The package is ESM-only (version 0.1.0, MIT). Node 18.18.0 or newer is required. It depends on jose at runtime for JWT and JWS verification. OpenTelemetry packages are optional peer dependencies; install them only if you want the bundled OTel observability adapter. TypeScript types are bundled.

Install

npm install @oid4pay/oid4ac-merchant
# or
pnpm add @oid4pay/oid4ac-merchant

API reference

verifyMandate(presentation, merchantAudience, options?)

Verifies a complete SD-JWT VC mandate presentation against the issuer JWKS. The first argument is the whole compact presentation string (issuer SD-JWT VC, then disclosures, then the Key-Binding JWT). The second argument is your merchant audience, which must appear in the mandate's aud. Returns a VerifiedMandate on success and throws MandateVerifyError on failure.

VerifyMandateOptions fields: jwks, jwksUrl, jwksCache, expectedIssuer, expectedVct, expectedOid4acVersion, expectedNonce, expectedOfferDigest, iatToleranceSeconds. Supply the issuer keys through jwks (a pre-fetched JWKS document) or through jwksUrl (the SDK fetches it over HTTPS, optionally cached with jwksCache).

VerifiedMandate fields: mandateId, agentClientId, principalId, amountMinor (number or null), currency, merchant, lineItems, cnfJkt, audienceResolved, offerDigest, issuedAt, expiresAt, verifiedAt, issuerClaims, disclosedClaims.

verifyOffer(offerBody, signatureHeaders, merchantJwks, options?)

Verifies an RFC 9421 signed offer body against a merchant JWKS. Throws OfferVerifyError on failure. Returns { keyid, alg, created, expires, bodyDigest, method, targetUri, authority } on success. VerifyOfferOptions fields: expectedTargetUri, now, createdSkewSeconds.

canonicalOfferDigest(offerBody)

Computes the canonical SHA-256 digest of an offer body and returns it as a base64url string. Use it to pin expectedOfferDigest when verifying a mandate against a known offer.

Catalog helpers

verifyCatalog verifies a signed catalog page; serveCatalog assembles a catalog page for serving; pickCheapestMatching and pickInStock are selection helpers over catalog items.

JWKS helpers

fetchJwks(url) fetches a JWKS document over HTTPS. JWKSCache caches fetched documents for repeated verifications and is accepted by verifyMandate through the jwksCache option.

Example: verify-only storefront route

A storefront route verifies the offer and the mandate, then returns the verified result. Settlement is not an SDK call: the Authorization Server performs the charge over HTTP after verification succeeds.

import {
  verifyOffer,
  verifyMandate,
  canonicalOfferDigest,
  fetchJwks,
} from "@oid4pay/oid4ac-merchant";

export async function POST(request) {
  const { offerBody, offerHeaders, presentation } = await request.json();

  const ownJwks = await fetchJwks("https://shop.example.com/.well-known/jwks.json");
  const issuerJwksUrl = "https://as.oid4pay.com/.well-known/jwks.json";

  const offer = await verifyOffer(offerBody, offerHeaders, ownJwks, {
    expectedTargetUri: `https://shop.example.com/products/${offerBody.sku}`,
  });

  const mandate = await verifyMandate(presentation, "https://shop.example.com", {
    jwksUrl: issuerJwksUrl,
    expectedOfferDigest: canonicalOfferDigest(offerBody),
  });

  // Verification passed. The order is authorised for amountMinor in currency.
  // Settlement is performed by the Authorization Server over HTTP; the SDK
  // does not charge.
  return Response.json({
    mandateId: mandate.mandateId,
    principalId: mandate.principalId,
    amountMinor: mandate.amountMinor,
    currency: mandate.currency,
    offerDigest: offer.bodyDigest,
  });
}

Algorithm whitelist

The Node SDK accepts ed25519 and ecdsa-p256-sha256 for signed offers; it refuses HMAC, alg=none, and every other RFC 9421 algorithm. Mandate issuer signatures are verified as EdDSA only, per the algorithm whitelist.

Error reference

ClassCodes
MandateVerifyErrorinvalid_mandate_format, audience_mismatch, jwks_fetch_failed
OfferVerifyErroroffer_signature_malformed
NetworkErrorRaised on network-layer failures during JWKS fetches.

Source

The package lives at sdks/node-oid4ac-merchant/ in the OID4Pay repo. Releases follow SemVer; breaking wire-version bumps move the major. Subscribe to the changelog for advance notice.