fix(payments): soft-warn payout mismatch + surface manual payouts to schools (#499) - #507
Conversation
…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
|
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. After: entering an amount far from the suggested 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. After: a Method column (Manual vs. Stripe Connect) with the note shown, and the copy no longer implies Stripe-only. Flow recordingEnd-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. Please give this a visual once-over before merging. |
|
Merged as
No follow-ups spun off from this one — closes out #499. |





Description
Part of the payout money-accuracy work (epic #493, phase 2). Two independent gaps closed:
markPayoutPaidonly validatedamount > 0— a typo (extra zero, wrong decimal) recorded silently with no confirmation beyond the dialog's pre-filled suggested amount./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.ts—markPayoutPaidnow recomputes the tenant's currentnetOwedserver-side (reusing the existinggetPayoutsOwed(), never trusting a client-supplied value) and returns{ status: 'warning', netOwed }instead of inserting when the entered amount is more than 10% off. AconfirmMismatchflag 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 selectsnoteandpayout_method; a new "Method" column shows Manual vs. Stripe Connect with the note as a secondary line. Copy inmessages/en.json/es.jsonno longer implies Stripe-only.Type of change
Closes #499
Testing
npm run typecheck— passesnpm 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)/dashboard/admin/payoutsimmediately showed the payout with Method = Manual and the note.QA verification steps
netOwed > 0. Enter an amount within ~10% of the suggested value and submit — it should record in one step, no warning./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.