@flopay/react 1.3.0 → 1.3.1
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 +23 -0
- package/dist/index.cjs +112 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +115 -47
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -248,6 +248,9 @@ import {
|
|
|
248
248
|
resolveAVSConfig,
|
|
249
249
|
isAVSFieldVisible,
|
|
250
250
|
getStateFromPostalCode,
|
|
251
|
+
isPostalCodeSupported,
|
|
252
|
+
isValidPostalCode,
|
|
253
|
+
getPostalCodeExample,
|
|
251
254
|
filterStripeMethodsByCountry,
|
|
252
255
|
getStripeMethodDisplayName,
|
|
253
256
|
hasVendoredStripeMethodLogo,
|
|
@@ -1416,6 +1419,18 @@ function getButtonMethodLabel(method) {
|
|
|
1416
1419
|
return "Card";
|
|
1417
1420
|
}
|
|
1418
1421
|
}
|
|
1422
|
+
function malformedPostcodeMessage(country) {
|
|
1423
|
+
const example = getPostalCodeExample(country);
|
|
1424
|
+
return `Enter a valid ${getPostalCodeLabel(country)}${example ? ` (e.g. ${example})` : ""}`;
|
|
1425
|
+
}
|
|
1426
|
+
function computePostalCodeState(country, zip, visible) {
|
|
1427
|
+
const supported = isPostalCodeSupported(country);
|
|
1428
|
+
const trimmed = zip.trim();
|
|
1429
|
+
const required = visible && supported;
|
|
1430
|
+
const empty = required && !trimmed;
|
|
1431
|
+
const malformed = required && !!trimmed && !isValidPostalCode(country, trimmed);
|
|
1432
|
+
return { visible, supported, required, empty, malformed };
|
|
1433
|
+
}
|
|
1419
1434
|
function normalizeBeforeButtonClickError(method, err) {
|
|
1420
1435
|
return err instanceof FloPayError3 ? err : new FloPayError3(
|
|
1421
1436
|
err instanceof Error ? err.message : `${getButtonMethodLabel(method)} before-click hook failed.`,
|
|
@@ -2648,6 +2663,11 @@ function SplitCardFormInner({
|
|
|
2648
2663
|
if (!flopay || !vaultActive || !sessionId) return null;
|
|
2649
2664
|
return flopay.cardCapture({ sessionId });
|
|
2650
2665
|
}, [flopay, vaultActive, sessionId]);
|
|
2666
|
+
const postalCodeState = useMemo3(() => {
|
|
2667
|
+
const cc = selectedCountry;
|
|
2668
|
+
const visible = avsConfig ? isAVSFieldVisible(avsConfig.postal_code, cc) : false;
|
|
2669
|
+
return computePostalCodeState(cc, zipCode, visible);
|
|
2670
|
+
}, [avsConfig, selectedCountry, zipCode]);
|
|
2651
2671
|
const { avsInvalid, invalidAvsFields } = useMemo3(() => {
|
|
2652
2672
|
const none = { line1: false, city: false, state: false, zip: false };
|
|
2653
2673
|
if (!vaultActive || !avsConfig) return { avsInvalid: false, invalidAvsFields: none };
|
|
@@ -2657,12 +2677,15 @@ function SplitCardFormInner({
|
|
|
2657
2677
|
line1: isEmpty(avsConfig.address_line_1, addressLine1),
|
|
2658
2678
|
city: isEmpty(avsConfig.city, city),
|
|
2659
2679
|
state: isEmpty(avsConfig.state, stateValue),
|
|
2660
|
-
|
|
2680
|
+
// Empty (required) or malformed both block; unsupported/no-postcode
|
|
2681
|
+
// locales never block (`postalCodeState` fails open above).
|
|
2682
|
+
zip: postalCodeState.empty || postalCodeState.malformed
|
|
2661
2683
|
};
|
|
2662
2684
|
const avsInvalid2 = invalidAvsFields2.line1 || invalidAvsFields2.city || invalidAvsFields2.state || invalidAvsFields2.zip;
|
|
2663
2685
|
return { avsInvalid: avsInvalid2, invalidAvsFields: invalidAvsFields2 };
|
|
2664
|
-
}, [vaultActive, avsConfig, selectedCountry, addressLine1, city, stateValue,
|
|
2686
|
+
}, [vaultActive, avsConfig, selectedCountry, addressLine1, city, stateValue, postalCodeState]);
|
|
2665
2687
|
const [hasAttemptedSubmit, setHasAttemptedSubmit] = useState3(false);
|
|
2688
|
+
const [zipTouched, setZipTouched] = useState3(false);
|
|
2666
2689
|
const [vaultMount, setVaultMount] = useState3(
|
|
2667
2690
|
() => toVaultMount(session?.vault)
|
|
2668
2691
|
);
|
|
@@ -2872,27 +2895,56 @@ function SplitCardFormInner({
|
|
|
2872
2895
|
nonce,
|
|
2873
2896
|
updateError
|
|
2874
2897
|
]);
|
|
2898
|
+
const vaultOutcomeRef = useRef4({
|
|
2899
|
+
onComplete,
|
|
2900
|
+
onError,
|
|
2901
|
+
updateError,
|
|
2902
|
+
emitDecline,
|
|
2903
|
+
resolvedAccount,
|
|
2904
|
+
fullName,
|
|
2905
|
+
avsConfig,
|
|
2906
|
+
avsCheckProp,
|
|
2907
|
+
sessionId,
|
|
2908
|
+
nonce,
|
|
2909
|
+
baseUrl
|
|
2910
|
+
});
|
|
2911
|
+
vaultOutcomeRef.current = {
|
|
2912
|
+
onComplete,
|
|
2913
|
+
onError,
|
|
2914
|
+
updateError,
|
|
2915
|
+
emitDecline,
|
|
2916
|
+
resolvedAccount,
|
|
2917
|
+
fullName,
|
|
2918
|
+
avsConfig,
|
|
2919
|
+
avsCheckProp,
|
|
2920
|
+
sessionId,
|
|
2921
|
+
nonce,
|
|
2922
|
+
baseUrl
|
|
2923
|
+
};
|
|
2924
|
+
const vaultCompletedRef = useRef4(false);
|
|
2875
2925
|
useEffect5(() => {
|
|
2876
2926
|
if (!vaultActive || !cardCapture) return;
|
|
2927
|
+
vaultCompletedRef.current = false;
|
|
2877
2928
|
let cancelled = false;
|
|
2878
2929
|
const offSubmitting = cardCapture.on("submitting", () => {
|
|
2879
2930
|
setHasAttemptedSubmit(true);
|
|
2880
2931
|
setOverlayStatus("processing");
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
const
|
|
2884
|
-
const
|
|
2885
|
-
const
|
|
2886
|
-
const
|
|
2887
|
-
const
|
|
2932
|
+
const { resolvedAccount: resolvedAccount2, avsConfig: avsConfig2, fullName: fullName2, avsCheckProp: avsCheckProp2, baseUrl: baseUrl2, sessionId: sessionId2, nonce: nonce2 } = vaultOutcomeRef.current;
|
|
2933
|
+
if (!sessionId2 || !nonce2) return;
|
|
2934
|
+
const cc = selectedCountryRef.current || resolvedAccount2.country || "US";
|
|
2935
|
+
const stateVisible = avsConfig2 ? isAVSFieldVisible(avsConfig2.state, cc) : false;
|
|
2936
|
+
const line1Visible = avsConfig2 ? isAVSFieldVisible(avsConfig2.address_line_1, cc) : false;
|
|
2937
|
+
const zipVisible = avsConfig2 ? isAVSFieldVisible(avsConfig2.postal_code, cc) : false;
|
|
2938
|
+
const cityVisible = avsConfig2 ? isAVSFieldVisible(avsConfig2.city, cc) : false;
|
|
2939
|
+
const line2Visible = avsConfig2 ? isAVSFieldVisible(avsConfig2.address_line_2, cc) : false;
|
|
2888
2940
|
const derivedState = line1Visible && !stateVisible && zipVisible ? getStateFromPostalCode(cc, zipCodeRef.current ?? "") : null;
|
|
2889
2941
|
const stateValue2 = stateVisible ? stateRef.current : derivedState;
|
|
2890
|
-
void new PaymentAPI2(
|
|
2942
|
+
void new PaymentAPI2(baseUrl2).patchAccountSnapshot(sessionId2, nonce2, {
|
|
2891
2943
|
accountData: {
|
|
2892
|
-
userId:
|
|
2893
|
-
email:
|
|
2894
|
-
firstName:
|
|
2895
|
-
lastName:
|
|
2944
|
+
userId: resolvedAccount2.userId ?? "",
|
|
2945
|
+
email: resolvedAccount2.email ?? "",
|
|
2946
|
+
firstName: resolvedAccount2.firstName ?? fullName2.trim().split(/\s+/)[0] ?? "",
|
|
2947
|
+
lastName: resolvedAccount2.lastName ?? fullName2.trim().split(/\s+/).slice(1).join(" ") ?? "",
|
|
2896
2948
|
...zipVisible && zipCodeRef.current ? { zip: zipCodeRef.current } : {},
|
|
2897
2949
|
...cityVisible && cityRef.current ? { city: cityRef.current } : {},
|
|
2898
2950
|
...stateValue2 ? { state: stateValue2 } : {},
|
|
@@ -2900,10 +2952,10 @@ function SplitCardFormInner({
|
|
|
2900
2952
|
...line2Visible && addressLine2Ref.current ? { addressLine2: addressLine2Ref.current } : {},
|
|
2901
2953
|
country: cc
|
|
2902
2954
|
},
|
|
2903
|
-
...
|
|
2904
|
-
...
|
|
2955
|
+
...avsCheckProp2 !== void 0 ? { avsCheck: avsCheckProp2 } : {},
|
|
2956
|
+
...avsConfig2 ? {
|
|
2905
2957
|
avsConfig: {
|
|
2906
|
-
country: isAVSFieldVisible(
|
|
2958
|
+
country: isAVSFieldVisible(avsConfig2.country, cc),
|
|
2907
2959
|
postal_code: zipVisible,
|
|
2908
2960
|
address_line_1: line1Visible,
|
|
2909
2961
|
address_line_2: line2Visible,
|
|
@@ -2915,30 +2967,33 @@ function SplitCardFormInner({
|
|
|
2915
2967
|
});
|
|
2916
2968
|
});
|
|
2917
2969
|
const offComplete = cardCapture.on("complete", async (event) => {
|
|
2918
|
-
markSessionRecentlyCompleted(event.sessionId ?? sessionId);
|
|
2970
|
+
markSessionRecentlyCompleted(event.sessionId ?? vaultOutcomeRef.current.sessionId);
|
|
2919
2971
|
setOverlayStatus("success");
|
|
2920
2972
|
await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_SUCCESS_DELAY_MS));
|
|
2921
|
-
if (
|
|
2922
|
-
|
|
2973
|
+
if (vaultCompletedRef.current) return;
|
|
2974
|
+
vaultCompletedRef.current = true;
|
|
2975
|
+
vaultOutcomeRef.current.onComplete?.({
|
|
2923
2976
|
status: "succeeded",
|
|
2924
2977
|
paymentIntentId: event.intentId,
|
|
2925
2978
|
checkoutMethod: "card"
|
|
2926
2979
|
});
|
|
2927
2980
|
});
|
|
2928
2981
|
const offDecline = cardCapture.on("decline", async (event) => {
|
|
2982
|
+
const { updateError: updateError2, emitDecline: emitDecline2 } = vaultOutcomeRef.current;
|
|
2929
2983
|
const message = event.message ?? "Your payment was declined. Please try another card or contact your bank.";
|
|
2930
|
-
|
|
2984
|
+
updateError2(message);
|
|
2931
2985
|
setOverlayStatus("error");
|
|
2932
|
-
|
|
2986
|
+
emitDecline2("card", message, event.declineReason ? { declineCode: event.declineReason } : void 0);
|
|
2933
2987
|
await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
|
|
2934
2988
|
if (cancelled) return;
|
|
2935
2989
|
setOverlayStatus(null);
|
|
2936
2990
|
});
|
|
2937
2991
|
const offError = cardCapture.on("error", async (event) => {
|
|
2992
|
+
const { updateError: updateError2, onError: onError2 } = vaultOutcomeRef.current;
|
|
2938
2993
|
const message = event.message ?? "There was a problem processing your payment. Please try again.";
|
|
2939
|
-
|
|
2994
|
+
updateError2(message);
|
|
2940
2995
|
setOverlayStatus("error");
|
|
2941
|
-
|
|
2996
|
+
onError2?.(new FloPayError3(message, "api_error"));
|
|
2942
2997
|
await new Promise((r) => setTimeout(r, PROCESSING_OVERLAY_ERROR_DELAY_MS));
|
|
2943
2998
|
if (cancelled) return;
|
|
2944
2999
|
setOverlayStatus(null);
|
|
@@ -2950,25 +3005,7 @@ function SplitCardFormInner({
|
|
|
2950
3005
|
offDecline();
|
|
2951
3006
|
offError();
|
|
2952
3007
|
};
|
|
2953
|
-
}, [
|
|
2954
|
-
vaultActive,
|
|
2955
|
-
cardCapture,
|
|
2956
|
-
sessionId,
|
|
2957
|
-
nonce,
|
|
2958
|
-
baseUrl,
|
|
2959
|
-
avsConfig,
|
|
2960
|
-
avsCheckProp,
|
|
2961
|
-
resolvedAccount.userId,
|
|
2962
|
-
resolvedAccount.email,
|
|
2963
|
-
resolvedAccount.firstName,
|
|
2964
|
-
resolvedAccount.lastName,
|
|
2965
|
-
resolvedAccount.country,
|
|
2966
|
-
fullName,
|
|
2967
|
-
onComplete,
|
|
2968
|
-
onError,
|
|
2969
|
-
updateError,
|
|
2970
|
-
emitDecline
|
|
2971
|
-
]);
|
|
3008
|
+
}, [vaultActive, cardCapture]);
|
|
2972
3009
|
const directPaypalConfigured = !!directPaypal?.clientId;
|
|
2973
3010
|
const hasEnabledMethodsPayload = Array.isArray(enabledPaymentMethods);
|
|
2974
3011
|
const hasEnabledMethods = hasEnabledMethodsPayload && enabledPaymentMethods.length > 0;
|
|
@@ -3470,10 +3507,21 @@ function SplitCardFormInner({
|
|
|
3470
3507
|
try {
|
|
3471
3508
|
if (avsConfig) {
|
|
3472
3509
|
const country = selectedCountryRef.current;
|
|
3473
|
-
|
|
3510
|
+
const postal = computePostalCodeState(
|
|
3511
|
+
country,
|
|
3512
|
+
zipCodeRef.current,
|
|
3513
|
+
isAVSFieldVisible(avsConfig.postal_code, country)
|
|
3514
|
+
);
|
|
3515
|
+
if (postal.empty) {
|
|
3516
|
+
setZipTouched(true);
|
|
3474
3517
|
updateError(getPostalCodeLabel(country) + " is required");
|
|
3475
3518
|
return;
|
|
3476
3519
|
}
|
|
3520
|
+
if (postal.malformed) {
|
|
3521
|
+
setZipTouched(true);
|
|
3522
|
+
updateError(malformedPostcodeMessage(country));
|
|
3523
|
+
return;
|
|
3524
|
+
}
|
|
3477
3525
|
if (isAVSFieldVisible(avsConfig.address_line_1, country) && !addressLine1Ref.current.trim()) {
|
|
3478
3526
|
updateError("Street address is required");
|
|
3479
3527
|
return;
|
|
@@ -3614,6 +3662,8 @@ function SplitCardFormInner({
|
|
|
3614
3662
|
const resolvedBorder = bStyles.cardInputBorder ?? (isButtons ? "#e5e7eb" : "#A4A4FF");
|
|
3615
3663
|
const resolvedDangerColor = appearanceVars?.colorDanger ?? "#dc2626";
|
|
3616
3664
|
const avsBorderColor = (fieldInvalid) => hasAttemptedSubmit && fieldInvalid ? resolvedDangerColor : resolvedBorder;
|
|
3665
|
+
const showPostcodeError = (postalCodeState.malformed || postalCodeState.empty) && (zipTouched || hasAttemptedSubmit);
|
|
3666
|
+
const zipBorderColor = showPostcodeError ? resolvedDangerColor : avsBorderColor(invalidAvsFields.zip);
|
|
3617
3667
|
const cardBg = bStyles.cardFormContainer?.backgroundColor ?? themedWrapperBg ?? (isButtons ? "white" : "#EDEDFF");
|
|
3618
3668
|
const cardInputBg = bStyles.cardInputBackground ?? appearanceVars?.colorBackground ?? "white";
|
|
3619
3669
|
const hideBackButtonLabel = isEmptySlotContent(cardBackButtonContent);
|
|
@@ -4043,7 +4093,7 @@ function SplitCardFormInner({
|
|
|
4043
4093
|
isAVSFieldVisible(avsConfig.postal_code, cc) && /* @__PURE__ */ jsx7("div", { style: {
|
|
4044
4094
|
flex: avsLayoutProp === "row" ? 1 : void 0,
|
|
4045
4095
|
backgroundColor: cardInputBg,
|
|
4046
|
-
border: `1px solid ${
|
|
4096
|
+
border: `1px solid ${zipBorderColor}`,
|
|
4047
4097
|
padding: "10px",
|
|
4048
4098
|
...avsLayoutProp === "row" && isAVSFieldVisible(avsConfig.country, cc) ? { borderRadius: "0", borderTopRightRadius: resolvedBorderRadius, borderBottomRightRadius: resolvedBorderRadius } : { borderRadius: resolvedBorderRadius }
|
|
4049
4099
|
}, children: /* @__PURE__ */ jsx7(
|
|
@@ -4060,13 +4110,31 @@ function SplitCardFormInner({
|
|
|
4060
4110
|
setZipCode(e.target.value);
|
|
4061
4111
|
onZipChange?.(e.target.value);
|
|
4062
4112
|
},
|
|
4113
|
+
onBlur: () => setZipTouched(true),
|
|
4063
4114
|
disabled: isSubmitting,
|
|
4064
|
-
required:
|
|
4115
|
+
required: postalCodeState.required,
|
|
4116
|
+
"aria-invalid": showPostcodeError || void 0,
|
|
4117
|
+
"aria-describedby": showPostcodeError ? "flopay-billing-postal-code-error" : void 0,
|
|
4065
4118
|
"data-testid": "flopay-zip",
|
|
4066
4119
|
style: inputFieldStyle()
|
|
4067
4120
|
}
|
|
4068
4121
|
) })
|
|
4069
|
-
] })
|
|
4122
|
+
] }),
|
|
4123
|
+
showPostcodeError && /* @__PURE__ */ jsx7(
|
|
4124
|
+
"div",
|
|
4125
|
+
{
|
|
4126
|
+
id: "flopay-billing-postal-code-error",
|
|
4127
|
+
role: "alert",
|
|
4128
|
+
"data-testid": "flopay-zip-error",
|
|
4129
|
+
style: {
|
|
4130
|
+
marginTop: "0.375rem",
|
|
4131
|
+
color: resolvedDangerColor,
|
|
4132
|
+
...sharedInputTypography,
|
|
4133
|
+
fontSize: "0.75rem"
|
|
4134
|
+
},
|
|
4135
|
+
children: postalCodeState.empty ? `${getPostalCodeLabel(cc)} is required` : malformedPostcodeMessage(cc)
|
|
4136
|
+
}
|
|
4137
|
+
)
|
|
4070
4138
|
] });
|
|
4071
4139
|
})() }),
|
|
4072
4140
|
vaultActive && cardPreFormSlot && /* @__PURE__ */ jsx7("div", { style: { order: -2, width: "100%" }, children: cardPreFormSlot }),
|