**Planning next steps**

I need to wrap things up with a final summary that includes any changed files and the next steps. It’s also a good idea to mention if I’ve saved anything to memory, so it’s all clear for future reference. This will help keep everything organized, allowing for a smoother transition into the next phase of the project. I want to ensure everything is noted properly so nothing gets overlooked.

Done.

Docs changed
- `docs/tcn-payments-integration-plan.md`
- `docs/implemented-payments.md`
- `docs/next-steps-queue.md`
- `PLAN.md`
- `docs/index.md`

Artifacts kept
- `test-results/tcn-payment-protocol/Protocol 2.5V English version.pdf`
- `test-results/tcn-payment-protocol/findings-2026-04-23.md`

What changed
- Reframed `/admin/payments` QR page as test/debug harness.
- Set production direction to TCN adapter over Ballbox’s native MercadoPago layer.
- Replaced old `PAY-3` split-payments-next item with:
  - `PAY-3 TCN vending-machine adapter over native MercadoPago`
- Moved split payments to:
  - `PAY-4`, after machine flow is stable.
- Added concrete TCN integration plan:
  - `FunCode=8000` adapter
  - request/response mapping
  - success/dispense handshake
  - machine identity mapping
  - remaining unknowns

Answer to “docs should be enough right?”
- Enough to plan and start adapter design: yes.
- Not enough to implement blindly end-to-end: no.

What still needs confirming
- Exact `CodeUrl` payload TCN expects:
  - raw URL
  - QR payload string
  - provider-specific deeplink
- Exact success handshake after approval:
  - does Ballbox call `NotityUrl`
  - does TCN poll another endpoint
  - how dispense gets unlocked against `TradeNo`

Best next move
1. finish `OPS-1` real MP webhook/payment proof
2. draft exact TCN adapter contract from this doc
3. implement stub TCN endpoints in Ballbox
4. test against one real machine

I also saved durable memory:
- `mem_6b468c8ed38a488392762d473ab20904`

If you want, next I can turn `docs/tcn-payments-integration-plan.md` into a more concrete implementation spec with proposed endpoint shapes and field mapping into current Ballbox models.


Todavía no tenemos las máquinas de TCN así que cualquier duda hacia ellos podríamos confirmarlas con el de ventas que de por sí puede hablar con los ingenieros y en cuanto a lo que necesitemos de mercado pago si necesitamos seguir probando y arreglando Si hace falta el webhook
Crea los documentos que quieras, siempre y cuando en el archivo de plan estén ordenados todos los pasos en orden y linkeando a los documentos que te van falta.
Incluí como parte del plan cosas ajenas al código como hacer consultas.


**Updating the plan**

I need to update my plan with organized steps and relevant links. It might be helpful to create an ask log document for tracking questions or requests. I'll also make sure to include non-code steps in the plan, possibly by creating a to-do list. This way, everything will be clearly outlined for anyone who looks at it. It seems like a systematic approach will really help here!

todo

Restructure payments plan around TCN integration and external questions

Add ordered plan steps in PLAN.md with links to supporting docs, including non-code tasks like questions for TCN sales/engineers and Mercado Pago webhook validation.


Created #2: Restructure payments plan around TCN integration and external questions (pending)

Review TCN 3rd-party payments manual from laptop

Copy the PDF from laptop into repo investigation artifacts near test-results, inspect contents, and answer whether QR can be displayed in-app, needs sticker, needs extra hardware, or blocks the approach.


# Ballbox Host App Plan

## Goal
- Convert `ballbox` into the host app for Ballbox platform.
- Preserve the current marketing home at `/`.
- Add a shared admin shell at `/admin`.
- Keep product boundaries explicit with dedicated subroutes.
- Converge on a shared `network` core model that all product domains reference.

## Current State
- Ballbox overall is still in validation stage for padel centers; there are no customers yet and near-term proof still depends on pilots with the first machines.
- `/` remains the marketing home.
- `/admin` is live as the shared Ballbox admin shell.
- Product-separated admin areas exist for `tvs`, `payments`, `network`, `sports`, and `players`.
- `/admin/screens` exists as the runtime entry area for TVs.
- `/player/[screenId]` is the lightweight TV runtime route served as plain HTML + JavaScript inside Ballbox.
- `/player/[screenId]` now exists as the lightweight TV runtime route served as plain HTML + JavaScript inside Ballbox.
- TVs are internal to Ballbox as a module, not a separate app.
- TVs admin and `/api/tvs/*` use an internal Ballbox ownership model for clubs, venues, screens, and campaigns.
- TVs runtime/admin already read Ballbox Postgres directly through Prisma.
- TVs runtime heartbeats and impressions already write to Ballbox DB-backed flows.
- TVs manifest/admin scheduling now respects campaign and assignment time windows.
- TVs now expose a strict Zod-backed API contract plus contract tests.
- TVs runtime now exposes on-screen diagnostics for cache/object-URL/manual parity verification on real devices.
- TVs runtime diagnostics now also support a one-click parity snapshot JSON export (playback/cache/network/reporting state + user agent + capture timestamp) to standardize real-device evidence capture.
- OPS-2 local parity rerun on 2026-04-19 identified two machine-local issues: stale TV seed scheduling and unstable repo Playwright `webServer` behavior on this Pi. TV seed scheduling is now fixed in `prisma/seed-data.ts`; remaining OPS-2 closure work is local parity rerun plus real-device parity capture.
- TVs player runtime cache/media/impression/heartbeat/manifest-refresh logic now lives in one shared core used by both the React player and the lightweight plain-JS player shell.
- Maintainability review for the next refactor cycle is documented in `docs/maintainability-priorities-2026-03.md`.
- Payments are now owned inside Ballbox host app: native MercadoPago webhook intake exists under `/notification/mercadopago` and resolves QR notifications by fetching the upstream order, not by signature validation.
- Payments host app now also exposes native QR order creation under `/api/integrations/payments/orders`, generating canonical `externalReference` values required for approved-session persistence correlation.
- `/admin/payments` now also generates live MercadoPago QR orders for arbitrary amounts or per-product prices, with money inputs expressed in major units (for example `15.00` or `1000.00`), then shows the QR preview/order id for scan-and-pay validation.
- That admin QR surface is now positioned as a provider test/debug harness; production vending-machine payments should integrate through the TCN machine protocol and let the machine render the buyer-facing QR.
- Legacy approved-callback compatibility has been removed from operations; `/api/integrations/payments/approved` now returns `410` so only the native MercadoPago path remains active.
- `/admin/payments` now exposes Ballbox-local CRUD/reconciliation for approved payment sessions plus correlation checks against canonical `payment-vm_{vendingMachineId}-txn_{machineTxnId}` references.
- Payments admin session routes now read Prisma error helpers from `lib/prisma-errors.ts` instead of importing them via `lib/ballbox-store.ts`.
- Network core now has DB-backed admin CRUD for ATC-linked clubs, venues, and vending machines inside Ballbox.
- Prisma `Club.atcSportclubId` now matches live nullable data to prevent admin loader `P2032` crashes on legacy clubs without ATC linkage (`/admin/network`, `/admin/players`, `/admin/sports`, `/admin/tvs`).
- Clubs keep nullable `atcSportclubId` support for legacy/local rows, and repo now ships `db:backfill-atc-clubs` to reconcile local club names with ATC summary data when ids exist.
- Network club list/detail/admin query shaping has started moving out of `lib/ballbox-store.ts` into a dedicated backend module.
- Network club create/update/delete helpers have now also started moving out of `lib/ballbox-store.ts` into a dedicated backend module.
- Network venue mutation/delete helpers have also started moving out of `lib/ballbox-store.ts` into dedicated backend modules.
- Network vending-machine route callers now import `lib/network-backend-vending-machines.ts` directly and read unique-constraint classification from `lib/prisma-errors.ts`.
- Network club/venue route callers and `/admin/network` page now import direct network backend modules (`lib/network-backend-clubs.ts`, `lib/network-backend-club-mutations.ts`, `lib/network-backend-venues.ts`) plus `lib/prisma-errors.ts`.
- Network route handlers now share `lib/http/network-route-helpers.ts` for JSON parsing plus success/error envelopes across club, venue, and vending-machine contract routes.
- Players, sports, and payments route handlers now also share `lib/http/*-route-helpers.ts` so public/admin contract surfaces no longer depend on deep route-local helper imports.
- Network admin frontend view extraction has started moving venue-level and club-level rendering blocks out of `components/network-admin-client.tsx` into smaller components.
- Network admin frontend section composition has now also started moving club-list rendering, create-club section wiring, and derived venue-form wiring out of `components/network-admin-client.tsx` so it trends further toward a shell.
- Network admin frontend client-state, mutation orchestration, and local form-state wiring have started moving out of `components/network-admin-client.tsx` into dedicated hooks so the client trends toward a shell.
- Network admin frontend form defaults and status-option constants now live in a shared client config so shell and child components consume one source of truth.
- Network admin frontend layout/class wiring now also lives in a dedicated shell component so `components/network-admin-client.tsx` is closer to trivial hook composition.
- Network admin frontend shell chrome styles now also live in a tiny shared helper so the shell stays closer to pure composition/wiring.
- Players admin frontend client-state, form defaults, shell composition, and presentational stats/create/list-card seams now live in dedicated frontend modules so the client follows the same maintainable pattern already used by network.
- Sports admin frontend client-state, form defaults, shell composition, and coach/event section rendering now live in dedicated frontend seams so `components/sports-admin-client.tsx` is closer to pure wiring.
- Final naming direction is `network` core with `Club`, `Venue`, `VendingMachine`, `Screen`, and `TvDevice`.
- Post-`pnpm db:push` plus `pnpm db:seed` verification confirms physical DB columns stay canonical as `vendingMachineId` and `tvDeviceId`, with no `machineId`, `deviceId`, `/devices`, or `PaymentSessionRef` persistence aliases reintroduced.
- TVs admin now has DB-backed CRUD for screens and tv devices inside Ballbox.
- TV device admin mutations have started moving out of `lib/ballbox-store.ts` into a dedicated backend module.
- TV screen admin mutations have now also started moving out of `lib/ballbox-store.ts` into a dedicated backend module.
- TV campaign admin mutations have now also started moving out of `lib/ballbox-store.ts` into a dedicated backend module.
- TV creative admin mutations have now also started moving out of `lib/ballbox-store.ts` into a dedicated backend module.
- TV assignment admin mutations have now also started moving out of `lib/ballbox-store.ts` into a dedicated backend module.
- TV admin venue-option query shaping has now also started moving out of `lib/ballbox-store.ts` into a dedicated backend module.
- TV admin device/screen/campaign operations query shaping has now also started moving out of `lib/ballbox-store.ts` into a dedicated backend module.
- TV runtime source/manifest/heartbeat/impression helpers now also delegate from `lib/ballbox-store.ts` into a dedicated TV runtime backend module.
- TVs admin now has DB-backed CRUD for campaigns, creatives, and screen assignments inside Ballbox.
- Admin now supports a simple shared-password access layer when `BALLBOX_ADMIN_PASSWORD` is configured.
- TVs impressions now reject creatives that are not active for the target screen.
- Players API now reads Ballbox Postgres for `Player` and `Membership` rollups.
- Player backend read/admin query+mutation helpers have now started moving out of `lib/ballbox-store.ts` into a dedicated backend module.
- Sports coach backend helpers have now started moving out of `lib/ballbox-store.ts` into a dedicated backend module.
- Sports events plus classes offer/request backend helpers have now also started moving out of `lib/ballbox-store.ts` into dedicated backend modules.
- Sports now has DB-backed entities and admin/read APIs for coaches, open matches, and classes.
- `/classes` now exists as the first non-admin public Ballbox surface.
- `/classes` now lists Ballbox-local `ClassOffer` records, shows offer detail, surfaces visible ATC public-read slots, and accepts local `ClassRequest` submissions with either a concrete selected slot or a flexible any-schedule/any-location fallback.
- `/classes-by-date` now ships as a zone-first reservation calendar driven by coach availability plus ATC club availability, including explicit “coach disponible, pero sin canchas” slots and a required first zone selection based on the club's public location label when available.
- `/classes/me` now ships as a lightweight read-only student follow-up view (email-filtered request history) so demos can show the request loop without claiming full reservation confirmation.
- `/classes` initial HTML now renders from Ballbox-local offer data first and resolves ATC public-read context asynchronously after paint, with a short shared server cache to avoid repeating the same ATC lookup per visitor.
- Rapid demo execution now follows `docs/demo-iteration-mode.md` (`L1 story` / `L2 contracted demo` / `L3 production candidate`) so pivots stay fast without leaking demo shortcuts into core modules.
- `/coaches` now acts as the Ballbox account hub where the first differentiation is `Coach` vs `Club`.
- `/coaches` now defaults to `Sign in`, and `Sign up` is the fallback path for new accounts.
- `/coaches/settings` now lands on a coach dashboard first with `Calendar` and `Analytics` sub-sections: calendar shows the current week's coach availability plus taken student reservations, while analytics shows weekly earnings and student consistency/review rows; setup stays in `Mi Perfil` and student follow-up stays in `Alumnos`.
- `/clubs` now exists as the club-only service home that opens right after club sign-in/sign-up and groups the operational surfaces into service cards, with `Entrenadores` as the active entry point into the existing coaches workspace and the remaining services marked as `Proximamente`.
- `/clubs/coaches` now lands on a club workspace with top-level `Calendar`, `Analytics`, `Sedes`, and `Profesores` sections: calendar shows the current week's coach availability and reservations with sede/coach/court filters plus in-place manual class loading, analytics shows professor/reservation money splits and review placeholders, `Sedes` groups venue creation plus venue quick settings, and `Profesores` groups invite links plus the club coach table.
- `/clubs/coaches` now also enforces the club session at the page boundary before rendering the workspace, preventing the `Entrenadores` service-card hop from dropping users back into the shared `/coaches` access hub after a valid club login.
- Club workspace clickable actions are now visually distinguished with stronger accent borders, CTA chips, and hover/focus states, while existing-setting edits in club quick settings and venue detail auto-save in place instead of requiring explicit `Guardar` buttons or reloading the user back to the top.
- `/api/coaches/students` now lets a coach load existing Ballbox players into concrete occupied time slots, validating availability, venue fit, and overlap rules, and the coach workspace now surfaces those students in a dedicated `Alumnos` section with WhatsApp / Telegram contact fields.
- `/clubs/venues/[venueId]` now exists as the dedicated venue detail view for configuring venue opening/closing time, coach hours, external-coach availability, reservation settings, courts, and the coaches assigned to that venue.
- `CoachAvailability` weekly ranges and `CoachClub` selections now model coach intent separately from ATC court availability, while coach-side editing is clamped to the hours enabled by the assigned venue or the selected external clubs.
- Clubs now carry optional ATC sportclub ids to query availability at the club level.
- Clubs now also carry an optional contact email used for demo-grade club access and roster management.
- Venues now also store one opening time, one closing time, weekly coach-availability ranges, an external-coach availability flag, coach-enabled courts per venue, a special reservation price for coach classes, a club-coach-only discount on that fee, and a configurable late-cancellation window in hours.
- Coaches now can also be assigned to one venue inside their club.
- Coaches now also persist a Mercado Pago placeholder handle plus reusable sales-package rows for pricing, duration, class format, and max-student setup.
- Coaches and clubs now also store per-account password hashes for sign-in.
- Clubs now also persist coach invite tokens for generic join links, while still tolerating older email-bound invites.
- Local development now has a first-class Docker Postgres path via `docker-compose.yml` plus `.env.local`, and the seed now provisions demo coach/club passwords for local sign-in.
- Auth/session routes now return explicit database-unavailable errors when Prisma cannot reach or borrow a DB connection, instead of surfacing those failures as generic bad-login states.
- `/admin` overview now reads lightweight aggregate counts instead of hydrating full product datasets during SSR.
- Coach experience discovery is still early: current research input is an online form, so Ballbox does not yet have enough real-world coach workflow evidence to lock the sports product thesis.
- ATC admin/write is still not integrated; only a small public-read availability boundary now exists for `/classes` offer context and must degrade cleanly when unavailable.
- Local quality gate now includes `pnpm lint`, `pnpm typecheck`, `pnpm test:contracts`, `pnpm test:e2e`, and `pnpm build`.
- On `ballbox-first`, repo Playwright now uses a stable production-style `webServer` path for e2e (`pnpm build:e2e && pnpm start:e2e` on port `3020`), replacing the unstable Next 16 dev-server setup previously used on this Pi.
- Vitest taxonomy now includes layered includes for `tests/contracts`, `tests/unit`, and `tests/integration`, with dedicated scripts for each layer.

## Route Map
- `/`: marketing home
- `/admin`: admin overview
- `/admin/tvs`: TV and screen operations
- `/admin/payments`: payment operations and reconciliation
- `/admin/network`: clubs, venues, vending machines
- `/admin/sports`: open matches, coaches, academy
- `/admin/players`: players y memberships
- `/admin/screens`: screen runtime admin surface hosted inside Ballbox
- `/classes`: public class discovery + request surface
- `/classes/[offerId]`: public offer detail + request form
- `/classes-by-date`: zone-first reservation discovery calendar
- `/coaches`: account hub for coach vs club
- `/classes/me`: read-only student request follow-up
- `/coaches/settings`: coach dashboard with `Calendar` / `Analytics`, plus `Mi Perfil` and `Alumnos`
- `/clubs`: club service home
- `/clubs/coaches`: club workspace with `Calendar` / `Analytics` / `Sedes` / `Profesores`
- authenticated coach/club workspace routes now use a shared top navbar with section tabs and an avatar-triggered sign-out menu instead of top-corner text links or standalone explainer hero panels
- coach workspace now also includes student follow-up and manually occupied-hour management using existing `Player` records, including coach-editable WhatsApp and Telegram contact data
- `/clubs/venues/[venueId]`: dedicated venue setup and venue roster

## Backend Split
- Ballbox Next.js host:
  - app shell
  - admin UI
  - shared auth/RBAC foundation
  - low-complexity CRUD
  - BFF aggregation and integration routes
  - shared `network` core entities
  - internal TVs module and runtime
- External specialized services:
  - none required for current native MercadoPago payments flow in Ballbox host app

## Implemented
- Shared admin shell and product-separated routes are in place.
- Admin shell copy now adapts per route instead of repeating one generic hero across every module.
- TVs now resolve admin/runtime state from a canonical internal Ballbox model with explicit club, venue, screen, and campaign ownership.
- TVs runtime now has a per-screen route and manifest-driven player UI.
- TVs now also expose a lightweight non-React runtime at `/player/[screenId]` for constrained devices.
- Prisma/Postgres foundation now exists in Ballbox for the durable replacement.
- Ballbox Postgres has the TVs schema pushed and seeded with current local fixture data.
- Network APIs now read/write Ballbox Postgres through Prisma.
- TVs source/manifest/impression/heartbeat paths already read/write against Ballbox Postgres.
- TVs admin now exposes direct runtime and manifest links per screen for operations.
- TVs errors now use a shared strict shape and the contract is validated by `pnpm test:contracts`.
- Payments integration health and sessions routes now report Ballbox-internal native MercadoPago status plus webhook/order metadata.
- Payments native MercadoPago webhook flow now lands in Ballbox, validates canonical payment correlation from fetched orders, and persists approved payments as `PaymentSession` records.
- QR order creation now sets a public `notification_url` so Mercado Pago routes order updates back to Ballbox explicitly.
- Unsupported merchant-order notification topics are ignored so only QR order events drive persistence.
- Replay testing with two older live `ORD...` ids returned `MERCADOPAGO_ORDER_NOT_APPROVED_expired`; see `docs/mercadopago-webhook-replay-notes-2026-04-23.md` for the current blocker and next resume point.
- Payments now have an admin CRUD/reconciliation surface over local `PaymentSession` rows via `/api/admin/payments/*`.
- Final target model is documented in `docs/domain-model-target.md`; end state does not keep retro-compatibility shims.
- ESLint is now wired and the repo exposes an explicit `typecheck` script.
- `/admin` and current admin-only APIs can now be gated by a shared password cookie session.
- `/admin/tvs` now performs admin CRUD for screens and tv devices through admin-only APIs under `/api/admin/tvs/*`.
- `/admin/tvs` now performs admin CRUD for campaigns, creatives, and assignments through admin-only APIs under `/api/admin/tvs/*`.
- TVs impression writes now validate creative-to-screen activity windows before persisting telemetry.
- Players now have DB-backed read/admin APIs for `Player` and `Membership`.
- Players route + page callers now import `lib/players-backend.ts` (and `lib/network-backend-clubs.ts` for club lists) directly instead of relying on `lib/ballbox-store.ts` for runtime access.
- Sports now have DB-backed read/admin APIs for `Coach`, `OpenMatch`, and `Class`.
- Classes MVP now also has DB-backed public APIs for `ClassOffer` and `ClassRequest`.
- `/classes/me` now provides a lightweight student request-history surface over Ballbox-local `ClassRequest` records (read-only, demo-grade email lookup).
- `ClassRequest` now snapshots the student's selected visible slot when they choose one, and otherwise persists an explicit flexible-selection fallback mode.
- Coach availability now ships as weekly ranges with 90-minute slots, filtered by coach club selection and ATC club availability.
- Coach and club account entry now starts at `/coaches`, with `Sign in` first and `Sign up` only for users without an account.
- Coaches and clubs now sign in with per-account email + password credentials, and `/api/account/session` routes each session to the correct dashboard.
- Coach sign-up now asks only for name, email, and password; if a club invited that coach, the invite link auto-links the account to that club.
- `/coaches/settings` now opens on a dashboard split into `Calendar` and `Analytics`, then lets coaches move into configuration from the shared navbar to choose external clubs, edit weekly availability only inside allowed venue/club windows, configure reusable class packages, and save a Mercado Pago placeholder handle.
- `/coaches/settings` now also lets coaches attach existing Ballbox players to concrete booked slots in their active venues, so taken hours, weekly earnings, consistency tracking, and lightweight student follow-up live inside the coach workspace.
- Club sign-in/sign-up now lands on `/clubs`, a service home that separates the club's operational modules before entering the detailed coaches workspace.
- Club accounts now have a dedicated workspace at `/clubs/coaches` split into `Calendar`, `Analytics`, `Sedes`, and `Profesores`, while venue-by-venue deep setup still lives at `/clubs/venues/[venueId]`, all under the same shared navbar/profile chrome.
- Venue setup now uses two simple fields for opening and closing time plus a collapsible weekly half-hour grid for coach hours, shows closed periods in red while editing coach availability, lets clubs mark whether external coaches are allowed, keeps coach-enabled courts in a dedicated selection panel, and includes reservation pricing/cancellation settings both in the venue detail and as quick settings on `/clubs/coaches`, including a discount that applies only to coaches assigned to that club.
- ATC lookups for class surfaces now resolve by club (no court coupling), and class request validation rechecks the selected slot date/time against ATC public-read.
- `/classes` SSR now avoids blocking first paint on ATC public-read fetches; the page paints local offer data first, then hydrates ATC availability from cached API responses.
- `/admin` overview SSR now uses direct aggregate counts instead of loading full admin datasets only to derive metrics.
- Sports/classes facade exports in `lib/ballbox-store.ts` now delegate to dedicated sports-events and classes backend modules instead of keeping that slice inline.
- Sports route + page callers now import `lib/sports-backend-events.ts` and `lib/sports-backend-coaches.ts` directly, so `lib/ballbox-store.ts` is no longer the primary sports entrypoint.
- TV runtime/backend facade exports in `lib/ballbox-store.ts` now delegate to `lib/tvs-backend-runtime.ts` instead of keeping source/manifest/heartbeat/impression logic inline.
- UI color/system tokens are split between landing (`app/styles/landing-tokens.css`) and admin (`app/styles/admin-tokens.css`).

## Streetcast Migration Check
- Source of truth for parity review: `/Users/sebas/Code/street-cast-pwa`.
- Covered in Ballbox today:
  - manifest polling exists in `components/screen-player.tsx`
  - sequential creative playback exists in `components/screen-player.tsx`
  - impression writes exist in `components/screen-player.tsx`
  - runtime health signaling exists via heartbeats in `components/screen-player.tsx`
- Missing or not yet verified against Streetcast behavior:
  - no manual real-device verification yet proves CacheStorage/object-URL playback outside local browser automation

## Next Steps (Priority + Dependency Queue)
- Source of truth for task backlog and dependencies: `docs/next-steps-queue.md`.
- Selection rule for the next agent: pick the highest-priority task marked `ready` in that doc.
- Current pick (`P0`, `ready`): `OPS-1` payments real-world validation closure.
- Immediately after `OPS-1`: `OPS-2` TVs runtime parity closure.
- Then continue with `P1` in dependency order: `ARCH-2` and `ARCH-3` -> `PERF-1` -> `PAY-3`.
- `PAY-4` (split payments per club) stays behind the TCN machine-facing adapter work.
- Keep detailed backlog/status only in `docs/next-steps-queue.md` to avoid drift.

### Product Continuation Constraints
- Keep `sports` discovery honest: no claims beyond evidence in `docs/coaches-discovery-stage.md`.
- Next sports expansion after `/classes/me`: lightweight operator review for incoming requests.
- Expand ATC scope beyond public-read only after the request loop proves it is needed.

## Coach Discovery Docs
- Company-level strategic mirror for this track lives in `~/ballbox-company-agent/projects/coaches-os/`; source of truth for product-repo execution remains this `PLAN.md` plus local docs.
- Source of truth for current coach/padel/ATC discovery framing lives in `docs/coaches-discovery-stage.md`.
- Current form instrument is documented in `docs/coaches-form-current.md`.
- Current ATC API discovery is documented in `docs/atc-integration-discovery.md`.
- Working templates for this track live in `docs/coaches-opportunity-brief.md`, `docs/coaches-research-repository.md`, and `docs/coaches-validation-checklist-2-weeks.md`.
- Guided execution docs for the next discovery phase live in `docs/coaches-discovery-execution-plan.md`, `docs/coaches-icp-segmentation.md`, `docs/coaches-wedges-matrix.md`, and `docs/coaches-interview-script.md`.
- Next synthesis docs live in `docs/coaches-jtbd-workflow-map.md`, `docs/coaches-solution-thesis.md`, and `docs/atc-access-request-checklist.md`.
- Outreach and post-access planning docs live in `docs/coaches-design-partner-outreach.md` and `docs/atc-integration-spec-skeleton.md`.
- Implementation gap analysis for the earlier coach-coordination wedge lives in `docs/coaches-coordination-gap-analysis.md`.
- Current first non-admin public route plan lives in `docs/classes-subroute-plan.md`.
- Current MVP RFC lives in `docs/classes-mvp-rfc.md`.

[71 more lines in file. Use offset=221 to continue.]


# TCN payments integration plan

## Decision
- Treat the current `/admin/payments` QR generator as an operator test harness, not the final production payment UX for vending machines.
- Production vending flow should integrate with the TCN machine protocol and let the machine render the buyer-facing QR on its own screen.

## Why
- TCN protocol `2.5V` states: `to scan QR codes additional hardware devices are necessary.`
- That wording applies to **machine-side QR scanning**, not to the machine displaying a QR for the buyer to scan with a phone.
- The payment section (`2.1.5 Integral exchange`, `FunCode=8000`) already defines a dynamic payment flow:
  - machine sends `MachineID`, `TradeNo`, `SlotNo`, `Price`, `NotityUrl`
  - server returns `CodeUrl`, `TradeNo`
- `CodeUrl` is described as the returned value used to generate the QR code.
- That is enough to proceed with integration planning without assuming extra scanner hardware.

## Intended production flow
1. Buyer selects product on TCN machine.
2. TCN machine requests Ballbox payment session using the TCN payment contract.
3. Ballbox creates a Mercado Pago QR order.
4. Ballbox maps the Mercado Pago output into the TCN response shape and returns `CodeUrl`.
5. TCN machine renders the QR on its screen.
6. Buyer scans with Mercado Pago app on phone.
7. Payment provider notifies Ballbox webhook.
8. Ballbox validates/persists approved payment.
9. TCN machine receives the success signal through the TCN-side notify/result path and dispenses product.

## Ballbox work now
### 1. Lock the machine-facing contract
- Define a Ballbox route for TCN `8000` payment-init requests.
- Define exact request/response mapping for:
  - `MachineID` -> Ballbox `vendingMachineId` or external machine identifier
  - `TradeNo` -> machine transaction id / canonical `externalReference`
  - `SlotNo`
  - `Price`
  - `NotityUrl`
  - response `CodeUrl`
- Confirm whether TCN expects raw payment URL, QR payload string, or a specific provider deep link in `CodeUrl`.

### 2. Define result/dispense handshake
- Map how Ballbox should signal payment success back into the TCN flow.
- Verify whether Ballbox must call `NotityUrl`, expose a callback endpoint, or rely on a polling/result interface in the broader TCN protocol set.
- Confirm how TCN links approved payment to dispense using `TradeNo` and/or `SlotNo`.

### 3. Normalize machine identity
- Decide the canonical mapping between TCN `MachineID` and Ballbox `VendingMachine`.
- Add any missing external-id field only if needed for clean lookup.

### 4. Keep admin QR page as a test tool
- `/admin/payments` remains useful for:
  - Mercado Pago config checks
  - live QR creation smoke tests
  - webhook validation
  - manual provider debugging
- Do not frame it as the final vending-machine payment UX.

## Unknowns still to close
- Exact payload expected inside TCN `CodeUrl`.
- Exact success callback / notify shape between Ballbox and TCN after payment approval.
- Whether machine-side polling (`4000`) participates in payment completion or only app-driven vend/load flows.
- Whether TCN needs additional payment-status endpoints beyond `8000` for a full dispense flow.

## Recommendation
- Next implementation slice should be a TCN adapter spec + stub endpoints, not more work on the standalone admin QR page.
- Keep Mercado Pago-native Ballbox routes as the provider layer underneath that adapter.

## Evidence
- `test-results/tcn-payment-protocol/Protocol 2.5V English version.pdf`
- `test-results/tcn-payment-protocol/findings-2026-04-23.md`


**Creating organized documentation**

I think I need to create some new documents, specifically an external questions checklist and an execution plan. It feels essential to update the overall plan by including ordered steps that link to these new docs. Keeping things concise is important, so I'll focus on clarity and organization. That way, everything becomes easier to follow and manage! Let's get started on this!

write

# TCN payments open questions

## Goal
Close the remaining machine-integration unknowns before Ballbox implements the full TCN adapter.

## Questions for TCN sales / engineering
1. In `FunCode=8000`, what exact value should Ballbox return in `CodeUrl`?
   - raw URL
   - QR payload string
   - Mercado Pago deep link
   - other provider-specific format
2. After payment approval, how does the machine learn that the vend can proceed?
   - Ballbox calls the request `NotityUrl`
   - machine polls another TCN endpoint
   - cloud pushes through `4000`
   - other flow
3. Is `NotityUrl` always machine-provided per transaction, or is there also a machine-level configured callback?
4. What is the exact payload shape expected for the payment-success notify/result message?
5. Which field is the true dispense correlation key?
   - `TradeNo`
   - `SlotNo`
   - both
   - another internal machine field
6. Does the stock TCN app render the QR natively after a successful `8000` response, or is any machine-side UI configuration required?
7. Are there any timeout / expiry expectations for the QR payment window from the machine side?
8. What should Ballbox return to the machine when payment creation fails or times out?
9. Is there any machine retry behavior Ballbox must tolerate for duplicate `TradeNo` requests?
10. Are there additional payment-related endpoints beyond `8000` required for a production dispense flow?

## Questions for Mercado Pago / internal validation
1. Which returned value from Ballbox should be safest to map into TCN `CodeUrl` for dynamic QR display?
2. Does current Ballbox order creation produce the same QR/deeplink payload shape expected by a buyer using Mercado Pago app?
3. Is the current webhook exposure stable enough for real public notifications on the production-like Ballbox URL?
4. Are there provider-side expiry, duplicate-order, or idempotency constraints we should encode before machine rollout?

## Output expected from this doc
- A resolved field-mapping note added back into `docs/tcn-payments-integration-plan.md`
- A concrete implementation spec once answers arrive

# TCN payments execution plan

## Purpose
Ordered execution plan for getting from current Ballbox native Mercado Pago support to real TCN machine payments.

## Step 1. Finish provider-layer proof
- Goal: prove Ballbox native Mercado Pago flow works end to end without TCN hardware.
- Inputs:
  - `docs/implemented-payments.md`
  - `docs/mercadopago-webhook-replay-notes-2026-04-23.md`
- Tasks:
  - validate public webhook reachability for `/notification/mercadopago`
  - run one real approved payment
  - confirm local `PaymentSession` persistence in `/admin/payments`
  - document exact payload and public URL evidence
- Exit criterion:
  - one real approved payment persists locally through webhook flow

## Step 2. Ask TCN the machine-contract questions
- Goal: close the remaining machine-side unknowns before coding the adapter.
- Inputs:
  - `docs/tcn-payments-integration-plan.md`
  - `docs/tcn-payments-open-questions.md`
  - `test-results/tcn-payment-protocol/findings-2026-04-23.md`
- Tasks:
  - send the question list to the TCN sales contact
  - ask them to confirm with engineers
  - capture answers in repo docs
- Exit criterion:
  - `CodeUrl` meaning and payment-success handshake are confirmed

## Step 3. Freeze Ballbox-TCN contract
- Goal: convert answers into a concrete adapter spec.
- Inputs:
  - `docs/tcn-payments-integration-plan.md`
  - answers captured from Step 2
- Tasks:
  - define request/response schema for TCN `8000`
  - define success/failure callback or polling behavior
  - define duplicate/idempotency behavior for repeated `TradeNo`
  - define machine-id mapping rules
- Exit criterion:
  - implementation spec is precise enough to code without guesswork

## Step 4. Implement adapter layer
- Goal: add the TCN-facing Ballbox endpoints over existing Mercado Pago primitives.
- Inputs:
  - concrete contract spec from Step 3
- Tasks:
  - add machine-facing endpoint(s)
  - map TCN requests to Ballbox/Mercado Pago order creation
  - map approved-payment state back into TCN success flow
  - persist any extra machine correlation fields needed
- Exit criterion:
  - local/manual adapter flow works without real machine hardware

## Step 5. Hardware validation when machines arrive
- Goal: verify actual stock TCN machine behavior.
- Tasks:
  - test real QR render on machine screen
  - test payment approval -> vend path
  - capture timing, retries, and failure cases
- Exit criterion:
  - one real vend succeeds through Ballbox-backed payment flow

## Step 6. Only then revisit payout split work
- Goal: avoid solving club payout distribution before the machine path is stable.
- Related backlog item:
  - `PAY-4` in `docs/next-steps-queue.md`


Successfully wrote 2110 bytes to docs/tcn-payments-open-questions.md


Successfully wrote 2681 bytes to docs/tcn-payments-execution-plan.md


edit

## Next Steps (Priority + Dependency Queue)
- Source of truth for task backlog and dependencies: `docs/next-steps-queue.md`.
- Selection rule for the next agent: pick the highest-priority task marked `ready` in that doc.
- Current pick (`P0`, `ready`): `OPS-1` payments real-world validation closure.
- Immediately after `OPS-1`: `OPS-2` TVs runtime parity closure.
- Then continue with `P1` in dependency order: `ARCH-2` and `ARCH-3` -> `PERF-1` -> `PAY-3`.
- `PAY-4` (split payments per club) stays behind the TCN machine-facing adapter work.
- Keep detailed backlog/status only in `docs/next-steps-queue.md` to avoid drift.

## Ordered Payments Track
1. Finish provider-level Mercado Pago proof without TCN hardware.
   - backlog: `OPS-1` in `docs/next-steps-queue.md`
   - docs: `docs/implemented-payments.md`, `docs/mercadopago-webhook-replay-notes-2026-04-23.md`
2. Ask TCN sales to confirm the remaining machine-contract questions with their engineers.
   - docs: `docs/tcn-payments-open-questions.md`, `docs/tcn-payments-integration-plan.md`
   - note: this is required because the machines are not here yet
3. Freeze the Ballbox <-> TCN adapter contract once those answers come back.
   - docs: `docs/tcn-payments-integration-plan.md`, `docs/tcn-payments-execution-plan.md`
4. Implement the TCN-facing adapter over the existing native Mercado Pago layer.
   - backlog: `PAY-3` in `docs/next-steps-queue.md`
   - docs: `docs/tcn-payments-execution-plan.md`
5. Validate on real TCN hardware once machines arrive.
   - docs: `docs/tcn-payments-execution-plan.md`
6. Revisit split payouts only after the machine-facing payment flow is stable.
   - backlog: `PAY-4` in `docs/next-steps-queue.md`

### Payments Track Docs
- Implemented provider layer: `docs/implemented-payments.md`
- TCN direction and target flow: `docs/tcn-payments-integration-plan.md`
- Open external questions: `docs/tcn-payments-open-questions.md`
- Ordered execution sequence: `docs/tcn-payments-execution-plan.md`
- Current Mercado Pago webhook blocker/evidence: `docs/mercadopago-webhook-replay-notes-2026-04-23.md`
- TCN protocol evidence artifact: `test-results/tcn-payment-protocol/findings-2026-04-23.md`


Successfully replaced 1 block(s) in PLAN.md.

     ...
 200 - Then continue with `P1` in dependency order: `ARCH-2` and `ARCH-3` -> `PERF-1` -> `PAY-3`.
 201 - `PAY-4` (split payments per club) stays behind the TCN machine-facing adapter work.
 202 - Keep detailed backlog/status only in `docs/next-steps-queue.md` to avoid drift.
 203
+204 ## Ordered Payments Track
+205 1. Finish provider-level Mercado Pago proof without TCN hardware.
+206    - backlog: `OPS-1` in `docs/next-steps-queue.md`
+207    - docs: `docs/implemented-payments.md`, `docs/mercadopago-webhook-replay-notes-2026-04-23.md`
+208 2. Ask TCN sales to confirm the remaining machine-contract questions with their engineers.
+209    - docs: `docs/tcn-payments-open-questions.md`, `docs/tcn-payments-integration-plan.md`
+210    - note: this is required because the machines are not here yet
+211 3. Freeze the Ballbox <-> TCN adapter contract once those answers come back.
+212    - docs: `docs/tcn-payments-integration-plan.md`, `docs/tcn-payments-execution-plan.md`
+213 4. Implement the TCN-facing adapter over the existing native Mercado Pago layer.
+214    - backlog: `PAY-3` in `docs/next-steps-queue.md`
+215    - docs: `docs/tcn-payments-execution-plan.md`
+216 5. Validate on real TCN hardware once machines arrive.
+217    - docs: `docs/tcn-payments-execution-plan.md`
+218 6. Revisit split payouts only after the machine-facing payment flow is stable.
+219    - backlog: `PAY-4` in `docs/next-steps-queue.md`
+220
+221 ### Payments Track Docs
+222 - Implemented provider layer: `docs/implemented-payments.md`
+223 - TCN direction and target flow: `docs/tcn-payments-integration-plan.md`
+224 - Open external questions: `docs/tcn-payments-open-questions.md`
+225 - Ordered execution sequence: `docs/tcn-payments-execution-plan.md`
+226 - Current Mercado Pago webhook blocker/evidence: `docs/mercadopago-webhook-replay-notes-2026-04-23.md`
+227 - TCN protocol evidence artifact: `test-results/tcn-payment-protocol/findings-2026-04-23.md`
+228
 204 ### Product Continuation Constraints
 205 - Keep `sports` discovery honest: no claims beyond evidence in `docs/coaches-discovery-stage.md`.
 206 - Next sports expansion after `/classes/me`: lightweight operator review for incoming requests.
 207 - Expand ATC scope beyond public-read only after the request loop proves it is needed.
     ...


## Current Build Docs
- `docs/classes-subroute-plan.md`
- `docs/classes-mvp-rfc.md`
- `docs/coaches-coordination-gap-analysis.md`
- `docs/atc-mock-integration-plan.md`
- `docs/tcn-payments-integration-plan.md`
- `docs/tcn-payments-open-questions.md`
- `docs/tcn-payments-execution-plan.md`


Successfully replaced 1 block(s) in docs/index.md.

    ...
 49 - `docs/classes-mvp-rfc.md`
 50 - `docs/coaches-coordination-gap-analysis.md`
 51 - `docs/atc-mock-integration-plan.md`
 52 - `docs/tcn-payments-integration-plan.md`
+53 - `docs/tcn-payments-open-questions.md`
+54 - `docs/tcn-payments-execution-plan.md`


Updated #2 (pending → completed)


Hecho.

Nuevo
- `docs/tcn-payments-open-questions.md`
- `docs/tcn-payments-execution-plan.md`

Actualizado
- `PLAN.md`
- `docs/index.md`

Qué quedó ordenado en `PLAN.md`
- pasos en orden
- links a cada doc
- tareas no-código incluidas:
  - consultar a ventas de TCN
  - que ventas confirme con ingeniería
  - seguir validando webhook/flujo real de Mercado Pago
  - validar hardware real cuando lleguen las máquinas

Orden ahora
1. probar bien Mercado Pago/webhook sin TCN
2. hacer consultas a TCN
3. congelar contrato Ballbox↔TCN
4. implementar adapter TCN
5. validar en máquina real
6. recién después split payments

Eso refleja mejor la realidad:
- hoy sí podemos avanzar en capa proveedor + spec
- no podemos cerrar adapter final sin respuestas de TCN
- no tiene sentido adelantar split payouts antes del flujo máquina→pago→vend

Si querés, siguiente paso útil:
- te dejo redactado un mensaje corto para el de ventas de TCN con las preguntas exactas, listo para enviar.


que dificultad tendria cambiar el sistema para que sea otra cuenta de MP la principal donde se reciban los pagos?


**Considering conceptual answers**

I need to approach this in a conceptual way. Maybe I should consider mentioning if it's the same country or related business. It seems like I should give a direct and compressed answer in Spanish to keep things clear and concise. I think focusing on key ideas may help direct the conversation better. Let's aim to make it straightforward for understanding while still being engaging.

Baja a media. No parece cambio estructural grande.

Depende de qué signifique “otra cuenta principal”:

## Caso 1: cambiar la cuenta dueña de los cobros
Si es solo reemplazar la cuenta de Mercado Pago cuyas credenciales usa Ballbox:
- cambiar `access token`
- revisar `external POS id`
- revisar webhook secret
- revalidar webhook público
- hacer 1 pago real de prueba
- confirmar que las órdenes nuevas se crean bien y persisten en `/admin/payments`

Esto debería ser bastante directo.

## Caso 2: otra cuenta pero mismo flujo técnico
Si querés que todo siga igual, solo cobrando en otra cuenta:
- dificultad baja
- riesgo principal: config rota, no código
- puede haber que recrear POS / caja / sucursal en esa nueva cuenta
- puede cambiar cómo responde MP si el `externalPosId` viejo pertenece a la cuenta anterior

Acá lo más sensible no es Ballbox. Es la config en Mercado Pago.

## Caso 3: cuenta nueva + split payments / partners / subcuentas
Ahí sube bastante.
Porque ya no es solo “cambiar credenciales”, sino:
- onboarding de cuentas
- permisos
- routing de fondos
- tal vez nuevos campos por club
- reglas de reconciliación
- soporte operativo
- manejo de credenciales y rotación

Eso ya es otro proyecto. No un simple cambio.

## En Ballbox hoy
Por lo que tenemos ahora, el sistema está bastante acoplado a:
- token de acceso MP
- webhook secret
- external POS id
- webhook URL pública

Entonces cambiar la cuenta principal hoy suena más a:
- cambio de configuración
- pequeña validación operativa
- quizá algún ajuste menor si el POS id o metadata cambian

No suena a refactor serio.

## Qué podría complicarlo
Las trabas probables:
1. `BALLBOX_MERCADOPAGO_EXTERNAL_POS_ID` no reusable en la nueva cuenta
2. webhooks no configurados o secret distinto
3. cuenta nueva con restricciones o permisos incompletos
4. órdenes viejas y nuevas mezcladas en reconciliación
5. si querés mantener histórico cruzado entre dos cuentas MP, la operación se ensucia un poco

## Mi estimación
- solo cambiar cuenta receptora principal: fácil
- dejarlo prolijo y revalidado: 0.5 a 1 día
- si hay que reprovisionar POS/webhooks y pelear config: 1 a 2 días
- si además querés multi-cuenta o split real: bastante más

## Recomendación
Sí conviene diseñar esto para que la cuenta principal de MP sea reemplazable por config, no por código.
O sea:
- credenciales por env o por registro
- POS id desacoplado
- checklist de rotación/prueba

Si querés, hago el siguiente paso:
- revisar el código actual y decirte exactamente qué tan hardcodeado o acoplado quedó el cambio de cuenta.


que dificultad tendria cambiar el sistema para que sea otra cuenta de MP la principal donde se reciban los pagos? a una cuenta que ya recibe pagos, capaz no con este flujo especifico pero es un local


**Checking answer approach**

I want to provide a direct answer, but I’m considering inspecting the documentation to make sure I give the best info. Maybe it’s not necessary to dig too deep? I imagine the answer could be medium-low, depending on the credentials or the webhook POS configuration. It’s important to mention if they’re using the same country or legal entity, as that could impact the situation.

Bastante baja si hablamos de:

- cambiar `access token`
- configurar webhook de esa cuenta
- usar un `external POS id` de esa cuenta
- seguir cobrando todo en una sola cuenta principal

O sea: si Ballbox sigue siendo “un integrador técnico” pero la cuenta dueña de los cobros pasa a ser otra cuenta de MP, no suena como refactor grande.

Dificultad real: baja a media.

Qué habría que tocar
1. Credenciales
- cambiar `BALLBOX_MERCADOPAGO_ACCESS_TOKEN`
- posiblemente `BALLBOX_MERCADOPAGO_WEBHOOK_SECRET`
- revisar `BALLBOX_MERCADOPAGO_EXTERNAL_POS_ID`

2. Configuración en Mercado Pago
- asegurar que esa cuenta tenga habilitado el producto/flujo QR que estamos usando
- registrar webhook hacia `/notification/mercadopago`
- confirmar que el POS/external POS usado pertenezca a esa cuenta

3. Validación funcional
- crear una orden real con esa cuenta
- pagarla
- ver que webhook llegue
- ver que se persista en `/admin/payments`

Dónde puede complicarse
- si esa cuenta “ya cobra” pero usa otro producto de MP distinto al que estamos usando ahora
- si el POS/QR setup actual está atado a la cuenta vieja y no es portable
- si hay restricciones comerciales/KYC/rubro/sucursal en esa cuenta
- si quieren mantener histórico o conciliación cruzada entre cuenta vieja y nueva

Lo que probablemente NO sería difícil
- cambiar el código base de Ballbox para “apuntar” a otra cuenta principal
- porque hoy la integración parece bastante centralizada en credenciales + order creation + webhook

En resumen
- cambio técnico: probablemente chico
- cambio operativo en MP: ahí está el riesgo real
- estimación rápida: si la nueva cuenta ya tiene habilitado el mismo flujo QR, esto puede ser casi solo reconfiguración + pruebas
- si no tiene ese flujo habilitado, puede trabarse más por MP que por código

Lo que revisaría antes de prometer nada
1. esa cuenta ya tiene habilitado exactamente el mismo producto QR/order API?
2. podemos crear órdenes desde esa cuenta con el token nuevo?
3. el webhook firma/notifica bien desde esa cuenta?
4. el `external POS id` actual sirve o hay que crear otro?
5. hay algún requisito fiscal/comercial por tratarse de un local existente?

Si querés, te hago un checklist exacto de migración de cuenta MP:
- qué confirmar en MP
- qué vars cambiar
- qué prueba hacer
- cómo validar que no rompimos nada.


para mantener el webhook llegando a mi tendria que decirles que lo configuren ellos en su cuenta de MP?


Sí.

Si la cuenta de MP dueña de los cobros pasa a ser la de ellos, entonces el webhook lo tienen que configurar en esa cuenta para apuntar a tu endpoint.

O sea:
- Ballbox puede exponer `/notification/mercadopago`
- pero Mercado Pago manda notificaciones según la config de la cuenta que crea/cobra la orden
- si esa cuenta es de ellos, ellos tienen que poner tu webhook URL ahí

En práctica, les tenés que pedir:
- configurar webhook hacia tu URL pública
- darte el secret si MP usa secret separado en esa cuenta
- confirmar que el flujo/credenciales/POS pertenezcan a esa misma cuenta

Muy importante
- no alcanza con que te den un access token si el webhook no está bien configurado del lado de esa cuenta
- y si cambian de cuenta, probablemente cambie también:
  - secret
  - POS/external POS id
  - permisos/habilitación del producto QR

Modelo mental simple
- cuenta que cobra = cuenta que decide adónde MP notifica

Entonces sí:
- si quieren cobrar en su cuenta
- y vos querés seguir recibiendo webhook en Ballbox
- les tenés que pedir que configuren tu webhook en su cuenta MP

Si querés, te redacto el pedido exacto para mandarles:
- webhook URL
- qué tienen que configurar
- qué credenciales/datos te tienen que pasar.


y si ahora en realidad son una cuenta por club (para las maquinas expendedoras que ubiquemos en cada club), como cambia la exuacion?


Cambia bastante.

Pasa de:
- 1 sola cuenta MP central
a:
- 1 cuenta MP por club

Eso no rompe la idea. Pero sí cambia arquitectura, operación y onboarding.

## Qué cambia técnicamente

### Antes
Ballbox podía tener:
- un solo `access token`
- un solo webhook secret
- un solo POS/external POS
- una sola cuenta que cobra todo

### Con una cuenta por club
Ballbox necesita resolver por cada pago:
- qué club es dueño de esa máquina
- qué cuenta MP usa ese club
- qué credenciales usar para crear esa orden
- qué webhook/secret corresponde
- cómo mapear el pago recibido al club correcto

O sea: ya no alcanza config global. Necesitás config por club.

## Qué implica en Ballbox

### 1. Guardar credenciales/config por club
Mínimo por club:
- access token
- webhook secret, si aplica
- external POS id / caja / sucursal, si el flujo lo requiere
- estado de conexión/configuración
- quizás merchant/user id de MP para soporte

Esto ya entra en el terreno del viejo `PAY-4` / partner-ish setup, aunque no sea Partners formal.

### 2. Resolver cuenta correcta al crear pago
Flujo:
- máquina -> Ballbox
- Ballbox identifica `vendingMachineId`
- de ahí saca `clubId`
- de ahí saca config MP del club
- crea orden con esa cuenta

Si no existe config para ese club:
- no se puede iniciar pago

### 3. Webhooks
Acá hay dos modelos:

#### Modelo A: todos los clubes configuran el mismo webhook URL tuyo
Ejemplo:
- todos apuntan a `https://tu-dominio/notification/mercadopago`

Esto es viable.

Entonces Ballbox recibe webhooks de muchas cuentas distintas en el mismo endpoint y tiene que:
- identificar de qué cuenta vino
- usar las credenciales correctas para consultar la orden
- persistirla contra el club/máquina correctos

Este sería el mejor modelo.

#### Modelo B: un webhook URL distinto por club
También posible, pero peor.
Más lío operativo. No lo elegiría salvo obligación.

## Dónde se pone más difícil

### Dificultad técnica
Media.

No por crear el pago.
Eso es relativamente directo.

La complejidad está en:
- multi-tenant credentials
- seguridad de secretos por club
- onboarding/soporte
- debugging cuando un club rompe su config
- reconciliación y visibilidad por club

### Dificultad operativa
Alta comparado con cuenta única.

Porque por cada club necesitás:
- pedir acceso/config
- validar que tenga habilitado el producto correcto
- hacer que pongan tu webhook
- confirmar POS/external POS
- probar un pago real
- mantener esa config viva

## Qué no cambiaría
La lógica base de Ballbox igual sirve:
- crear orden
- recibir webhook
- buscar orden
- persistir `PaymentSession`
- correlacionar con máquina/transacción

Pero deja de ser “global” y pasa a ser “por club”.

## Qué decisión de producto/negocio aparece

Tenés 3 caminos:

### Opción 1: cuenta única Ballbox
- más fácil técnica y operativamente
- peor para algunos clubes si quieren cobrar directo ellos

### Opción 2: una cuenta MP por club
- más compleja
- más alineada si cada club quiere cobrar directo

### Opción 3: Ballbox cobra y luego reparte
- requiere split/partners/liquidación
- suele ser más compleja todavía, regulatoria y operativamente

Para near-term, si querés cerrar máquinas rápido:
- cuenta única central es mucho más simple

Si el requisito comercial real es:
- “cada club debe cobrar en su propia cuenta”
entonces hay que diseñar multi-club MP desde ahora.

## Cómo cambia el plan
Se agregan pasos antes de implementar full prod:

1. decidir si Ballbox soporta cuenta única o multi-cuenta por club
2. definir modelo de datos de credenciales MP por club
3. definir onboarding por club
4. definir estrategia de webhook compartido
5. probar 1 club real end-to-end
6. recién después escalar a varios clubes

## Mi recomendación
Si esto va para máquinas en clubes distintos:
- diseñar el adapter TCN para que el pago ya se resuelva por `club -> cuenta MP`
- pero implementar primero con soporte para **una sola cuenta efectiva**
- y dejar la capa lista para extender a per-club config

O sea:
- arquitectura multi-club en mente
- rollout inicial simple

Porque si hoy te vas directo a full multi-club bien hecho, se te mezcla:
- TCN adapter
- webhook
- onboarding MP
- secretos por club
- POS por club
- soporte operativo

Demasiado junto.

## Respuesta corta
Sí cambia bastante la ecuación:
- de config global pasás a config por club
- webhook sigue pudiendo llegar a tu endpoint
- pero cada club tendría que configurar su cuenta para apuntarte ahí
- Ballbox tendría que elegir credenciales correctas por club para cada pago

Si querés, siguiente te hago un doc/plan corto de arquitectura:
- `single MP account`
vs
- `one MP account per club`
con pros, contras y qué conviene para esta etapa.


unica por club
y si habria que planear social y tecnicamente como es el onboarding ahora que es mas complejo. es buen momento para unir con el setup de maquinas inicial y hacer un master plan (grill me por esto)
lamentablemente el rollout inicial es a 5 clubes, 1 maquina cada uno. Si es muy complejo se puede cobrar con la cuenta de ballbox pero habria que cambiarlo lo antes posible
haceme preguntas exploratorias para encontrar el mejor camino. cuando este todo armamos rfc con eso. split payments queda

info extra, charlando con los socios (invalida algunas cosas que dije recien)
sebas
che consulta tema facturacion de maquinas. es:
1. split payments: ballbox cobra -> saca comision como marketplace -> el resto cobra el club
2. club cobra -> arreglamos como nos paga nuestra parte a fin de mes
3. ballbox cobra -> pagamos su parte a fin de mes
?

por lo que entiendo opcion 1 lleva un proceso de MP para validarte como marketplace/intermediario y ademas igual facturas la totalidad del pago.
Tambien como el que compra las pelotas es el club me dijo un amigo algo asi como que venderlas nosotros llevaria a una doble facturacion (nuestra y del club). Asi que opciones 1 y 3 tendrian ese quilombo

El que sepa mas corrijame

seba vekselman
Vamos con opcion 1
Tengo linea directa con MP para que nos validen eso

sebas
okeyy. osea
- tenemos contacto para validar rapido marketplace en MP
- no es doble facturación, es cuenta y orden. Y eso no aplica a la opcion 1
(repito como loro porque despues quiero buscar y no encuentro nada jaja)
y quien factura en opc 1? el club? (decime que si 🙏)

seba vekselman
Nosotros lo nuestro y ellos lo de ellos

sebas
okeyy
bien
mucho mejor
y que onda con una cuenta personal nuestra siendo “Ballbox”?
1. hace falta declarar algo?
2. me explota el monotributo si dejo la mia?
3. conviene hacer una SRL/otra y hacerle cuenta?
“estas exagerando” es respuesta valida

seba vekselman
Estas exagerando
Jaj
Dejamos un MT
Hasta que tenga sentido cambiarlo

sebas
jaja la mejor respuesta
que es MT?

ilo
Nosotros facturamos la venta de 3 maquinas. Y despues nosotros le rendimos al club.
que es MT?
Monotributo
Pasa que es sobre el margen neto hay que ver como termina cobrando. Para mi si el predio tiene el control de cobro cagamos.
Mejor nosotros tenerlo y poder decirle al predio nos dejo esto te damos esto

sebas
okey para las 3 puestas:
1. cobrar todo nosotros y arreglar a fin de mes con los clubes su parte
2. idealmente el modelo marketplace asi se hace solo y no facturamos su parte (esto Ilo se puede igual o nos rompe el arreglo pagarles porcentualmente? Entiendo que la promesa era mas "vas a facturar al menos lo mismo que antes", osea darles un fijo)

para alquiler (las de adidas) sí necesitamos configurar sus cuentas de MP
`Mejor nosotros tenerlo y poder decirle al predio nos dejo esto te damos esto`
no había leido, ya me respondiste


**Structuring exploratory questions**

I'm thinking we should focus on asking exploratory questions in a more direct and concise way. It might help to synthesize any contradictions and clearly outline the options. I don’t think we need tools right now; we can always update our plan later. The user's asking these questions to figure out the best path, so I aim to respond by framing grouped questions for clarity!

Sí. Acá cambió fuerte la cosa.

Nueva lectura
- objetivo deseado: **opción 1 / split payments**
- contacto con MP para validarlo rápido
- pero rollout inmediato: **5 clubes, 1 máquina cada uno**
- fallback posible: **Ballbox cobra todo temporalmente**
- además hay dos negocios mezclados:
  1. máquinas propias / revenue share con club
  2. alquiler tipo Adidas donde **sí** parece más natural cuenta por club

Mi lectura brutal:
- no conviene decidir arquitectura sin separar esos casos
- y no conviene asumir “una cuenta MP por club” como primera implementación si igual quieren empujar **marketplace/split**
- primero hay que cerrar **modelo comercial-operativo exacto**
- después recién arquitectura

Las preguntas que importan:

## 1. Modelo comercial real
1. Para las **5 máquinas iniciales**, ¿el acuerdo real con cada club es:
   - `% sobre ventas`
   - `mínimo garantizado`
   - `fijo mensual`
   - mezcla?

2. En esas 5 máquinas, ¿quieren que el club:
   - reciba su parte **por cada venta automáticamente**
   - o alcanza con **liquidación mensual/manual** al inicio?

3. ¿Hay un compromiso ya vendido a clubes de que:
   - “van a cobrar directo en su cuenta”
   - o eso todavía no fue prometido?

4. ¿Lo importante para el club es:
   - control del dinero
   - facturación prolija
   - confianza/transparencia
   - velocidad de implementación?

5. ¿Para ustedes qué duele más si elijan mal:
   - tardar rollout
   - más carga operativa mensual
   - riesgo fiscal/contable
   - dependencia de aprobación de MP?

## 2. Split payments / marketplace
6. Cuando dicen “vamos con opción 1”, ¿eso significa:
   - **decisión firme**
   - o **ideal target** sujeto a que MP lo apruebe rápido?

7. ¿Ya tienen con MP confirmación de:
   - producto exacto
   - requisitos
   - plazo
   - documentación societaria/fiscal necesaria?
   O solo “tenemos contacto”.

8. ¿Ese split sería:
   - dinero dividido en el momento del cobro
   - o Ballbox cobra y MP liquida partes después?

9. ¿En el modelo marketplace, quién sería el merchant visible para el comprador:
   - Ballbox
   - club
   - depende del caso?

10. ¿La promesa a los clubes es porcentaje variable o puede ser un monto fijo aunque la arquitectura interna sea split?

## 3. Fallback temporal
11. Si MP marketplace tarda 1-2 meses, ¿aceptan lanzar así?
   - **Plan A**: Ballbox cobra todo y liquida manualmente
   - **Plan B**: no lanzar hasta tener split
   - **Plan C**: lanzar solo algunos clubes

12. Si Ballbox cobra todo temporalmente, ¿están cómodos con:
   - conciliación manual mensual
   - transferencias manuales a clubes
   - soporte ante reclamos de “cuánto vendió mi máquina”?

13. ¿Tienen a alguien que pueda operar esa liquidación manual 5 clubes / todos los meses?

14. ¿Qué tan rápido “habría que cambiarlo lo antes posible”?
   - 2 semanas
   - 1 mes
   - 1 trimestre

## 4. Cuenta por club
15. Cuando decís “única por club”, ¿querés decir:
   - una cuenta MP distinta por club
   - desde el día 1
   - incluso si no hay split marketplace todavía?

16. Si fuera una cuenta por club sin marketplace, ¿quién les paga a ustedes?
   - transferencia mensual
   - débito automático
   - factura mensual
   - retención automática imposible hoy

17. ¿Confían en que los clubes paguen su parte a tiempo si el dinero entra primero a ellos?

18. ¿Les sirve una etapa donde:
   - máquinas propias: cobra Ballbox
   - alquiler / terceros: cobra club
   - o quieren un solo modelo para todo?

19. ¿Hay clubes que ya dijeron explícitamente “quiero cobrar yo en mi cuenta”?

## 5. Setup social / onboarding
20. ¿Quién hace onboarding de cada club?
   - ustedes
   - una sola persona interna
   - ventas + ops
   - tercero

21. ¿Qué tan asistido puede ser?
   - ustedes hacen call y comparten pantalla
   - mandan instructivo
   - mandan formulario y después revisan

22. ¿Qué datos/config de club están dispuestos a pedir al inicio?
   - razón social
   - CUIT
   - cuenta MP
   - webhook config
   - sucursal/POS
   - cuenta bancaria
   - responsable operativo
   - datos de facturación

23. ¿Quién en el club suele manejar MP?
   - dueño
   - admin
   - encargado del predio
   - contador

24. ¿Qué parte del onboarding creen que más se va a trabar?
   - acceso a MP
   - aprobación marketplace
   - cargar datos legales
   - instalar máquina
   - alinear liquidación/comisiones
   - soporte post-lanzamiento

25. ¿Quieren que el onboarding de club y de máquina sean:
   - un solo proceso maestro
   - o dos procesos: comercial/fiscal y técnico/instalación?

## 6. Operación de máquinas
26. ¿Cada máquina va a estar asociada a un solo club de forma estable?
   - si sí, simplifica mucho
   - si no, complica todo

27. ¿Puede haber más de una máquina por club pronto, o eso viene bastante después?

28. ¿La reposición de producto la hace siempre el club?
   Eso importa para stock, rendición y narrativa fiscal/comercial.

29. ¿El precio de producto lo define:
   - Ballbox
   - club
   - ambos
   - depende del contrato?

30. ¿Necesitan dashboard por club con:
   - ventas
   - liquidación
   - comisión Ballbox
   - stock
   desde el día 1?
   O puede venir después.

## 7. Riesgo y secuencia
31. ¿Qué prefieren optimizar primero?
   - lanzar sí o sí rápido
   - dejar base correcta para split marketplace
   - minimizar trabajo manual
   - minimizar riesgo contable/fiscal

32. Si hubiera que elegir, ¿qué aceptan como deuda temporal?
   - liquidación manual
   - una sola cuenta cobradora
   - sin portal para clubes
   - sin onboarding self-serve

33. ¿Qué sería un fracaso peor?
   - no lanzar por esperar arquitectura ideal
   - lanzar y tener quilombo contable/operativo con clubes

## 8. Preguntas de decisión duras
34. ¿Querés diseñar el sistema para el modelo ideal final, o para sobrevivir bien los primeros 5 clubes?
35. ¿Es indispensable que el diseño inicial ya soporte migración limpia a marketplace/split sin rehacer pagos?
36. ¿Querés separar desde ya estos tracks?
   - **cobro y liquidación**
   - **integración TCN**
   - **onboarding club**
   - **instalación máquina**

## Mi sospecha actual
Sin responder esto, la mejor arquitectura no aparece.

Pero mi sospecha:
- **TCN adapter** y **Mercado Pago provider layer** hay que hacerlos sí o sí
- el gran nodo no es técnico puro, es:
  - **quién cobra**
  - **cómo se liquida**
  - **qué se promete a clubes**
  - **qué tarda realmente MP marketplace**

Mi hipótesis de camino más sano, a validar:
1. diseñar **master onboarding plan** club + máquina + pagos
2. separar 3 modos de cobro:
   - `central_ballbox`
   - `club_direct`
   - `marketplace_split`
3. lanzar primeros 5 con el modo que minimice riesgo real
4. dejar arquitectura lista para migrar a split sin romper TCN

Si querés, respondeme estas preguntas en bloque, aunque sea telegráfico, y con eso te armo:
- diagnóstico
- recomendación de camino
- y recién ahí el RFC/master plan.


fijate los ultimos commits de hoy. un poco se decidio esto en paralelo. preguntame dudas si te quedan despues de leer eso


**Inspecting git log**

I need to take a look at the git log today. It’s crucial to review the commits, changes, and maybe spot any important updates that have been made. I wonder if there are specific entries I should pay extra attention to. I'll also think about how this impacts our current project and whether we need to strategize based on the recent changes. So, let’s get started and dive into the log!

todo

Review today's commits for payments/onboarding decisions

Inspect today's git commits to extract decisions made in parallel about payment model, TCN integration, and onboarding, then ask only remaining clarifying questions.


Created #3: Review today's commits for payments/onboarding decisions (pending)


bash

git log --since='today 00:00' --oneline --decorate --graph --all --max-count=40


* 6add5bd (HEAD -> main) docs: align payments flow and remove webhook signature helper
* d5c5ec7 docs: settle club revenue share strategy


(no output)


**Inspecting commits**

I need to look closely at the commits, which might mean checking the files and their contents. I'm considering how to approach it, maybe by getting a list of the changes made. It's essential to understand the details in order to provide a clear overview. So, let me think about how best to display that information for easier understanding. Getting the content right is important!


commit d5c5ec79013e0a8cb88be5f3651c064d023bd96e
Author: Sebastian Favaron <sebastianfavaron@gmail.com>
Date:   Fri Apr 24 12:34:04 2026 -0300

    docs: settle club revenue share strategy

 PLAN.md                                            |  33 +++++-
 docs/implemented-payments.md                       |   6 +-
 docs/index.md                                      |   4 +
 .../mercadopago-webhook-replay-notes-2026-04-23.md |  29 ++++++
 docs/next-steps-queue.md                           |  35 +++++--
 docs/payments-club-settlement-strategy.md          | 115 +++++++++++++++++++++
 docs/tcn-payments-execution-plan.md                |  72 +++++++++++++
 docs/tcn-payments-integration-plan.md              |  68 ++++++++++++
 docs/tcn-payments-open-questions.md                |  38 +++++++
 9 files changed, 389 insertions(+), 11 deletions(-)
 create mode 100644 docs/mercadopago-webhook-replay-notes-2026-04-23.md
 create mode 100644 docs/payments-club-settlement-strategy.md
 create mode 100644 docs/tcn-payments-execution-plan.md
 create mode 100644 docs/tcn-payments-integration-plan.md
 create mode 100644 docs/tcn-payments-open-questions.md


commit 6add5bd079ce6dcbce5c20175240feba9b859e40
Author: Sebastian Favaron <sebastianfavaron@gmail.com>
Date:   Fri Apr 24 14:59:13 2026 -0300

    docs: align payments flow and remove webhook signature helper

 PLAN.md                                   |  6 +--
 docs/implemented-payments.md              |  5 ++-
 docs/next-steps-queue.md                  | 10 ++---
 docs/payments-club-settlement-strategy.md | 44 ++----------------
 docs/tcn-payments-integration-plan.md     |  2 +-
 lib/payments-mercadopago.ts               | 75 -------------------------------
 tests/unit/payments/mercadopago.test.ts   | 42 +----------------
 7 files changed, 17 insertions(+), 167 deletions(-)


# Club settlement strategy

## Decision
- Use scheduled settlements and manual bookkeeping for now.
- Do not force Mercado Pago split payments into the current vending QR flow.
- Treat Mercado Pago native split as a future option, not the default operating model.

## Why this is the default
- Current Ballbox payments flow is QR/order-based at `/v1/orders`.
- Mercado Pago split docs point to marketplace / checkout flows, OAuth, and seller onboarding.
- That is a poor fit for a small pilot set of clubs and adds operational drag before the machine flow is stable.
- For the current stage, speed and correctness matter more than perfect payout automation.

## Current operating model
- Each vending machine belongs to one club/venue.
- Ballbox records gross sales as it already does.
- Club share is tracked off-platform for now.
- Payouts are settled on a fixed cadence:
  - weekly if volume is active
  - monthly if volume is low
- For the current pilot phase, manual bookkeeping is acceptable because we expect at most 5 clients.

## Minimal bookkeeping fields
Keep one settlement record per club per period.

Recommended fields:
- clubId
- venueId
- vendingMachineId
- periodStart
- periodEnd
- grossSalesMinor
- refundsMinor
- adjustmentsMinor
- clubShareBps
- clubShareMinor
- payoutMethod
- payoutReference
- paidAt
- notes
- approvedBy

Recommended rules:
- store percentages as basis points, not floats
- round once, at settlement time
- keep refunds and adjustments explicit
- never let manual edits overwrite raw sales totals

## Future option if we later automate

### Mercado Pago native split
Best if Ballbox later changes the buyer flow to a supported Mercado Pago checkout path.

Pros:
- native platform split
- less manual payout work
- one provider for collection and distribution

Cons:
- current QR vending flow does not map cleanly
- likely needs OAuth / seller onboarding
- adds commercial-team dependency
- more support burden around account linking, disputes, and credential rotation

Fit:
- only if we replatform the buyer payment flow to something MP split supports

## Decision triggers to revisit automation
Re-open automation when one or more is true:
- more than 5 clubs need payouts
- manual settlement becomes error-prone
- payout timing becomes a sales blocker
- club trust requires automated reconciliation
- a future buyer flow changes to a checkout model MP split actually supports

## Recommendation
- Keep manual scheduled settlements now.
- Build a clean settlement ledger and export path first.
- Revisit Mercado Pago native split only after machine payments are stable and the club count justifies it.


# TCN payments execution plan

## Purpose
Ordered execution plan for getting from current Ballbox native Mercado Pago support to real TCN machine payments.

## Step 1. Finish provider-layer proof
- Goal: prove Ballbox native Mercado Pago flow works end to end without TCN hardware.
- Inputs:
  - `docs/implemented-payments.md`
  - `docs/mercadopago-webhook-replay-notes-2026-04-23.md`
- Tasks:
  - validate public webhook reachability for `/notification/mercadopago`
  - run one real approved payment
  - confirm local `PaymentSession` persistence in `/admin/payments`
  - document exact payload and public URL evidence
- Exit criterion:
  - one real approved payment persists locally through webhook flow

## Step 2. Ask TCN the machine-contract questions
- Goal: close the remaining machine-side unknowns before coding the adapter.
- Inputs:
  - `docs/tcn-payments-integration-plan.md`
  - `docs/tcn-payments-open-questions.md`
  - `test-results/tcn-payment-protocol/findings-2026-04-23.md`
- Tasks:
  - send the question list to the TCN sales contact
  - ask them to confirm with engineers
  - capture answers in repo docs
- Exit criterion:
  - `CodeUrl` meaning and payment-success handshake are confirmed

## Step 3. Freeze Ballbox-TCN contract
- Goal: convert answers into a concrete adapter spec.
- Inputs:
  - `docs/tcn-payments-integration-plan.md`
  - answers captured from Step 2
- Tasks:
  - define request/response schema for TCN `8000`
  - define success/failure callback or polling behavior
  - define duplicate/idempotency behavior for repeated `TradeNo`
  - define machine-id mapping rules
- Exit criterion:
  - implementation spec is precise enough to code without guesswork

## Step 4. Implement adapter layer
- Goal: add the TCN-facing Ballbox endpoints over existing Mercado Pago primitives.
- Inputs:
  - concrete contract spec from Step 3
- Tasks:
  - add machine-facing endpoint(s)
  - map TCN requests to Ballbox/Mercado Pago order creation
  - map approved-payment state back into TCN success flow
  - persist any extra machine correlation fields needed
- Exit criterion:
  - local/manual adapter flow works without real machine hardware

## Step 5. Hardware validation when machines arrive
- Goal: verify actual stock TCN machine behavior.
- Tasks:
  - test real QR render on machine screen
  - test payment approval -> vend path
  - capture timing, retries, and failure cases
- Exit criterion:
  - one real vend succeeds through Ballbox-backed payment flow

## Step 6. Only then revisit payout automation
- Goal: avoid solving club payout distribution before the machine path is stable.
- Current default:
  - scheduled manual settlements / bookkeeping
- Related docs:
  - `docs/payments-club-settlement-strategy.md`
  - `PAY-4` in `docs/next-steps-queue.md`


# Next Steps Queue

## Purpose
- Keep `PLAN.md` actionable without turning it into a long unordered backlog.
- Let any agent pick the next best task quickly using priority + dependency.

## Picking Rule
- Pick the highest-priority task with `Status: ready`.
- If a task is `blocked`, unblock its dependency first.
- After shipping, update this file, `PLAN.md`, and relevant `docs/implemented-*.md` in the same work.

## Recently Completed (2026-04-14)

### DOC-1 Docs normalization baseline
- Priority: P0
- Status: completed
- Depends on: none
- Scope shipped:
  - docs navigation/categories normalized in `docs/index.md`
  - `docs/repo-map.md` maintained as a fast entrypoint map
  - stale/missing references removed from active docs surfaces (`README.md`, `PLAN.md`, `docs/index.md`)
- Evidence: `node scripts/check-doc-references.mjs` passes

### DOC-2 PLAN execution hygiene
- Priority: P0
- Status: completed
- Depends on: none
- Scope shipped:
  - `PLAN.md` next-steps section reduced to queue pointer + explicit pick order
  - active queue wording avoids mixing done narrative
  - handoff wording keeps one clear "pick this next" target
- Expected payoff: next-agent selection is immediate

### DOC-3 Docs drift CI guard
- Priority: P0
- Status: completed
- Depends on: none
- Scope shipped:
  - CI check validates docs references in `README.md`, `PLAN.md`, and `docs/index.md`
  - CI fails on broken/missing local doc references
- Evidence: `.github/workflows/docs-drift-guard.yml`, `scripts/check-doc-references.mjs`

### ARCH-1 Route helper boundary cleanup
- Priority: P1
- Status: completed
- Depends on: DOC-1 (done)
- Scope shipped:
  - route helper/envelope parsing moved to shared `lib/http/*`
  - deep relative helper imports removed from network, players, sports, and payments route handlers
- Evidence: route handlers now import `@/lib/http/*-route-helpers`

### OPS-0 Admin surface 500 fix (`/admin/tvs`, `/admin/network`, `/admin/sports`, `/admin/players`)
- Priority: P0
- Status: completed
- Depends on: none
- Scope shipped:
  - aligned Prisma `Club.atcSportclubId` with live nullable data to prevent runtime `P2032` decode failures
  - regenerated Prisma client after schema alignment
  - added `db:backfill-atc-clubs` to sync local club names with ATC summary by `atcSportclubId` while keeping null-ATC clubs supported
- Evidence: Prisma `club.findMany` now handles rows with `atcSportclubId = null`

## P0 Ready

### OPS-1 Payments real-world validation closure
- Priority: P0
- Status: ready
- Depends on: none
- Scope:
  - run one public webhook smoke against the native MercadoPago webhook path
  - run one real payment and verify persisted approved payment in `/admin/payments`
  - document observed payload + public URL evidence
- Progress (2026-04-14): payments migration to host app started in one feature branch; health/sessions now expose internal MercadoPago mode, Ballbox ships `/notification/mercadopago` intake with order fetch verification, and native order creation now exists at `/api/integrations/payments/orders`.
- Progress (2026-04-19): PAY-2 cleanup removed bridge-oriented operational status from host-app health/docs; OPS-1 is now focused on validating the native MercadoPago order-create + webhook-persist path only.
- Progress (2026-04-23): TCN protocol review shows the `additional hardware devices` note is about machine-side QR scanning, while the machine payment flow (`FunCode=8000`) already returns `CodeUrl` for machine-screen QR display. This means the admin QR page should be treated as a provider test harness, and production integration should move to a TCN adapter layer.
- Expected payoff: closes highest-risk integration uncertainty

### OPS-2 TVs runtime parity closure
- Priority: P0
- Status: ready
- Depends on: none
- Scope:
  - manually verify real-device CacheStorage/object-URL behavior on `/player/[screenId]`
  - update `docs/streetcast-parity.md` with concrete evidence
- Progress (2026-04-14): runtime now exports a JSON parity snapshot from `/player/[screenId]` to standardize evidence capture; remaining work is one real-device run and doc evidence entry.
- Progress (2026-04-19): machine-local Playwright setup is now standardized outside the repo at `/mnt/rpi/repos/browser-harness`; repo Playwright now uses a stable production-style e2e server path on this Pi, and stock TV seed data now keeps `screen_001` actively programmed after `pnpm db:seed`.
- Remaining closure needs:
  - run `pnpm db:seed`, then rerun local parity/e2e on `screen_001`
  - run the documented real-device parity capture (online snapshot, offline snapshot, reconnect recovery)
  - then update this queue item and `PLAN.md` to completed
- Expected payoff: de-risks constrained-device production behavior

## P1 After P0

### ARCH-2 Continue `ballbox-store` caller migration
- Priority: P1
- Status: ready
- Depends on: ARCH-1 (done)
- Scope:
  - migrate callers to direct domain modules where clearer
  - keep `lib/ballbox-store.ts` as minimal compatibility facade only
- Progress (2026-04-14): sports route/page callers and sports UI/test types now import direct sports backend modules; players route/page callers now import `lib/players-backend.ts` directly (plus `lib/network-backend-clubs.ts` for admin club list hydration); payments admin session routes now consume Prisma error helpers from `lib/prisma-errors.ts`; and network route/page callers now import direct network backend modules (`clubs`, `club-mutations`, `venues`, `vending-machines`) plus `lib/prisma-errors.ts`. Remaining scope continues with the same migration pattern for TVs and classes surfaces still reading via `lib/ballbox-store.ts`.
- Expected payoff: less coupling and better module discoverability

### ARCH-3 Test taxonomy split
- Priority: P1
- Status: ready
- Depends on: DOC-1 (done)
- Scope:
  - separate tests by intent: `contracts`, `unit`, `integration`
  - align `vitest.config.ts` includes with those layers
- Progress (2026-04-14): `vitest` include patterns now cover `tests/contracts`, `tests/unit`, and `tests/integration`; dedicated scripts now exist for each layer, and first unit moves landed (`network/route-helpers`, sports client config/state tests).
- Expected payoff: faster debugging and more intentional coverage growth

### PERF-1 TTFB focused pass
- Priority: P1
- Status: ready
- Depends on: OPS-1
- Scope:
  - measure `/classes` and `/admin` server timings
  - add only the cache/instrumentation changes justified by measurement
- Expected payoff: better responsiveness without blind cache complexity

### PAY-2 Full payments migration into Ballbox host app
- Priority: P1
- Status: completed
- Depends on: OPS-1
- Scope shipped:
  - native MercadoPago webhook + QR order lifecycle is now owned in Ballbox routes/lib
  - bridge-oriented operational status copy removed from health/docs/admin summary
  - legacy approved-callback route reduced to transitional compatibility only; native path is now the documented operational flow
- Evidence:
  - `/notification/mercadopago`
  - `/api/integrations/payments/orders`
  - `app/api/integrations/payments/health/route.ts`
  - `app/api/integrations/payments/sessions/route.ts`
  - `docs/implemented-payments.md`
- Expected payoff: one-repo payments ownership, simpler operations, faster debugging

### PAY-3 TCN vending-machine adapter over native MercadoPago
- Priority: P1
- Status: ready
- Depends on: OPS-1, PAY-2
- Scope:
  - define the Ballbox machine-facing adapter for TCN payment init (`FunCode=8000`) over the existing MercadoPago order-create route
  - map TCN `MachineID`, `TradeNo`, `SlotNo`, `Price`, `NotityUrl`, and response `CodeUrl` to Ballbox-native payment fields
  - define the payment-success handshake back to TCN so approved payments can unlock dispense
  - keep `/admin/payments` positioned as an operator test/debug harness rather than the production vending UX
  - document the exact adapter contract and remaining TCN unknowns before implementation
- Notes:
  - TCN protocol evidence is documented in `docs/tcn-payments-integration-plan.md`
  - do not assume extra scanner hardware for the buyer flow; the open question is adapter contract detail, not QR display feasibility
  - club settlement automation can be revisited after the machine-facing flow is stable; manual bookkeeping is the current default
- Expected payoff: closes the actual machine integration path instead of overinvesting in the standalone admin QR surface

## P2 Sequenced Refactors

### PAY-4 Mercado Pago native split for club revenue share
- Priority: P2
- Status: blocked
- Depends on: OPS-1, PAY-2, PAY-3
- Scope:
  - keep scheduled manual settlements as the current operating default for club revenue share
  - document Mercado Pago native split as the only future automation path under consideration for club payouts
  - if automation is ever chosen, define onboarding, credential storage, rotation, and support rules before implementation
  - capture the trigger conditions that justify leaving manual bookkeeping behind
- Notes:
  - Ballbox currently runs QR vending payments through the native Mercado Pago order/webhook flow, so split automation is a separate future decision
  - keep this behind real-world payments validation first (`OPS-1`) and machine-facing TCN adapter definition (`PAY-3`)
- Expected payoff: a clear path from manual settlements to a future Mercado Pago split if the machine flow and business volume justify it

### ARCH-4 Domain folder architecture
- Priority: P2
- Status: blocked
- Depends on: ARCH-2, ARCH-3
- Scope:
  - move flat prefix-based modules into domain folders in `lib/` and `components/`
  - preserve behavior; focus on discoverability and ownership boundaries
- Expected payoff: major readability improvement for future work

### ARCH-5 Route IA normalization (`/classes*` + `/coaches*`)
- Priority: P2
- Status: blocked
- Depends on: DOC-1
- Scope:
  - align route grouping and naming with real auth/surface behavior
  - keep redirects/aliases explicit during transition
- Expected payoff: lower confusion in feature placement and routing maintenance

### ARCH-6 Naming conventions and metadata cleanup
- Priority: P2
- Status: blocked
- Depends on: ARCH-4
- Scope:
  - codify canonical naming terms and singular/plural rules
  - align package metadata with repo identity
- Expected payoff: fewer naming mismatches and clearer ownership vocabulary


- `/coaches/settings`: coach dashboard with `Calendar` / `Analytics`, plus `Mi Perfil` and `Alumnos`
- `/clubs`: club service home
- `/clubs/coaches`: club workspace with `Calendar` / `Analytics` / `Sedes` / `Profesores`
- authenticated coach/club workspace routes now use a shared top navbar with section tabs and an avatar-triggered sign-out menu instead of top-corner text links or standalone explainer hero panels
- coach workspace now also includes student follow-up and manually occupied-hour management using existing `Player` records, including coach-editable WhatsApp and Telegram contact data
- `/clubs/venues/[venueId]`: dedicated venue setup and venue roster

## Backend Split
- Ballbox Next.js host:
  - app shell
  - admin UI
  - shared auth/RBAC foundation
  - low-complexity CRUD
  - BFF aggregation and integration routes
  - shared `network` core entities
  - internal TVs module and runtime
- External specialized services:
  - none required for current native MercadoPago payments flow in Ballbox host app

## Implemented
- Shared admin shell and product-separated routes are in place.
- Admin shell copy now adapts per route instead of repeating one generic hero across every module.
- TVs now resolve admin/runtime state from a canonical internal Ballbox model with explicit club, venue, screen, and campaign ownership.
- TVs runtime now has a per-screen route and manifest-driven player UI.
- TVs now also expose a lightweight non-React runtime at `/player/[screenId]` for constrained devices.
- Prisma/Postgres foundation now exists in Ballbox for the durable replacement.
- Ballbox Postgres has the TVs schema pushed and seeded with current local fixture data.
- Network APIs now read/write Ballbox Postgres through Prisma.
- TVs source/manifest/impression/heartbeat paths already read/write against Ballbox Postgres.
- TVs admin now exposes direct runtime and manifest links per screen for operations.
- TVs errors now use a shared strict shape and the contract is validated by `pnpm test:contracts`.
- Payments integration health and sessions routes now report Ballbox-internal native MercadoPago status plus webhook/order metadata.
- Payments native MercadoPago webhook flow now lands in Ballbox, validates canonical payment correlation from fetched orders, and persists approved payments as `PaymentSession` records.
- QR order creation now sets a public `notification_url` so Mercado Pago routes order updates back to Ballbox explicitly.
- Unsupported merchant-order notification topics are ignored so only QR order events drive persistence.
- Replay testing with two older live `ORD...` ids returned `MERCADOPAGO_ORDER_NOT_APPROVED_expired`; see `docs/mercadopago-webhook-replay-notes-2026-04-23.md` for the current blocker and next resume point.
- Payments now have an admin CRUD/reconciliation surface over local `PaymentSession` rows via `/api/admin/payments/*`.
- Final target model is documented in `docs/domain-model-target.md`; end state does not keep retro-compatibility shims.
- ESLint is now wired and the repo exposes an explicit `typecheck` script.
- `/admin` and current admin-only APIs can now be gated by a shared password cookie session.
- `/admin/tvs` now performs admin CRUD for screens and tv devices through admin-only APIs under `/api/admin/tvs/*`.
- `/admin/tvs` now performs admin CRUD for campaigns, creatives, and assignments through admin-only APIs under `/api/admin/tvs/*`.
- TVs impression writes now validate creative-to-screen activity windows before persisting telemetry.
- Players now have DB-backed read/admin APIs for `Player` and `Membership`.
- Players route + page callers now import `lib/players-backend.ts` (and `lib/network-backend-clubs.ts` for club lists) directly instead of relying on `lib/ballbox-store.ts` for runtime access.
- Sports now have DB-backed read/admin APIs for `Coach`, `OpenMatch`, and `Class`.
- Classes MVP now also has DB-backed public APIs for `ClassOffer` and `ClassRequest`.
- `/classes/me` now provides a lightweight student request-history surface over Ballbox-local `ClassRequest` records (read-only, demo-grade email lookup).
- `ClassRequest` now snapshots the student's selected visible slot when they choose one, and otherwise persists an explicit flexible-selection fallback mode.
- Coach availability now ships as weekly ranges with 90-minute slots, filtered by coach club selection and ATC club availability.
- Coach and club account entry now starts at `/coaches`, with `Sign in` first and `Sign up` only for users without an account.
- Coaches and clubs now sign in with per-account email + password credentials, and `/api/account/session` routes each session to the correct dashboard.
- Coach sign-up now asks only for name, email, and password; if a club invited that coach, the invite link auto-links the account to that club.
- `/coaches/settings` now opens on a dashboard split into `Calendar` and `Analytics`, then lets coaches move into configuration from the shared navbar to choose external clubs, edit weekly availability only inside allowed venue/club windows, configure reusable class packages, and save a Mercado Pago placeholder handle.
- `/coaches/settings` now also lets coaches attach existing Ballbox players to concrete booked slots in their active venues, so taken hours, weekly earnings, consistency tracking, and lightweight student follow-up live inside the coach workspace.
- Club sign-in/sign-up now lands on `/clubs`, a service home that separates the club's operational modules before entering the detailed coaches workspace.
- Club accounts now have a dedicated workspace at `/clubs/coaches` split into `Calendar`, `Analytics`, `Sedes`, and `Profesores`, while venue-by-venue deep setup still lives at `/clubs/venues/[venueId]`, all under the same shared navbar/profile chrome.
- Venue setup now uses two simple fields for opening and closing time plus a collapsible weekly half-hour grid for coach hours, shows closed periods in red while editing coach availability, lets clubs mark whether external coaches are allowed, keeps coach-enabled courts in a dedicated selection panel, and includes reservation pricing/cancellation settings both in the venue detail and as quick settings on `/clubs/coaches`, including a discount that applies only to coaches assigned to that club.
- ATC lookups for class surfaces now resolve by club (no court coupling), and class request validation rechecks the selected slot date/time against ATC public-read.
- `/classes` SSR now avoids blocking first paint on ATC public-read fetches; the page paints local offer data first, then hydrates ATC availability from cached API responses.
- `/admin` overview SSR now uses direct aggregate counts instead of loading full admin datasets only to derive metrics.
- Sports/classes facade exports in `lib/ballbox-store.ts` now delegate to dedicated sports-events and classes backend modules instead of keeping that slice inline.
- Sports route + page callers now import `lib/sports-backend-events.ts` and `lib/sports-backend-coaches.ts` directly, so `lib/ballbox-store.ts` is no longer the primary sports entrypoint.
- TV runtime/backend facade exports in `lib/ballbox-store.ts` now delegate to `lib/tvs-backend-runtime.ts` instead of keeping source/manifest/heartbeat/impression logic inline.
- UI color/system tokens are split between landing (`app/styles/landing-tokens.css`) and admin (`app/styles/admin-tokens.css`).

## Streetcast Migration Check
- Source of truth for parity review: `/Users/sebas/Code/street-cast-pwa`.
- Covered in Ballbox today:
  - manifest polling exists in `components/screen-player.tsx`
  - sequential creative playback exists in `components/screen-player.tsx`
  - impression writes exist in `components/screen-player.tsx`
  - runtime health signaling exists via heartbeats in `components/screen-player.tsx`
- Missing or not yet verified against Streetcast behavior:
  - no manual real-device verification yet proves CacheStorage/object-URL playback outside local browser automation

## Next Steps (Priority + Dependency Queue)
- Source of truth for task backlog and dependencies: `docs/next-steps-queue.md`.
- Selection rule for the next agent: pick the highest-priority task marked `ready` in that doc.
- Current pick (`P0`, `ready`): `OPS-1` payments real-world validation closure.
- Immediately after `OPS-1`: `OPS-2` TVs runtime parity closure.
- Then continue with `P1` in dependency order: `ARCH-2` and `ARCH-3` -> `PERF-1` -> `PAY-3`.
- Club revenue share is currently handled with scheduled manual settlements; `PAY-4` stays behind the TCN machine-facing adapter work as a future Mercado Pago split option.
- Keep detailed backlog/status only in `docs/next-steps-queue.md` to avoid drift.

## Ordered Payments Track
1. Finish provider-level Mercado Pago proof without TCN hardware.
   - backlog: `OPS-1` in `docs/next-steps-queue.md`
   - docs: `docs/implemented-payments.md`, `docs/mercadopago-webhook-replay-notes-2026-04-23.md`
2. Ask TCN sales to confirm the remaining machine-contract questions with their engineers.
   - docs: `docs/tcn-payments-open-questions.md`, `docs/tcn-payments-integration-plan.md`
   - note: this is required because the machines are not here yet
3. Freeze the Ballbox <-> TCN adapter contract once those answers come back.
   - docs: `docs/tcn-payments-integration-plan.md`, `docs/tcn-payments-execution-plan.md`
4. Implement the TCN-facing adapter over the existing native Mercado Pago layer.
   - backlog: `PAY-3` in `docs/next-steps-queue.md`
   - docs: `docs/tcn-payments-execution-plan.md`
5. Validate on real TCN hardware once machines arrive.
   - docs: `docs/tcn-payments-execution-plan.md`
6. Revisit settlement automation only after the machine-facing payment flow is stable.
   - backlog: `PAY-4` in `docs/next-steps-queue.md`
   - doc: `docs/payments-club-settlement-strategy.md`

### Payments Track Docs
- Implemented provider layer: `docs/implemented-payments.md`
- TCN direction and target flow: `docs/tcn-payments-integration-plan.md`
- Open external questions: `docs/tcn-payments-open-questions.md`
- Ordered execution sequence: `docs/tcn-payments-execution-plan.md`
- Current Mercado Pago webhook blocker/evidence: `docs/mercadopago-webhook-replay-notes-2026-04-23.md`
- TCN protocol evidence artifact: `test-results/tcn-payment-protocol/findings-2026-04-23.md`

### Product Continuation Constraints
- Keep `sports` discovery honest: no claims beyond evidence in `docs/coaches-discovery-stage.md`.
- Next sports expansion after `/classes/me`: lightweight operator review for incoming requests.
- Expand ATC scope beyond public-read only after the request loop proves it is needed.

## Coach Discovery Docs
- Company-level strategic mirror for this track lives in `~/ballbox-company-agent/projects/coaches-os/`; source of truth for product-repo execution remains this `PLAN.md` plus local docs.
- Source of truth for current coach/padel/ATC discovery framing lives in `docs/coaches-discovery-stage.md`.
- Current form instrument is documented in `docs/coaches-form-current.md`.

[79 more lines in file. Use offset=240 to continue.]


**Clarifying decisions**

I need to ask the remaining clarifying questions based on the commits. It's essential to confirm that I've understood the decisions involved. Also, I should inspect the tcn-payments-integration-plan to see if it's been updated. This way, I can ensure I'm fully aware of any changes or new information that might affect our work. Keeping everything aligned is so important for the smooth progress of the project!