Skip to content

fix(payments): settle through the admin client — RLS silently voided recipient confirmation - #534

Merged
catomean merged 1 commit into
mainfrom
fix/seller-confirm-rls-settle
Aug 2, 2026
Merged

fix(payments): settle through the admin client — RLS silently voided recipient confirmation#534
catomean merged 1 commit into
mainfrom
fix/seller-confirm-rls-settle

Conversation

@catomean

@catomean catomean commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator

Found by a live end-to-end payment simulation (no real money): RLS grants UPDATE on payment_intents to buyers only, so a recipient confirming an acknowledged bare-Lightning payment updated zero rows with no error — misread as a lost settle race, every side-effect skipped, API still answered "paid". The recipient-confirmation flow had never worked in production.

Status transitions are system facts recorded after authorization; they now write through the admin client (claimPaidTransition, updatePaymentStatus, handlePaymentConfirmed, and the NWC wallet read in checkNWCPaymentStatus). seller_confirm on a non-awaiting intent returns 400 instead of 500. Regression test pins the transition to the admin client.

🤖 Generated with Claude Code

…recipient confirmation

Found by a live end-to-end simulation: RLS grants UPDATE on payment_intents
to buyers only, so a recipient confirming an acknowledged bare-Lightning
payment updated ZERO rows with no error. claimPaidTransition misread that as
"another observer already settled it", skipped every side-effect
(notification, funding, order), and sellerConfirmPayment still answered
"paid". The recipient-confirmation flow had never worked in production.

A status transition is a system fact recorded AFTER the caller was
authorized — the caller's RLS visibility must never decide it:

- claimPaidTransition / updatePaymentStatus / handlePaymentConfirmed now
  write through the admin client (side-effects span multiple users' data:
  buyer's order, seller's notification, entity inventory).
- checkNWCPaymentStatus reads the seller's wallet via admin too — it only
  worked under caller clients because wallets_select is currently
  public-read; tightening that policy must not silently kill detection.
- seller_confirm on a non-awaiting intent now returns 400, not 500.
- Regression test pins the transition to the admin client and asserts the
  caller-scoped client is never asked to write.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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