Skip to content

fix(checkout): populate outcome.rail_key from MPP receipt method - #51

Merged
vvillait88 merged 1 commit into
mainfrom
mpp-rail-key-population
May 17, 2026
Merged

fix(checkout): populate outcome.rail_key from MPP receipt method#51
vvillait88 merged 1 commit into
mainfrom
mpp-rail-key-population

Conversation

@vvillait88

Copy link
Copy Markdown
Contributor

Summary

  • _handle_mppx now derives outcome.rail_key from the credential's receipt method (tempo / solana / stripe) and maps it to the merchant's rails-dict key via isinstance checks against TempoRailSpec / SolanaMppRailSpec / StripeRailSpec. Falls back to _mpp_rail_key() only when no signal is present.
  • Adds _rails_key_for_mppx_method() helper and two unit tests (matching + missing-rail) covering the new path.
  • Bumps minor deps via uv sync --upgrade: click 8.3.3 → 8.4.0, pympp 0.7.0 → 0.8.0, python-multipart 0.0.28 → 0.0.29, ty 0.0.36 → 0.0.37.
  • Version: 2.0.2.

Why

SettleOutcome.rail is binary ("x402" | "mpp"), so Tempo, Solana, and Stripe SPT settles all surface as rail="mpp". The MppxComposeOutcome.rail_key field defaults to the literal "tempo" and the auto-built compose hook never set it from the receipt, so every MPP settle reported the same rail_key. Two downstream effects:

  • Slack / email rail labels couldn't tell Solana from Tempo.
  • Testnet Stripe deposit simulators that branched on rail_key only ever fired for the first MPP rail.

Mirrors the same fix in node-commerce (cross-language parity).

Test plan

  • uv run ruff check . — clean
  • uv run ruff format . — clean
  • uv run ty check agentscore_commerce/ — clean
  • uv run pytest tests/ — 1205 passed, 4 skipped, coverage 95.20% (≥95% threshold)

🤖 Generated with Claude Code

_handle_mppx previously defaulted SettleOutcome.rail_key to the literal
"tempo" (the field default on MppxComposeOutcome) when the compose hook
didn't set it explicitly. With Tempo + Solana + Stripe rails registered,
every MPP settle reported the same rail_key, so merchants could not tell
Solana settles from Tempo settles (both report rail="mpp"). The
rail_key field was designed for exactly this distinction but was never
populated authoritatively.

Read the credential method off the MPP receipt (composed.raw["receipt"]
or composed.raw.receipt -- the auto-built compose hook exposes the
pympp Receipt object directly via raw) and map to the merchant's
rails-dict key by iterating self.rails and matching against
TempoRailSpec / SolanaMppRailSpec / StripeRailSpec via isinstance.
Falls through to the existing _mpp_rail_key() heuristic only when the
receipt has no method or no rail matches.

Also bumps minor deps via uv sync --upgrade: click 8.3.3 -> 8.4.0,
pympp 0.7.0 -> 0.8.0, python-multipart 0.0.28 -> 0.0.29,
ty 0.0.36 -> 0.0.37.

Version: 2.0.2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vvillait88
vvillait88 merged commit b97bc47 into main May 17, 2026
7 checks passed
@vvillait88
vvillait88 deleted the mpp-rail-key-population branch May 17, 2026 20:33
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