Stéphane Tailland fcca497737 Stop caching reads on a read-write API
The proxy cache held every GET on the whole service for 60 seconds, and these
APIs accept writes. Kong's proxy cache has no invalidation on write: a POST
that changes a product passes through the same gateway, and the gateway goes on
serving the value it captured before it, for up to a minute, with nothing in the
response saying so.

It produced a real wrong answer. An agent woken by a stock event read the figure
from before that event and reported, in one sentence, "68 in stock, above the
threshold" and "it crossed the threshold, from 68".

The manifest is kept, unreferenced, carrying the reason — so reinstating it is a
decision rather than a rediscovery.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 08:26:24 +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 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