just got the manual from vending machine company TCN about how to integrate 3rd party payments. Recent developments on qr code generation on this page were meant mainly for this. however im left worried by some wording which says "QR code scanning requires additional hardware installation".

file is in laptop in ~/Downloads/Protocol 2.5V English version.pdf
copy here into a similar place where test-results are (which was an investigation on the software running on the vending machines), then read to see if we
1. can display a QR code inside the vending machine app
2. should generate a QR code and stick as sticker outside
3. should actually connect external hardware and what
4. are doomed


**Inspecting project setup**

I need to inspect the repository, focusing on the test-results directory. I think using my laptop helper via bash will be helpful. I wonder if reading skill memory is necessary; probably not. Searching the agents database might not be essential either. I need to copy files from the laptop to a similar location. Since this involves multiple steps, I might use a to-do list to keep track of everything. Let’s get started!

todo

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.


Created #1: Review TCN 3rd-party payments manual from laptop (pending)


bash

pwd && git status --short && find . -maxdepth 3 \( -iname '*test-results*' -o -path './test-results' -o -path './docs' \) | sed 's#^./##' | sort | head -200

laptop-run 'python3 - <<"PY"
from pathlib import Path
p=Path.home()/"Downloads"/"Protocol 2.5V English version.pdf"
print(p)
print(p.exists())
PY'


/mnt/rpi/repos/ballbox
 M PLAN.md
 M docs/implemented-payments.md
?? docs/mercadopago-webhook-replay-notes-2026-04-23.md
docs
test-results


/Users/sebas/Downloads/Protocol 2.5V English version.pdf
True


**Examining test results**

I need to check the structure of the test results. First, I'll copy the file. It might be a good idea to use pdftotext to convert it to a readable format. This way, I can inspect the content more easily. I'm curious about how the data is organized, and I want to ensure that everything is clear before proceeding. Let’s see how this goes!

find test-results -maxdepth 3 | sed 's#^#/#' | head -200

find docs -maxdepth 2 | sed 's#^#/#' | head -200


/docs
/docs/ballbox-db-plan.md
/docs/coaches-coordination-gap-analysis.md
/docs/implemented-network.md
/docs/atc-integration-discovery.md
/docs/repo-map.md
/docs/atc-access-request-checklist.md
/docs/atc-mock-integration-plan.md
/docs/coaches-solution-thesis.md
/docs/streetcast-parity.md
/docs/coaches-icp-segmentation.md
/docs/maintainability-priorities-2026-03.md
/docs/classes-mvp-rfc.md
/docs/coaches-discovery-execution-plan.md
/docs/demo-iteration-mode.md
/docs/coaches-interview-script.md
/docs/classes-implementation-notes.md
/docs/coaches-wedges-matrix.md
/docs/coach-availability-rfc.md
/docs/coaches-design-partner-outreach.md
/docs/next-steps-queue.md
/docs/classes-subroute-plan.md
/docs/implemented-players.md
/docs/index.md
/docs/mercadopago-webhook-replay-notes-2026-04-23.md
/docs/implemented-host-app.md
/docs/implemented-sports.md
/docs/coaches-opportunity-brief.md
/docs/coaches-form-current.md
/docs/implemented-tvs.md
/docs/coaches-validation-checklist-2-weeks.md
/docs/screen-runtime-delivery-investigation.md
/docs/coaches-research-repository.md
/docs/implemented-payments.md
/docs/atc-integration-spec-skeleton.md
/docs/coaches-jtbd-workflow-map.md
/docs/coaches-discovery-stage.md
/docs/domain-model-target.md


**Looking for investigation docs**

I need to find a place similar to where we investigated test results, perhaps in the document on screen runtime delivery. Maybe I should look through relevant docs and also consider mentioning vending machines if they apply. It feels like a good idea to inspect those relevant documents carefully to see if there's any useful information that can help steer this investigation. I'm ready to dive in and search!

rg -n "vending|ourvend|TCN|qr|payment|test-results" docs PLAN.md test-results -S


PLAN.md:14:- Product-separated admin areas exist for `tvs`, `payments`, `network`, `sports`, and `players`.
PLAN.md:30:- 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.
PLAN.md:31:- `/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.
PLAN.md:32:- Legacy approved-callback compatibility has been removed from operations; `/api/integrations/payments/approved` now returns `410` so only the native MercadoPago path remains active.
PLAN.md:33:- `/admin/payments` now exposes Ballbox-local CRUD/reconciliation for approved payment sessions plus correlation checks against canonical `payment-vm_{vendingMachineId}-txn_{machineTxnId}` references.
PLAN.md:35:- Network core now has DB-backed admin CRUD for ATC-linked clubs, venues, and vending machines inside Ballbox.
PLAN.md:41:- Network vending-machine route callers now import `lib/network-backend-vending-machines.ts` directly and read unique-constraint classification from `lib/prisma-errors.ts`.
PLAN.md:43:- 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.
PLAN.md:44:- 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.
PLAN.md:54:- 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.
PLAN.md:108:- `/admin/payments`: payment operations and reconciliation
PLAN.md:109:- `/admin/network`: clubs, venues, vending machines
PLAN.md:135:  - none required for current native MercadoPago payments flow in Ballbox host app
PLAN.md:150:- Payments native MercadoPago webhook flow now lands in Ballbox, validates canonical payment correlation from fetched orders, and persists approved payments as `PaymentSession` records.
PLAN.md:154:- Payments now have an admin CRUD/reconciliation surface over local `PaymentSession` rows via `/api/admin/payments/*`.
PLAN.md:197:- Current pick (`P0`, `ready`): `OPS-1` payments real-world validation closure.
PLAN.md:245:- `/admin/tvs`, `/admin/payments`, `/admin/network`, `/admin/sports`, `/admin/players`, and `/admin/screens` render in Ballbox.
PLAN.md:251:- Ballbox persists approved payments locally through the native MercadoPago webhook flow at `/notification/mercadopago`.
PLAN.md:265:  - vendingMachines: 4
docs/domain-model-target.md:4:- Define the final Ballbox ownership model across `network`, `payments`, `tvs`, `sports`, and `players`.
docs/domain-model-target.md:23:  - optional: `vendingMachineId`
docs/domain-model-target.md:35:  - required: `vendingMachineId`
docs/coach-availability-rfc.md:45:- Booking writes or payment flow.
docs/coaches-wedges-matrix.md:15:| Collections/admin follow-up | Chasing payments and reminders consumes time | Time savings, maybe cashflow | Low | Medium | Medium | Could be too generic and weakly differentiated |
docs/coaches-wedges-matrix.md:35:- If a wedge requires broad CRM/payments/attendance/product surface from day one, narrow it or drop it.
docs/coaches-discovery-stage.md:11:- Host app, admin shell, network core, TVs runtime/admin, and payments callback persistence are already implemented and should stay documented as implemented state, not future plans.
docs/coaches-discovery-stage.md:24:- Whether the top pain is scheduling, reprogramming, filling empty slots, communication, payments, or something else.
docs/implemented-payments.md:6:- `/api/integrations/payments/orders` is the first-party QR order creation route.
docs/implemented-payments.md:7:- `/api/integrations/payments/sessions` lists Ballbox-local approved payment records plus native MercadoPago config status.
docs/implemented-payments.md:8:- `/admin/payments` now manages local approved payment sessions directly for reconciliation/correction and also generates live MercadoPago QR orders for arbitrary amounts or product-priced orders.
docs/implemented-payments.md:12:- `/api/admin/payments/operations`
docs/implemented-payments.md:13:- `/api/admin/payments/sessions`
docs/implemented-payments.md:14:- `/api/admin/payments/sessions/[sessionId]`
docs/implemented-payments.md:15:- `/api/integrations/payments/health`
docs/implemented-payments.md:16:- `/api/integrations/payments/sessions`
docs/implemented-payments.md:17:- `/api/integrations/payments/orders`
docs/implemented-payments.md:21:- Ballbox persists approved payment records as `PaymentSession` rows in Ballbox Postgres.
docs/implemented-payments.md:22:- Native MercadoPago webhook intake fetches the upstream order, maps approved orders into the canonical Ballbox payment shape, and persists them locally.
docs/implemented-payments.md:25:- Post-`pnpm db:push` plus `pnpm db:seed` verification confirms Ballbox Postgres keeps canonical `PaymentSession.vendingMachineId` and does not expose a `PaymentSessionRef` table or `machineId` compatibility column.
docs/implemented-payments.md:27:- Playwright now covers local `/admin/payments` create, edit, and delete flows with deterministic cleanup of E2E `PaymentSession` rows.
docs/implemented-payments.md:31:- Ballbox native QR order creation path is `/api/integrations/payments/orders`.
docs/implemented-payments.md:36:- Legacy approved-callback path `/api/integrations/payments/approved` is removed as an operational integration and now returns `410 PAYMENTS_APPROVED_CALLBACK_REMOVED`.
docs/implemented-payments.md:37:- Native payment correlation requires canonical `externalReference` values shaped as `payment-vm_{vendingMachineId}-txn_{machineTxnId}`.
docs/implemented-payments.md:42:- Remaining gap is operational/manual: one real public webhook + one real approved payment persisted and visible in `/admin/payments`.
docs/implemented-payments.md:47:- End-to-end payment validation now depends on Ballbox native MercadoPago webhook configuration, not just local admin CRUD.
docs/implemented-payments.md:54:  - one real payment is approved upstream
docs/implemented-payments.md:56:  - Ballbox fetches order details and records the approved payment locally
docs/implemented-payments.md:58:  - the session is visible in `/admin/payments`
docs/maintainability-priorities-2026-03.md:15:  - P0.1 fail-closed payments callback auth
docs/maintainability-priorities-2026-03.md:19:  - P1.6 first route-helper/envelope standardization pass across `network`, `players`, `sports`, and `payments`
docs/maintainability-priorities-2026-03.md:22:  - P0.3 remaining admin clients: `payments`, `tvs`
docs/maintainability-priorities-2026-03.md:33:### 1. Fail closed on payments callback auth
docs/maintainability-priorities-2026-03.md:36:- Risk: `app/api/integrations/payments/approved/route.ts` can accept writes in a misconfigured environment.
docs/maintainability-priorities-2026-03.md:39:  - `app/api/integrations/payments/approved/route.ts`
docs/maintainability-priorities-2026-03.md:42:  - It weakens the whole payments integration if env setup drifts.
docs/maintainability-priorities-2026-03.md:60:  - Split by domain: `network`, `tvs`, `payments`, `players`, `sports`, `classes`.
docs/maintainability-priorities-2026-03.md:80:  - `components/payments-admin-client.tsx`
docs/maintainability-priorities-2026-03.md:100:  - apply the same pattern to `payments-admin-client.tsx`
docs/maintainability-priorities-2026-03.md:129:  - `components/payments-admin-client.tsx`
docs/maintainability-priorities-2026-03.md:149:  - Inconsistent candidates: `app/api/admin/tvs/screens/[screenId]/route.ts`, `app/api/admin/tvs/tv-devices/route.ts`, `app/api/admin/payments/sessions/route.ts`
docs/maintainability-priorities-2026-03.md:158:- Follow-on slices shipped: players, sports, and payments route families now use the same helper pattern for JSON parsing plus strict success/error envelopes, including focused invalid-payload contract coverage.
docs/maintainability-priorities-2026-03.md:241:  - `app/api/integrations/payments/health/route.ts`
docs/maintainability-priorities-2026-03.md:265:### 14. Tighten domain lifecycle modeling in sports and payments
docs/maintainability-priorities-2026-03.md:269:  - `lib/payments.ts`
docs/maintainability-priorities-2026-03.md:284:1. Fix payments callback auth and add env validation.
docs/coaches-icp-segmentation.md:39:- Strong evidence that payment/collections pain dominates over scheduling pain.
docs/coaches-solution-thesis.md:36:- Not assuming the first win is payments
docs/implemented-host-app.md:13:  - `/admin/payments`
docs/implemented-host-app.md:22:- Ballbox owns native MercadoPago QR order creation at `/api/integrations/payments/orders` and native webhook intake at `/notification/mercadopago`.
docs/implemented-host-app.md:23:- Native payment persistence validates canonical `payment-vm_{vendingMachineId}-txn_{machineTxnId}` references before writing local approved sessions.
docs/implemented-host-app.md:24:- Ballbox exposes admin payments operations under `/api/admin/payments/*`.
docs/implemented-host-app.md:26:- Route JSON/envelope helper boundaries now live in shared `lib/http/*-route-helpers.ts` modules (network, payments, players, sports) instead of route-local helper files under `app/api`.
docs/implemented-host-app.md:39:- Network now has DB-backed admin CRUD for ATC-linked clubs, venues, and vending machines.
docs/implemented-host-app.md:50:- Browser e2e currently covers TVs offline runtime, active admin navigation, mobile smoke for `/admin/network` and `/admin/tvs`, network CRUD, payments CRUD/reconciliation smoke, players CRUD, and sports CRUD.
docs/mercadopago-webhook-replay-notes-2026-04-23.md:25:  - a real buyer-side payment to create a new approved order.
docs/mercadopago-webhook-replay-notes-2026-04-23.md:29:- When resuming, use a fresh approved order id from a recent live payment or the buyer app flow.
docs/index.md:33:- `docs/implemented-payments.md`
docs/next-steps-queue.md:49:  - deep relative helper imports removed from network, players, sports, and payments route handlers
docs/next-steps-queue.md:70:  - run one real payment and verify persisted approved payment in `/admin/payments`
docs/next-steps-queue.md:72:- 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 signature validation + order fetch, and native order creation now exists at `/api/integrations/payments/orders`.
docs/next-steps-queue.md:100:- 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`.
docs/next-steps-queue.md:122:### PAY-2 Full payments migration into Ballbox host app
docs/next-steps-queue.md:132:  - `/api/integrations/payments/orders`
docs/next-steps-queue.md:133:  - `app/api/integrations/payments/health/route.ts`
docs/next-steps-queue.md:134:  - `app/api/integrations/payments/sessions/route.ts`
docs/next-steps-queue.md:135:  - `docs/implemented-payments.md`
docs/next-steps-queue.md:136:- Expected payoff: one-repo payments ownership, simpler operations, faster debugging
docs/next-steps-queue.md:138:### PAY-3 Split payments (MercadoPago Partners) per club
docs/next-steps-queue.md:143:  - define a fixed Ballbox split-payments setup equivalent to v1 so every approved payment can route a configurable percentage to the club owning the vending machine
docs/next-steps-queue.md:149:  - keep this behind real-world payments validation first (`OPS-1`)
docs/next-steps-queue.md:150:- Expected payoff: platform-controlled split payments with club-aligned revenue distribution
docs/coaches-form-current.md:15:- Detect whether court reservation, payments, club coordination, or growth/admin issues appear often enough to justify follow-up interviews.
docs/coaches-form-current.md:119:- Whether scheduling, attendance reminders, payments, court reservation, club friction, or acquisition appear frequently in self-report.
docs/coaches-opportunity-brief.md:9:- Ballbox today already has host app, admin, network, TVs, payments, players, and a sports foundation.
docs/coaches-opportunity-brief.md:56:  - focuses on one recurring operational loop instead of trying to solve scheduling, CRM, payments, and growth all at once
docs/implemented-tvs.md:64:- Post-`pnpm db:push` plus `pnpm db:seed` verification confirms Ballbox Postgres keeps canonical `Screen.vendingMachineId` and `Impression.tvDeviceId`, with no legacy `deviceId` column reintroduced.
docs/implemented-network.md:5:- Ballbox owns the canonical shared `network` model for venues, vending machines, screens, and tv devices; clubs are now ATC-sourced via `atcSportclubId` and hydrated at runtime.
docs/implemented-network.md:7:- Current network CRUD covers clubs, venues, and vending machines.
docs/implemented-network.md:14:- Vending-machine route handlers now call `lib/network-backend-vending-machines.ts` directly and use `lib/prisma-errors.ts` for unique-constraint classification.
docs/implemented-network.md:28:- `Screen` and `TvDevice` also live in the same shared core and are consumed today by TVs and payments flows.
docs/implemented-network.md:33:- Cross-links from TVs/payments back into `/admin/network` are not in place yet.
docs/repo-map.md:23:- UI surface: `app/admin/payments`
docs/repo-map.md:24:- APIs: `app/api/admin/payments/*`, `app/api/integrations/payments/*`
docs/repo-map.md:25:- Backend: `lib/payments.ts`, `lib/payments-auth.ts`, `lib/shared-secret.ts`, `lib/integrations.ts`
docs/repo-map.md:26:- Implemented doc: `docs/implemented-payments.md`
docs/repo-map.md:53:- Route JSON/envelope helpers: `lib/http/json.ts`, `lib/http/network-route-helpers.ts`, `lib/http/payments-route-helpers.ts`, `lib/http/players-route-helpers.ts`, `lib/http/sports-route-helpers.ts`
docs/ballbox-db-plan.md:51:2. Shared `network` references are live for TVs and payments.
docs/ballbox-db-plan.md:55:4. Payments now persist canonical `vendingMachineId` and `productId` foreign keys.
docs/ballbox-db-plan.md:56:5. Post-`pnpm db:push` plus `pnpm db:seed` verification against Ballbox Postgres confirms physical tables still expose canonical `Screen.vendingMachineId`, `Impression.tvDeviceId`, and `PaymentSession.vendingMachineId`.
docs/ballbox-db-plan.md:63:- Shared physical/operational entities must live in `network`, not inside `payments` or `tvs`.


# Screen Runtime Delivery Investigation

## Question
- TVs with limited connectivity should not have to download the broader Ballbox Next runtime just to play scheduled media.

## Current State
- Historical baseline: `/screens/[screenId]` was rendered from the Ballbox Next host app through a client component in `components/screen-player.tsx`.
- Current production direction: `/player/[screenId]` is served from Ballbox as plain HTML + JavaScript.
- Runtime behavior already depends mostly on browser primitives:
  - `fetch`
  - `CacheStorage`
  - `URL.createObjectURL`
  - `setInterval`
  - `localStorage`
  - `<video>`
- Measured current client chunks for the legacy `/screens/[screenId]` build:
  - `/_next/static/chunks/050hxdgi1a4mo.js`: 5,786 bytes
  - `/_next/static/chunks/15f4hissf3db_.js`: 54,628 bytes
  - `/_next/static/chunks/0-mbqwx08vb4q.js`: 13,855 bytes
  - total known route client JS: 74,269 bytes uncompressed
- That number excludes HTML, RSC payload, request overhead, and future shared-chunk drift.

## Why This Matters
- TVs are a constrained runtime, not a general app surface.
- Hydration and app-router plumbing add bytes and moving parts without helping actual playback.
- A player that is mostly timers, fetches, cache operations, and a `<video>` element is a poor fit for a framework-heavy delivery path when connectivity is the bottleneck.

## Constraints To Keep
- Ballbox should remain the source of truth for:
  - `/api/tvs/manifest/[screenId]`
  - `/api/tvs/heartbeat`
  - `/api/tvs/impression`
- Existing DB ownership, contracts, and admin flows should stay in Ballbox.
- Offline cache/object-URL behavior must remain first-class.
- Diagnostics for real-device verification should remain available.

## Options
### Option A
- Keep the current Next page.
- Pros:
  - no migration work
  - shared tooling and test setup
- Cons:
  - still ships Next/React client runtime to TVs
  - hydration cost remains
  - weakest fit for low-connectivity devices

### Option B
- Keep Ballbox APIs, but replace the screen page with a minimal HTML + vanilla JS player shell.
- Delivery shapes:
  - static shell under `public/`
  - or a dedicated `route.ts` HTML response that does not participate in layouts/client-side navigation
- Pros:
  - best byte reduction without creating another service
  - no React hydration requirement
  - runtime can be tuned directly for kiosk behavior
  - Ballbox still owns deployment and APIs
- Cons:
  - requires extracting runtime logic out of React component state/hooks
  - separate test surface for the lightweight shell

### Option C
- Split the player into a separate static app/repo/deployment.
- Pros:
  - strongest isolation from host app complexity
  - can optimize caching/CDN/deploy cadence independently
- Cons:
  - extra deployment surface
  - duplicated operational/debug burden
  - more auth/config coordination
  - likely unnecessary before Option B is tried

### Option D
- Keep React, but move the player into a separate lightweight SPA.
- Pros:
  - some UI ergonomics preserved
- Cons:
  - still ships framework/runtime bytes
  - weaker payoff than Option B
  - adds build complexity without removing the main problem

## Recommendation
- Choose Option B.
- Keep Ballbox as the API/backend host.
- Build a minimal player shell that uses plain browser APIs and no Next app-router/client-component runtime on the device path.
- Keep the runtime surface focused on the lightweight player.

## Suggested Shape
1. Extract playback/cache/reporting logic from `ScreenPlayer` into framework-agnostic modules.
2. Serve a dedicated player shell for TVs:
   - candidate path: `/player/[screenId]`
   - implementation: HTML response plus one small ES module
3. Reuse the current Ballbox APIs unchanged.
4. Keep the diagnostics panel in the lightweight shell.
5. Legacy `/screens/[screenId]` path removed in favor of `/player/[screenId]`.

## Migration Notes
- Start by extracting logic, not by creating a new deployment.
- Preserve Playwright coverage by pointing the offline/runtime specs at the new shell once ready.
- Do not move manifest/reporting/auth logic out of Ballbox in the same step.
- Treat the TV runtime as an explicit repo-level exception: inside Ballbox, but outside the Next client runtime.

## Sources
- Next.js route handlers: https://nextjs.org/docs/app/getting-started/route-handlers-and-middleware
- Next.js server/client component model: https://nextjs.org/docs/app/getting-started/server-and-client-components
- Next.js navigation behavior: https://nextjs.org/docs/app/getting-started/linking-and-navigating
- MDN CacheStorage: https://developer.mozilla.org/en-US/docs/Web/API/CacheStorage
- Workbox runtime caching overview: https://developer.chrome.com/docs/workbox/caching-resources-during-runtime/


**Considering file management**

I need to copy a file into the docs or artifacts, similar to where the test results are stored. I'm thinking of creating a path like docs/artifacts/tcn-... or investigations/. I'll need to inspect the PDF first, so I'm planning to use laptop-rsync to copy it locally. After that, I can use pdftotext to convert the PDF into text. It feels like a solid plan!

mkdir -p test-results/tcn-payment-protocol && laptop-rsync '/Users/sebas/Downloads/Protocol 2.5V English version.pdf' 'test-results/tcn-payment-protocol/Protocol 2.5V English version.pdf' && ls -l test-results/tcn-payment-protocol


sending incremental file list
rsync: [sender] change_dir "/Users/sebas/Downloads" failed: No such file or directory (2)

sent 19 bytes  received 12 bytes  62.00 bytes/sec
total size is 0  speedup is 0.00
rsync error: some files/attrs were not transferred (see previous errors) (code 23) at main.c(1338) [sender=3.4.1]

Command exited with code 23


**Evaluating file access methods**

I’m considering how to quote paths on my laptop—maybe using a tilde () or the base path from a remote home. I’m also thinking about using laptop-read binary, but it might be easier to use SCP for transferring from remote instead. I realize I need to inspect helper functions to see what works best. Maybe I should even try using a bash-type approach. I'll weigh my options and see which is most efficient!

type laptop-rsync && type laptop-read && type laptop-run

laptop-run 'echo $HOME && ls -l ~/Downloads | sed -n "1,10p"'


laptop-rsync is /mnt/rpi/pi-config/bin/laptop-rsync
laptop-read is /mnt/rpi/pi-config/bin/laptop-read
laptop-run is /mnt/rpi/pi-config/bin/laptop-run


/Users/sebas
total 1682992
drwxr-xr-x     3 sebas  staff         96 Mar 20 14:58 __pycache__
-rw-rw-r--@    1 sebas  staff      74295 Mar  3 19:02 _chat 2.txt
-rw-rw-r--@    1 sebas  staff    3843017 Sep 14  2024 _chat.txt
-rw-r--r--@    1 sebas  staff    5018733 Feb  4 15:30 （中英）云数后台操作说明文档(界面英文版) 2.pdf
-rw-r--r--@    1 sebas  staff    5018733 Feb  4 17:01 （中英）云数后台操作说明文档(界面英文版).pdf
-rw-r--r--@    1 sebas  staff    1512394 May  7  2025 [Infinite Jest 1 ] Wallace, David Foster - Infinite Jest (Back Bay Books) - libgen.li.epub
-rw-r--r--@    1 sebas  staff      75589 Feb 14 12:26 02925fa577c3052125fb25698e8845ab.jpg
-rw-r--r--@    1 sebas  staff   28707899 Jul 18  2025 1_08. Writing’s On The Wall (BBC Proms At The Royal Albert Hall)_(Instrumental).flac
-rw-r--r--@    1 sebas  staff   27142434 Jun 27  2025 1_3 Doors Down - Here Without You (Official Music Video) [kPBzTxZQG5Q]_(Instrumental).flac


**Inspecting rsync usage**

I’m thinking about how maybe I need to ensure rsync includes the correct remote path with the host defaults. It seems like that might be necessary for the script to work properly. I should definitely inspect the script to see if I’m missing anything. It’s important to get the details right, so I’ll check that and adjust as needed to make sure everything runs smoothly.


#!/usr/bin/env bash
set -euo pipefail
host="${LAPTOP_HOST:-sebastians-macbook-pro-2}"
if [[ $# -lt 2 ]]; then
  echo "usage: laptop-rsync <src> <dest> [extra rsync args...]" >&2
  echo "examples:" >&2
  echo "  laptop-rsync sebastians-macbook-pro-2:/Users/sebas/Downloads/file ./" >&2
  echo "  laptop-rsync ./file sebastians-macbook-pro-2:/Users/sebas/Downloads/" >&2
  exit 2
fi
rsync -av --progress -e ssh "$@"


mkdir -p 'test-results/tcn-payment-protocol' && laptop-rsync 'sebastians-macbook-pro-2:/Users/sebas/Downloads/Protocol 2.5V English version.pdf' 'test-results/tcn-payment-protocol/' && ls -lh 'test-results/tcn-payment-protocol'


receiving file list ... 1 file to consider
Protocol 2.5V English version.pdf

              0   0%    0.00kB/s    0:00:00
        459,248 100%    6.84MB/s    0:00:00 (xfr#1, to-chk=0/1)

sent 42 bytes  received 459,613 bytes  306,436.67 bytes/sec
total size is 459,248  speedup is 1.00
total 452K
-rw-r--r-- 1 sebas sebas 449K Apr 23 16:51 Protocol 2.5V English version.pdf


pdftotext 'test-results/tcn-payment-protocol/Protocol 2.5V English version.pdf' 'test-results/tcn-payment-protocol/Protocol 2.5V English version.txt' && wc -l 'test-results/tcn-payment-protocol/Protocol 2.5V English version.txt' && sed -n '1,220p' 'test-results/tcn-payment-protocol/Protocol 2.5V English version.txt'


714 test-results/tcn-payment-protocol/Protocol 2.5V English version.txt
2.4 Interface design

Confidential：

Interface design
（V2.5）

TCN Vending Machines
R&D center

TCN Vending machines
www.tcnvend.com

I

Page 1

2.4 Interface design

1 Preface
1.1 Purposes
To illustrate how the interface is designed including the its flowchart, the organization and
structure, the modules, the functions, the interface design and the data structures.
Notes: Different users work with this protocol in different ways by their different requests, to
scan QR codes additional hardware devices are necessary.

1.2 Terminologies and Abbreviations
Illustrations

Terminologies

Remarks

Verification for operators who load the

Loading the machine

Passwords to take
the products away

machine to pen the doors, can be entered in

8-digit Arabic numerals

the touch screen or by scanning QR code.

Passwords for customers to take the
products, can be entered in the touch screen

8-digit Arabic numerals

or by scanning QR code.

Abbreviations

Full name

Remarks

2. Cloud system designs
All interfaces send requests in http-Post format and all results reverted are in
json format.
Requesting address VMC set configurable.

1

TCN Vending machines
www.tcnvend.com

Page1

2.4 Interface design

2.1 Interface design
2.1.1 Load（delivery）
2.1.1.1

Request message

FIELD NAME
FunCode
MachineID
TradeNo
SlotNo
KeyNum
Status
Quantity
Stock
Capacity
ProductID
Price
Type
Introduction
Name
2.1.1.2

TYPE
String
String
String
String
Int
Int
Int
Int
Int
String
String
String
String
String

SPECIFICATION
Function code no 1000
Machine ID
Trade NO，return as original
Slot NO
Key No（ Use for have keyboard machine）
Slot status（0：ok other：fail）
Load / delivery quantity
stock（after load/ delivery）
capacity
Product ID
Unit price（1.5）
Product type
Product introduction
Product name

Answering

Server return json data：

{"Status":"0","SlotNo":"22","TradeNo":"20170802193446876",
"Err":"success","ImageUrl":"http://xxx.com/201708029502889.png","ImageDetailUrl":"http://xxx.com/201708011243
23318.png"}

说明:
FIELD NAME

Status
SlotNo

SPECIFICATION
0：Suc 1：upload again other：fail(must fill in)
Slot no (must fill in)

ImageUrl

Upload trade no, return as original
Products image URL

ImageDetailUrl

Products detail image URL

Err

Err description

TradeNo

2

湖南中吉科技

www.t-cn.com

页码 2

2.4 接口设计

2.1.2

Identify password

2.1.2.1

Request message

FIELD NAME
FunCode
TradeNo
SessionCode

TYPE
String
String
String

MachineID
SlotNo
Price
Account

String
String
String
String

PWD

String

2.1.2.2

SPECIFICATION
Function code no 2000
Trade no, return as original
Delivery password,customer by input
password on touch screen or scan QR CODE
Machine ID（Unified logo）
Slot No
Unit price（1.5）
Account （can’t use together with
SessionCode，choose one of them）
password

Answering

Return json data：


rg -n "QR|scan|hardware|scanner|camera|SessionCode|password|payment|wechat|alipay|union|扫码|二维码|code" 'test-results/tcn-payment-protocol/Protocol 2.5V English version.txt' -S | sed -n '1,240p'

pdfinfo 'test-results/tcn-payment-protocol/Protocol 2.5V English version.pdf'


25:scan QR codes additional hardware devices are necessary.
45:the touch screen or by scanning QR code.
52:or by scanning QR code.
114:Function code no 1000
174:Identify password
183:SessionCode
207:Function code no 2000
209:Delivery password,customer by input
210:password on touch screen or scan QR CODE
215:SessionCode，choose one of them）
216:password
266:Function code no 4000
383:"Ftp":"host|user|password",
404:form ："host|user|password"
650:The only logo for Server return ， generate QR
651:code


Title:
Subject:
Keywords:
Author:          gpc
Creator:         Adobe Acrobat Pro DC 18.9.20050
Producer:        Adobe Acrobat Pro DC 18.9.20050
CreationDate:    Fri Feb 22 05:43:32 2019 -03
ModDate:         Fri Feb 22 05:43:32 2019 -03
Custom Metadata: yes
Metadata Stream: yes
Tagged:          no
UserProperties:  no
Suspects:        no
Form:            AcroForm
JavaScript:      no
Pages:           9
Encrypted:       no
Page size:       595.3 x 841.9 pts (A4)
Page rot:        0
File size:       459248 bytes
Optimized:       yes
PDF version:     1.7


read

test-results/tcn-payment-protocol/Protocol 2.5V English version.txt


String
String
String
String

SPECIFICATION
Interface no 8000
Machine ID（Unified logo）
Trade NO
Slot NO
Unit price（1.5）
Return Url，pay result inform Url，
content is 2.1.9.2

answering

{ "Status": "0","Err":"suc","CodeUrl":"123456789","TradeNo":"20170609123523569"}
specification:
Status

FIELD NAME

Err
CodeUrl
TradeNo

SPECIFICATION
0 : SUC
Others: Fail
Err description
The only logo for Server return ， generate QR
code
Trade no

7

湖南中吉科技

www.t-cn.com

页码 7

2.4 接口设计

2.1.6

Checking integral exchange pay result

2.1.6.1

Request message

FIELD NAME
FunCode
MachineID
TradeNo
2.1.6.2

TYPE
String
Machine ID
Trade no

SPECIFICATION
Interface no 9000
Machine ID（Unified logo）

Answering

{ "Status": "0","Err":"suc","MachineID":"123456789","TradeNo":"20170609123523569"}
specification:
Status

FIELD NAME

MachineID
Err
TradeNo

0: suc
Others: fail
Machine ID
Err description
Machine iD

SPECIFICATION

8

湖南中吉科技

www.t-cn.com

页码 8


Download advertising result to request message

FIELD NAME
FunCode
MachineID
FileName
Status
2.1.4.6

TYPE
String
String
String
Int

SPECIFICATION
Interface no 5002
Machine ID（Unified logo）
Advertising file name
0：Suc 1：advertising type don’t match
2：fail

Answering

Return json data：
{ "Status": "0","MachineID":"1234567890","AdFiles":"d123.png","Err":"suc"}
specification:
FIELD NAME
SPECIFICATION
Status
0：confirm feedback successfully others:failed
MachineID
Machine ID
AdFiles
Advertising file name
Err
Err description

2.1.5

Integral exchange

2.1.5.1

Request message

FIELD NAME
FunCode
MachineID
TradeNo
SlotNo
Price
NotityUrl

2.1.5.2

TYPE
String
String
String
String
String
String

SPECIFICATION
Interface no 8000
Machine ID（Unified logo）
Trade NO
Slot NO
Unit price（1.5）
Return Url，pay result inform Url，
content is 2.1.9.2

answering

{ "Status": "0","Err":"suc","CodeUrl":"123456789","TradeNo":"20170609123523569"}
specification:
Status

[76 more lines in file. Use offset=640 to continue.]


**Exploring protocol structures**

I need to clarify the whole protocol structure, specifically whether "integral exchange" refers to points redemption. It might still be a generic QR code. I wonder if I need to inspect images further — maybe the PDF has flowcharts missed in the text. I could convert images or inspect them page by page. Perhaps extracting pages to PNG using pdftoppm could help too. I'll search for any relevant wording to guide me.

bash

sed -n '150,260p' 'test-results/tcn-payment-protocol/Protocol 2.5V English version.txt'


Products image URL

ImageDetailUrl

Products detail image URL

Err

Err description

TradeNo

2

湖南中吉科技

www.t-cn.com

页码 2

2.4 接口设计

2.1.2

Identify password

2.1.2.1

Request message

FIELD NAME
FunCode
TradeNo
SessionCode

TYPE
String
String
String

MachineID
SlotNo
Price
Account

String
String
String
String

PWD

String

2.1.2.2

SPECIFICATION
Function code no 2000
Trade no, return as original
Delivery password,customer by input
password on touch screen or scan QR CODE
Machine ID（Unified logo）
Slot No
Unit price（1.5）
Account （can’t use together with
SessionCode，choose one of them）
password

Answering

Return json data：

{ "Status": "0","SlotNo":"23","ProductID":"1002356","TradeNo":"20170609123523569","Err":"Suc"}

Specification :

FIELD NAME

SPECIFICATION
0: Suc Other: fail (must fill in)
Slot no which need delivery product, if more
than1 slot use | to separate. For example:
delivery 23 and 28 slot products in the same
time, need to write 23|28。(must fill in)
Product ID which need delivery produc ， if
SlotNo>0 ， then delivery products based on
SlotNo 。 If delivery products based on products
ID，than the SlotNo have to < 1。
Trade NO, return as original (must fill in)
Error description

Status
SlotNo

ProductID

TradeNo
Err

2.1.3

Return cycle interface

2.1.3.1

:Request

FIELD NAME
FunCode
MachineID


**Inspecting headings for accuracy**

I think it's important to check all the headings carefully. There might be an issue with section 2.1.9, as it seems to be missing due to OCR errors. I want to make sure everything is accurate and complete, so it's a good idea to go through each heading and verify them. I'll ensure to address any discrepancies along the way!

python3 - <<'PY'
from pathlib import Path
text=Path('test-results/tcn-payment-protocol/Protocol 2.5V English version.txt').read_text(errors='ignore').splitlines()
for i,l in enumerate(text,1):
    if l.strip().startswith('2.1.') or l.strip().startswith('1.1') or l.strip().startswith('1.2'):
        print(i, l)
PY


22 1.1 Purposes
28 1.2 Terminologies and Abbreviations
77 2.1.1 Load（delivery）
78 2.1.1.1
97 2.1.1.2
175 2.1.2
179 2.1.2.1
207 2.1.2.2
252 2.1.3
256 2.1.3.1
282 2.1.3.2
439 2.1.4
443 2.1.4.1
488 2.1.4.2
514 2.1.4.3
523 2.1.4.4
566 2.1.4.5
575 2.1.4.6
606 2.1.5
610 2.1.5.1
622 2.1.5.2
672 2.1.6
676 2.1.6.1
684 2.1.6.2


Return cycle interface

2.1.3.1

:Request

FIELD NAME
FunCode
MachineID

TYPE
String
String

SPECIFICATION
Function code no 4000
Machine ID

3

湖南中吉科技

www.t-cn.com

页码 3

2.4 接口设计
2.1.3.2

Answering

1、When MsgType is 0，means Server control VMC to delivery products，return json data as below：
{ "Status": "0","MsgType":"0","TradeNo":"20170609123523569","SlotNo":"25","ProductID":"1005678692","Err":"SUC"}

Specification :

SPECIFICATION
0: Suc Other: Fail
MsgType
Message type
TradeNo
Trade NO
The slot no which need delivery products
SlotNo
ProductID
Product ID which need delivery produc ， if
SlotNo>0 ， then delivery products based on
SlotNo 。 If delivery products based on products
ID，than the SlotNo have to < 1
Err description
Err
Remark：The function is use for connecting with delivery products by APP，VMC need to use this
interface timing。Suggest the request Intervals time is 3 second。
Status

FIELD NAME

2、When MsgType is 1，means the APP loading inform VMC，return json data as below：
{ " Status ": "0","MsgType":"1","SlotNo":"25"," TradeNo":"111111"," Capacity":"1"," Quantity ":"1","
ProductID":"111111"," Name":"dsff"," Price":"2.0"," Type":"yinl"," Introduction":"ssfsf","
ImageUrl":"http://xxx.com/201708029502889.png"," ImageDetailUrl":"http://xxx.com/201708029502889.png","
GoodsAdUrl":"ssfsf","Err":"Suc"}

Specification :

SPECIFICATION
0： means need load
others：don’t need
MsgType
Message type is 1
SlotNo
Slot no
Err
Err description
Capacity
Capacity
Load/delivery quantity
Quantity
Product ID
ProductID
Name
Produce name
Price
Product price
Type
Product type
Introduction
Production introduction
ImageUrl
Product image Url
ImageDetailUrl
Produce detail image Url
Remarl：After load product successfully, need use load result feedback the interface. As the request is
every 3 second, so after machine received message, it start to revise product message,
this process need enough time. So after server sent send request, pls wait some times
first, then you can check if have received load suc or fail news. During waiting time,
you can return Server to control the VMC delivery content.
Status

FIELD NAME

4

湖南中吉科技

www.t-cn.com

页码 4

2.4 接口设计

3、when MsgType is 2，means release advertising by remote control，return json data（[json data,json
data ,json data ]）：
{

"MsgType":"2",
"AdInfo":[
{

},
{

]

}

Specification :

}

"Status":"1",
"MachingID":"1234567890",
"Ftp":"host|user|password",
"PlayTime":"08:30~17:30|19:30~21:30",
"AdFiles":"s123.png|m98.jpg",
"AdType":"1"
"Status":"0",
"MachingID":"1234567890",
"Ftp":"host|zhang|123",
"PlayTime":"08:30~21:30",
"AdFiles":"s653.mp4",
"AdType":"0"

FIELD NAME

SPECIFICATION
MsgType
Message type is 2
Status
0:need download others：don’t need
MachingID
machine ID
Ftp
form ："host|user|password"
PlayTime
if more than 1 play time, use | to
separate
AdInfo
AdFiles if more than 1 adverting files, use | to
separate
AdType
0：normal advertising 1：standby
adverting
2：background picture 3：help
4：word advertising
Remark: After download, need use download result feedback the interface. As the request is every 3
second, download need time, so after server send message, pls wait some time, then check if have
received any news about download suc or fail. During the waiting time, can return Server to control
the content of VMC delivery. Besides when return the advertising connect, pls return all adverting
content which have been released, included downloaded adverting content. Because local will
compare with the return advertising content, which not exist content will be deleted.

5

湖南中吉科技

www.t-cn.com

页码 5

2.4 接口设计

2.1.4

Resule feedback

2.1.4.1

Delivery result feedback

FIELD NAME
FunCode
MachineID
PayType
TradeNo
SlotNo
Status

TYPE
String
String
Int
String
String
Int

SPECIFICATION
Interface ID 5000
Machine ID（Unified logo）
Pay type
Trade no
Slot no

Time
Amount
ProductID
Name
Type

String
String
String
String
String

Delivery time
Amount
Product ID
Product name
Product type

2.1.4.2

0:delivery suc 1：delivery fail
2:delivery suc, but can’t find trade no or
trade no is not correct
3:delivery failed,but can’t find trade no or
trade no is not correct
4：don’t know delivery result

Answering type

Return json data：
{ "Status": "0","TradeNo":"20170609123523569","SlotNo":"25","Err":"Suc"}
Specification:
FIELD NAME
SPECIFICATION
Status
0: confirm feedback suc
Others: fail
TradeNo
Trade no
SlotNo
Slot no
Err
Err description

2.1.4.3

APP load result feedback request

FIELD NAME
FunCode
MachineID
SlotNo
Status
2.1.4.4

TYPE
String
String
String
Int

SPECIFICATION
Interface ID 5001
Machine ID（Unified logo）
Slot no
0 suc
Others : fail

Answering type

Return json data：
{ "Status": "0","SlotNo":"25","TradeNo":"20170609123523569","Err":"suc"}
specification:
FIELD NAME
SPECIFICATION
Status
0: confirm feedback suc
Others: fail
SlotNo
Slot no
TradeNo
Trade no, return as original
6

湖南中吉科技

www.t-cn.com

页码 6

2.4 接口设计
Err

Err description

2.1.4.5

Download advertising result to request message

FIELD NAME
FunCode
MachineID
FileName
Status
2.1.4.6

TYPE
String
String
String
Int

SPECIFICATION
Interface no 5002
Machine ID（Unified logo）
Advertising file name
0：Suc 1：advertising type don’t match
2：fail

Answering

Return json data：
{ "Status": "0","MachineID":"1234567890","AdFiles":"d123.png","Err":"suc"}
specification:
FIELD NAME
SPECIFICATION
Status
0：confirm feedback successfully others:failed
MachineID
Machine ID
AdFiles
Advertising file name
Err
Err description

2.1.5

Integral exchange

2.1.5.1

Request message

FIELD NAME
FunCode
MachineID
TradeNo
SlotNo
Price
NotityUrl

2.1.5.2

TYPE
String
String

[96 more lines in file. Use offset=620 to continue.]


**Considering vending software and QR codes**

It seems that the vending software should know how to render QR codes from CodeUrl in its UI after calling API 8000. The hardware captures QR codes for delivery passwords but not for customer payments. If I want users to scan a QR code on their phone into the machine, a scanner module is necessary. However, if customers scan a QR from the machine, additional hardware isn’t needed—just a display. I might document this by creating a markdown file in the findings folder.

write

# TCN 3rd-party payments protocol quick read

Artifacts:
- `test-results/tcn-payment-protocol/Protocol 2.5V English version.pdf`
- `test-results/tcn-payment-protocol/Protocol 2.5V English version.txt`

## Key quotes
- Preface: `to scan QR codes additional hardware devices are necessary.`
- `2.1.2 Identify password`: `customer by input password on touch screen or scan QR CODE`
- `2.1.5 Integral exchange` request includes:
  - `FunCode=8000`
  - `MachineID`
  - `TradeNo`
  - `SlotNo`
  - `Price`
  - `NotityUrl` (`pay result inform Url`)
- `2.1.5` response includes:
  - `Status`
  - `Err`
  - `CodeUrl`
  - `TradeNo`
- `CodeUrl` description: `The only logo for Server return, generate QR code`

## Working interpretation
1. The hardware warning appears to refer to **machine-side QR scanning**, not showing a QR on the machine screen.
2. The payment flow in `2.1.5` looks like this:
   - machine asks payment server for a payment session (`8000`)
   - server returns `CodeUrl`
   - machine software generates/displays a QR from that returned value
   - payment result is sent back via `NotityUrl`
3. The protocol does **not** mention any extra hardware for that outbound payment QR display flow.
4. Extra hardware seems relevant only if the machine must read a QR presented to it by someone else (operator/customer scan-in flow).

## Practical implication for Ballbox
- Best-fit path is likely: Ballbox generates the payment order, returns QR payload / payment URL in the form expected by TCN `CodeUrl`, and the machine UI displays it.
- A printed sticker is only a fallback if the vending UI cannot render the returned `CodeUrl` at runtime.
- External scanner hardware is likely unnecessary unless product requirements depend on the machine scanning codes.

## Confidence / gaps
- Confidence: medium.
- Gap: this document is an interface spec, not a UI behavior manual. It strongly suggests dynamic on-screen QR display, but does not explicitly say `the screen renders the QR`. That inference comes from the `CodeUrl` payment response shape and the lack of any printer/sticker flow in the protocol.
- Best next proof: verify on one TCN machine whether invoking `8000` produces a native payment QR screen in the stock app.


Successfully wrote 2227 bytes to test-results/tcn-payment-protocol/findings-2026-04-23.md


**Considering evidence for an answer**

I should think about using evidence lines in my response. Maybe incorporating page images could help convey my point better. But I want to make sure I don't overwhelm the user. It's important to mention that things aren't all doom and gloom. I’ll also update my to-do list to reflect that I've completed this task. Let’s see how I can make my answer clear but not too cluttered!


Updated #1 (pending → completed)


Copied here:

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

Bottom line: not doomed.

What manual actually says
- Preface says: `to scan QR codes additional hardware devices are necessary.`
- Payment section `2.1.5 Integral exchange` defines a payment API:
  - machine sends `FunCode=8000`, `MachineID`, `TradeNo`, `SlotNo`, `Price`, `NotityUrl`
  - server returns `Status`, `Err`, `CodeUrl`, `TradeNo`
- `CodeUrl` is described as: `The only logo for Server return, generate QR code`

My read
1. Can display a QR inside vending machine app?
- Likely yes.
- Strong signal: protocol has a dedicated payment flow that returns `CodeUrl` for QR generation.
- That makes sense only if machine software can render the QR on screen.

2. Should generate a QR and stick as sticker outside?
- No as primary path.
- Static sticker would not match this protocol.
- This spec is for dynamic transaction QR generation per purchase.

3. Should connect external hardware? What?
- Only if you need the machine to scan codes.
- Wording appears about inbound scanning hardware: customer/operator shows QR to machine, machine reads it.
- Likely hardware: QR scanner / barcode scanner module, maybe camera-based reader depending machine model.
- For customer scans machine QR with phone: probably no extra hardware needed.

4. Are we doomed?
- No.
- Most likely path is:
  - Ballbox creates payment
  - returns payment URL/token as `CodeUrl`
  - TCN app renders QR on machine screen
  - payment result hits `NotityUrl`

Important nuance
- This doc is interface spec, not full device UI manual.
- So I’m confident on “hardware warning is about scanning, not displaying”.
- I’m medium confidence on “stock TCN app definitely renders the returned QR” — but the API shape strongly implies it.

Best next proof
- Test one real machine with `8000`.
- If stock TCN flow pops a payment QR screen, done.
- If not, then fallback options are:
  - custom on-screen rendering inside whatever app surface TCN exposes
  - or ugly sticker/manual flow

Recommendation
- Proceed assuming dynamic on-screen QR is the intended path.
- Do not design around sticker.
- Do not buy scanner hardware unless product requires machine-side QR reading.

If you want, next step I can extract the exact TCN payment flow into a Ballbox integration note: request/response mapping, what `CodeUrl` should contain, and what we need from `NotityUrl`.


ahh aditional for  _scanning_ QR. of course makes total sense. user's phones with the MP app are that
hey so then adapt plans on the payment feature to keep the page we have for manual custom qr code gen but more as a test usage, and plan out how we can integrate with TCN now that these docs should be enough right?


**Updating plan documentation**

I think I need to update the plan documents, like PLAN.md and docs related to payments. The user is asking me to adapt the plans, so I should inspect the PLAN and possibly add an investigation document. It seems I’ll likely modify the docs rather than the code. I might also need to save some relevant memory and read specific sections of the docs to ensure I’m aligning with the user’s needs.


- 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-2` -> `PAY-3`.
- 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`.
- Minimum doc set for the sports discovery track, in order:
  1. opportunity brief for coaches/padel centers
  2. ICP plus segment scoring
  3. research repository for form results and interviews
  4. JTBD/workflow map based on real evidence
  5. solution thesis for the first wedge
  6. ATC integration discovery/spec only if the chosen wedge requires it
  7. MVP PRD for one workflow loop
  8. GTM note with ATC treated as a possible future channel, not current affiliation
- Until form/interview evidence exists, these docs must use hypothesis language and avoid claims that coach pains, willingness to pay, or ATC-dependent workflows are already validated.

## Auth Direction
- `/admin` should get a simple access layer first, with no product-level RBAC in the first pass.
- `/player` stays unauthenticated for now; do not block runtime rollout on device or screen identity flows yet.
- Server-to-server integrations should use shared secrets as the immediate default.
- Revisit stronger auth and role models only after the host app surfaces and integration boundaries stabilize.

## Persistence Direction
- Do not invest further in memory-backed or file-backed persistence for TVs.
- Ballbox PostgreSQL is the chosen durable store for TVs after migration.
- Ballbox must own the schema for the final shared `network` entities plus the downstream product models that reference them.
- No compatibility layer is required for local-only development; Ballbox can be reinvented directly on its own DB contract.

## Acceptance Criteria
- Landing still works at `/`.
- `/admin` exists and links to product-specific areas.
- `/admin/tvs`, `/admin/payments`, `/admin/network`, `/admin/sports`, `/admin/players`, and `/admin/screens` render in Ballbox.
- `app/api` contains a host-app BFF foundation for admin and integrations.
- TVs no longer depend on Streetcast or hardcoded ownership data; Ballbox exposes internal `/api/tvs/*` backed by Ballbox Postgres.
- Ballbox exposes the concrete per-screen TV runtime under `/player/[screenId]`.
- TVs migration is not complete until the Streetcast parity checklist is reviewed and every legacy runtime feature is marked as covered, intentionally dropped, or replaced.
- TVs runtime must support local cached playback for video creatives instead of relying only on remote streaming.
- Ballbox persists approved payments locally through the native MercadoPago webhook flow at `/notification/mercadopago`.
- Ballbox exposes network CRUD under `/admin/network` plus `/api/network*`.
- Final schema ownership and relationships match `docs/domain-model-target.md`.
- `pnpm lint`, `pnpm typecheck`, `pnpm test:contracts`, `pnpm test:e2e`, and `pnpm build` run successfully in local repo state.
- `pnpm db:push` plus `pnpm db:seed` must keep demo `ClassOffer` bootstrap aligned with the shipped `/classes` MVP.
- `pnpm test:contracts` passes for TVs contract enforcement.
- `pnpm test:e2e` passes for browser-level TVs cached playback verification.

## Agent Handoff
- Real DB in use: Ballbox Prisma schema was pushed to the Postgres configured in Ballbox `DATABASE_URL`.
- Seed already run successfully against that DB.
- Current DB counts after seed were:
  - clubs: 2
  - venues: 3
  - vendingMachines: 4
  - tvDevices: 2
  - screens: 2
  - advertisers: 2
  - campaigns: 2
  - creatives: 3
  - assignments: 3
  - products: 2
  - players: 3
  - memberships: 4
  - coaches: 2
  - openMatches: 2
  - classes: 2
- New DB-backed read/write layer lives in `lib/ballbox-store.ts`.
- Bridge-era in-memory TVs modules were removed; seed fixtures now live under `prisma/seed-data.ts`.
- Routes already moved:
  - `/api/network`
  - `/api/network/[clubId]`
  - `/api/tvs/heartbeat`
  - `/api/tvs/impression`
  - `/api/tvs/manifest/[screenId]` via `lib/tvs-source.ts`
  - `/api/tvs/screens` and `/api/tvs/health` via `lib/tvs-source.ts`
- TVs persistence cleanup is complete; do not reintroduce in-memory compatibility layers.


- 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 signature validation + order fetch, 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.
- 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 Split payments (MercadoPago Partners) per club
- Priority: P1
- Status: blocked
- Depends on: OPS-1, PAY-2
- Scope:
  - define a fixed Ballbox split-payments setup equivalent to v1 so every approved payment can route a configurable percentage to the club owning the vending machine
  - add an admin CTA in club configuration to start/link MercadoPago partner onboarding for that club
  - persist the minimum per-club MercadoPago partner metadata/credentials required for payout routing and operational support
  - document security and rotation rules for stored credentials before go-live
- Notes:
  - implementation details are intentionally pending discovery when this task is started
  - keep this behind real-world payments validation first (`OPS-1`)
- Expected payoff: platform-controlled split payments with club-aligned revenue distribution

## P2 Sequenced Refactors

### 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

[1 more lines in file. Use offset=180 to continue.]


# Payments Implemented State

## Current Split
- Ballbox owns the admin surface, native MercadoPago webhook intake, and native QR order creation.
- `/notification/mercadopago` is the first-party MercadoPago QR webhook route that fetches the upstream order, ignores unsupported merchant-order topics, and persists approved orders.
- `/api/integrations/payments/orders` is the first-party QR order creation route.
- `/api/integrations/payments/sessions` lists Ballbox-local approved payment records plus native MercadoPago config status.
- `/admin/payments` now manages local approved payment sessions directly for reconciliation/correction and also generates live MercadoPago QR orders for arbitrary amounts or product-priced orders.
- The QR generator accepts money values in major units with decimals (for example `15.00` or `1000.00`) and converts them to Mercado Pago minor units before submission.

## APIs
- `/api/admin/payments/operations`
- `/api/admin/payments/sessions`
- `/api/admin/payments/sessions/[sessionId]`
- `/api/integrations/payments/health`
- `/api/integrations/payments/sessions`
- `/api/integrations/payments/orders`
- `/notification/mercadopago`

## Persistence
- Ballbox persists approved payment records as `PaymentSession` rows in Ballbox Postgres.
- Native MercadoPago webhook intake fetches the upstream order, maps approved orders into the canonical Ballbox payment shape, and persists them locally.
- QR Code notifications do not rely on origin-signature validation in this flow; the upstream order fetch plus canonical-reference mapping is the acceptance gate.
- Admin operators can now create, update, and delete local `PaymentSession` rows for reconciliation.
- Post-`pnpm db:push` plus `pnpm db:seed` verification confirms Ballbox Postgres keeps canonical `PaymentSession.vendingMachineId` and does not expose a `PaymentSessionRef` table or `machineId` compatibility column.
- Payments admin session routes now share Prisma error classification through `lib/prisma-errors.ts` instead of importing those helpers from `lib/ballbox-store.ts`.
- Playwright now covers local `/admin/payments` create, edit, and delete flows with deterministic cleanup of E2E `PaymentSession` rows.

## Integration Contract
- Ballbox native MercadoPago webhook path is `/notification/mercadopago`.
- Ballbox native QR order creation path is `/api/integrations/payments/orders`.
- That route now also attaches a public `notification_url` so Mercado Pago sends QR order updates back to `/notification/mercadopago`.
- That route accepts an explicit amount for arbitrary charges or falls back to the Ballbox `Product.priceMinor` when the amount is omitted, which powers per-product QR generation in the admin UI.
- The order route rejects values below Mercado Pago's minimum charge (`15.00 ARS`) before calling the provider.
- QR webhook persistence now depends on the upstream order fetch plus canonical-reference mapping; merchant-order topic events are ignored.
- Legacy approved-callback path `/api/integrations/payments/approved` is removed as an operational integration and now returns `410 PAYMENTS_APPROVED_CALLBACK_REMOVED`.
- Native payment correlation requires canonical `externalReference` values shaped as `payment-vm_{vendingMachineId}-txn_{machineTxnId}`.
- `NEXT_PUBLIC_SITE_URL` is required if Ballbox should expose a concrete `recommendedMercadoPagoWebhookUrl`.

## Validation Status
- Host app now owns the native MercadoPago order creation + webhook persistence path inside Ballbox.
- Remaining gap is operational/manual: one real public webhook + one real approved payment persisted and visible in `/admin/payments`.
- The admin surface now includes a live QR generator preview for scan-and-pay validation.
- Recent replay validation against two older live `ORD...` ids returned `MERCADOPAGO_ORDER_NOT_APPROVED_expired`; see `docs/mercadopago-webhook-replay-notes-2026-04-23.md` when resuming webhook persistence checks.

## Manual E2E Still Pending
- End-to-end payment validation now depends on Ballbox native MercadoPago webhook configuration, not just local admin CRUD.
- Before running the check, set:
  - Ballbox `BALLBOX_MERCADOPAGO_ACCESS_TOKEN`
  - Ballbox `BALLBOX_MERCADOPAGO_WEBHOOK_SECRET`
  - Ballbox `BALLBOX_MERCADOPAGO_EXTERNAL_POS_ID` (or enviar `externalPosId` por request)
  - public HTTPS exposure for Ballbox webhook endpoint `/notification/mercadopago`
- Acceptance for the manual check:
  - one real payment is approved upstream
  - MercadoPago reaches Ballbox webhook publicly
  - Ballbox fetches order details and records the approved payment locally
  - a `PaymentSession` row is persisted locally
  - the session is visible in `/admin/payments`
  - the exact public webhook URL and observed payload quirks are documented after the run


# 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`


Successfully wrote 3551 bytes to docs/tcn-payments-integration-plan.md


edit

## Current Split
- Ballbox owns the admin surface, native MercadoPago webhook intake, and native QR order creation.
- `/notification/mercadopago` is the first-party MercadoPago QR webhook route that fetches the upstream order, ignores unsupported merchant-order topics, and persists approved orders.
- `/api/integrations/payments/orders` is the first-party QR order creation route.
- `/api/integrations/payments/sessions` lists Ballbox-local approved payment records plus native MercadoPago config status.
- `/admin/payments` now manages local approved payment sessions directly for reconciliation/correction and also generates live MercadoPago QR orders for arbitrary amounts or product-priced orders.
- The QR generator accepts money values in major units with decimals (for example `15.00` or `1000.00`) and converts them to Mercado Pago minor units before submission.

## Current Split
- Ballbox owns the admin surface, native MercadoPago webhook intake, and native QR order creation.
- `/notification/mercadopago` is the first-party MercadoPago QR webhook route that fetches the upstream order, ignores unsupported merchant-order topics, and persists approved orders.
- `/api/integrations/payments/orders` is the first-party QR order creation route.
- `/api/integrations/payments/sessions` lists Ballbox-local approved payment records plus native MercadoPago config status.
- `/admin/payments` now manages local approved payment sessions directly for reconciliation/correction and also generates live MercadoPago QR orders for arbitrary amounts or product-priced orders.
- The admin QR generator should now be treated as an operator test/debug harness for Mercado Pago and webhook validation, not as the final buyer-facing vending-machine payment UX.
- The QR generator accepts money values in major units with decimals (for example `15.00` or `1000.00`) and converts them to Mercado Pago minor units before submission.

## Validation Status
- Host app now owns the native MercadoPago order creation + webhook persistence path inside Ballbox.
- Remaining gap is operational/manual: one real public webhook + one real approved payment persisted and visible in `/admin/payments`.
- The admin surface now includes a live QR generator preview for scan-and-pay validation.
- Recent replay validation against two older live `ORD...` ids returned `MERCADOPAGO_ORDER_NOT_APPROVED_expired`; see `docs/mercadopago-webhook-replay-notes-2026-04-23.md` when resuming webhook persistence checks.

## Validation Status
- Host app now owns the native MercadoPago order creation + webhook persistence path inside Ballbox.
- Remaining gap is operational/manual: one real public webhook + one real approved payment persisted and visible in `/admin/payments`.
- The admin surface includes a live QR generator preview for provider-level scan-and-pay validation, but production vending flow should move behind the TCN machine protocol instead of relying on that admin page.
- TCN protocol review indicates the `additional hardware devices` warning applies to machine-side QR scanning, while the payment flow (`FunCode=8000`) returns `CodeUrl` for machine-side QR display; see `docs/tcn-payments-integration-plan.md` and `test-results/tcn-payment-protocol/findings-2026-04-23.md`.
- Recent replay validation against two older live `ORD...` ids returned `MERCADOPAGO_ORDER_NOT_APPROVED_expired`; see `docs/mercadopago-webhook-replay-notes-2026-04-23.md` when resuming webhook persistence checks.


Successfully replaced 2 block(s) in docs/implemented-payments.md.

    ...
  5 - `/notification/mercadopago` is the first-party MercadoPago QR webhook route that fetches the upstream order, ignores unsupported merchant-order topics, and persists approved orders.
  6 - `/api/integrations/payments/orders` is the first-party QR order creation route.
  7 - `/api/integrations/payments/sessions` lists Ballbox-local approved payment records plus native MercadoPago config status.
  8 - `/admin/payments` now manages local approved payment sessions directly for reconciliation/correction and also generates live MercadoPago QR orders for arbitrary amounts or product-priced orders.
+ 9 - The admin QR generator should now be treated as an operator test/debug harness for Mercado Pago and webhook validation, not as the final buyer-facing vending-machine payment UX.
  9 - The QR generator accepts money values in major units with decimals (for example `15.00` or `1000.00`) and converts them to Mercado Pago minor units before submission.
 10
 11 ## APIs
 12 - `/api/admin/payments/operations`
    ...
 39
 40 ## Validation Status
 41 - Host app now owns the native MercadoPago order creation + webhook persistence path inside Ballbox.
 42 - Remaining gap is operational/manual: one real public webhook + one real approved payment persisted and visible in `/admin/payments`.
-43 - The admin surface now includes a live QR generator preview for scan-and-pay validation.
+44 - The admin surface includes a live QR generator preview for provider-level scan-and-pay validation, but production vending flow should move behind the TCN machine protocol instead of relying on that admin page.
+45 - TCN protocol review indicates the `additional hardware devices` warning applies to machine-side QR scanning, while the payment flow (`FunCode=8000`) returns `CodeUrl` for machine-side QR display; see `docs/tcn-payments-integration-plan.md` and `test-results/tcn-payment-protocol/findings-2026-04-23.md`.
 44 - Recent replay validation against two older live `ORD...` ids returned `MERCADOPAGO_ORDER_NOT_APPROVED_expired`; see `docs/mercadopago-webhook-replay-notes-2026-04-23.md` when resuming webhook persistence checks.
 45
 46 ## Manual E2E Still Pending
 47 - End-to-end payment validation now depends on Ballbox native MercadoPago webhook configuration, not just local admin CRUD.
    ...


### 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 signature validation + order fetch, 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.
- Expected payoff: closes highest-risk integration uncertainty

### 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 signature validation + order fetch, 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 buyer-side on-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

### PAY-3 Split payments (MercadoPago Partners) per club
- Priority: P1
- Status: blocked
- Depends on: OPS-1, PAY-2
- Scope:
  - define a fixed Ballbox split-payments setup equivalent to v1 so every approved payment can route a configurable percentage to the club owning the vending machine
  - add an admin CTA in club configuration to start/link MercadoPago partner onboarding for that club
  - persist the minimum per-club MercadoPago partner metadata/credentials required for payout routing and operational support
  - document security and rotation rules for stored credentials before go-live
- Notes:
  - implementation details are intentionally pending discovery when this task is started
  - keep this behind real-world payments validation first (`OPS-1`)
- Expected payoff: platform-controlled split payments with club-aligned revenue distribution

### 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
  - split payouts per club can be revisited after the machine-facing flow is stable
- Expected payoff: closes the actual machine integration path instead of overinvesting in the standalone admin QR surface

### PAY-4 Split payments (MercadoPago Partners) per club
- Priority: P2
- Status: blocked
- Depends on: OPS-1, PAY-2, PAY-3
- Scope:
  - define a fixed Ballbox split-payments setup equivalent to v1 so every approved payment can route a configurable percentage to the club owning the vending machine
  - add an admin CTA in club configuration to start/link MercadoPago partner onboarding for that club
  - persist the minimum per-club MercadoPago partner metadata/credentials required for payout routing and operational support
  - document security and rotation rules for stored credentials before go-live
- Notes:
  - implementation details are intentionally pending discovery when this task is started
  - keep this behind real-world payments validation first (`OPS-1`) and machine-facing TCN adapter definition (`PAY-3`)
- Expected payoff: platform-controlled split payments with club-aligned revenue distribution

### 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


Successfully replaced 3 block(s) in docs/next-steps-queue.md.

     ...
  70   - run one real payment and verify persisted approved payment in `/admin/payments`
  71   - document observed payload + public URL evidence
  72 - 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 signature validation + order fetch, and native order creation now exists at `/api/integrations/payments/orders`.
  73 - 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.
+ 74 - 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 buyer-side on-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.
  74 - Expected payoff: closes highest-risk integration uncertainty
  75
  76 ### OPS-2 TVs runtime parity closure
  77 - Priority: P0
     ...
 134   - `app/api/integrations/payments/sessions/route.ts`
 135   - `docs/implemented-payments.md`
 136 - Expected payoff: one-repo payments ownership, simpler operations, faster debugging
 137
-138 ### PAY-3 Split payments (MercadoPago Partners) per club
+139 ### PAY-3 TCN vending-machine adapter over native MercadoPago
 139 - Priority: P1
-140 - Status: blocked
+141 - Status: ready
 141 - Depends on: OPS-1, PAY-2
 142 - Scope:
+144   - define the Ballbox machine-facing adapter for TCN payment init (`FunCode=8000`) over the existing MercadoPago order-create route
+145   - map TCN `MachineID`, `TradeNo`, `SlotNo`, `Price`, `NotityUrl`, and response `CodeUrl` to Ballbox-native payment fields
+146   - define the payment-success handshake back to TCN so approved payments can unlock dispense
+147   - keep `/admin/payments` positioned as an operator test/debug harness rather than the production vending UX
+148   - document the exact adapter contract and remaining TCN unknowns before implementation
+149 - Notes:
+150   - TCN protocol evidence is documented in `docs/tcn-payments-integration-plan.md`
+151   - do not assume extra scanner hardware for the buyer flow; the open question is adapter contract detail, not QR display feasibility
+152   - split payouts per club can be revisited after the machine-facing flow is stable
+153 - Expected payoff: closes the actual machine integration path instead of overinvesting in the standalone admin QR surface
+154
+155 ## P2 Sequenced Refactors
+156
+157 ### PAY-4 Split payments (MercadoPago Partners) per club
+158 - Priority: P2
+159 - Status: blocked
+160 - Depends on: OPS-1, PAY-2, PAY-3
+161 - Scope:
 143   - define a fixed Ballbox split-payments setup equivalent to v1 so every approved payment can route a configurable percentage to the club owning the vending machine
 144   - add an admin CTA in club configuration to start/link MercadoPago partner onboarding for that club
 145   - persist the minimum per-club MercadoPago partner metadata/credentials required for payout routing and operational support
 146   - document security and rotation rules for stored credentials before go-live
 147 - Notes:
 148   - implementation details are intentionally pending discovery when this task is started
-149   - keep this behind real-world payments validation first (`OPS-1`)
+168   - keep this behind real-world payments validation first (`OPS-1`) and machine-facing TCN adapter definition (`PAY-3`)
 150 - Expected payoff: platform-controlled split payments with club-aligned revenue distribution
 151
-152 ## P2 Sequenced Refactors
-153
 154 ### ARCH-4 Domain folder architecture
 155 - Priority: P2
 156 - Status: blocked
 157 - Depends on: ARCH-2, ARCH-3
     ...


- `/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.

- 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-2` -> `PAY-3`.

- 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.

- Ballbox persists approved payments locally through the native MercadoPago webhook flow at `/notification/mercadopago`.
- Production vending-machine payments should target a TCN adapter over that native MercadoPago layer; see `docs/tcn-payments-integration-plan.md`.


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

     ...
  28 - Maintainability review for the next refactor cycle is documented in `docs/maintainability-priorities-2026-03.md`.
  29 - 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.
  30 - 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.
  31 - `/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.
+ 32 - 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.
  32 - Legacy approved-callback compatibility has been removed from operations; `/api/integrations/payments/approved` now returns `410` so only the native MercadoPago path remains active.
  33 - `/admin/payments` now exposes Ballbox-local CRUD/reconciliation for approved payment sessions plus correlation checks against canonical `payment-vm_{vendingMachineId}-txn_{machineTxnId}` references.
  34 - Payments admin session routes now read Prisma error helpers from `lib/prisma-errors.ts` instead of importing them via `lib/ballbox-store.ts`.
  35 - Network core now has DB-backed admin CRUD for ATC-linked clubs, venues, and vending machines inside Ballbox.
     ...
 195 - Source of truth for task backlog and dependencies: `docs/next-steps-queue.md`.
 196 - Selection rule for the next agent: pick the highest-priority task marked `ready` in that doc.
 197 - Current pick (`P0`, `ready`): `OPS-1` payments real-world validation closure.
 198 - Immediately after `OPS-1`: `OPS-2` TVs runtime parity closure.
-199 - Then continue with `P1` in dependency order: `ARCH-2` and `ARCH-3` -> `PERF-1` -> `PAY-2` -> `PAY-3`.
+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.
 200 - Keep detailed backlog/status only in `docs/next-steps-queue.md` to avoid drift.
 201
 202 ### Product Continuation Constraints
 203 - Keep `sports` discovery honest: no claims beyond evidence in `docs/coaches-discovery-stage.md`.
     ...
 248 - Ballbox exposes the concrete per-screen TV runtime under `/player/[screenId]`.
 249 - TVs migration is not complete until the Streetcast parity checklist is reviewed and every legacy runtime feature is marked as covered, intentionally dropped, or replaced.
 250 - TVs runtime must support local cached playback for video creatives instead of relying only on remote streaming.
 251 - Ballbox persists approved payments locally through the native MercadoPago webhook flow at `/notification/mercadopago`.
+254 - Production vending-machine payments should target a TCN adapter over that native MercadoPago layer; see `docs/tcn-payments-integration-plan.md`.
 252 - Ballbox exposes network CRUD under `/admin/network` plus `/api/network*`.
 253 - Final schema ownership and relationships match `docs/domain-model-target.md`.
 254 - `pnpm lint`, `pnpm typecheck`, `pnpm test:contracts`, `pnpm test:e2e`, and `pnpm build` run successfully in local repo state.
 255 - `pnpm db:push` plus `pnpm db:seed` must keep demo `ClassOffer` bootstrap aligned with the shipped `/classes` MVP.
     ...


**Updating documentation**

I think I need to update the entry point in the documentation to include the new doc. It's important to keep the documentation current, so the users can easily find and use the information. I'll check to make sure I properly note all the changes required so that everything is consistent. This way, I can ensure that no details are missed and that the update process goes smoothly. Alright, let's get to it!


# Docs Index

## How To Navigate
- `PLAN.md`: current repo execution state and next implementation steps
- `docs/next-steps-queue.md`: prioritized backlog with dependency states
- `docs/repo-map.md`: fast file-level entrypoint for each domain
- `docs/implemented-*.md`: shipped behavior only
- `docs/*-rfc.md`: proposed implementation slices
- `docs/*-plan.md`: route/product planning
- `docs/*-discovery*.md`: validation and research framing
- `docs/*-gap-analysis.md`: what Ballbox still needs to build
- `docs/maintainability-priorities-2026-03.md`: prioritized frontend/backend maintainability work for the next refactor cycle
- `docs/demo-iteration-mode.md`: rapid demo delivery model with anti-debt guardrails

## Operator Priority Path
1. `PLAN.md`
2. `docs/next-steps-queue.md`
3. `docs/repo-map.md`
4. `docs/demo-iteration-mode.md`
5. relevant `docs/implemented-*.md` for the touched domain

## Current Refactor Input
- `docs/maintainability-priorities-2026-03.md`

## Execution And Coordination
- `docs/next-steps-queue.md`
- `docs/repo-map.md`
- `docs/demo-iteration-mode.md`

## Implemented State
- `docs/implemented-host-app.md`
- `docs/implemented-network.md`
- `docs/implemented-payments.md`
- `docs/implemented-players.md`
- `docs/implemented-sports.md`
- `docs/implemented-tvs.md`

## Discovery And Strategy
- `docs/coaches-discovery-stage.md`
- `docs/coaches-form-current.md`
- `docs/atc-integration-discovery.md`
- `docs/coaches-opportunity-brief.md`
- `docs/coaches-research-repository.md`
- `docs/coaches-validation-checklist-2-weeks.md`
- `docs/coaches-solution-thesis.md`

## 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`


## 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`


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

    ...
 48 - `docs/classes-subroute-plan.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`


**Considering memory storage**

I need to think about saving memory and whether I should use a skill. Maybe using simple bash curl is a good option? There’s also the instruction to use the agents database. I might need to save some high-signal project decisions. Should I check the API documentation? It might not be necessary, but I should consider it since creating memory can be useful. I’ll also quickly read through the memory-write load skill.


---
name: memory-write
description: Save a durable high-signal memory into agents-database
---

# memory-write

## Use When

- new information is likely useful beyond current chat or task
- saving a decision, preference, project fact, reusable source/artifact, or handoff summary
- user asks to remember something durable

## Do Not Use When

- content is trivial, redundant, ephemeral, or likely useless later
- content is secret unless user explicitly asked to store it

## Required Workflow

1. If useful context may already exist, optionally check first:
   - `curl -fsSL "${AGENTS_DB_API:-http://100.116.176.16:8091}/api/search?q=<query>&scope=global&scope=project&limit=10"`
2. Choose best memory type:
   - `profile`: stable user preference, priority, recurring constraint
   - `decision`: chosen approach or operating rule
   - `project`: durable project fact or invariant
   - `artifact`: reusable source, claim, reference, output
   - `episode`: concise session summary, blocked state, handoff context
3. Build a concise payload with:
   - required: `type`, `scope`, `title`, `content`
   - recommended when known: `subtype`, `project_id`, `repo_id`, `task_id`, `run_id`, `source_ref`, `origin_agent`, `url`, `domain`, `metadata`
4. Save it with the helper:
   - `~/.agents/skills/memory-write/memory-write --type <type> --scope <scope> --title "<title>" --content "<content>"`
5. Confirm the created memory id and what was stored.

## Quick Examples

```bash
~/.agents/skills/memory-write/memory-write \
  --type profile \
  --subtype working_preference \
  --scope global \
  --title "User prefers concise status updates" \
  --content "Sebas prefers short, direct updates with business impact first." \
  --source-ref "conversation:2026-04-17" \
  --origin-agent personal-agent

~/.agents/skills/memory-write/memory-write \
  --type decision \
  --subtype operating_rule \
  --scope global \
  --title "Agents should save durable high-signal memory opportunistically" \
  --content "Treat agents-database as shared durable memory, not only search." \
  --metadata '{"source":"conversation"}'
```

## Rules

- prefer one strong memory over many weak ones
- keep `title` short and searchable
- keep `content` factual and compact
- use `episode` for summaries, not for every chat turn
- include `url` and `domain` for web-derived artifacts when known
- include `project_id`, `repo_id`, `task_id`, and `run_id` when context exists


**Thinking about memory storage**

I’m considering whether I need to search existing memory, but maybe it’s not necessary. I think I'll proceed with writing to memory. I need to think about the project scope, but I'm not sure about the project ID or repo ID. I guess I'll just use the repo name for now. Let’s see how that works out!

bash

~/.agents/skills/memory-write/memory-write --type decision --subtype integration_direction --scope project --title 'TCN buyer QR uses on-screen display, not scanner hardware' --content 'TCN Protocol 2.5V states extra hardware is needed to scan QR codes, but the payment flow (FunCode=8000) returns CodeUrl for QR generation. Decision: treat scanner hardware warning as machine-side scan-only; Ballbox production vending payments should integrate through a TCN adapter and let the machine display the buyer QR. Keep /admin/payments QR page as an operator test/debug harness.' --source-ref 'test-results/tcn-payment-protocol/findings-2026-04-23.md' --origin-agent personal-agent --metadata '{"repo":"ballbox","doc":"docs/tcn-payments-integration-plan.md"}'


TCN buyer QR uses on-screen display, not scanner hardware

TCN Protocol 2.5V states extra hardware is needed to scan QR codes, but the payment flow (FunCode=8000) returns CodeUrl for QR generation. Decision: treat scanner hardware warning as machine-side scan-only; Ballbox production vending payments should integrate through a TCN adapter and let the machine display the buyer QR. Keep /admin/payments QR page as an operator test/debug harness.

Summary: TCN Protocol 2.5V states extra hardware is needed to scan QR codes, but the payment flow (FunCode=8000) returns CodeUrl for QR generation. Decision: treat scanner hardware warning…