Frontend: verified-only save-card gating + shared nonce-staleness helper
canSaveCardsForRole(role) in square.ts is the single source of truth for the
save-card product rule (verified_email, admin — never affiliate). All four
predicate sites (account page, UserBookingModal, BookingFlow, TipPayment) were
wrong before, excluding admin and including affiliate. The worst gap was
BookingFlow passing canSaveCards={authStore.isAuthenticated} to the Pay-Early
modal, which let unverified users save cards — it now passes the derived value.
isNonceStale() + NONCE_STALENESS_MS replace the 240s staleness check duplicated
five times, keeping the amount-bound re-tokenization semantics identical.
This commit is contained in:
@@ -18,6 +18,32 @@ export interface SquareConfig {
|
||||
locationId: string;
|
||||
}
|
||||
|
||||
// Square card nonces are single-use, and the SCA verification token issued with
|
||||
// them is amount-bound. A cached nonce older than this is treated as stale and
|
||||
// forces a fresh tokenization before the charge attempt.
|
||||
export const NONCE_STALENESS_MS = 240_000;
|
||||
|
||||
/**
|
||||
* True when a user's role is allowed to save cards for reuse. Only VERIFIED
|
||||
* accounts (verified_email, admin) may save cards — guests, unverified accounts
|
||||
* and affiliates must never see the save-card option. Single source of truth so
|
||||
* the predicate can't drift between booking, account and tip surfaces.
|
||||
*/
|
||||
export function canSaveCardsForRole(role: string | undefined): boolean {
|
||||
return role === 'verified_email' || role === 'admin';
|
||||
}
|
||||
|
||||
/** True when a cached card nonce can no longer be reused: it was tokenized for a
|
||||
* different amount than `amount`, or it is older than NONCE_STALENESS_MS. */
|
||||
export function isNonceStale(
|
||||
nonceTokenizedAt: number,
|
||||
nonceTokenizedFor: number,
|
||||
amount: number,
|
||||
now: number = Date.now()
|
||||
): boolean {
|
||||
return nonceTokenizedFor !== amount || now - nonceTokenizedAt > NONCE_STALENESS_MS;
|
||||
}
|
||||
|
||||
/** True when the frontend runs in local-dev mock mode: VITE_SQUARE_ENVIRONMENT === 'mock'
|
||||
* AND the dev build (import.meta.env.DEV). The DEV gate makes the mock structurally
|
||||
* impossible in any production bundle — even if the env var is mis-set at build time. */
|
||||
|
||||
Reference in New Issue
Block a user