feat(payments): negotiate CEP-8 payment interaction on the server transport - #107
Merged
ContextVM-org merged 1 commit intoAug 4, 2026
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #100
Fourth piece of CEP-8, after the authorization store in #104. First one that touches the live server transport rather than adding a self-contained module.
The server now speaks the payment-interaction handshake: parse the client's
pmiandpayment_interactiontags, negotiate an effective mode for the session, rejectexplicit_gatingwith-32602when the server can't offer it, disclose the negotiated mode on the first response, and hangcappricing tags off capability-list responses. The negotiated mode lands inInboundContext, which is what the payment middlewares will read.No middleware and no payment flow yet. A server that never calls
set_supported_payment_interactionand gets no payment tags behaves exactly as it does today.What's here
extract_pmis/extract_payment_interactionindiscovery_tags.rsClientSession, mirrored ontoSessionSnapshotand both mappersserver/mod.rs, hooked into the inbound loop after the capability OR-learn, plus the-32602emit on the drop pathset_supported_payment_interaction, frozen into the event loop atstart()cap-on-list append insend_response, and the same composition on the CEP-41 deferred pathtests/conformance_cep8_wire_format.rspinning the tag tuples and-32602bytes, 9 integration tests overMockRelayPoolNo new dependency, no
Cargo.tomlchange, no FFI change. The reasoning for each design call is in rustdoc on the item itself, including the odd-looking one where-32602correlates on the inner request id instead of the event id.CEP-41 fix
A deferred final response, held back while a stream is still open, went out with routing tags only. ts re-enters
route()on flush and sends the full first-response set; our deferred publish is static and built its own bare tag list.The fix covers the CEP-35 tags as well as the disclosure, because fixing one alone is worse than fixing neither. A client that negotiated gating and whose first call streams needs the disclosure or it concludes negotiation failed. But a response carrying the disclosure and nothing else un-gates the client's baseline capture, and it then latches that thin baseline and reports no server identity for the rest of the session.
So the deferred path now composes what the normal path composes: discovery and disclosure behind the same one-shot latches, then
capon a list result. Both flush orderings go through it. The test for it drives the real path end to end throughstart(), not the composer directly.Heads up
Breaking.
ClientSessionandSessionSnapshotare now#[non_exhaustive]. This PR adds three fields to each, so downstream struct literals and exhaustive destructures break oncargo updateeither way; marking them now makes future additions free.InboundContextalready had it. Next release is 0.3.0.Pre-existing bug, not fixed here. The deferred publish never fragments.
send_responsemeasures an oversized response and splits it into CEP-22 frames; the deferred path callssend_mcp_messageonce with no size check, so a streaming tool returning a large final result fails and the flush only logs it. The client sees the stream complete, then waits for a response that never arrives.Not in scope: the announcement availability tags. They need the processors and priced capabilities from the server payments options, so they belong with
with_server_payments, same as ts.