@flopay/react 1.3.0 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -0
- package/dist/index.cjs +148 -71
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +151 -71
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -464,6 +464,53 @@ The `SplitCardForm` layout:
|
|
|
464
464
|
6. Full Name input
|
|
465
465
|
7. Submit button
|
|
466
466
|
|
|
467
|
+
#### AVS postcode validation
|
|
468
|
+
|
|
469
|
+
When AVS collection is enabled (`enableAVS`) and the postcode field is shown,
|
|
470
|
+
`SplitCardForm` validates the postcode **format against the live selected
|
|
471
|
+
country before the card is captured**, on both the Stripe and vault card paths.
|
|
472
|
+
It reuses `@flopay/shared`'s country-aware
|
|
473
|
+
[postcode helpers](../shared/README.md#postal-code-helpers) (the same
|
|
474
|
+
`validator` rules the billing API applies), so client and server agree.
|
|
475
|
+
|
|
476
|
+
- **Supported country + malformed postcode** → the submit is blocked (the vault
|
|
477
|
+
widget's submit button is gated; the Stripe path returns before tokenizing)
|
|
478
|
+
and an inline, country-specific message shows the expected format
|
|
479
|
+
(e.g. *"Enter a valid ZIP Code (e.g. 12345 or 12345-6789)"*). On the vault
|
|
480
|
+
path the inline message appears once the field is blurred, since the disabled
|
|
481
|
+
submit means it can't be reached by a submit attempt.
|
|
482
|
+
- **Supported country + empty postcode** → the same submit gate applies, and
|
|
483
|
+
once the field is blurred (or a submit is attempted) an inline *required*
|
|
484
|
+
message shows (e.g. *"ZIP Code is required"*) rather than the expected-format
|
|
485
|
+
copy.
|
|
486
|
+
- **Country with no postal system** (or a locale `validator` doesn't recognise)
|
|
487
|
+
→ the postcode field is shown but **optional**: empty and format checks are
|
|
488
|
+
both skipped, so these buyers are never blocked.
|
|
489
|
+
|
|
490
|
+
On the **vault card path** the widget's submit button is gated **synchronously**
|
|
491
|
+
on the client-side AVS check above. Because the client postcode rules mirror the
|
|
492
|
+
backend validator (`validator@13.15.35`) verbatim, a postcode the server's
|
|
493
|
+
`PATCH /v1/checkouts/sessions/{id}/account` would reject is already blocked here —
|
|
494
|
+
before the button ever enables — with no network round-trip in the critical path.
|
|
495
|
+
The gate is **never** held pending an async request: the hosted widget silently
|
|
496
|
+
swallows a click that lands while the button is disabled and offers no way to
|
|
497
|
+
auto-resubmit on release, so an async gate would strand any buyer (or single-click
|
|
498
|
+
preview) who clicked before it resolved — the checkout would write no attempt and
|
|
499
|
+
time out (sdk#124).
|
|
500
|
+
|
|
501
|
+
When the buyer submits, `SplitCardForm` persists the billing snapshot
|
|
502
|
+
best-effort **in parallel** with the widget's charge:
|
|
503
|
+
|
|
504
|
+
- **Backend rejects the address (a `4xx`)** — a residual, non-postcode drift, since
|
|
505
|
+
the postcode is already client-validated — the specific message (e.g. *"Postal/ZIP
|
|
506
|
+
code is not valid for the selected country…"*) shows inline **and** fires `onError`
|
|
507
|
+
with the parsed `FloPayError`, and the processing overlay clears so the buyer can
|
|
508
|
+
correct and retry. The charge cannot be cancelled once submitted, so this surfaces
|
|
509
|
+
for the buyer's *next* attempt rather than stopping the in-flight one.
|
|
510
|
+
- **Transient failure (`5xx` / network)** → best-effort and silent: the backend
|
|
511
|
+
listener falls back to the session's baseline address while the widget completes
|
|
512
|
+
the charge.
|
|
513
|
+
|
|
467
514
|
### Vault PCI card form
|
|
468
515
|
|
|
469
516
|
When the billing API returns a hosted vault card form on the session
|
package/dist/index.cjs
CHANGED
|
@@ -1365,6 +1365,9 @@ var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
|
1365
1365
|
var STRIPE_RESUME_KEY = "flopay_stripe_resume";
|
|
1366
1366
|
var LEGACY_WALLET_RESUME_KEY = "flopay_wallet_resume";
|
|
1367
1367
|
var PAYPAL_RESUME_KEY = "flopay_paypal_resume";
|
|
1368
|
+
function isAccountValidationError(err) {
|
|
1369
|
+
return err instanceof import_shared6.FloPayError && typeof err.statusCode === "number" && err.statusCode >= 400 && err.statusCode < 500;
|
|
1370
|
+
}
|
|
1368
1371
|
function toVaultMount(block) {
|
|
1369
1372
|
if (!block?.html) return null;
|
|
1370
1373
|
return {
|
|
@@ -1447,6 +1450,18 @@ function getButtonMethodLabel(method) {
|
|
|
1447
1450
|
return "Card";
|
|
1448
1451
|
}
|
|
1449
1452
|
}
|
|
1453
|
+
function malformedPostcodeMessage(country) {
|
|
1454
|
+
const example = (0, import_shared5.getPostalCodeExample)(country);
|
|
1455
|
+
return `Enter a valid ${(0, import_shared5.getPostalCodeLabel)(country)}${example ? ` (e.g. ${example})` : ""}`;
|
|
1456
|
+
}
|
|
1457
|
+
function computePostalCodeState(country, zip, visible) {
|
|
1458
|
+
const supported = (0, import_shared5.isPostalCodeSupported)(country);
|
|
1459
|
+
const trimmed = zip.trim();
|
|
1460
|
+
const required = visible && supported;
|
|
1461
|
+
const empty = required && !trimmed;
|
|
1462
|
+
const malformed = required && !!trimmed && !(0, import_shared5.isValidPostalCode)(country, trimmed);
|
|
1463
|
+
return { visible, supported, required, empty, malformed };
|
|
1464
|
+
}
|
|
1450
1465
|
function normalizeBeforeButtonClickError(method, err) {
|
|
1451
1466
|
return err instanceof import_shared6.FloPayError ? err : new import_shared6.FloPayError(
|
|
1452
1467
|
err instanceof Error ? err.message : `${getButtonMethodLabel(method)} before-click hook failed.`,
|
|
@@ -2679,6 +2694,11 @@ function SplitCardFormInner({
|
|
|
2679
2694
|
if (!flopay || !vaultActive || !sessionId) return null;
|
|
2680
2695
|
return flopay.cardCapture({ sessionId });
|
|
2681
2696
|
}, [flopay, vaultActive, sessionId]);
|
|
2697
|
+
const postalCodeState = (0, import_react9.useMemo)(() => {
|
|
2698
|
+
const cc = selectedCountry;
|
|
2699
|
+
const visible = avsConfig ? (0, import_shared5.isAVSFieldVisible)(avsConfig.postal_code, cc) : false;
|
|
2700
|
+
return computePostalCodeState(cc, zipCode, visible);
|
|
2701
|
+
}, [avsConfig, selectedCountry, zipCode]);
|
|
2682
2702
|
const { avsInvalid, invalidAvsFields } = (0, import_react9.useMemo)(() => {
|
|
2683
2703
|
const none = { line1: false, city: false, state: false, zip: false };
|
|
2684
2704
|
if (!vaultActive || !avsConfig) return { avsInvalid: false, invalidAvsFields: none };
|
|
@@ -2688,12 +2708,15 @@ function SplitCardFormInner({
|
|
|
2688
2708
|
line1: isEmpty(avsConfig.address_line_1, addressLine1),
|
|
2689
2709
|
city: isEmpty(avsConfig.city, city),
|
|
2690
2710
|
state: isEmpty(avsConfig.state, stateValue),
|
|
2691
|
-
|
|
2711
|
+
// Empty (required) or malformed both block; unsupported/no-postcode
|
|
2712
|
+
// locales never block (`postalCodeState` fails open above).
|
|
2713
|
+
zip: postalCodeState.empty || postalCodeState.malformed
|
|
2692
2714
|
};
|
|
2693
2715
|
const avsInvalid2 = invalidAvsFields2.line1 || invalidAvsFields2.city || invalidAvsFields2.state || invalidAvsFields2.zip;
|
|
2694
2716
|
return { avsInvalid: avsInvalid2, invalidAvsFields: invalidAvsFields2 };
|
|
2695
|
-
}, [vaultActive, avsConfig, selectedCountry, addressLine1, city, stateValue,
|
|
2717
|
+
}, [vaultActive, avsConfig, selectedCountry, addressLine1, city, stateValue, postalCodeState]);
|
|
2696
2718
|
const [hasAttemptedSubmit, setHasAttemptedSubmit] = (0, import_react9.useState)(false);
|
|
2719
|
+
const [zipTouched, setZipTouched] = (0, import_react9.useState)(false);
|
|
2697
2720
|
const [vaultMount, setVaultMount] = (0, import_react9.useState)(
|
|
2698
2721
|
() => toVaultMount(session?.vault)
|
|
2699
2722
|
);
|
|
@@ -2846,10 +2869,6 @@ function SplitCardFormInner({
|
|
|
2846
2869
|
},
|
|
2847
2870
|
[onErrorChange]
|
|
2848
2871
|
);
|
|
2849
|
-
(0, import_react9.useEffect)(() => {
|
|
2850
|
-
if (!vaultActive || !cardCapture) return;
|
|
2851
|
-
cardCapture.setSubmitGate?.(avsInvalid);
|
|
2852
|
-
}, [vaultActive, cardCapture, avsInvalid]);
|
|
2853
2872
|
(0, import_react9.useEffect)(() => {
|
|
2854
2873
|
if (!vaultActive || !cardCapture) return;
|
|
2855
2874
|
cardCapture.setCardFieldOrder?.(cardFieldOrder ?? null, avsConfig == null);
|
|
@@ -2903,73 +2922,118 @@ function SplitCardFormInner({
|
|
|
2903
2922
|
nonce,
|
|
2904
2923
|
updateError
|
|
2905
2924
|
]);
|
|
2925
|
+
const vaultOutcomeRef = (0, import_react9.useRef)({
|
|
2926
|
+
onComplete,
|
|
2927
|
+
onError,
|
|
2928
|
+
updateError,
|
|
2929
|
+
emitDecline,
|
|
2930
|
+
resolvedAccount,
|
|
2931
|
+
fullName,
|
|
2932
|
+
avsConfig,
|
|
2933
|
+
avsCheckProp,
|
|
2934
|
+
sessionId,
|
|
2935
|
+
nonce,
|
|
2936
|
+
baseUrl
|
|
2937
|
+
});
|
|
2938
|
+
vaultOutcomeRef.current = {
|
|
2939
|
+
onComplete,
|
|
2940
|
+
onError,
|
|
2941
|
+
updateError,
|
|
2942
|
+
emitDecline,
|
|
2943
|
+
resolvedAccount,
|
|
2944
|
+
fullName,
|
|
2945
|
+
avsConfig,
|
|
2946
|
+
avsCheckProp,
|
|
2947
|
+
sessionId,
|
|
2948
|
+
nonce,
|
|
2949
|
+
baseUrl
|
|
2950
|
+
};
|
|
2951
|
+
const vaultCompletedRef = (0, import_react9.useRef)(false);
|
|
2952
|
+
const buildVaultAccountSnapshot = (0, import_react9.useCallback)(() => {
|
|
2953
|
+
const { resolvedAccount: resolvedAccount2, avsConfig: avsConfig2, fullName: fullName2, avsCheckProp: avsCheckProp2 } = vaultOutcomeRef.current;
|
|
2954
|
+
const cc = selectedCountryRef.current || resolvedAccount2.country || "US";
|
|
2955
|
+
const stateVisible = avsConfig2 ? (0, import_shared5.isAVSFieldVisible)(avsConfig2.state, cc) : false;
|
|
2956
|
+
const line1Visible = avsConfig2 ? (0, import_shared5.isAVSFieldVisible)(avsConfig2.address_line_1, cc) : false;
|
|
2957
|
+
const zipVisible = avsConfig2 ? (0, import_shared5.isAVSFieldVisible)(avsConfig2.postal_code, cc) : false;
|
|
2958
|
+
const cityVisible = avsConfig2 ? (0, import_shared5.isAVSFieldVisible)(avsConfig2.city, cc) : false;
|
|
2959
|
+
const line2Visible = avsConfig2 ? (0, import_shared5.isAVSFieldVisible)(avsConfig2.address_line_2, cc) : false;
|
|
2960
|
+
const derivedState = line1Visible && !stateVisible && zipVisible ? (0, import_shared5.getStateFromPostalCode)(cc, (zipCodeRef.current ?? "").trim()) : null;
|
|
2961
|
+
const stateValue2 = stateVisible ? stateRef.current : derivedState;
|
|
2962
|
+
return {
|
|
2963
|
+
accountData: {
|
|
2964
|
+
userId: resolvedAccount2.userId ?? "",
|
|
2965
|
+
email: resolvedAccount2.email ?? "",
|
|
2966
|
+
firstName: resolvedAccount2.firstName ?? fullName2.trim().split(/\s+/)[0] ?? "",
|
|
2967
|
+
lastName: resolvedAccount2.lastName ?? fullName2.trim().split(/\s+/).slice(1).join(" ") ?? "",
|
|
2968
|
+
...zipVisible && zipCodeRef.current ? { zip: zipCodeRef.current } : {},
|
|
2969
|
+
...cityVisible && cityRef.current ? { city: cityRef.current } : {},
|
|
2970
|
+
...stateValue2 ? { state: stateValue2 } : {},
|
|
2971
|
+
...line1Visible && addressLine1Ref.current ? { addressLine1: addressLine1Ref.current } : {},
|
|
2972
|
+
...line2Visible && addressLine2Ref.current ? { addressLine2: addressLine2Ref.current } : {},
|
|
2973
|
+
country: cc
|
|
2974
|
+
},
|
|
2975
|
+
...avsCheckProp2 !== void 0 ? { avsCheck: avsCheckProp2 } : {},
|
|
2976
|
+
...avsConfig2 ? {
|
|
2977
|
+
avsConfig: {
|
|
2978
|
+
country: (0, import_shared5.isAVSFieldVisible)(avsConfig2.country, cc),
|
|
2979
|
+
postal_code: zipVisible,
|
|
2980
|
+
address_line_1: line1Visible,
|
|
2981
|
+
address_line_2: line2Visible,
|
|
2982
|
+
city: cityVisible,
|
|
2983
|
+
state: stateVisible
|
|
2984
|
+
}
|
|
2985
|
+
} : {}
|
|
2986
|
+
};
|
|
2987
|
+
}, []);
|
|
2906
2988
|
(0, import_react9.useEffect)(() => {
|
|
2907
2989
|
if (!vaultActive || !cardCapture) return;
|
|
2990
|
+
cardCapture.setSubmitGate?.(avsInvalid);
|
|
2991
|
+
}, [vaultActive, cardCapture, avsInvalid]);
|
|
2992
|
+
(0, import_react9.useEffect)(() => {
|
|
2993
|
+
if (!vaultActive || !cardCapture) return;
|
|
2994
|
+
vaultCompletedRef.current = false;
|
|
2908
2995
|
let cancelled = false;
|
|
2909
2996
|
const offSubmitting = cardCapture.on("submitting", () => {
|
|
2910
2997
|
setHasAttemptedSubmit(true);
|
|
2911
2998
|
setOverlayStatus("processing");
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
const stateValue2 = stateVisible ? stateRef.current : derivedState;
|
|
2921
|
-
void new import_js2.PaymentAPI(baseUrl).patchAccountSnapshot(sessionId, nonce, {
|
|
2922
|
-
accountData: {
|
|
2923
|
-
userId: resolvedAccount.userId ?? "",
|
|
2924
|
-
email: resolvedAccount.email ?? "",
|
|
2925
|
-
firstName: resolvedAccount.firstName ?? fullName.trim().split(/\s+/)[0] ?? "",
|
|
2926
|
-
lastName: resolvedAccount.lastName ?? fullName.trim().split(/\s+/).slice(1).join(" ") ?? "",
|
|
2927
|
-
...zipVisible && zipCodeRef.current ? { zip: zipCodeRef.current } : {},
|
|
2928
|
-
...cityVisible && cityRef.current ? { city: cityRef.current } : {},
|
|
2929
|
-
...stateValue2 ? { state: stateValue2 } : {},
|
|
2930
|
-
...line1Visible && addressLine1Ref.current ? { addressLine1: addressLine1Ref.current } : {},
|
|
2931
|
-
...line2Visible && addressLine2Ref.current ? { addressLine2: addressLine2Ref.current } : {},
|
|
2932
|
-
country: cc
|
|
2933
|
-
},
|
|
2934
|
-
...avsCheckProp !== void 0 ? { avsCheck: avsCheckProp } : {},
|
|
2935
|
-
...avsConfig ? {
|
|
2936
|
-
avsConfig: {
|
|
2937
|
-
country: (0, import_shared5.isAVSFieldVisible)(avsConfig.country, cc),
|
|
2938
|
-
postal_code: zipVisible,
|
|
2939
|
-
address_line_1: line1Visible,
|
|
2940
|
-
address_line_2: line2Visible,
|
|
2941
|
-
city: cityVisible,
|
|
2942
|
-
state: stateVisible
|
|
2943
|
-
}
|
|
2944
|
-
} : {}
|
|
2945
|
-
}).catch(() => {
|
|
2999
|
+
const { baseUrl: baseUrl2, sessionId: sessionId2, nonce: nonce2, updateError: updateError2, onError: onError2 } = vaultOutcomeRef.current;
|
|
3000
|
+
if (!sessionId2 || !nonce2) return;
|
|
3001
|
+
void new import_js2.PaymentAPI(baseUrl2).patchAccountSnapshot(sessionId2, nonce2, buildVaultAccountSnapshot()).catch((err) => {
|
|
3002
|
+
if (!isAccountValidationError(err)) return;
|
|
3003
|
+
const message = err instanceof Error ? err.message : "Please check your billing address and try again.";
|
|
3004
|
+
updateError2(message);
|
|
3005
|
+
setOverlayStatus(null);
|
|
3006
|
+
onError2?.(err instanceof import_shared6.FloPayError ? err : new import_shared6.FloPayError(message, "api_error"));
|
|
2946
3007
|
});
|
|
2947
3008
|
});
|
|
2948
3009
|
const offComplete = cardCapture.on("complete", async (event) => {
|
|
2949
|
-
markSessionRecentlyCompleted(event.sessionId ?? sessionId);
|
|
3010
|
+
markSessionRecentlyCompleted(event.sessionId ?? vaultOutcomeRef.current.sessionId);
|
|
2950
3011
|
setOverlayStatus("success");
|
|
2951
3012
|
await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_SUCCESS_DELAY_MS));
|
|
2952
|
-
if (
|
|
2953
|
-
|
|
3013
|
+
if (vaultCompletedRef.current) return;
|
|
3014
|
+
vaultCompletedRef.current = true;
|
|
3015
|
+
vaultOutcomeRef.current.onComplete?.({
|
|
2954
3016
|
status: "succeeded",
|
|
2955
3017
|
paymentIntentId: event.intentId,
|
|
2956
3018
|
checkoutMethod: "card"
|
|
2957
3019
|
});
|
|
2958
3020
|
});
|
|
2959
3021
|
const offDecline = cardCapture.on("decline", async (event) => {
|
|
3022
|
+
const { updateError: updateError2, emitDecline: emitDecline2 } = vaultOutcomeRef.current;
|
|
2960
3023
|
const message = event.message ?? "Your payment was declined. Please try another card or contact your bank.";
|
|
2961
|
-
|
|
3024
|
+
updateError2(message);
|
|
2962
3025
|
setOverlayStatus("error");
|
|
2963
|
-
|
|
3026
|
+
emitDecline2("card", message, event.declineReason ? { declineCode: event.declineReason } : void 0);
|
|
2964
3027
|
await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
|
|
2965
3028
|
if (cancelled) return;
|
|
2966
3029
|
setOverlayStatus(null);
|
|
2967
3030
|
});
|
|
2968
3031
|
const offError = cardCapture.on("error", async (event) => {
|
|
3032
|
+
const { updateError: updateError2, onError: onError2 } = vaultOutcomeRef.current;
|
|
2969
3033
|
const message = event.message ?? "There was a problem processing your payment. Please try again.";
|
|
2970
|
-
|
|
3034
|
+
updateError2(message);
|
|
2971
3035
|
setOverlayStatus("error");
|
|
2972
|
-
|
|
3036
|
+
onError2?.(new import_shared6.FloPayError(message, "api_error"));
|
|
2973
3037
|
await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
|
|
2974
3038
|
if (cancelled) return;
|
|
2975
3039
|
setOverlayStatus(null);
|
|
@@ -2981,25 +3045,7 @@ function SplitCardFormInner({
|
|
|
2981
3045
|
offDecline();
|
|
2982
3046
|
offError();
|
|
2983
3047
|
};
|
|
2984
|
-
}, [
|
|
2985
|
-
vaultActive,
|
|
2986
|
-
cardCapture,
|
|
2987
|
-
sessionId,
|
|
2988
|
-
nonce,
|
|
2989
|
-
baseUrl,
|
|
2990
|
-
avsConfig,
|
|
2991
|
-
avsCheckProp,
|
|
2992
|
-
resolvedAccount.userId,
|
|
2993
|
-
resolvedAccount.email,
|
|
2994
|
-
resolvedAccount.firstName,
|
|
2995
|
-
resolvedAccount.lastName,
|
|
2996
|
-
resolvedAccount.country,
|
|
2997
|
-
fullName,
|
|
2998
|
-
onComplete,
|
|
2999
|
-
onError,
|
|
3000
|
-
updateError,
|
|
3001
|
-
emitDecline
|
|
3002
|
-
]);
|
|
3048
|
+
}, [vaultActive, cardCapture]);
|
|
3003
3049
|
const directPaypalConfigured = !!directPaypal?.clientId;
|
|
3004
3050
|
const hasEnabledMethodsPayload = Array.isArray(enabledPaymentMethods);
|
|
3005
3051
|
const hasEnabledMethods = hasEnabledMethodsPayload && enabledPaymentMethods.length > 0;
|
|
@@ -3187,7 +3233,7 @@ function SplitCardFormInner({
|
|
|
3187
3233
|
const stateVisible = (0, import_shared5.isAVSFieldVisible)(avsConfig.state, c);
|
|
3188
3234
|
const line1Visible = (0, import_shared5.isAVSFieldVisible)(avsConfig.address_line_1, c);
|
|
3189
3235
|
const zipVisible = (0, import_shared5.isAVSFieldVisible)(avsConfig.postal_code, c);
|
|
3190
|
-
const derivedState = line1Visible && !stateVisible && zipVisible ? (0, import_shared5.getStateFromPostalCode)(c, zipCodeRef.current ?? "") : null;
|
|
3236
|
+
const derivedState = line1Visible && !stateVisible && zipVisible ? (0, import_shared5.getStateFromPostalCode)(c, (zipCodeRef.current ?? "").trim()) : null;
|
|
3191
3237
|
const stateValue2 = stateVisible ? stateRef.current : derivedState;
|
|
3192
3238
|
return {
|
|
3193
3239
|
country: c,
|
|
@@ -3501,10 +3547,21 @@ function SplitCardFormInner({
|
|
|
3501
3547
|
try {
|
|
3502
3548
|
if (avsConfig) {
|
|
3503
3549
|
const country = selectedCountryRef.current;
|
|
3504
|
-
|
|
3550
|
+
const postal = computePostalCodeState(
|
|
3551
|
+
country,
|
|
3552
|
+
zipCodeRef.current,
|
|
3553
|
+
(0, import_shared5.isAVSFieldVisible)(avsConfig.postal_code, country)
|
|
3554
|
+
);
|
|
3555
|
+
if (postal.empty) {
|
|
3556
|
+
setZipTouched(true);
|
|
3505
3557
|
updateError((0, import_shared5.getPostalCodeLabel)(country) + " is required");
|
|
3506
3558
|
return;
|
|
3507
3559
|
}
|
|
3560
|
+
if (postal.malformed) {
|
|
3561
|
+
setZipTouched(true);
|
|
3562
|
+
updateError(malformedPostcodeMessage(country));
|
|
3563
|
+
return;
|
|
3564
|
+
}
|
|
3508
3565
|
if ((0, import_shared5.isAVSFieldVisible)(avsConfig.address_line_1, country) && !addressLine1Ref.current.trim()) {
|
|
3509
3566
|
updateError("Street address is required");
|
|
3510
3567
|
return;
|
|
@@ -3645,6 +3702,8 @@ function SplitCardFormInner({
|
|
|
3645
3702
|
const resolvedBorder = bStyles.cardInputBorder ?? (isButtons ? "#e5e7eb" : "#A4A4FF");
|
|
3646
3703
|
const resolvedDangerColor = appearanceVars?.colorDanger ?? "#dc2626";
|
|
3647
3704
|
const avsBorderColor = (fieldInvalid) => hasAttemptedSubmit && fieldInvalid ? resolvedDangerColor : resolvedBorder;
|
|
3705
|
+
const showPostcodeError = (postalCodeState.malformed || postalCodeState.empty) && (zipTouched || hasAttemptedSubmit);
|
|
3706
|
+
const zipBorderColor = showPostcodeError ? resolvedDangerColor : avsBorderColor(invalidAvsFields.zip);
|
|
3648
3707
|
const cardBg = bStyles.cardFormContainer?.backgroundColor ?? themedWrapperBg ?? (isButtons ? "white" : "#EDEDFF");
|
|
3649
3708
|
const cardInputBg = bStyles.cardInputBackground ?? appearanceVars?.colorBackground ?? "white";
|
|
3650
3709
|
const hideBackButtonLabel = isEmptySlotContent(cardBackButtonContent);
|
|
@@ -4074,7 +4133,7 @@ function SplitCardFormInner({
|
|
|
4074
4133
|
(0, import_shared5.isAVSFieldVisible)(avsConfig.postal_code, cc) && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: {
|
|
4075
4134
|
flex: avsLayoutProp === "row" ? 1 : void 0,
|
|
4076
4135
|
backgroundColor: cardInputBg,
|
|
4077
|
-
border: `1px solid ${
|
|
4136
|
+
border: `1px solid ${zipBorderColor}`,
|
|
4078
4137
|
padding: "10px",
|
|
4079
4138
|
...avsLayoutProp === "row" && (0, import_shared5.isAVSFieldVisible)(avsConfig.country, cc) ? { borderRadius: "0", borderTopRightRadius: resolvedBorderRadius, borderBottomRightRadius: resolvedBorderRadius } : { borderRadius: resolvedBorderRadius }
|
|
4080
4139
|
}, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
@@ -4091,13 +4150,31 @@ function SplitCardFormInner({
|
|
|
4091
4150
|
setZipCode(e.target.value);
|
|
4092
4151
|
onZipChange?.(e.target.value);
|
|
4093
4152
|
},
|
|
4153
|
+
onBlur: () => setZipTouched(true),
|
|
4094
4154
|
disabled: isSubmitting,
|
|
4095
|
-
required:
|
|
4155
|
+
required: postalCodeState.required,
|
|
4156
|
+
"aria-invalid": showPostcodeError || void 0,
|
|
4157
|
+
"aria-describedby": showPostcodeError ? "flopay-billing-postal-code-error" : void 0,
|
|
4096
4158
|
"data-testid": "flopay-zip",
|
|
4097
4159
|
style: inputFieldStyle()
|
|
4098
4160
|
}
|
|
4099
4161
|
) })
|
|
4100
|
-
] })
|
|
4162
|
+
] }),
|
|
4163
|
+
showPostcodeError && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
4164
|
+
"div",
|
|
4165
|
+
{
|
|
4166
|
+
id: "flopay-billing-postal-code-error",
|
|
4167
|
+
role: "alert",
|
|
4168
|
+
"data-testid": "flopay-zip-error",
|
|
4169
|
+
style: {
|
|
4170
|
+
marginTop: "0.375rem",
|
|
4171
|
+
color: resolvedDangerColor,
|
|
4172
|
+
...sharedInputTypography,
|
|
4173
|
+
fontSize: "0.75rem"
|
|
4174
|
+
},
|
|
4175
|
+
children: postalCodeState.empty ? `${(0, import_shared5.getPostalCodeLabel)(cc)} is required` : malformedPostcodeMessage(cc)
|
|
4176
|
+
}
|
|
4177
|
+
)
|
|
4101
4178
|
] });
|
|
4102
4179
|
})() }),
|
|
4103
4180
|
vaultActive && cardPreFormSlot && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { order: -2, width: "100%" }, children: cardPreFormSlot }),
|