Skip to content

fix(ack-pay): bind payment receipt issuer to the selected payment option's receiptService - #199

Open
ygd58 wants to merge 1 commit into
agentcommercekit:mainfrom
ygd58:fix/receipt-issuer-service-binding
Open

fix(ack-pay): bind payment receipt issuer to the selected payment option's receiptService#199
ygd58 wants to merge 1 commit into
agentcommercekit:mainfrom
ygd58:fix/receipt-issuer-service-binding

Conversation

@ygd58

@ygd58 ygd58 commented Sep 4, 2026

Copy link
Copy Markdown

Fixes #192

verifyPaymentReceipt only checked that the receipt's issuer was somewhere in the caller's global trustedReceiptIssuers list, and that its paymentOptionId existed in the verified Payment Request. It did not check that the receipt issuer matched the specific receiptService the selected payment option named.

In multi-rail deployments where a verifier trusts multiple receipt issuers globally (one per payment rail: card, USDC, Solana), this meant a receipt legitimately issued by one trusted service could be accepted for a payment option that named a different trusted receiptService, weakening per-option trust separation.

Fix: changed the existence check (paymentOptions.some) into a lookup (paymentOptions.find) for the selected option, then - when its receiptService is a DID - require it to equal verifiedReceipt.issuer.id. URL-form receiptService values are left unenforced, since binding a DID-based issuer to a URL isn't well-defined without an application-level policy; matches the option laid out in the issue.

Tests: added three regression tests: rejects a receipt from a different trusted issuer than the option's receiptService (the exact reproduction from the issue), accepts a receipt from the option's own receiptService, and confirms the check is a no-op when the option doesn't specify a receiptService. Verified the rejection test fails when the binding check is disabled and passes with it enabled.

Changeset: added (@agentcommercekit/ack-pay, patch).

Verified locally: pnpm --filter @agentcommercekit/ack-pay exec vitest run - 15/15 passing (full test file), 53/53 passing (full package). oxlint and oxfmt clean.


AI usage disclosure (per AI_POLICY.md): this fix was developed with Claude (Anthropic) assistance - identifying the exact code path, writing the fix and tests, and verifying locally including a manual before/after check that the tests catch the regression. I reviewed and understand the change: it's a targeted authorization check that binds a receipt's verified issuer to the specific receiptService the paid-for option named, closing a gap where a globally-trusted-but-wrong issuer could be accepted.

Summary by CodeRabbit

  • Bug Fixes
    • Payment receipt verification now requires the receipt issuer to match the selected payment option’s DID-based receipt service.
    • Receipts from other trusted issuers are rejected when a specific DID receipt service is configured.
    • URL-based receipt services and payment options without a specified service continue to be handled as before.

…ion's receiptService

Fixes agentcommercekit#192

verifyPaymentReceipt only checked that the receipt's issuer was
somewhere in the caller's global trustedReceiptIssuers list, and that
its paymentOptionId existed in the verified Payment Request. It did
not check that the receipt issuer matched the specific receiptService
the selected payment option named.

In multi-rail deployments where a verifier trusts multiple receipt
issuers globally (one per payment rail: card, USDC, Solana), this
meant a receipt legitimately issued by one trusted service could be
accepted for a payment option that named a different trusted
receiptService, weakening per-option trust separation.

Fix: changed the existence check (paymentOptions.some) into a lookup
(paymentOptions.find) for the selected option, then - when its
receiptService is a DID - require it to equal
verifiedReceipt.issuer.id. URL-form receiptService values are left
unenforced, since binding a DID-based issuer to a URL isn't
well-defined without an application-level policy; matches the option
laid out in the issue.

Added three regression tests: rejects a receipt from a different
trusted issuer than the option's receiptService (the exact
reproduction from the issue), accepts a receipt from the option's own
receiptService, and confirms the check is a no-op when the option
doesn't specify a receiptService. Verified the rejection test fails
when the binding check is disabled and passes with it enabled.

pnpm --filter @agentcommercekit/ack-pay exec vitest run - 15/15
passing (full test file), 53/53 passing (full package). oxlint and
oxfmt clean.

AI usage disclosure: this fix was developed with Claude (Anthropic)
assistance - identifying the exact code path, writing the fix and
tests, and verifying locally including a manual before/after check
that the tests catch the regression. I reviewed and understand the
change: it's a targeted authorization check that binds a receipt's
verified issuer to the specific receiptService the paid-for option
named, closing a gap where a globally-trusted-but-wrong issuer could
be accepted.
@coderabbitai

coderabbitai Bot commented Sep 4, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: d70722aa-3989-4fe0-a7c4-53660c5e21a5

📥 Commits

Reviewing files that changed from the base of the PR and between 7d23f83 and 5cad443.

📒 Files selected for processing (3)
  • .changeset/receipt-issuer-service-binding.md
  • packages/ack-pay/src/verify-payment-receipt.test.ts
  • packages/ack-pay/src/verify-payment-receipt.ts

Included review availability: Your plan provides up to 2 included reviews per hour; 1 remains after this review.


Walkthrough

verifyPaymentReceipt now binds a verified receipt issuer to the selected payment option’s DID receiptService. Tests cover mismatch rejection, matching acceptance, and omitted-service behavior. A patch changeset documents the update.

Changes

Receipt issuer binding

Layer / File(s) Summary
Selected option issuer validation
packages/ack-pay/src/verify-payment-receipt.ts
The verifier retrieves the selected payment option and rejects a receipt when its issuer does not match a DID receiptService. URL-form services remain unenforced.
Binding validation and release note
packages/ack-pay/src/verify-payment-receipt.test.ts, .changeset/receipt-issuer-service-binding.md
Tests cover mismatched issuers, matching issuers, and payment options without receiptService. The changeset documents the patch release.

Estimated code review effort: 2 (Simple) | ~15 minutes

Merge Risk: ⚪ Minimal · up to 5cad4

Receipt verification now binds DID receipt issuers to the selected payment option while preserving existing behavior for omitted and URL-form services. The intended behavior is covered without an identified current-head merge risk.

Suggested reviewers: venables

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 2 files. (1 skipped: 1… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: binding the payment receipt issuer to the selected payment option's receiptService.
Linked Issues check ✅ Passed The implementation and regression tests satisfy issue #192 by looking up the selected payment option and enforcing issuer equality for DID-based receiptService values. URL-form services remain unenfor…
Out of Scope Changes check ✅ Passed The changes are limited to the requested verification logic, related regression tests, and a patch changeset for @agentcommercekit/ack-pay. No unrelated code changes are present.
Full details: Docstring Coverage

Explanation

Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 2 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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.

fix(ack-pay): bind payment receipt issuer to the selected payment option receiptService

1 participant