Node SDK: @oid4pay/oid4ac-merchant
The Node SDK ships ESM + CJS bundles. Node 18.18+ is required (Web Crypto Ed25519). TypeScript types are bundled.
Install
npm install @oid4pay/oid4ac-merchant
# or
pnpm add @oid4pay/oid4ac-merchantAPI reference
verifyOffer(body, headers, jwks, options?)
Verifies an RFC 9421 signed offer body against a merchant JWKS. Throws OfferVerifyError with one of the offer_signature_expired, offer_keyid_unknown, offer_body_digest_mismatch, offer_target_uri_mismatch, offer_alg_rejected codes on failure. Returns { keyid, alg, created, expires, bodyDigest, method, targetUri, authority } on success.
verifyMandate(sdJwtVc, kbJwt, asJwks, options?)
Verifies an SD-JWT VC mandate + KB-JWT pair against the AS JWKS. Throws MandateVerifyError with one of the mandate_signature_invalid, mandate_kb_audience_mismatch, mandate_kb_nonce_mismatch, mandate_status_revoked codes on failure.
charge({ accessToken, dpopKey, offerDigest, idempotencyKey })
Settles a charge against the AS by presenting the JWT-AT and a fresh
DPoP proof. The DPoP key MUST be the same key whose jkt appears in the JWT-AT's cnf claim. Returns { charge_id, mandate_id, stripe_payment_intent_id }.
Example: verify-and-charge in one storefront route
import { verifyOffer, charge } from "@oid4pay/oid4ac-merchant";
export async function POST(request) {
const { offerBody, signedHeaders, accessToken, dpopProof } =
await request.json();
const merchantJwks = await fetchJwks(merchantId);
const v = await verifyOffer(offerBody, signedHeaders, merchantJwks, {
expectedTargetUri: `https://shop.example.com/oid4ac/offer/${offerBody.sku}`,
});
const result = await charge({
accessToken,
dpopProof,
offerDigest: v.bodyDigest,
idempotencyKey: crypto.randomUUID(),
});
return Response.json(result);
}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. JWT-AT
verification accepts EdDSA only, per the algorithm whitelist.