Investigate possible double refund on single click

Report

Rein processed a credit card refund on inspection 1008599788 and reports clicking Refund once, but the payments section shows two refunds for the same payment.

Rein suspects a double-click (mouse malfunction). No immediate customer action — client is checking with their bank. Rein is waiting before settling balance.

Triage verdict

Likely bug — missing double-submit / concurrency protection on Guardian card refunds.

Code review shows the product intends to block duplicate refunds (payment.status === 'refunded', remaining refundable amount checks, queued-refund guard), but POST /guardian-payments/refund does not use idempotency keys or a payment-level lock. Two requests in quick succession can both pass validation, both call CardPointe, and both create Refund rows if the processor accepts both.

Frontend (GuardianRefundHubModal) sets isLoading / isDisabled on confirm, but has no synchronous in-flight guard — a fast double-click can fire two submits before React re-renders.

Key code paths:

  • attik-backend/src/routes/guardianPayments.tsPOST /refund
  • attik-frontend/src/app/tools/inspections/[id]/components/GuardianRefundHubModal.tsx

Goal for this issue

Confirm whether this incident was caused by the double-submit race (vs. e.g. principal + fee-only refund, or queued + immediate refund).

Ryan does not have access to refund request timestamps — needs a teammate with DB / logs / Guardian visibility to complete confirmation.

Confirmation checklist (for assignee)

On inspection 1008599788, pull the two refund records for the affected Guardian card payment and compare:

  • [ ] createdAt on both Refund documents — milliseconds apart strongly suggests double-submit
  • [ ] amount on each — two full principal refunds vs. principal + fee-only (refundFeeOnly: true, amount: 0)
  • [ ] refundType / staterefund vs void, approved vs batched vs queued
  • [ ] guardianRefundId (CardPointe retref) — distinct values = two separate processor calls
  • [ ] Payment status and total refunded after both rows
  • [ ] API / server logs around guardian-payments/refund for the same paymentId — two POSTs within ~1s

If confirmed as double-submit bug

  1. Document findings on this issue (timestamps, amounts, retrefs).
  2. Coordinate with Guardian/CardPointe if a duplicate principal refund needs reversal.
  3. Engineering fix: payment-level idempotency or lock around refund; frontend useRef in-flight guard; keep confirm disabled through success.
  4. QA: rapid double-click Refund/Void on a test Guardian card payment.

If not confirmed

Note alternative root cause (e.g. fee-only second row, queued processor path) and close or re-scope accordingly.

Please authenticate to join the conversation.

Upvoters
Status

Next Up

Board
🏠

Main App

Date

About 22 hours ago

Author

Linear

Subscribe to post

Get notified by email when there are changes.