Skip to content

feat(sdk): add TypeScript SDK for AP2 mandates and receipts#305

Open
wakqasahmed wants to merge 2 commits into
google-agentic-commerce:mainfrom
wakqasahmed:feat/typescript-sdk-mandates
Open

feat(sdk): add TypeScript SDK for AP2 mandates and receipts#305
wakqasahmed wants to merge 2 commits into
google-agentic-commerce:mainfrom
wakqasahmed:feat/typescript-sdk-mandates

Conversation

@wakqasahmed

Copy link
Copy Markdown

Description

Thank you for opening a Pull Request!
Before submitting your PR, there are a few things you can do to make sure it goes smoothly:

Addresses #67 🦕

What this adds

code/sdk/typescript, a publishable @ap2-protocol/sdk npm package: Zod schemas + TypeScript types for AP2's mandate and receipt models, generated directly from the canonical JSON Schemas in code/sdk/schemas/ap2 (the same source of truth code/sdk/schemas/generate.py generates the Python SDK from).

import { PaymentMandateSchema } from '@ap2-protocol/sdk';

const result = PaymentMandateSchema.safeParse({
  transaction_id: 'tx_1',
  payee: { id: 's-1', name: 'Shop' },
  payment_amount: { amount: 1000, currency: 'USD' },
  payment_instrument: { id: 'pi-1', type: 'credit' },
});

Exports PaymentMandate, CheckoutMandate, OpenPaymentMandate, OpenCheckoutMandate, PaymentReceipt, CheckoutReceipt, plus the shared types (Amount, Merchant, Pisp, PaymentInstrument, Jwk, Item, ReceiptStatus) — each as both a Zod validator and its inferred TS type.

npm run generate (scripts/generate.ts) dereferences every schema under code/sdk/schemas/ap2 and emits the matching Zod code, so this can't drift from the protocol the way hand-maintained TS types would (the exact risk the issue calls out under "alternatives considered").

A correctness bug found and fixed along the way

payment_receipt.json / checkout_receipt.json each put a oneOf alongside properties/required at the schema root (Success branch requires psp_confirmation_id/network_confirmation_id; Error branch requires error/error_description). The underlying json-schema-to-zod generator only reads each branch's discriminator const and silently drops each branch's own required list in this "oneOf as sibling constraint" shape — so a status: "Success" receipt with no confirmation IDs generated as valid. materializeSiblingOneOf in scripts/generate.ts merges outer + branch properties/required before generation (the same shape the generator already handles correctly when branches are fully self-contained, e.g. OpenPaymentMandate.constraints). Covered by a regression test in test/schemas.test.ts.

Scope

This covers the TS types + validation half of #67 ("mirrors the AP2 IntentMandate, CartMandate, PaymentMandate ... models with validation, e.g. zod"). It intentionally does not port ap2.sdk's SD-JWT signing/verification, mandate chain builders, or JWT/JWK helpers — that's cryptography-sensitive code better reviewed on its own. Also unrelated to #144: that PR is a sample application (Genkit + A2A agents) with its own inline, non-reusable schema types, not a publishable library — this package is the standalone SDK #67 is asking for, and the sample could depend on it in a follow-up.

Known limitations (documented in the package README)

  • contains isn't enforced. Two schemas use contains to require "at least one array element matching a specific alternative" (e.g. OpenCheckoutMandate.constraints should include a checkout.line_items entry). json-schema-to-zod doesn't support contains; it's silently dropped rather than hand-patched into generated output. Flagged in the README rather than papered over — happy to follow up with a .refine()-emitting fix if that's the preferred direction.
  • No compile-time narrowing on the oneOf-branch schemas (PaymentReceipt, CheckoutReceipt, OpenPaymentMandate.constraints entries) — generated as z.object(...).and(z.any().superRefine(...)), and TS collapses T & any to any. Runtime validation is correct and tested; only static narrowing is coarser than the flat schemas.

Testing

cd code/sdk/typescript
npm install
npm run generate   # regenerates src/generated/ from code/sdk/schemas/ap2
npm run lint        # eslint, mirrors code/web-client's config
npm run typecheck   # tsc --noEmit
npm run test         # vitest — 20 tests, incl. the oneOf regression test
npm run build        # tsup esm+cjs+dts

All pass locally. dist/ is gitignored (not committed), matching a standard npm package layout; package-lock.json is committed, matching code/web-client.

CLA

I'll sign the Google CLA via the bot's prompt on this PR.

Adds code/sdk/typescript, a publishable @ap2-protocol/sdk package with
Zod schemas and TypeScript types for AP2's mandate and receipt models
(PaymentMandate, CheckoutMandate, OpenPaymentMandate,
OpenCheckoutMandate, PaymentReceipt, CheckoutReceipt, and their shared
types), generated directly from code/sdk/schemas/ap2 via a generator
script (scripts/generate.ts) that mirrors the role of
code/sdk/schemas/generate.py on the Python side.

The generator fixes a correctness gap in the underlying
json-schema-to-zod library: schemas where a oneOf sits alongside
properties/required (payment_receipt.json, checkout_receipt.json) had
their branch-specific required fields silently dropped, so a
'Success' payment receipt without confirmation IDs would incorrectly
validate. materializeSiblingOneOf merges outer and branch
properties/required before generation to fix this; covered by a
regression test.

Addresses the TS types + validation half of google-agentic-commerce#67. Scoped to the
type/validation layer only -- SD-JWT signing, mandate chain builders,
and JWT/JWK helpers from ap2.sdk are left for a follow-up PR. Unrelated
to google-agentic-commerce#144, which is a sample application with inline, non-reusable
schema definitions rather than a publishable SDK.
@wakqasahmed
wakqasahmed requested a review from a team as a code owner July 24, 2026 22:48
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@google-cla

google-cla Bot commented Jul 24, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

- generate.ts hardcoded singleQuote: true instead of reading the
  repo's .prettierrc, so generated output diverged from the repo's
  actual formatting (double quotes) and failed the Prettier check.
  Now resolves the project's own config via prettier.resolveConfig().
- Re-ran generate + prettier --write to bring all generated files and
  hand-written source into line with the resolved config.
- Added protocol-specific terms (PISP, IDAS, QWAC, PKIX, Genkit,
  apidevtools, subschema) to .cspell/custom-words.txt; these are
  legitimate terms from AP2's own JSON Schemas and package names, not
  typos in this PR's code.
- Fixed a markdownlint MD018 violation in the SDK README (line
  starting with "google-agentic-commerce#67" was parsed as an empty heading).
@wakqasahmed

Copy link
Copy Markdown
Author

Fixed the spellcheck/Prettier/markdownlint findings from the Lint Code Base check in f97b0e7 (root cause: generate.ts hardcoded singleQuote: true instead of reading this repo's own .prettierrc, plus added AP2-specific terms — PISP, IDAS, QWAC, etc. — to the cspell dictionary).

Two remaining Lint Code Base failures (n/no-missing-import on resolvable imports, and BIOME_LINT reporting errors across ~99 files well outside this PR's diff) trace back to CI infrastructure gaps unrelated to this PR's code — filed separately as #306 with repro details so they don't block review here on unrelated ground.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant