@adyen/kyc-components
Version:
This guide assumes that you have already an account with Adyen. A legalEntity needs to be created, and you need to have a `legalEntityId` to instatiate a Component.
470 lines (469 loc) • 19.5 kB
JavaScript
try {
let e = "undefined" != typeof window ? window : "undefined" != typeof global ? global : "undefined" != typeof globalThis ? globalThis : "undefined" != typeof self ? self : {}, n = new e.Error().stack;
n && (e._sentryDebugIds = e._sentryDebugIds || {}, e._sentryDebugIds[n] = "1ae6882f-dc40-4e36-a22b-7ba817f64fc2", e._sentryDebugIdIdentifier = "sentry-dbid-1ae6882f-dc40-4e36-a22b-7ba817f64fc2");
} catch (e) {}
import { a as Icon, r as useTranslation } from "./translation-BYvhW5zA.js";
import { ft as useAccountHolder, j as useToggleContext, k as Header } from "./resolveEnvironment-DNmu53Rr.js";
import { o as objectsDeepEqual } from "./AnalyticsContext-BFrJmsp0.js";
import { t as getNestedPropertyKeys } from "./getNestedPropertyKeys-Cd8HKupP.js";
import { c as CompanyTypesValues } from "./bafinUtils-BKq-s1Ka.js";
import { n as translateTranslatable } from "./utils-C1buyvRw.js";
import { n as Image } from "./unincorporated-partnership-nnTW3FnG.js";
import { t as ErrorPanel } from "./ErrorPanel-DGlzO81g.js";
import { t as createFormUtils } from "./formUtils-DVc3a6gf.js";
import { t as MaskedInput } from "./MaskedInput-_dYxgb7_.js";
import { A as taxInformationDefaultFieldMetadata, C as getTaxIdNumberOptions, S as getDefaultTaxIdNumberType, k as taxInformationDefaultFieldConfig, n as upsertTaxInfoForCountry, r as TaxIdNumberTypeSelector, t as removeTaxInfoForCountry, v as COUNTRIES_WITH_POSSIBLE_TRUSTEE_TAXID_ABSENT_V4 } from "./utils-C9vhUXDJ.js";
import { t as resolveFieldMetadata } from "./fieldConfigurations-CZCFghnK.js";
import { t as Field } from "./Field-DoXLi6EY.js";
import { t as Select } from "./Select-DUo2rOfa.js";
import { t as Checkbox } from "./Checkbox-D7BRIoQL.js";
import { t as TextArea } from "./TextArea-CT04Dg1N.js";
import { n as StateContextSetter, t as Dropzone } from "./Dropzone-BzDQ_VtL.js";
import { t as useForm } from "./useForm-C5EykRLY.js";
import { lazy, memo } from "preact/compat";
import { useCallback as useCallback$1, useEffect as useEffect$1, useMemo as useMemo$1, useRef as useRef$1, useState as useState$1 } from "preact/hooks";
import cx from "classnames";
import { Fragment as Fragment$1, jsx, jsxs } from "preact/jsx-runtime";
//#region src/utils/getFieldsWithExistingData.ts
var getFieldsWithExistingData = (legalEntity, keyMapping, documentMapping, knownProblematicFields = []) => {
const nonDocumentFields = getNestedPropertyKeys(legalEntity).map((field) => keyMapping[field]).filter(Boolean).filter((field) => !knownProblematicFields.includes(field));
if (documentMapping && legalEntity.documentDetails?.length) {
const documentFields = legalEntity.documentDetails.map((document) => documentMapping[document.type]).filter(Boolean);
return [...nonDocumentFields, ...documentFields];
}
return nonDocumentFields;
};
//#endregion
//#region src/utils/mapping/documentGuidanceMap.ts
/**
* Returns the appropriate prefix for the document guidance image based on api documentType.
*/
var getDocumentGuidancePrefix = (documentType) => {
switch (documentType) {
case "passport": return "passport";
case "driversLicense":
case "identityCard": return "id";
default: return "document";
}
};
//#endregion
//#region src/components/ui/molecules/DocumentGuidance/DocumentGuidance.tsx
var documentMap = {
"document-blurred": lazy(() => import("./document-blurred-iJRUsQR5.js")),
"document-cutoff": lazy(() => import("./document-cut-off-DhZYZ56P.js")),
"document-expired": lazy(() => import("./document-expired-CDV5yclN.js")),
"document-front": lazy(() => import("./document-front-tg-w81rp.js")),
"id-blurred": lazy(() => import("./id-blurred-VxdOFc9Z.js")),
"id-cutoff": lazy(() => import("./id-cut-off-Lf8yjnWT.js")),
"id-front": lazy(() => import("./id-front-C00ss5Ib.js")),
"id-glare": lazy(() => import("./id-glare-DyvU9bwI.js")),
"passport-blurred": lazy(() => import("./passport-blurred-0Uw8uYSJ.js")),
"passport-cutoff": lazy(() => import("./passport-cut-off-BPzI2BwO.js")),
"passport-front": lazy(() => import("./passport-front-D6Nky-ni.js")),
"passport-glare": lazy(() => import("./passport-glare-DFSFxNy9.js"))
};
function DocumentGuidance({ type, className }) {
const { t } = useTranslation("ui");
const documentGuidanceItems = useMemo$1(() => {
const guidancePrefix = getDocumentGuidancePrefix(type);
const guidanceItems = [
{
svgName: `${guidancePrefix}-front`,
translationKey: "good",
valid: true
},
{
svgName: `${guidancePrefix}-cutoff`,
translationKey: "notCutOff"
},
{
svgName: `${guidancePrefix}-blurred`,
translationKey: "notBlurry"
},
guidancePrefix === "document" ? {
svgName: `document-expired`,
translationKey: "notExpired"
} : {
svgName: `${guidancePrefix}-glare`,
translationKey: "noGlare"
}
];
return type === "constitutionalDocument" ? guidanceItems.filter((item) => item.svgName !== "document-expired") : guidanceItems;
}, [type]);
return /* @__PURE__ */ jsx("div", {
className: cx("adyen-kyc-document-guidance", className),
children: /* @__PURE__ */ jsx("div", {
className: "adyen-kyc-document-guidance__container",
children: documentGuidanceItems.map((item) => /* @__PURE__ */ jsxs("figure", {
className: "adyen-kyc-document-guidance__item",
children: [/* @__PURE__ */ jsx(Image, { lazyLoadedImage: documentMap[item.svgName] }), /* @__PURE__ */ jsxs("figcaption", {
className: "adyen-kyc-document-guidance__description",
children: [item.valid ? /* @__PURE__ */ jsx(Icon, {
name: "check",
className: "adyen-kyc-document-guidance__icon"
}) : /* @__PURE__ */ jsx(Icon, {
name: "cross",
className: "adyen-kyc-document-guidance__icon adyen-kyc-document-guidance__icon--invalid"
}), /* @__PURE__ */ jsx("span", {
className: "adyen-kyc-document-guidance__description-text",
children: t(($) => $[item.translationKey])
})]
})]
}, item.translationKey))
})
});
}
var DocumentUpload_module_default = {
"description-field": "_description-field_1i0mx_1",
descriptionField: "_description-field_1i0mx_1"
};
//#endregion
//#region src/components/Shared/forms/DocumentUpload/DocumentUpload.tsx
var DESCRIPTION_CHARACTERS_LIMIT = 200;
function DocumentUpload({ documentType, documentField, guidanceHeader, documentTypeSelect, guidanceContent, handleFieldChange, requiredFields, data: dataProp, valid: validProp, errors: errorProp, fieldValidationErrors: propFieldValidationErrors, supportsMultipleUploads, maxNumberOfFiles = 1, label, extraDropzoneDescription, hidePageCount, ...props }) {
const { t } = useTranslation("common");
const [document, setDocument] = useState$1();
const form = useForm({
...props,
schema: ["description"],
defaultData: dataProp,
rules: props.validators,
fieldProblems: propFieldValidationErrors
});
const { handleChangeFor, triggerValidation, setData } = form;
let { data, errors, valid, fieldProblems } = form;
const formUtils = createFormUtils(props, t);
const stateRef = useRef$1({ setState: null });
const isOptional = !(props?.formVerificationErrors && Object.keys(props?.formVerificationErrors)?.length === 0);
useEffect$1(() => {
if (isOptional) triggerValidation();
}, [dataProp]);
useEffect$1(() => {
stateRef.current.setState?.({
type: "addToState",
value: {
data,
valid,
errors,
fieldProblems,
dataStoreId: props.id,
schema: requiredFields
}
});
}, [
data,
valid,
errors,
fieldProblems
]);
useEffect$1(() => {
setDocument({ [documentField]: dataProp?.[documentField] });
setData("description", dataProp?.description);
triggerValidation();
}, [dataProp]);
if (handleFieldChange && requiredFields && propFieldValidationErrors) {
data = dataProp ?? {};
errors = errorProp;
valid = validProp;
fieldProblems = propFieldValidationErrors;
}
const handleInput = (e) => {
handleChangeFor("description")(e);
if (handleFieldChange) handleFieldChange?.("description")(e);
};
return /* @__PURE__ */ jsxs("div", {
className: `adyen-kyc-company-document-upload adyen-kyc-company-document-upload--${documentField}`,
children: [
/* @__PURE__ */ jsx(StateContextSetter, {
owner: props.id,
stateRef
}),
props.heading && /* @__PURE__ */ jsx(Header, {
title: props.heading,
description: props.description
}),
/* @__PURE__ */ jsx(ErrorPanel, {
verificationErrors: props?.formVerificationErrors,
validationErrors: propFieldValidationErrors,
formUtils,
id: "ariaErrorField"
}),
formUtils.isRequiredField(documentField) && /* @__PURE__ */ jsxs(Fragment$1, { children: [
guidanceHeader,
documentTypeSelect && /* @__PURE__ */ jsx(Field, {
name: "documentType",
label: t(($) => $["selectDocumentType"]),
children: (childProps) => /* @__PURE__ */ jsx(Select, {
...childProps,
onChange: (e) => documentTypeSelect.setSelectedType(e.target.value),
name: "documentType",
placeholder: t(($) => $["selectDocument"]),
selected: documentTypeSelect.selectedType,
items: documentTypeSelect.options.map(({ id, name }) => ({
id,
name: t(($) => $[name])
}))
})
}),
/* @__PURE__ */ jsx("div", { children: guidanceContent }),
/* @__PURE__ */ jsx(DocumentGuidance, {
type: documentType,
className: "u-margin-bottom-24"
}),
/* @__PURE__ */ jsx(Dropzone, {
defaultData: document ?? data,
id: props.id,
isOptional: isOptional || formUtils.isOptionalField(documentField),
maxSize: documentType === "constitutionalDocument" ? 31457280 : void 0,
name: documentField,
shouldValidate: props.shouldValidate,
handleFieldChange,
errors,
fieldValidationErrors: fieldProblems,
valid,
multiple: supportsMultipleUploads,
maxNumberOfFiles: maxNumberOfFiles ?? 1,
label,
extraDropzoneDescription,
hidePageCount
})
] }),
formUtils.isRequiredField("description") && /* @__PURE__ */ jsx("div", {
className: DocumentUpload_module_default.descriptionField,
children: /* @__PURE__ */ jsx(Field, {
name: "description",
label: formUtils.getLabel("descriptionOptional"),
errorMessage: formUtils.getErrorMessage("description", errors, fieldProblems),
isValid: valid.description,
children: (childProps) => /* @__PURE__ */ jsx(TextArea, {
...childProps,
name: "description",
value: data.description,
maxLength: DESCRIPTION_CHARACTERS_LIMIT,
onInput: handleInput,
"aria-invalid": !valid.description,
handleFieldChange: handleInput,
errors,
fieldValidationErrors: fieldProblems,
valid
})
})
})
]
});
}
var MemoizedDocumentUpload = memo(DocumentUpload, (prevProps, nextProps) => prevProps.shouldValidate === nextProps.shouldValidate && objectsDeepEqual(prevProps.requiredFields, nextProps.requiredFields) && objectsDeepEqual(prevProps.errors, nextProps.errors) && objectsDeepEqual(prevProps.optionalFields, nextProps.optionalFields) && objectsDeepEqual(prevProps.data, nextProps.data) && objectsDeepEqual(prevProps.formVerificationErrors, nextProps.formVerificationErrors) && objectsDeepEqual(prevProps.fieldValidationErrors, nextProps.fieldValidationErrors) && objectsDeepEqual(prevProps.documentTypeSelect, nextProps.documentTypeSelect) && prevProps.documentField === nextProps.documentField && prevProps.name === nextProps.name && prevProps.country === nextProps.country && prevProps.companyType === nextProps.companyType);
//#endregion
//#region src/components/Shared/fields/TaxInformationField/TaxInformationField.tsx
var TAX_INFORMATION_FIELD = [
"taxInformation",
"exemptedFromTax",
"isUen"
];
/**
* Renders a dynamic form field for tax identification information, adapting its display
* and behavior based on country and company type. It supports various tax ID input
* scenarios, including two specialized handling for US Sole Proprietorships (where SSN option acts as exemption selection)
* and Singapore Companies (where registration number (UEN) can be used for tax ID).
*/
function TaxInformationField({ data, valid, errors, labels, mask, placeholder, readonly, optional, shouldValidate, handleChangeFor, country, canExempt, registrationNumber, entityType, defaultData }) {
const { isFeatureEnabled } = useToggleContext();
const { t } = useTranslation("common");
const { accountHolder } = useAccountHolder();
const currentCountryTaxInformation = useMemo$1(() => data?.taxInformation?.find((taxInfo) => taxInfo.country === country) ?? (!data.exemptedFromTax ? defaultData?.find((taxInfo) => taxInfo.country === country) : void 0), [
data?.taxInformation,
data.exemptedFromTax,
defaultData,
country
]);
const isUsSoleProp = country === "US" && entityType === CompanyTypesValues.SOLE_PROPRIETORSHIP;
const options = getTaxIdNumberOptions(country, entityType);
const showTaxIdExemptedOptions = canExempt && (options?.length ?? 0) > 1 || isUsSoleProp;
const canSaveAbsentState = isFeatureEnabled("EnableRegistrationAndTaxAbsentStatesV4") && COUNTRIES_WITH_POSSIBLE_TRUSTEE_TAXID_ABSENT_V4.includes(country) && accountHolder === "aTrust";
const exemptionLabel = isUsSoleProp ? t(($) => $["socialSecurityNumber"]) : labels.exemptedFromTax;
const shouldRenderInput = isUsSoleProp ? currentCountryTaxInformation?.type === "EIN" : !data.isUen;
const taxIdNumberType = useMemo$1(() => {
if (data.exemptedFromTax) return "exempted";
if (currentCountryTaxInformation?.type) return currentCountryTaxInformation.type;
if (isUsSoleProp) return void 0;
return getDefaultTaxIdNumberType(data.taxInformation, country, entityType);
}, [
country,
currentCountryTaxInformation?.type,
data.exemptedFromTax,
data.taxInformation,
entityType,
isUsSoleProp
]);
const fieldMetadata = resolveFieldMetadata(taxInformationDefaultFieldConfig[country], {
taxIdNumberType: currentCountryTaxInformation?.type,
companyType: entityType
}, taxInformationDefaultFieldMetadata);
const updateTaxInfo = useCallback$1((mode, newEntry) => {
const entry = canSaveAbsentState ? {
...newEntry,
numberAbsent: newEntry.numberAbsent ?? false
} : newEntry;
handleChangeFor("taxInformation", mode)(upsertTaxInfoForCountry(data.taxInformation, country, entry));
}, [
canSaveAbsentState,
country,
data.taxInformation,
handleChangeFor
]);
const updateTaxType = useCallback$1((type, mode) => {
updateTaxInfo(mode, {
country,
number: currentCountryTaxInformation?.number,
type
});
}, [
country,
currentCountryTaxInformation?.number,
updateTaxInfo
]);
const resetToDefaultData = useCallback$1(() => {
if (defaultData !== void 0) handleChangeFor("taxInformation", "input")(defaultData);
}, [defaultData, handleChangeFor]);
const removeCountryTaxInfo = useCallback$1(() => {
handleChangeFor("taxInformation", "input")(removeTaxInfoForCountry(data.taxInformation, country));
}, [
country,
data.taxInformation,
handleChangeFor
]);
const setUenAsTaxId = useCallback$1((uen) => {
updateTaxInfo("blur", {
country,
number: uen ?? "",
type: "UEN"
});
}, [country, updateTaxInfo]);
const addTaxAbsentStatus = useCallback$1((exempted) => {
handleChangeFor("taxInformation", "input")(upsertTaxInfoForCountry(data.taxInformation, country, {
country,
numberAbsent: exempted
}));
}, [
country,
data.taxInformation,
handleChangeFor
]);
const handleTaxNumberChange = useCallback$1((e, mode) => {
updateTaxInfo(mode, {
country,
number: e ? typeof e === "string" ? e : e.currentTarget.value : void 0,
type: currentCountryTaxInformation?.type ?? (taxIdNumberType !== "exempted" ? taxIdNumberType : void 0)
});
}, [
country,
currentCountryTaxInformation?.type,
taxIdNumberType,
updateTaxInfo
]);
const handleTaxIdNumberTypeChange = useCallback$1((taxIdType) => {
if (!canExempt && taxIdType === "exempted") throw Error(`${country} does not allow tax exemptions`);
if (taxIdType === "exempted") {
handleChangeFor("exemptedFromTax")(true);
updateTaxType(void 0, "input");
return canSaveAbsentState ? addTaxAbsentStatus(true) : removeCountryTaxInfo();
}
handleChangeFor("exemptedFromTax")(false);
if (country === "SG") if (taxIdType === "UEN") {
handleChangeFor("isUen")(true);
setUenAsTaxId(registrationNumber);
} else {
handleChangeFor("isUen")(false);
removeCountryTaxInfo();
}
updateTaxType(taxIdType, "input");
}, [
addTaxAbsentStatus,
canExempt,
canSaveAbsentState,
country,
handleChangeFor,
updateTaxType,
registrationNumber,
removeCountryTaxInfo,
setUenAsTaxId
]);
const handleExemptedFromTaxChange = useCallback$1((exempted) => {
handleChangeFor("exemptedFromTax")(exempted);
if (canSaveAbsentState) addTaxAbsentStatus(exempted);
else removeCountryTaxInfo();
}, [
addTaxAbsentStatus,
canSaveAbsentState,
handleChangeFor,
removeCountryTaxInfo
]);
const prevCountryRef = useRef$1(country);
useEffect$1(() => {
if (!country || prevCountryRef.current === country) return;
prevCountryRef.current = country;
const hasSavedTaxId = defaultData?.find((taxId) => taxId.country === country);
if (currentCountryTaxInformation && !hasSavedTaxId) return;
if (entityType === CompanyTypesValues.SOLE_PROPRIETORSHIP) return;
resetToDefaultData();
}, [country]);
useEffect$1(() => {
if (country === "SG" && data.isUen && registrationNumber && registrationNumber !== currentCountryTaxInformation?.number) setUenAsTaxId(registrationNumber);
}, [
registrationNumber,
currentCountryTaxInformation?.number,
data.isUen,
country,
setUenAsTaxId
]);
const isInitialized = useRef$1(false);
if (!isInitialized.current && country === "SG" && !defaultData?.find((taxInfo) => taxInfo.country === country)?.number) {
isInitialized.current = true;
handleChangeFor("isUen")(false);
}
return /* @__PURE__ */ jsxs("div", { children: [
/* @__PURE__ */ jsx(TaxIdNumberTypeSelector, {
country,
companyType: entityType,
setSelected: handleTaxIdNumberTypeChange,
selected: taxIdNumberType,
exemptedOptionLabel: showTaxIdExemptedOptions ? exemptionLabel : void 0,
isUsSoleProp,
errorMessage: isUsSoleProp ? errors.taxInformation : false
}),
shouldRenderInput && /* @__PURE__ */ jsx(MaskedInput, {
type: "text",
"aria-required": true,
"aria-invalid": !valid.taxInformation,
name: "taxInformation",
label: labels.taxInformation ?? "",
formatGuidance: translateTranslatable(t, fieldMetadata.guidanceText),
value: currentCountryTaxInformation?.number ?? "",
errorMessage: errors.taxInformation,
placeholder,
isValid: Boolean(valid.taxInformation) && !!currentCountryTaxInformation,
onInput: (e) => handleTaxNumberChange(e, "input"),
onBlur: (e) => handleTaxNumberChange(e, "blur"),
disabled: data.exemptedFromTax || data.isUen,
readonly,
optional,
shouldValidate,
...mask
}),
canExempt && !showTaxIdExemptedOptions && /* @__PURE__ */ jsx(Field, {
name: "exemptedFromTax",
children: (childProps) => /* @__PURE__ */ jsx(Checkbox, {
...childProps,
"aria-required": false,
"aria-invalid": false,
name: "exemptedFromTax",
label: labels.exemptedFromTax,
value: "exemptedFromTax",
checked: Boolean(data.exemptedFromTax),
onChange: handleExemptedFromTaxChange
})
})
] });
}
//#endregion
export { getFieldsWithExistingData as a, DocumentGuidance as i, TaxInformationField as n, MemoizedDocumentUpload as r, TAX_INFORMATION_FIELD as t };