Skip to content

feat(rust-core): buyer confirm-receipt and dispute endpoints - #43

Merged
DCT-Berinyuy merged 1 commit into
devfrom
rust-confirm-receipt-dispute
Sep 27, 2026
Merged

DCT-Berinyuy merged 1 commit into
devfrom
rust-confirm-receipt-dispute

Conversation

@DCT-Berinyuy

Copy link
Copy Markdown
Collaborator

Why

Right now confirm-receipt and dispute go through the process-escrow edge function, which caused tonight's incident (GHSA-xq84-qm9j-hf6p). This PR is step 1 of moving them to Rust before any real money moves. Rust already has the safe payout path (release_escrow), which checks that both rows are held and asks Fapshi whether a payout already exists, failing closed if it can't tell.

What

  • POST /escrow/confirm-receipt {transaction_id}
  • POST /escrow/dispute {transaction_id, dispute_reason}
  • Callers must send Authorization: Bearer <Supabase access token>. The token is checked with GET {SUPABASE_URL}/auth/v1/user. An invalid token gets 401; if Supabase Auth itself fails, the endpoint returns 502 with a generic message.
  • Only the buyer of a held transaction can act. Someone else's transaction gets 404, same as one that doesn't exist, so ids can't be probed. A transaction that isn't held gets 409.
  • Dispute takes the same in-process lock as release (InProgressGuard, pulled out of release_escrow). It then updates the escrow and the transaction in one DB transaction, and both updates only apply if the row is still held.

Deploy notes

  • New required env vars on Render: SUPABASE_URL and SUPABASE_ANON_KEY. The service won't start without them.
  • The lock only works within one process, so this assumes a single instance, same as release_escrow today.

Not in this PR (next steps)

  1. Point the app's confirmReceipt/disputeTransaction at these endpoints.
  2. Point the hourly auto-release-escrows-job cron at /internal/escrow/process-releases, or unschedule it.
  3. Delete supabase/functions/process-escrow and remove it from Supabase.
  4. Rate limiting on the new public endpoints.

Test plan

  • cargo test: new tests/buyer_tests.rs (13 tests) runs against a mock Supabase Auth server. It covers missing, malformed and invalid tokens, auth running before the body is read, auth outages, a valid token reaching the DB, malformed bodies, reason validation, a dispute during an in-progress payout (409, lock kept), and the lock being released afterwards.
  • cargo clippy --all-targets -- -D warnings is clean.
  • Gap: the dispute SQL hasn't been run against the real schema, because the repo has no full schema to build a local database from. Check it on a Neon/Supabase branch, or with one test transaction, before switching the app over.

Add authenticated POST /escrow/confirm-receipt and /escrow/dispute so the
app can stop using the process-escrow edge function for these actions.

- Verify the caller's Supabase access token via GET /auth/v1/user
- Only the buyer of a held transaction can act; others get 404
- Confirm-receipt reuses release_escrow (held checks, payout dedup)
- Dispute takes the same in-process lock as release and updates escrow
  and transaction atomically with status guards
- New required env vars: SUPABASE_URL, SUPABASE_ANON_KEY

Co-authored-by: Copilot App <[email protected]>
@vercel

vercel Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
book-bridge Ready Ready Preview Sep 27, 2026 9:35pm UTC

@DCT-Berinyuy
DCT-Berinyuy merged commit da9495b into dev Sep 27, 2026
4 checks passed

This branch was successfully deployed

1 active deployment
Preview — c871845e Deployed Sep 27, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant