feat: proactive saved-card SCA — challenge runs BEFORE the first charge, never a naked ccof attempt
Square's card.tokenize(verificationDetails, squareCardId) determines the SCA requirement UP FRONT and returns a fresh verification_token (or an explicit outcome), so the customer-initiated saved-card flow now runs it before the first charge attempt instead of the reactive 'attempt naked ccof -> 402 verification_required -> challenge + retry' round-trip. - UserPaymentModal/TipPayment/BookingFlow/account gift-card buy: call runSavedCardSCAProactively before charging; 'verified' carries the token on attempt #1; 'sca-unavailable' demotes to the 2FA gate (the only tokenless path); 'challenge-cancelled'/'sca-failed' never charge and keep the pending row retryable with the same cached idempotency key - The reactive re-challenge hook is removed; a defensive verification-required 402 (stale/consumed token) surfaces VERIFICATION_REQUIRED_MESSAGE and lets the user retry - Admin PaymentModal + till saved-card charges remain MERCHANT-INITIATED (customer_initiated=false, SCA-exempt, no liability shift) — unchanged - square.ts comments updated (saved-card charges now carry a token proactively; SAVED_CARD_VERIFICATION_MESSAGE is the defensive path) - Tests: 98 frontend tests (proactive decision coverage); build clean
This commit is contained in:
@@ -297,6 +297,18 @@ describe('shouldFallbackTo2FA', () => {
|
||||
])('outcome %s → %s', (outcome, expected) => {
|
||||
expect(shouldFallbackTo2FA(outcome)).toBe(expected);
|
||||
});
|
||||
|
||||
it('demotes to the 2FA gate only on sca-unavailable', () => {
|
||||
expect(shouldFallbackTo2FA('sca-unavailable')).toBe(true);
|
||||
});
|
||||
|
||||
it('keeps SCA primary after a successful verification', () => {
|
||||
expect(shouldFallbackTo2FA('verified')).toBe(false);
|
||||
});
|
||||
|
||||
it('does NOT treat a cancelled challenge as sca-unavailable (retryable via SCA)', () => {
|
||||
expect(shouldFallbackTo2FA('challenge-cancelled')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isTwoFactorVerificationGateFailure', () => {
|
||||
|
||||
Reference in New Issue
Block a user