PAT amount_type IDX 3366
ity": "CABA", "state": "Bs As", "complement": "Apto 303" } }, "transactions": { "payments": [ { "amount": "500.00", "payment_method": { "id": "master", "type": "credit_card", "token": "your_card_token_here", "installments": 1, "statement_descriptor": "MYSTORE" } } ] }, "config": { "split_rules": { "amount_type": "fixed" } }, "splits": [ { "user_id": {{SELLER_1_USER_ID}}, "amount": "350.00", "description": "Seller 1" }, { "user_id": {{SELLER_2_USER_ID}}, "amount": "150.00", "description": "Seller 2" } ], "items": [ { "title": "Sample product", "description": "Product description", "category_id": "electronics", "quantity": 1, "unit_price": "500.00" } ], "shipment": { "address": { "zip_code": "1515", "street_name": "Av. Cordoba", "street_number": "3003", "neighborhood": "Palermo", "city": "CABA", "complement": "Apto 123" } }, "integration_data": { "integrator_id": "dev_123", "platform_id": "1234567890" } }' Currently, for orders with Split Payments 1:N, only credit card and debit card payments are accepted. Parameter Type Description Required Authorization String (Header) test Access Token Private key of the application created in Mercado Pago, used in the backend. You can access it in Your integrations > Application details > Test > Test credentials . Test credentials Format: Bearer {{YOUR_ACCESS_TOKEN}} . Required x-idempotency-key String (Header) Unique identifier (UUID) to ensure request idempotency. Required x-meli-session-id String (Header) Device ID of the buyer's device. Required to improve payment approval rates. Required type String Order type. Use "online" for onlin

PAT percentage IDX 4925
String Order type. Use "online" for online payments. Required external_reference String External reference that identifies the order in your system. Required total_amount String Total order amount. The sum of splits.amount must equal this value when amount_type is "fixed" , or equal 100 when it is "percentage" . Required processing_mode String Payment processing mode. Use "automatic" for automatic processing. Required payer Object Buyer data. Required payer.email String Payer email. For testing use test@testuser.com . Required payer.first_name String Payer's first name. Optional payer.last_name String Payer's last name. Optional payer.entity_type String Entity type. Use "individual" for individuals. Optional payer.identification Object Payer's identification type and number. Optional payer.phone Object Payer's phone number. Includes: area_code , number . Optional payer.address Object Payer's address. Includes: street_name , street_number , zip_code , neighborhood , city , state , complement . Optional transactions Object Payment transactions object. Required transactions.payments Array List of payments. Required transactions.payments.amount String Payment amount. Required transactions.payments.payment_method.id String Payment method identifier (e.g., "master" ). Required transactions.payments.payment_method.type String Payment method type (e.g., "credit_card" ). Required transactions.payments.payment_method.token String Card token generated by tokenization. Required transactions.payments.payment_method.installments Integer Number of installments the payment is divided into.

PAT fixed IDX 3381
state": "Bs As", "complement": "Apto 303" } }, "transactions": { "payments": [ { "amount": "500.00", "payment_method": { "id": "master", "type": "credit_card", "token": "your_card_token_here", "installments": 1, "statement_descriptor": "MYSTORE" } } ] }, "config": { "split_rules": { "amount_type": "fixed" } }, "splits": [ { "user_id": {{SELLER_1_USER_ID}}, "amount": "350.00", "description": "Seller 1" }, { "user_id": {{SELLER_2_USER_ID}}, "amount": "150.00", "description": "Seller 2" } ], "items": [ { "title": "Sample product", "description": "Product description", "category_id": "electronics", "quantity": 1, "unit_price": "500.00" } ], "shipment": { "address": { "zip_code": "1515", "street_name": "Av. Cordoba", "street_number": "3003", "neighborhood": "Palermo", "city": "CABA", "complement": "Apto 123" } }, "integration_data": { "integrator_id": "dev_123", "platform_id": "1234567890" } }' Currently, for orders with Split Payments 1:N, only credit card and debit card payments are accepted. Parameter Type Description Required Authorization String (Header) test Access Token Private key of the application created in Mercado Pago, used in the backend. You can access it in Your integrations > Application details > Test > Test credentials . Test credentials Format: Bearer {{YOUR_ACCESS_TOKEN}} . Required x-idempotency-key String (Header) Unique identifier (UUID) to ensure request idempotency. Required x-meli-session-id String (Header) Device ID of the buyer's device. Required to improve payment approval rates. Required type String Order type. Use "online" for online payments. Req

PAT splits.amount IDX 4834
Device ID of the buyer's device. Required to improve payment approval rates. Required type String Order type. Use "online" for online payments. Required external_reference String External reference that identifies the order in your system. Required total_amount String Total order amount. The sum of splits.amount must equal this value when amount_type is "fixed" , or equal 100 when it is "percentage" . Required processing_mode String Payment processing mode. Use "automatic" for automatic processing. Required payer Object Buyer data. Required payer.email String Payer email. For testing use test@testuser.com . Required payer.first_name String Payer's first name. Optional payer.last_name String Payer's last name. Optional payer.entity_type String Entity type. Use "individual" for individuals. Optional payer.identification Object Payer's identification type and number. Optional payer.phone Object Payer's phone number. Includes: area_code , number . Optional payer.address Object Payer's address. Includes: street_name , street_number , zip_code , neighborhood , city , state , complement . Optional transactions Object Payment transactions object. Required transactions.payments Array List of payments. Required transactions.payments.amount String Payment amount. Required transactions.payments.payment_method.id String Payment method identifier (e.g., "master" ). Required transactions.payments.payment_method.type String Payment method type (e.g., "credit_card" ). Required transactions.payments.payment_method.token String Card token generated by tokenization. Required transactions.payme

PAT 100 IDX 4909
. Required type String Order type. Use "online" for online payments. Required external_reference String External reference that identifies the order in your system. Required total_amount String Total order amount. The sum of splits.amount must equal this value when amount_type is "fixed" , or equal 100 when it is "percentage" . Required processing_mode String Payment processing mode. Use "automatic" for automatic processing. Required payer Object Buyer data. Required payer.email String Payer email. For testing use test@testuser.com . Required payer.first_name String Payer's first name. Optional payer.last_name String Payer's last name. Optional payer.entity_type String Entity type. Use "individual" for individuals. Optional payer.identification Object Payer's identification type and number. Optional payer.phone Object Payer's phone number. Includes: area_code , number . Optional payer.address Object Payer's address. Includes: street_name , street_number , zip_code , neighborhood , city , state , complement . Optional transactions Object Payment transactions object. Required transactions.payments Array List of payments. Required transactions.payments.amount String Payment amount. Required transactions.payments.payment_method.id String Payment method identifier (e.g., "master" ). Required transactions.payments.payment_method.type String Payment method type (e.g., "credit_card" ). Required transactions.payments.payment_method.token String Card token generated by tokenization. Required transactions.payments.payment_method.installments Integer Number of installments the payment

PAT equal 100 IDX 4903
 rates. Required type String Order type. Use "online" for online payments. Required external_reference String External reference that identifies the order in your system. Required total_amount String Total order amount. The sum of splits.amount must equal this value when amount_type is "fixed" , or equal 100 when it is "percentage" . Required processing_mode String Payment processing mode. Use "automatic" for automatic processing. Required payer Object Buyer data. Required payer.email String Payer email. For testing use test@testuser.com . Required payer.first_name String Payer's first name. Optional payer.last_name String Payer's last name. Optional payer.entity_type String Entity type. Use "individual" for individuals. Optional payer.identification Object Payer's identification type and number. Optional payer.phone Object Payer's phone number. Includes: area_code , number . Optional payer.address Object Payer's address. Includes: street_name , street_number , zip_code , neighborhood , city , state , complement . Optional transactions Object Payment transactions object. Required transactions.payments Array List of payments. Required transactions.payments.amount String Payment amount. Required transactions.payments.payment_method.id String Payment method identifier (e.g., "master" ). Required transactions.payments.payment_method.type String Payment method type (e.g., "credit_card" ). Required transactions.payments.payment_method.token String Card token generated by tokenization. Required transactions.payments.payment_method.installments Integer Number of installments the pa

PAT marketplace fee IDX -1

PAT application fee IDX -1


**Considering memory usage**

This is an important project fact: the MP split payments of 1:N aren't compatible with the current QR vending flow, which only supports 1:1 for checkout. It seems saving this information could be useful in the future. However, I’m wondering if I should wait until the user clarifies before I create memory. I think I might need to look into how to use the memory-write skill, or perhaps just do a simple HTTP POST with a summary.


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


bash

~/.agents/skills/memory-write/memory-write --type decision --scope global --title "Mercado Pago split payments do not match current QR vending flow" --content "Mercado Pago Split Payments 1:1 is for marketplace models using Checkout Pro/API/Bricks and OAuth; Split Payments 1:N is only for advised-portfolio sellers via the commercial team and accepts online card/debit orders with card tokens. Ballbox currently creates QR orders under /v1/orders for vending machines, so native MP split payments is not a direct fit for the present payment flow." --source-ref "Mercado Pago Developers split-payments docs" --origin-agent "personal-agent" --url "https://www.mercadopago.com.ar/developers/en/docs/split-payments/split-1-1/prerequisites" --domain "mercadopago.com.ar"


Traceback (most recent call last):
  File "/home/sebas/.agents/skills/memory-write/memory-write", line 86, in <module>
    main()
    ~~~~^^
  File "/home/sebas/.agents/skills/memory-write/memory-write", line 80, in main
    with urllib.request.urlopen(req, timeout=30) as r:
         ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.13/urllib/request.py", line 189, in urlopen
    return opener.open(url, data, timeout)
           ~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.13/urllib/request.py", line 495, in open
    response = meth(req, response)
  File "/usr/lib/python3.13/urllib/request.py", line 604, in http_response
    response = self.parent.error(
        'http', request, response, code, msg, hdrs)
  File "/usr/lib/python3.13/urllib/request.py", line 533, in error
    return self._call_chain(*args)
           ~~~~~~~~~~~~~~~~^^^^^^^
  File "/usr/lib/python3.13/urllib/request.py", line 466, in _call_chain
    result = func(*args)
  File "/usr/lib/python3.13/urllib/request.py", line 613, in http_error_default
    raise HTTPError(req.full_url, code, msg, hdrs, fp)
urllib.error.HTTPError: HTTP Error 500: table memories has no column named schema_version

Command exited with code 1


**Evaluating payment models**

I need to consider that if the user wants a per-sale club share, the best model might be an internal ledger with periodic payouts instead of a marketplace split. I should back this up with current product flow and documents. I might want an additional data point about the QR order creation payment route, and I need to note that implementing this could be a big product shift requiring OAuth and separate accounts for each club. So, tracking split ratios seems like the simplest solution!

**Investigating Mercado Pago fees**

I need to check if Mercado Pago includes a `marketplace_fee` for QR orders. It looks like I haven’t found any evidence in the documentation that the QR `orders` endpoint supports platform fees. I should verify this by looking at the create order documentation for QR to see if there's a `marketplace` section. It seems like the docs mainly describe splits under checkout or online order flows. I’ll keep this in mind and also inspect the current request body for any split fields!

**Considering split payments**

I think if we want to challenge the idea of split payments with MP, it might not be the best approach right now because of the complexities involved, like accounting and KYC support. It makes sense to use an internal split ledger first. The user has encouraged us to challenge decisions, so I should do that strongly. Maybe we don't need to ask a question, but I can provide an investigation and recommendations. There seems to be a misunderstanding about implementing customizable percentages for clubs without MP splits.

**Highlighting specifics on splits**

I need to clarify some specifics regarding split payments. For a 1:1 split in marketplace models, it requires OAuth, seller accounts, and specific integrations like Checkout Pro/API/Bricks, but not QR. A 1:N split would involve multiple sellers and requires particular configurations, only available through the advised portfolio. Currently, the Ballbox QR order API lacks split fields, meaning this change can’t just be added. The best path forward seems to be using an internal ledger based on venue percentages and scheduling payouts. If direct MP splits are necessary, we may require a different integration.

**Considering customizable percentages**

I should consider that the customizable percentage might need to be set per club or venue, rather than hard-coding it just for individual machines. It could make sense to recommend a percentage based on venue or club in the network configuration. Since a vending machine operates within a club, the revenue share should ideally connect to the Venue or Club instead of the individual machine. However, if some machines have different commission rates, perhaps we could allow for overrides on a per-vending machine basis. It really is a design challenge!

**Exploring QR vending compatibility**

I'm noticing that the current QR vending path is gated and not compatible. If we want a Ballbox-owned QR flow with club shares, we shouldn't force the Mercado Pago split. Instead, it seems better to use an internal revenue ledger for payouts.

This challenges my initial assumptions since each club would require a Mercado Pago seller account and OAuth. Plus, I need to think about permissions, support, and how to handle chargebacks. Additionally, there are questions about split percentages and rounding rules.

**Rethinking percentage splits**

I'm considering how small ticket values, like vending machine prices, can cause rounding issues. For instance, a 5% share on ARS 150 might result in tricky minor-unit rounding. It could make more sense to use a fixed cents minimum instead of a direct percentage split. Since these sales are low value, that might create odd cents. Chargebacks and refunds can complicate things further, so I’ll also check if there's space to store club-specific metadata in the schema.

**Considering payment configurations**

The club has fields like `contactEmail` and `passwordHash`, but no payment fields. To set up split configuration, we might need to add to the schema; I should mention this. Investigating past payment split decisions could be useful, but maybe not necessary. Since the user wants me to investigate, I can note that there's an existing queue entry for PAY-3 that’s blocked. It's important that we point out the current status of PAY-2, which is complete, but PAY-3 is blocked on specific tasks.

**Exploring payment integration options**

I'm considering if I need to use a subagent, but it might not be necessary. It could be helpful to look into `docs/implemented-payments.md` or `app/api/integrations/payments/orders/route.ts` to clarify the current flow. Responding with structured bullets sounds like a good idea. I might outline: what MP split payments can do, how they fit with the current Ballbox flow, my recommendations or challenges, and finally, any next decision questions. I should stress that I don't want to anchor this to MP split payments by default.

**Analyzing payment flow options**

I'm checking the current Ballbox payment flow with the MP documents. It looks like Ballbox uses QR orders through the `/v1/orders` endpoint with a `type: qr` parameter. I found two MP split payment options:

1. Split Payments 1:1, which applies to checkout models but not QR flows.
2. Split Payments 1:N, which supports fixed or percentage splits but is limited to advised portfolio sellers.

I conclude that using native MP split for vending machine QR payments isn't straightforward. Instead, I'll rely on a Ballbox-owned revenue share ledger and periodic settlements unless there's a strong need for direct payouts. If implemented, a model focusing on venue shares seems best.

**Considering backlog items**

I should mention that the existing backlog item PAY-3 is already related to this discovery, but I'm not sure if it's necessary to include this detail. I could say something like, "This is already your blocked PAY-3 item." It might be helpful to ask the user if they want me to map out exact implementation options since they mentioned investigating. If they’re interested, I can offer to create a two-option implementation plan, even though they didn’t explicitly request it. Should I ask that?

Checked.

Bottom line: native Mercado Pago split payments is **not a drop-in fit** for the current Ballbox vending QR flow.

What MP docs say:
- **Split Payments 1:1**: marketplace model, works with **Checkout Pro / Checkout API / Checkout Bricks**, needs **OAuth** and seller authorization. Not the QR vending flow.
- **Split Payments 1:N**: supports **fixed or percentage splits**, but it’s for **online card/debit payments with card tokenization**, and docs say it’s only for **advised-portfolio sellers via the commercial team**. Also not the QR vending flow.

What Ballbox does today:
- Creates **QR orders** at `/v1/orders`
- Uses `type: "qr"` + `external_pos_id`
- No split fields in the current order payload

So:
- If you want **direct per-sale payout to the club inside MP**, this likely means a **different payment product / different checkout path**, plus club onboarding and commercial-team dependency.
- If you want the feature fast and reliable, I’d **not** force MP split payments into the current flow.

My challenge to the current idea:
- For a vending machine, the clean business rule is probably **venue/club revenue share in Ballbox**, not MP-native split.
- Then settle to clubs on a schedule.
- That avoids: club MP onboarding, OAuth, payout support, chargeback split complexity, and product lock-in to MP’s marketplace rules.

If we do build it, I’d model it as:
- **basis points**, not float percentage
- default at **venue** level, with optional machine override
- explicit rounding rules
- explicit refund/chargeback rules

This is already aligned with your blocked `PAY-3` backlog item, but I’d treat “MP-native split” as a **later integration choice**, not the default architecture.

If you want, I can next map:
1. **Ballbox-led internal split + settlement**
2. **Mercado Pago native split**
3. **Hybrid**
and tell you which one is lowest risk.


okay scheduled settlements it is then. Dont know what how you plan to help with that. probably something we can bookkeep manually for now (max 5 clients), but do document

and modify/create plans for possible future options if we decide to do split (MP native or otherwise), with the full context of our situation, pros/cons, etc


**Updating plans and documentation**

I need to respond directly, focusing on updating the documentation and plans in the repo based on what the user asked. This involves inspecting current files, possibly updating the PAY-3 and the payments documentation. User prefers manual bookkeeping for now, which indicates a change. I might also consider creating a decision document about the payout strategy. However, since there was a previous issue with memory due to a DB schema problem, I’ll note that but might avoid unnecessary details unless prompted.

**Updating documentation for PAY-3**

I need to update the PLAN.md and the next-steps-queue documentation to clarify that PAY-3 is not blocked. I might consider keeping the blocked status but adding a new task for manual settlement or adjusting the scope and notes for PAY-3. It’s important for me to review the existing documentation related to the payment process and maybe the domain model. So, let's search for relevant payments planning documents to ensure I'm fully informed!

grep -RIn "PAY-3\|settlement\|club share\|split payments\|payout" docs PLAN.md app lib tests | head -n 200


docs/coach-availability-rfc.md:206:- Coaches can also save a Mercado Pago alias/email placeholder for future payout wiring.
docs/next-steps-queue.md:139:### PAY-3 TCN vending-machine adapter over native MercadoPago
docs/next-steps-queue.md:152:  - split payouts per club can be revisited after the machine-facing flow is stable
docs/next-steps-queue.md:160:- Depends on: OPS-1, PAY-2, PAY-3
docs/next-steps-queue.md:164:  - persist the minimum per-club MercadoPago partner metadata/credentials required for payout routing and operational support
docs/next-steps-queue.md:168:  - keep this behind real-world payments validation first (`OPS-1`) and machine-facing TCN adapter definition (`PAY-3`)
docs/next-steps-queue.md:169:- Expected payoff: platform-controlled split payments with club-aligned revenue distribution
docs/tcn-payments-execution-plan.md:66:## Step 6. Only then revisit payout split work
docs/tcn-payments-execution-plan.md:67:- Goal: avoid solving club payout distribution before the machine path is stable.
PLAN.md:200:- Then continue with `P1` in dependency order: `ARCH-2` and `ARCH-3` -> `PERF-1` -> `PAY-3`.
PLAN.md:201:- `PAY-4` (split payments per club) stays behind the TCN machine-facing adapter work.
PLAN.md:214:   - backlog: `PAY-3` in `docs/next-steps-queue.md`
PLAN.md:218:6. Revisit split payouts only after the machine-facing payment flow is stable.


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

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

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


# Ballbox Host App Plan

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

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

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

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

## Implemented
- Shared admin shell and product-separated routes are in place.
- Admin shell copy now adapts per route instead of repeating one generic hero across every module.

[176 more lines in file. Use offset=141 to continue.]


  - legacy approved-callback route reduced to transitional compatibility only; native path is now the documented operational flow
- Evidence:
  - `/notification/mercadopago`
  - `/api/integrations/payments/orders`
  - `app/api/integrations/payments/health/route.ts`
  - `app/api/integrations/payments/sessions/route.ts`
  - `docs/implemented-payments.md`
- Expected payoff: one-repo payments ownership, simpler operations, faster debugging

### PAY-3 TCN vending-machine adapter over native MercadoPago
- Priority: P1
- Status: ready
- Depends on: OPS-1, PAY-2
- Scope:
  - define the Ballbox machine-facing adapter for TCN payment init (`FunCode=8000`) over the existing MercadoPago order-create route
  - map TCN `MachineID`, `TradeNo`, `SlotNo`, `Price`, `NotityUrl`, and response `CodeUrl` to Ballbox-native payment fields
  - define the payment-success handshake back to TCN so approved payments can unlock dispense
  - keep `/admin/payments` positioned as an operator test/debug harness rather than the production vending UX
  - document the exact adapter contract and remaining TCN unknowns before implementation
- Notes:
  - TCN protocol evidence is documented in `docs/tcn-payments-integration-plan.md`
  - do not assume extra scanner hardware for the buyer flow; the open question is adapter contract detail, not QR display feasibility
  - 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

## P2 Sequenced Refactors

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

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


**Reviewing payment plans**

I need to modify the plans related to implemented payments because the user mentioned scheduled settlements. It seems sensible to update the next steps queue to include a new task for settlement bookkeeping and future options. I could create a new document with strategy options, including their pros and cons, and update implemented-payments with a manual settlement strategy. I’ll also check for consistency between PAY-3 and PAY-4 in the PLAN to simplify things. Maybe I can add a new task for a scheduled settlement ledger.

**Organizing payment tasks**

I need to consider setting a task in the queue to create a new document for "PAY-4 Scheduled settlements and future split options," replacing the current split payments item. However, since the user now wants manual bookkeeping, I think it would be better to create a new task called `PAY-4 Club settlement ledger (manual v1)` instead. This keeps things organized and allows me to update the backlog with a new task for `PAY-4 Manual scheduled settlements v1`. I’ll also check the existing docs to see if they mention anything relevant.

**Examining payment documentation**

I see that the payments implementation document references the current production path. If manual settlements aren't a coding issue, it might make more sense to document them in either `docs/tcn-payments-execution-plan.md` or create a new file like `docs/payments-settlement-options.md`. I should check the existing tcn-payments-execution-plan to see what information is there and decide how to proceed with documenting the settlements appropriately.


# TCN payments execution plan

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

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

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

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

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

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

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


**Considering document updates**

I think we can consider updating the documentation, possibly adding a new step to mention scheduled settlements. The user asked about creating plans for future options concerning any splits, which suggests we might need a dedicated document for that. I should check if any existing documentation addresses splits or payments. Also, I’m wondering if I should update the index to include this new doc – maybe it’s necessary, depending on how it's organized!


# 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`
- `docs/tcn-payments-integration-plan.md`
- `docs/tcn-payments-open-questions.md`
- `docs/tcn-payments-execution-plan.md`


grep -n "PAY-3\|PAY-4\|split payouts\|machine-facing payment flow\|revisit payout split" -n /mnt/rpi/repos/ballbox/PLAN.md /mnt/rpi/repos/ballbox/docs/next-steps-queue.md /mnt/rpi/repos/ballbox/docs/tcn-payments-execution-plan.md


/mnt/rpi/repos/ballbox/PLAN.md:200:- Then continue with `P1` in dependency order: `ARCH-2` and `ARCH-3` -> `PERF-1` -> `PAY-3`.
/mnt/rpi/repos/ballbox/PLAN.md:201:- `PAY-4` (split payments per club) stays behind the TCN machine-facing adapter work.
/mnt/rpi/repos/ballbox/PLAN.md:214:   - backlog: `PAY-3` in `docs/next-steps-queue.md`
/mnt/rpi/repos/ballbox/PLAN.md:218:6. Revisit split payouts only after the machine-facing payment flow is stable.
/mnt/rpi/repos/ballbox/PLAN.md:219:   - backlog: `PAY-4` in `docs/next-steps-queue.md`
/mnt/rpi/repos/ballbox/docs/next-steps-queue.md:139:### PAY-3 TCN vending-machine adapter over native MercadoPago
/mnt/rpi/repos/ballbox/docs/next-steps-queue.md:152:  - split payouts per club can be revisited after the machine-facing flow is stable
/mnt/rpi/repos/ballbox/docs/next-steps-queue.md:157:### PAY-4 Split payments (MercadoPago Partners) per club
/mnt/rpi/repos/ballbox/docs/next-steps-queue.md:160:- Depends on: OPS-1, PAY-2, PAY-3
/mnt/rpi/repos/ballbox/docs/next-steps-queue.md:168:  - keep this behind real-world payments validation first (`OPS-1`) and machine-facing TCN adapter definition (`PAY-3`)
/mnt/rpi/repos/ballbox/docs/tcn-payments-execution-plan.md:66:## Step 6. Only then revisit payout split work
/mnt/rpi/repos/ballbox/docs/tcn-payments-execution-plan.md:69:  - `PAY-4` in `docs/next-steps-queue.md`


**Updating payment documentation**

I need to create a new document and update my plans regarding payments. If I create a new doc, I'll ensure it's included in the docs/index. I might also need to revise the execution plan to mention the new settlements document. The user's decision is to go with scheduled settlements, up to five clients, relying on manual bookkeeping. I should describe this in my implemented payments section, noting that manual settlements for five or fewer clients are still a plan, not yet shipped.

**Creating a payment settlements plan**

I need to make a document called `payments-club-settlements-plan.md` with several sections. It will outline the decision to settle manually for now and discuss why we're not using MP split just yet. I'll detail an operating model for manual monthly/weekly settlements for a maximum of five clubs, including necessary data to record. I'll also include options for the future, such as adding a manual ledger and scheduled transfers and considerations for alternative processors, weighing their pros and cons. I’ll add a recommendation matrix for future automation.

**Evaluating payment options**

I need to consider that since the vending flow uses QR orders, the MP split isn't a perfect fit because it requires specific setups like Checkout Pro/API or OAuth. I’m thinking of editing the `next-steps-queue` document to possibly replace PAY-4 with a task focused on settlement bookkeeping. The user mentioned that scheduled settlements will be done manually for now, but it's essential to document potential future options for split payments. So, no coding is required at this stage!

**Evaluating backlog adjustments**

I’m considering how to handle the backlog, particularly with items like "PAY-4 Club settlement ops v1.” The user didn’t ask for changes, but they did mention modifying plans. So, I think updating the queue to reflect the current state of manual settlement makes sense. Future split work could be deprioritized. Maybe I should set PAY-4 as ready and create a new item for "Manual club settlement ledger v1," since manual bookkeeping is low complexity.

**Updating plans and documentation**

Since the user wants plans for future options, I’m considering keeping the split as P2 blocked but updating its notes to reflect that we're currently focusing on manual settlements. I could also add a document for the "settlement operating model" and update the queue item for current manual operations. However, the queue is meant for implementation tasks, so I need to be careful not to overcomplicate it. I think updating relevant docs to clarify the current strategy would be helpful too.

**Clarifying settlement processes**

I could mention that for now, club shares are being handled off-platform through manual settlement bookkeeping, while split-payment automation is deferred. It feels plan-like, which might be okay for our current operational model. However, if it's not implemented yet, it should probably be in a validation status rather than labeled as implemented. Adding a bullet to the Validation Status section about this decision makes sense. I’ll also need to update PLAN.md to reflect the current state in the payments section.

**Exploring payment methods**

I'm thinking about how to organize information on payment methods. It might be good to place a new bullet point under the current state section to clarify that manual scheduled settlements are the default option while also noting that split automation is deferred. I'll want to read around those lines for context to provide a clearer view. There’s a lot to consider, so let's make sure we get it right!


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

[217 more lines in file. Use offset=100 to continue.]


**Updating payment tasks**

I’m thinking about inserting a new bullet point after the existing payment bullets for clarity. I need to update the docs/next-steps-queue, especially regarding PAY-3 and PAY-4. I might add a note to PAY-4 about manual settlements and future splits. Perhaps I should create a new task for settlement ledger bookkeeping. However, the user is looking for a modified plan, not an implementation, so I need to be mindful of that. Adding a task like `Club settlement operations v1` could be useful.

**Considering manual bookkeeping**

I’m realizing the queue is like a backlog, but the user wants to "bookkeep manually for now." So, creating a task for a manual ledger with exports might be handy. Since we’re currently managing a maximum of five clients, keeping things manual through a spreadsheet avoids app changes. The user explicitly wants to document and modify plans for possible future options related to split payments. I'll update the next-steps queue note for PAY-4 to reflect "Future club settlement automation," noting that manual settlement is the default for now.

**Exploring settlement documentation**

I’m considering how to reflect our decisions in the documentation—perhaps in the implemented-payments and PLAN files. Maybe a new document on settlement strategy would help keep the queue focused on future options since there’s no implementation to address right now. I’ll check if there's an existing document for TCN and potential splits. Adding a new doc called `payments-club-settlement-strategy.md` and updating the index seems like a good idea. I can summarize manual schedule settlements and the maximum of five clients in this new doc.

**Drafting club settlement strategy**

I'm working on the content for a new document about our club settlement strategy. For now, I'll use scheduled settlements and manual bookkeeping. The plan includes a maximum of five clubs, each with a vending machine, and controlling gross sales through Ballbox. In the operating model, I’ll set revenue shares based on certain metrics, track sales, and implement manual payouts. I’m also thinking about future automation options and when to revisit our recommendations based on various triggers.

**Clarifying current payment flow**

I’m thinking about how the current payment flow doesn’t align with MP split methods, particularly since split documentation is focused on online card checkouts. It seems I should say we’re not building a club payout UI for now, and emphasize that club revenue sharing will be manual for the time being. I want to also mention this decision in our documentation, adding a note about the current state and next steps regarding manual settlements. That way, it keeps everything clear and factual for the user.

**Updating club revenue share plan**

I need to update the current state bullet to say that club revenue sharing is currently managed through manual scheduled settlements, with automation as a possibility for the future. That should help set clear expectations. I also want to mention the `PAY-4` future options document towards the end of the plan. I'll have to look through lines 190 to 225 to find the right place for the next-step queue pointer. That way, everything will be nicely organized!


  - manifest polling exists in `components/screen-player.tsx`
  - sequential creative playback exists in `components/screen-player.tsx`
  - impression writes exist in `components/screen-player.tsx`
  - runtime health signaling exists via heartbeats in `components/screen-player.tsx`
- Missing or not yet verified against Streetcast behavior:
  - no manual real-device verification yet proves CacheStorage/object-URL playback outside local browser automation

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

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

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

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

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

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


**Updating payment details**

I need to think about updating this section. I might change the PAY-4 line to reflect manual settlements instead. It could be easier to update the PLAN current state payment bullets and ordered payments to include the new document. I'll note that club revenue sharing is manual right now, with future automation being another option. I might also want to change the line about PAY-4 split payments to clarify that it's currently manual. Renaming the next-steps queue seems like a good idea too.

**Clarifying settlement options**

I should add a note mentioning that the current operating default is manual scheduled settlements, while future automation options are documented elsewhere. Since there's no implementation yet, the queue item can reflect future options. It might make sense to rename PAY-4 to "Club settlement automation and split options," keeping its status as blocked. My scope could include keeping manual settlements as a baseline, documenting future automation options, and only implementing automation when necessary. However, I wonder if it’s better to create a separate document for this.

**Aligning documentation**

I think we should update the description to reflect the current manual schedule as the default, along with the future options document. Keeping the status as “blocked” seems reasonable. Additionally, I should consider creating a new document and revising the doc index accordingly. It could be helpful to add a small section in `docs/implemented-payments.md` related to validation status. I’ll also update the PLAN and next steps. Finally, I'm contemplating if step 6 in `docs/tcn-payments-execution-plan.md` will become manual settlement.

write

# Club settlement strategy

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

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

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

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

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

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

## Future options if we later automate

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

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

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

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

### Option B. Keep Ballbox collection, settle clubs later
Current default.

Pros:
- simplest and fastest
- no club onboarding dependency
- no payment-provider coupling
- easy to run with a small club count

Cons:
- manual ops burden
- weaker audit trail unless bookkeeping is disciplined
- not ideal once club count or payment volume grows

Fit:
- best for pilots and low client count

### Option C. Non-MP split / payout rail later
Examples:
- direct bank payouts from Ballbox
- another PSP with marketplace/payout features
- an accounting layer that exports payout files for treasury ops

Pros:
- may fit vending better than MP split
- can preserve Ballbox collection flow
- gives more control over settlement timing and policy

Cons:
- more integration work
- more compliance / treasury overhead
- still needs support around refunds, chargebacks, and reversals

Fit:
- worth revisiting if vendor lock-in becomes a problem or manual ops stop scaling

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

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


Successfully wrote 3592 bytes to /mnt/rpi/repos/ballbox/docs/payments-club-settlement-strategy.md


**Organizing document updates**

I’m focusing on editing the document index to add new files like the PLAN and others. I should craft precise replacements throughout. I’ll insert a new bullet for the current state after “payments,” and modify the next steps line. I want to ensure better tracking for ordered and implemented payments. I might also update the PAY-4 heading and scope, then determine relevant snippets to insert under Current Build Docs. This requires careful editing!

**Updating payment documentation**

I’m considering replacing some text in the documentation. I’ll keep the order for `P1`: `ARCH-2` and `ARCH-3`, followed by `PERF-1` and `PAY-3`. Regarding club revenue, I'll mention that it’s currently managed through scheduled manual settlements, and that `PAY-4` will remain behind the TCN machine-facing adapter work as an automation option. I might add a line about the documentation in the Ordered Payments Track, especially around step 6. Plus, I want to rename the PAY-4 block to reflect its new status.

**Planning manual settlement automation**

I’m outlining the scope for future automation while keeping manual scheduled settlements as the current default. I’ll document options like MP native split, alternate PSPs, or other payout methods. If Ballbox automates in the future, I want to include club onboarding and security rules, but I won’t start until the machine-facing flow stabilizes. It seems appropriate to mention that we shouldn’t block this just for documentation. However, since the user requested modifications, I might choose to leave the status blocked for now.

**Documenting updates**

I'm thinking about the current state that's already been documented, which is fine. I wonder if I should update the PAY-3 notes to mention future split documents. Maybe that’s not entirely necessary? In any case, I’ll go ahead and apply the edits across multiple files just to keep everything aligned and organized. It feels important to maintain clarity in documentation!

edit

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

- Then continue with `P1` in dependency order: `ARCH-2` and `ARCH-3` -> `PERF-1` -> `PAY-3`.
- Club revenue share is currently handled with scheduled manual settlements; `PAY-4` stays behind the TCN machine-facing adapter work as a future automation option.

6. Revisit split payouts only after the machine-facing payment flow is stable.
   - backlog: `PAY-4` in `docs/next-steps-queue.md`

6. Revisit settlement automation only after the machine-facing payment flow is stable.
   - backlog: `PAY-4` in `docs/next-steps-queue.md`
   - doc: `docs/payments-club-settlement-strategy.md`


Successfully replaced 2 block(s) in /mnt/rpi/repos/ballbox/PLAN.md.

     ...
 197 - Selection rule for the next agent: pick the highest-priority task marked `ready` in that doc.
 198 - Current pick (`P0`, `ready`): `OPS-1` payments real-world validation closure.
 199 - Immediately after `OPS-1`: `OPS-2` TVs runtime parity closure.
 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.
+201 - Club revenue share is currently handled with scheduled manual settlements; `PAY-4` stays behind the TCN machine-facing adapter work as a future automation option.
 202 - Keep detailed backlog/status only in `docs/next-steps-queue.md` to avoid drift.
 203
 204 ## Ordered Payments Track
 205 1. Finish provider-level Mercado Pago proof without TCN hardware.
     ...
 214    - backlog: `PAY-3` in `docs/next-steps-queue.md`
 215    - docs: `docs/tcn-payments-execution-plan.md`
 216 5. Validate on real TCN hardware once machines arrive.
 217    - docs: `docs/tcn-payments-execution-plan.md`
-218 6. Revisit split payouts only after the machine-facing payment flow is stable.
+218 6. Revisit settlement automation only after the machine-facing payment flow is stable.
 219    - backlog: `PAY-4` in `docs/next-steps-queue.md`
+220    - doc: `docs/payments-club-settlement-strategy.md`
 220
 221 ### Payments Track Docs
 222 - Implemented provider layer: `docs/implemented-payments.md`
 223 - TCN direction and target flow: `docs/tcn-payments-integration-plan.md`
     ...


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

## 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`.
- Club revenue share is currently handled through scheduled manual settlements/bookkeeping; no provider-native split payout path is live.
- 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 1 block(s) in /mnt/rpi/repos/ballbox/docs/implemented-payments.md.

    ...
 40
 41 ## Validation Status
 42 - Host app now owns the native MercadoPago order creation + webhook persistence path inside Ballbox.
 43 - Remaining gap is operational/manual: one real public webhook + one real approved payment persisted and visible in `/admin/payments`.
+44 - Club revenue share is currently handled through scheduled manual settlements/bookkeeping; no provider-native split payout path is live.
 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`.
 46 - 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.
 47
    ...


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

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


Successfully replaced 1 block(s) in /mnt/rpi/repos/ballbox/docs/tcn-payments-execution-plan.md.

    ...
 62   - capture timing, retries, and failure cases
 63 - Exit criterion:
 64   - one real vend succeeds through Ballbox-backed payment flow
 65
-66 ## Step 6. Only then revisit payout split work
+66 ## Step 6. Only then revisit payout automation
 67 - Goal: avoid solving club payout distribution before the machine path is stable.
-68 - Related backlog item:
+68 - Current default:
+69   - scheduled manual settlements / bookkeeping
+70 - Related docs:
+71   - `docs/payments-club-settlement-strategy.md`
 69   - `PAY-4` in `docs/next-steps-queue.md`