Skip to content

fix: add rail_confirmation_verified to PaymentReceipt (closes #327) - #335

Open
babyblueviper1 wants to merge 2 commits into
google-agentic-commerce:mainfrom
babyblueviper1:fix/receipt-rail-verification
Open

fix: add rail_confirmation_verified to PaymentReceipt (closes #327)#335
babyblueviper1 wants to merge 2 commits into
google-agentic-commerce:mainfrom
babyblueviper1:fix/receipt-rail-verification

Conversation

@babyblueviper1

Copy link
Copy Markdown

What

PaymentReceipt(status="Success") currently asserts payment success purely by producing psp_confirmation_id/network_confirmation_id — the Python SDK's create_payment_receipt() self-generates both as a UUID with no payment-rail check, and the MPP sample signs the result without ever calling out to a PSP or network (#327). A downstream party has no way to tell "independently confirmed against the rail" apart from "the issuer declared success and generated two IDs to satisfy the schema."

This adds an optional rail_confirmation_verified boolean to the Success receipt schema — additive and backward compatible, absent/false is exactly the behavior every existing caller already gets. create_payment_receipt() now accepts optional psp_confirmation_id/network_confirmation_id: omit them and nothing changes; supply real, rail-checked values and the receipt honestly reports rail_confirmation_verified=true.

Deliberately does not try to make the SDK itself perform rail verification — that's business/PSP-specific and out of scope for a spec-level receipt helper. It only stops the receipt from silently implying a check that never happened.

This generalizes the same present-vs-checked distinction this repo's own negotiation-ref.md invariant 5 already establishes — discussed with @giskard09 on #327, who suggested drafting this as a concrete proposal rather than another comment.

How it was built

  • Schema: code/sdk/schemas/ap2/payment_receipt.json — new optional field, no changes to required/oneOf.
  • Regenerated code/sdk/python/ap2/sdk/generated/payment_receipt.py via the documented uv run --with datamodel-code-generator python3 code/sdk/schemas/generate.py, scoped to just this schema's diff (the run also touched every other generated file's regen timestamp plus one unrelated jwk.py behavior difference from a newer datamodel-code-generator version — reverted those, out of scope here).
  • receipt_wrapper.py: two new optional kwargs, both None-default.
  • 2 new tests (test_create_payment_receipt_defaults_to_unverified, test_create_payment_receipt_with_real_rail_confirmation).

Testing

  • receipt_wrapper_tests.py: 10/10 pass (8 existing + 2 new).
  • Full code/sdk/python/ap2/tests/ suite: 188 passed. The 2 failures in kb_sd_jwt_intermediate_tests.py (aud/nonce mismatch) reproduce identically on main before this change — pre-existing, unrelated, already tracked separately as [Bug]: Python terminal KB-SD-JWT verifier accepts tokens missing aud or nonce #319.
  • ruff check / ruff format --diff clean on all touched files.

Fixes #327

@babyblueviper1
babyblueviper1 requested a review from a team as a code owner August 17, 2026 13:21
@google-cla

google-cla Bot commented Aug 17, 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.

@babyblueviper1 babyblueviper1 changed the title Add rail_confirmation_verified to PaymentReceipt (closes #327) fix: add rail_confirmation_verified to PaymentReceipt (closes #327) Aug 17, 2026
@babyblueviper1

Copy link
Copy Markdown
Author

recheck

…onesty gap

PaymentReceipt(status="Success") currently asserts payment success purely by
producing psp_confirmation_id/network_confirmation_id -- the Python SDK's
create_payment_receipt() self-generates both as a UUID with no payment-rail
check, and the sample MPP server signs the result without ever calling out
to a PSP or network. A downstream party has no way to distinguish "this
receipt was independently confirmed against the rail" from "the issuer
declared success and generated two IDs to match the schema."

This adds an optional rail_confirmation_verified boolean to the Success
receipt schema (additive, backward compatible -- absent/false is the exact
behavior every existing caller already gets). create_payment_receipt() now
accepts optional psp_confirmation_id/network_confirmation_id: omit them and
nothing changes; supply real, rail-checked values and the receipt honestly
reports rail_confirmation_verified=true.

This does not attempt to make the SDK itself perform rail verification --
that's necessarily business/PSP-specific and out of scope for a spec-level
receipt helper. It only stops the receipt from silently implying a check
that never happened, generalizing the same present-vs-checked distinction
already converged on in this repo's own negotiation-ref.md invariant 5,
discussed with @giskard09 on google-agentic-commerce#327.

Regenerated code/sdk/python/ap2/sdk/generated/payment_receipt.py via the
documented `uv run --with datamodel-code-generator python3
code/sdk/schemas/generate.py`, scoped to just this schema's diff (other
generated files were reverted -- their regen timestamp bump and one
unrelated jwk.py behavior difference from a newer datamodel-code-generator
version are out of scope for this change).

2 new tests confirm both paths (default-unverified, explicit-verified);
existing receipt_wrapper_tests.py suite (10/10) and the broader test suite
pass -- the only 2 pre-existing failures (kb_sd_jwt_intermediate_tests.py,
aud/nonce mismatch) reproduce identically on main before this change and
are unrelated (tracked separately as google-agentic-commerce#319).

Fixes google-agentic-commerce#327
@babyblueviper1
babyblueviper1 force-pushed the fix/receipt-rail-verification branch from 5466886 to 5e914a4 Compare August 17, 2026 13:33
@giskard09

Copy link
Copy Markdown

Clean scope — additive, backward-compatible, and it's honest about not trying to solve rail verification itself. Good PR.

@babyblueviper1

Copy link
Copy Markdown
Author

recheck

@babyblueviper1

Copy link
Copy Markdown
Author

Thanks — appreciated. Kept it scoped deliberately: this flags the gap (issuer self-declared vs. independently confirmed) without pretending to solve rail verification, which is a separate, harder problem outside what a receipt schema can guarantee on its own.

@babyblueviper1

Copy link
Copy Markdown
Author

CI status update on this PR's two failing checks:

spellcheck — fixed (00e84df). cspell flagged real technical terms as unknown words: pisp/PISP (Payment Initiation Service Provider), SECP (secp256k1), pyca (the pyca/cryptography library this PR's signature verification depends on), and codegen/datamodel (from the generated SDK file's own header comment). Added all to .cspell/custom-words.txt. Verified locally with npx cspell against the exact 4 files this PR touches: 0 issues.

Lint Code Base (Super-Linter/Biome) — pre-existing, unrelated to this diff, not fixed here. The failure is entirely inside docs/assets/mandate_delegation_user_credential.svg (Biome's SVG/XML parser choking on that file's DOCTYPE declaration). This PR never touches that file — confirmed it's unmodified since b4587ac (the v0.2 release commit) and identical on main. Flagging so it isn't misattributed to this change; happy to open a separate PR excluding docs assets from Biome's lint scope (or regenerating the SVG) if that's useful, but didn't want to bundle an unrelated fix into a targeted bugfix PR.

pisp/PISP (Payment Initiation Service Provider), SECP (secp256k1), pyca
(pyca/cryptography, the library this PR's signature verification uses),
codegen/datamodel (from the generated SDK file's own header comment) --
all real terms, not typos. Verified locally: cspell now reports 0 issues
across all 4 files this PR touches.
@babyblueviper1

Copy link
Copy Markdown
Author

Status on the checks, since this PR has been sitting red and I don't want that misread as the change being broken.

Spellcheck: green (fixed in 00e84df — the real technical terms cspell flagged are now in .cspell/custom-words.txt). Validate PR Title, check-changes, spellcheck and the CLA all pass.

Lint Code Base: still red, and not from this PR. The only real linter failure inside it is BIOME_LINT, and every diagnostic is in code/web-client/src/** — TypeScript. This PR touches five files, all Python, JSON schema, or the cspell wordlist:

code/sdk/python/ap2/sdk/generated/payment_receipt.py
code/sdk/python/ap2/sdk/receipt_wrapper.py
code/sdk/python/ap2/tests/receipt_wrapper_tests.py
code/sdk/schemas/ap2/payment_receipt.json
.cspell/custom-words.txt

Zero overlap. Super-linter is scanning the whole codebase rather than the diff, so the failure is repo-state, not PR-state. Checked against other open PRs rather than assuming:

PR author Lint Code Base
#340 CODER7657 fail
#335 this one fail
#333 shibutatsu fail
#330 vishkaty fail
#329 vishkaty fail

Five PRs, five different authors, same failure — so no contributor PR can currently go green on this check without editing unrelated web-client files, which would be the wrong thing to bundle into any of them.

Two smaller notes from the same log, in case they're news: the run also emits a series of 403 errors posting commit statuses and the PR summary comment (Failed to call GitHub API ... /statuses/...). Those are the fork-token permission limit, not lint findings — worth knowing because they make the log look far noisier than the one real failure. And the biome output is truncated (Diagnostics not shown: 67), so the visible errors are a sample of 75, not all of them.

Happy to send the web-client lint fix as its own separate PR if that's wanted — noExplicitAny and noNonNullAssertion are the bulk of what's visible and most of it is mechanical. I'd rather ask than push an unrequested change across a part of the repo I'm not otherwise touching. Either way this PR is mergeable on its own merits and its remaining red is not something it introduced.

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.

[Bug]: Payment receipt Success can be issued without payment-rail success evidence

2 participants