Skip to content

SL-375 Fix Saferpay Fields CVC and loading states in checkout - #343

Merged
TLabutis merged 1 commit into
feature/react-admin-settingsfrom
BUGFIX/saferpay-fields-cvc-disabled-state
Jul 30, 2026
Merged

SL-375 Fix Saferpay Fields CVC and loading states in checkout#343
TLabutis merged 1 commit into
feature/react-admin-settingsfrom
BUGFIX/saferpay-fields-cvc-disabled-state

Conversation

@TLabutis

Copy link
Copy Markdown
Contributor

QA follow-up on SL-375. Two states where a Saferpay Fields card field is drawn as fully usable while it cannot be typed into, so the form reads as broken.

1. CVC field looks editable while the card number is incomplete

The SDK keeps the CVC input disabled until the card number passes its CheckCard lookup, and the injected .form-control rule had no :disabled variant. The disabled input therefore kept the text cursor on hover, the normal text colour and a live caret colour, so clicking it gave no caret and dropped every keystroke.

  • Added .form-control:disabled to the injected style: cursor: not-allowed, muted colour and caret-color: transparent. -webkit-text-fill-color is required because Blink and WebKit override color on a disabled input.
  • Mirrored the lock on our own fieldset with an is-locked class, driven by card-number validity in onValidated and cleared in onFocus for the CVC (a disabled input cannot take focus, so reaching it proves the SDK unlocked it).

The field is deliberately not rendered pre-locked: with an untouched card number the CVC is genuinely enabled and typeable, so pre-locking would grey out a usable field.

card number state disabled cursor text fill caret fieldset
pristine, empty false text rgb(31,36,38) visible normal
1234567890123456, blurred true not-allowed rgb(154,164,168) transparent locked
4111111111111111, blurred false text rgb(31,36,38) visible normal

2. Card form accepted clicks and keystrokes while still loading

The field iframes were hidden with opacity: 0 until the SDK reported success. An opacity: 0 iframe still hit-tests, so a customer clicking a field during init focused the invisible input and the characters they typed landed in a field they could not see. Verified: typed GHOST during the window and it went into the holder-name input.

Measured window from selecting the payment option, on a low-latency connection:

time state what a click did
0-400 ms placeholder only, no iframe swallowed, focus on BODY
400-600 ms iframe present, opacity: 0 focused and typed into an invisible input
600-800 ms fading in partially visible
  • pointer-events: none alongside opacity: 0, so the field is inert while hidden.
  • The fieldsets render muted (grey border, grey background, grey legend, cursor: progress), sharing declarations with is-locked since both mean "drawn but not typeable". Both are declared ahead of the focus/error rules so those still take precedence.
  • The 0-400 ms window cannot be fixed at all (there is nothing to click yet), only made legible.

Fallback timeout

The loading class now also has a 10s fallback, cleared in onSuccess, onError and removeSlot. Verified by blocking the field iframe requests: the SDK calls neither onSuccess nor onError, so a class gated only on those callbacks stays on forever. Combined with pointer-events: none that would leave the form permanently inert, which is worse than the original bug. With the timer it recovers.

Not changed, ruled out with evidence

The unconditional cvc entry in the submit gate's REQUIRED_FIELDS was suspected of deadlocking cards without a mandatory CVC. Bancontact BIN 6703444444444449 (isCvcMandatory: false) with a blank CVC returned {"success":true} from Store and the gate did not block: the SDK reports an empty CVC as valid when it is not mandatory. reEnablePlaceOrder() also already covers all four failure branches.

Testing

PS 8.2.3 / PHP 8.1, Chromium, live test terminal. All four field states verified through the real cross-origin iframes, including that the injected :disabled rule reaches the iframe's own document.styleSheets. Also checked that the muted loading and locked looks do not override has-error or is-focused, that typing works normally once ready (JOHN DOE, CVC 456), and that a full submit still reaches Store.

Changelog updated in the 2.1.0 section.

Two states where a card field was drawn as fully usable while it could not
be typed into, so customers read the form as broken.

CVC: the SDK keeps the CVC input disabled until the card number passes its
CheckCard lookup. The injected .form-control rule had no :disabled variant,
so the disabled input kept the text cursor, the normal text colour and a
live caret colour. Add the :disabled rule (with -webkit-text-fill-color,
since Blink and WebKit override color on a disabled input) and mirror the
lock on the fieldset through an is-locked class, driven by card-number
validity in onValidated and cleared in onFocus.

Loading: the field iframes were hidden with opacity 0 until the SDK
reported success, but an invisible iframe still takes clicks and
keystrokes, so a customer clicking during init typed into a field they
could not see. Add pointer-events: none for that window and mute the
fieldsets so the state is legible before the click.

The loading class now also has a fallback timeout. Verified with the field
iframe requests blocked: the SDK calls neither onSuccess nor onError, so a
class gated only on those callbacks would leave the form permanently inert.
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@TLabutis
TLabutis merged commit 5036539 into feature/react-admin-settings Jul 30, 2026
3 checks passed
@TLabutis
TLabutis deleted the BUGFIX/saferpay-fields-cvc-disabled-state branch July 30, 2026 11:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant