fix: payments hardening — SCA wire contract (saved-card ref + tokenize-result), terminal/till token routing, tip-cap overflow carve, completion campaign atomicity, orphan B1-evidence gate, gift-card gates/locks, admin backstops
- ValidateCardInfo accepts saved-card ref + new_card_token coexistence (matches resolveChargeSource); new_card_token added to terminal/till request structs so SCA tokens are never dropped - maxOnlineTipPence (£250) enforced on the overflow-tip carve AND buildSplitRecords (both carve paths) — closes the £10k bypass - completion-path campaign increments made atomic reserve-first (conditional UPDATE ... RETURNING) + schema backstops (chk_times_redeemed, partial unique index on milestone redemptions) - webhook orphan detection gated on B1 evidence (b1_attempts / sweep-duplicate refund row) so a delayed legit completion is never marked failed - gift-card: per-user £500/day cap lock held across read-modify-write, expired-card top-up gate, NaN/Inf float bounds, refund_failed ack filter, on_the_house excluded from balance, postChargeRecheck notification - admin apply-redemption route + admin-or-owner, in-handler isAdminRequest on 4 gift-card handlers, tip lock key aligned - 2FA fallback machinery removed (insertTwoFAFallbackAudit/reissue/consent), dead fields stripped from charge structs - tests: prod-tag suite, mock SCA parity, tip-cap overflow, completion races, cards pagination, ValidateCardInfo tables
This commit is contained in:
@@ -302,6 +302,18 @@ func postChargeRecheck(ctx context.Context, w http.ResponseWriter, tx pgx.Tx, bo
|
||||
log.Printf("CRITICAL: Square payment %s (ID=%s) landed on %q booking %s and committing the failed mark errored: %v — manual reconciliation required",
|
||||
sqStatus, sqPayID, recheckStatus, bookingID, cErr)
|
||||
}
|
||||
// The CRITICAL log line alone was the only operator signal for a
|
||||
// stranded charge — no admin-visible trace. Raise the flood-capped
|
||||
// critical-payment admin notification (sweep.go's
|
||||
// insertCriticalPaymentNotification, which applies the shared
|
||||
// unacknowledged-queue cap atomically and dedups per
|
||||
// booking/user). It runs AFTER the commit: this transaction held the
|
||||
// booking row FOR UPDATE (recheckBookingPayable), and the notification
|
||||
// INSERT's FK check on bookings(id) takes FOR KEY SHARE — inserting
|
||||
// before the commit would self-deadlock against this tx's own booking
|
||||
// lock (the sweep's gateStalePaymentRescueOnBooking commits first for
|
||||
// the same reason).
|
||||
insertCriticalPaymentNotification(ctx, &bookingID, nil)
|
||||
http.Error(w, conflictMsg, http.StatusConflict)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user