Skip to content

fix(payments): soft-warn payout mismatch + surface manual payouts to schools (#499) - #507

Merged
guillermoscript merged 1 commit into
masterfrom
fix/payout-mismatch-guardrail-499
Jul 24, 2026
Merged

fix(payments): soft-warn payout mismatch + surface manual payouts to schools (#499)#507
guillermoscript merged 1 commit into
masterfrom
fix/payout-mismatch-guardrail-499

Conversation

@guillermoscript

Copy link
Copy Markdown
Owner

Description

Part of the payout money-accuracy work (epic #493, phase 2). Two independent gaps closed:

  1. No overpayment guardrail. markPayoutPaid only validated amount > 0 — a typo (extra zero, wrong decimal) recorded silently with no confirmation beyond the dialog's pre-filled suggested amount.
  2. No manual-payout visibility for schools. The tenant-facing payout page (/dashboard/admin/payouts) already existed and is already RLS-scoped to the tenant's own rows, but its query and copy assumed Stripe Connect only — manually-recorded payouts (PayPal/Binance/Lemon Squeezy settlement) showed up with no note and copy that didn't fit them.

Changes made

  • app/actions/platform/payouts.tsmarkPayoutPaid now recomputes the tenant's current netOwed server-side (reusing the existing getPayoutsOwed(), never trusting a client-supplied value) and returns { status: 'warning', netOwed } instead of inserting when the entered amount is more than 10% off. A confirmMismatch flag lets the caller push through after the user confirms. No hard block — this is a soft warning, matching the issue's ask.
  • components/platform/mark-payout-paid-dialog.tsx — on a warning response, the dialog stays open and shows an inline banner ("Amount differs from suggested payout — suggested $X, entered $Y") with the submit button relabeled "Confirm anyway"; editing the amount or note clears the pending warning so a corrected value gets re-checked.
  • app/[locale]/dashboard/admin/payouts/page.tsx — the query now selects note and payout_method; a new "Method" column shows Manual vs. Stripe Connect with the note as a secondary line. Copy in messages/en.json / es.json no longer implies Stripe-only.

Type of change

  • Bug fix (overpayment guardrail)
  • Enhancement (tenant payout visibility)

Closes #499

Testing

  • npm run typecheck — passes
  • npm run lint — no new errors introduced (pre-existing baseline unrelated to these files unaffected; confirmed no errors under the three changed files)
  • npm run test:unit -- payouts-owed — 18/18 passing (pure balance-computation logic untouched by this change)
  • Manually verified end-to-end in the local app (see GIF + screenshots in the comment below): seeded a $250 platform-settled transaction for Default School (netOwed $200 at 80% split), entered $20 in "Mark as Paid" → inline mismatch warning appeared → "Confirm anyway" recorded it → tenant's /dashboard/admin/payouts immediately showed the payout with Method = Manual and the note.

QA verification steps

  1. As a super admin, open the payouts dashboard for a tenant with netOwed > 0. Enter an amount within ~10% of the suggested value and submit — it should record in one step, no warning.
  2. Repeat with an amount far from the suggested value (e.g. 10x smaller) — a warning banner should appear instead of submitting; the button should read "Confirm anyway". Clicking it should complete the payout.
  3. Edit the amount after a warning appears — the warning should clear, requiring a fresh check on next submit.
  4. As that tenant's school admin, open /dashboard/admin/payouts — the newly recorded manual payout should show a "Manual" method and its note, and the page description should no longer say "Stripe Connect" exclusively.

Screenshots

Before/after screenshots and a verification GIF are posted as a comment on this PR.

…schools (#499)

markPayoutPaid recomputes netOwed server-side and returns a warning (not a
hard block) when the entered amount is more than 10% off, requiring a
second confirm before recording — catches a typo like an extra zero.
The tenant-facing payout history page now selects payout_method/note and
shows a Method column so manually-recorded payouts (PayPal/Binance/Lemon
Squeezy settlement) are visible there too, not just Stripe Connect ones.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012udrSAiyuKyZRzfCmBQVU7
@guillermoscript guillermoscript added enhancement New feature or request payments Payment provider / billing related Sub-task labels Jul 24, 2026
@guillermoscript guillermoscript self-assigned this Jul 24, 2026
@guillermoscript

Copy link
Copy Markdown
Owner Author

Visual verification for #499 — before/after screenshots and a recorded GIF of the flow.

Super admin — Mark as Paid mismatch warning (Before → After)

Before: single-step submit, no check against the suggested amount.
issue-499-before-platform-payouts

After: entering an amount far from the suggested netOwed now shows the mismatch banner and requires "Confirm anyway" before it records.
issue-499-after-platform-payouts

Tenant admin — payout history visibility (Before → After)

Before: page copy and columns assumed Stripe Connect only; no way to see a manually-recorded payout's method or note.
issue-499-before-tenant-payouts

After: a Method column (Manual vs. Stripe Connect) with the note shown, and the copy no longer implies Stripe-only.
issue-499-after-tenant-payouts

Flow recording

End-to-end: super admin enters an amount far below the suggested payout, sees the warning, confirms anyway, then the tenant admin immediately sees the recorded manual payout with its note.
issue-499-verification

Please give this a visual once-over before merging.

@guillermoscript
guillermoscript marked this pull request as ready for review July 24, 2026 17:07
@guillermoscript
guillermoscript merged commit 3a36763 into master Jul 24, 2026
2 checks passed
@guillermoscript

Copy link
Copy Markdown
Owner Author

Merged as 3a367631. Final scope shipped as planned, no changes during review:

  • markPayoutPaid recomputes netOwed server-side and soft-warns (doesn't block) when the entered amount is more than 10% off the suggested payout, requiring a "Confirm anyway" click to push a mismatched amount through.
  • The tenant-facing /dashboard/admin/payouts page now surfaces payout_method/note, with a Method column distinguishing manual payouts from Stripe Connect disbursements, and copy that no longer implies Stripe-only.
  • Verified end-to-end locally (screenshots + GIF above): a mismatched amount triggers the warning banner, "Confirm anyway" completes it, and the tenant admin immediately sees the recorded manual payout with its note.
  • Checks: typecheck/lint clean (no new errors), payouts-owed unit suite 18/18, CI verify green.

No follow-ups spun off from this one — closes out #499.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request payments Payment provider / billing related Sub-task

Projects

None yet

Development

Successfully merging this pull request may close these issues.

No overpayment guardrail on Mark as Paid + no tenant-facing payout visibility

1 participant