- square.ts: shouldFallbackTo2FA replaced by shouldShowSCARefusal — a genuine 'sca-unavailable' now drives the REFUSAL path (the customer is told the payment cannot complete and to pay online later), never the 2FA code fallback (PSR 2017 SCA is non-waivable; merchant liability is not cured by consent). SCA_REFUSAL_MESSAGE_ONLINE/TILL copy added; SCA_FALLBACK_CONSENT_VERSION 'v1' + scaFallbackConsentFields() carry the versioned consent on the explicit opt-in path only (shipped surfaces send none). SquareTokenizeResult docs updated: tokenize-result token is the charge source, tokenless OK proceeds token-less under the backend's SCA-only gate. - C1 wire contract on every saved-card surface (booking, tip, gift-card buy, till, account): the proactive SCA tokenize-result is sent as new_card_token (the charge SOURCE alongside the saved-card ref), never the legacy verification_token; 402 verification-required now means the tokenize-result was consumed/expired between tokenize and charge. - New ScaFallbackConsentDialog surfaces the refusal notice; the code input (useTwoFactorCodeForSavedCard scaAvailable: () => true) only ever appears via a backend gate rejection (defensive/opt-in). - Till (M10): proactive saved-card SCA runs per sale line BEFORE the first charge; sca-unavailable aborts the whole sale before any charge. - Card save (M11/M12): STORE-intent tokenizeForStore with SCA at tokenization; 402 verification-required on save surfaces SCA-first guidance instead of a generic failure.
57 lines
1.8 KiB
Svelte
57 lines
1.8 KiB
Svelte
<!--
|
|
ScaFallbackConsentDialog.svelte — C6 refusal notice shown whenever a
|
|
saved-card charge hits genuine `sca-unavailable` (the issuer's in-app SCA
|
|
challenge cannot run). The C6 legal verdict (PSR 2017 SCA is non-waivable;
|
|
the merchant is liable regardless of consent) removed the homegrown 2FA code
|
|
gate as an SCA fallback for saved-card charges, so this is now a REFUSAL:
|
|
the customer is told the payment cannot complete and invited to pay online
|
|
later, with a single OK button that closes the flow cleanly (no charge
|
|
submitted). It must NEVER offer a "continue with verification code" action
|
|
under the default SCA-only posture.
|
|
|
|
Inline panel (no portal) so it nests cleanly inside every payment surface —
|
|
the modals, the booking-flow step, the tip card and the till card.
|
|
-->
|
|
<script lang="ts">
|
|
import { Button } from '$lib/components/ui/button';
|
|
import { SCA_REFUSAL_MESSAGE_ONLINE } from '$lib/square/square';
|
|
|
|
let {
|
|
open = false,
|
|
onOk,
|
|
message = SCA_REFUSAL_MESSAGE_ONLINE
|
|
}: {
|
|
open?: boolean;
|
|
onOk: () => void;
|
|
message?: string;
|
|
} = $props();
|
|
</script>
|
|
|
|
{#if open}
|
|
<div
|
|
role="alertdialog"
|
|
aria-label="Payment cannot be completed"
|
|
class="rounded-lg border border-red-200 bg-red-50 p-4"
|
|
>
|
|
<div class="flex items-start gap-3">
|
|
<svg
|
|
class="mt-0.5 h-5 w-5 shrink-0 text-red-600"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
stroke-width="2"
|
|
aria-hidden="true"
|
|
>
|
|
<circle cx="12" cy="12" r="10" />
|
|
<line x1="12" y1="8" x2="12" y2="12" />
|
|
<line x1="12" y1="16" x2="12.01" y2="16" />
|
|
</svg>
|
|
<div class="min-w-0">
|
|
<h4 class="text-sm font-semibold text-red-900">Payment can't be processed right now</h4>
|
|
<p class="mt-1 text-sm text-red-800">{message}</p>
|
|
</div>
|
|
</div>
|
|
<Button class="mt-4 min-h-11 w-full" onclick={onOk}>OK</Button>
|
|
</div>
|
|
{/if}
|