Stéphane Tailland 6dd6b61eb0 refactor: rename the token-exchange broker to drop the middleware's name
Every agent has to name this client in the audience scopes it requests, so the
one thing an agent cannot avoid naming was the very thing it should know
nothing about. The gateway is replaceable; the contract should not say
otherwise.

token-exchange-broker, same credential, same behaviour. The old client stays in
the realm until every consumer has moved.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-29 07:40:08 +02:00

payments-api

Kong routing/policy layer for the payments slice of classic-models-api (see /Users/stephanetailland/git/classic-models-api), split out per the DDD decomposition in ../kong-konnect-deployment-strategy.md. The backend itself stays a single monolith — only Kong does the domain split. Structure mirrors ../customers-api, not ../catalog-api — see ../STATUS.md section 18 for why (catalog-api's two-route MCP pattern has a latent bug; customers-api's single-route pattern is the corrected one to copy going forward).

Two exposure surfaces, both proxying to the real classic-models-api backend:

  • REST (kong/rest/) — 6 routes over Payment, which uses a composite key (customernumber + checknumber, not a single id like Customer/Product) — both URI segments are captured and passed through to the backend's own composite-key URL pattern.
  • MCP (kong/mcp/) — list-payments/get-payment (all 5 roles) and create-payment (admin only — verified against PaymentPermission's actual logic in authentication/permissions.py, not guessed: writes are admin-only in practice, customer can only write their own via the REST API directly, not exposed as an MCP tool). One KongRoute for all three MCP HTTP methods, one ai-mcp-proxy binding — see ../STATUS.md section 18 for why this matters (a second route + binding for the same plugin creates a duplicate-tool-name bug, found and fixed on customers-mcp, built correctly here from the start).
Route Method Backend path
/payments/v1/payments/ GET, POST /classic-models/api/v1/payments/
/payments/v1/payments/{customernumber}/{checknumber}/ GET, PUT, PATCH, DELETE /classic-models/api/v1/payments/{customernumber}/{checknumber}/
/payments-mcp GET, POST, DELETE MCP streamable-http (tools call into the REST routes above)

Auth

openid-connect on both surfaces, distinct audience each (payments-api for REST, mcp-payments for MCP) with a real RFC 8693 exchange chain: MCP → payments-api → classic-models-api, same pattern as catalog-api/customers-api. IdP is the local Keycloak (https://keycloak.sttlab.local/realms/classic-models) — see ../STATUS.md sections 15/16. payments-api-audience and mcp-payments-audience client scopes + payments-api/mcp-payments resource clients created on the local Keycloak, attached to kong-token-exchange-broker's default scopes (REST audience) and to librechat's optional scopes (MCP audience) respectively — no dedicated protocol mappers on either new client itself, same practice as every other domain built this session. mcp-payments's OAuth callback added to librechat's redirectUris, and a payments-mcp server entry added to LibreChat's librechat.yaml (infra-librechat repo).

Other policies

Per-consumer rate limiting, response caching (GET/HEAD), audit log (http-log, endpoint still a placeholder — no real log collector in this lab, same caveat as the other domains).

Structure

Same dev/ + kong/rest/ layout as customers-api. Deploy key (kong-v2/payments-api/git-deploy-key) is a copy of catalog-api's, not a freshly generated one — per the user's explicit instruction this session ("no reason a working key for catalog-api wouldn't work for customers-api"), the same reused key is used here too rather than minting yet another one per domain.

S
Description
No description provided
Readme 70 KiB