Hello, OID4Pay
This page walks a merchant author through a complete OID4Pay testmode flow in under ten minutes. By the end you have:
- An SDK installed (Node, Python, or Go).
- A sandbox client registered against
sandbox.oid4pay.com. - A signed offer verified and a testmode mandate verified with the merchant SDK.
sandbox.oid4pay.com is being finalised. When it opens, sandbox
tokens are throwaway and rate limited per client.Step 1: install an SDK
Pick whichever SDK matches your stack:
# Node / TypeScript
npm install @oid4pay/oid4ac-merchant
# Python
pip install oid4pay-oid4ac
# Go
go get github.com/oid4pay/oid4ac-goStep 2: register a sandbox client
Sandbox client registration uses RFC 7591 dynamic registration. POST a
minimal JSON body to /oauth/register:
curl -sS https://sandbox.oid4pay.com/oauth/register \
-H "content-type: application/json" \
-d '{
"client_name": "my-agent-prototype",
"token_endpoint_auth_method": "private_key_jwt",
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"jwks": {"keys": [/* your client public JWK */]}
}'The response carries your client_id. Store it; the secret
counterpart is the private key whose public JWK you just published.
Step 3: request a pushed authorization (PAR)
Every authorization request goes through PAR. The response carries a request_uri with a one minute TTL. See the PAR response for the exact shape.
curl -sS https://sandbox.oid4pay.com/oauth/par \
-H "content-type: application/x-www-form-urlencoded" \
-d "client_id=$CLIENT_ID" \
--data-urlencode "client_assertion=$CLIENT_ASSERTION_JWT" \
-d "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \
-d "response_type=code" \
-d "scope=oid4ac:payment" \
-d "code_challenge=$PKCE_S256" \
-d "code_challenge_method=S256" \
--data-urlencode "redirect_uri=https://my-agent.example.com/callback"The private_key_jwt assertion is the only client authentication method; the algorithm whitelist pins EdDSA as the only accepted algorithm. The PAR response shape is
on the spec page.
Step 4: redirect the principal
Send the principal to the wallet portal at https://wallet.oid4pay.com/authorize?request_uri=$REQUEST_URI.
The wallet authenticates the principal, displays the mandate-binding
consent screen, and returns to your redirect_uri with a
one-time authorization code.
Step 5: exchange the code for a DPoP-bound JWT-AT
curl -sS https://sandbox.oid4pay.com/oauth/token \
-H "DPoP: $DPOP_PROOF_JWT" \
-H "content-type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "code=$CODE" \
-d "code_verifier=$PKCE_VERIFIER" \
-d "client_id=$CLIENT_ID" \
--data-urlencode "client_assertion=$CLIENT_ASSERTION_JWT" \
-d "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \
--data-urlencode "redirect_uri=https://my-agent.example.com/callback"The response carries an access_token with typ=at+jwt (see the JWT-AT claim set) and an id_token if the OIDC scope was requested. The DPoP proof binds
the token to your keypair via the cnf.jkt claim.
Step 6: verify the offer and the mandate
The merchant SDK is verify-only. Verify your signed offer body against your own published JWKS, then verify the mandate presentation against your merchant audience. Both calls are local cryptographic checks; neither one moves money.
// Node / TypeScript
import { verifyOffer, verifyMandate } from "@oid4pay/oid4ac-merchant";
// Verify the RFC 9421 signature over your offer body.
const offer = await verifyOffer(offerBody, signedOfferHeaders, merchantJwks, {
expectedTargetUri,
});
// Verify the mandate presentation (the whole compact SD-JWT VC + KB-JWT
// string) against your merchant audience.
const mandate = await verifyMandate(presentation, merchantAudience, {
jwksUrl: "https://sandbox.oid4pay.com/.well-known/jwks.json",
expectedOfferDigest: offer.bodyDigest,
});
console.log(mandate.mandateId, mandate.amountMinor, mandate.currency);A verified mandate exposes mandateId, agentClientId, principalId, amountMinor, currency, merchant, lineItems, and the offerDigest it was bound to.
The SDK stops here.
Next steps
- Read the wire reference end-to-end so you know which wire fields are mandatory and which are extensible.
- Add the merchant SDK to your storefront and register a verification keypair.
- Subscribe to the changelog for breaking
wire-version bumps. The current SDK version is
0.1.0.