UNPKG

@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.

419 lines (418 loc) 20.4 kB
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] = "f5213005-6c74-4ce0-83c7-bc913fccb877", e._sentryDebugIdIdentifier = "sentry-dbid-f5213005-6c74-4ce0-83c7-bc913fccb877"); } catch (e) {} import { o as createLogger } from "./translation-BFxyJ1c5.js"; import { t as LegalEntityTypes } from "./legal-entity-type-VIfNYnJP.js"; import { t as getLegalEntityCountry } from "./getLegalEntityCountry-C6bSV6sB.js"; import { r as cloneObject, u as entriesOf } from "./useAnalyticsContext-BVFDMrVE.js"; import { r as TaskStatuses } from "./taskStatus-C7XU4UIF.js"; import { C as EntityTypes, n as getCapabilityProblems, t as getCapabilities, w as CUSTOMER_SUPPORT_DATA_MISSING_ERROR_CODES } from "./processCapabilities-DlZY9-Jc.js"; import { a as getLegalRepresentative, i as getDirectEntityAssociations, l as getOwnSoleProprietorshipIdArray, p as TaskTypes } from "./entityAssociationUtil-BEzUdPbm.js"; import { i as getOwnDecisionMakersLegalEntityIds, o as hasMinRequiredDecisionMakerCount } from "./decisionMakerRoles-CAy-eeNV.js"; import { a as hasRequiredTrustMemberCount, n as getOwnTrustLegalEntityIdAsArray, r as getOwnTrustMembersLegalEntityIds } from "./trustUtil-BTHvlo55.js"; import { t as isEmptyEntity } from "./isEmptyEntity-BM_3oo07.js"; import { t as doesLegalEntityRequiresLegalRepresentative } from "./birthDateUtils-C686KOxO.js"; import { useCallback, useContext } from "preact/hooks"; import { computed, signal } from "@preact/signals"; import { createContext } from "preact"; //#region src/core/models/api/transfer-instrument.ts function isTransferInstrument(account) { return account.bankAccount !== void 0; } var isInstantVerifiedAccount = (account) => Boolean(isTransferInstrument(account) ? account.bankAccount.trustedSource : account.trustedSource); var getObscuredAccountNumber = ({ accountIdentifier, realLastFour }) => realLastFour ? `*******${realLastFour}` : accountIdentifier; //#endregion //#region src/components/UnincorporatedPartnership/utils/unincorporatedParnershipUtil.ts var isPartOfUnincorporatedPartnership = (legalEntity) => legalEntity?.entityAssociations?.some((ea) => ea.entityType === LegalEntityTypes.UNINCORPORATED_PARTNERSHIP && ea.associatorId === legalEntity.id); var getOwnUnincorporatedPartnershipLegalEntityId = (legalEntity) => legalEntity?.entityAssociations?.find((ea) => ea.entityType === LegalEntityTypes.UNINCORPORATED_PARTNERSHIP && ea.associatorId === legalEntity.id)?.legalEntityId; var getOwnUnincorporatedPartnershipLegalEntityIdAsArray = (legalEntity) => { const legalEntityId = getOwnUnincorporatedPartnershipLegalEntityId(legalEntity); return legalEntityId ? [legalEntityId] : []; }; var getOwnUnincorporatedPartnershipProblems = (legalEntity, capabilityProblems) => { const legalEntityId = getOwnUnincorporatedPartnershipLegalEntityId(legalEntity); if (legalEntityId) return capabilityProblems?.LegalEntity?.[legalEntityId]; }; /** * Gathers all legal entity IDs relevant for calculating the task status of an unincorporated partnership. *. * These ids includes the root legal entity itself, the direct members of the partnership, and any owners * (e.g., UBOs through ownership) associated with those members. * * @param rootLegalEntity The root legal entity * @returns A list of legal entity IDs, including the root entity, its partnership members, and their owners. */ var getOwnMembersLegalEntityIds = (rootLegalEntity) => { const unincorporatedPartnershipEntityId = getOwnUnincorporatedPartnershipLegalEntityId(rootLegalEntity); if (!unincorporatedPartnershipEntityId) return []; const memberAssociationsIds = getDirectEntityAssociations(rootLegalEntity, unincorporatedPartnershipEntityId).map((association) => association.legalEntityId).filter((id) => id !== void 0); const ownerIds = memberAssociationsIds.flatMap((associationId) => getDirectEntityAssociations(rootLegalEntity, associationId)).map((association) => association.legalEntityId).filter((id) => id !== void 0); return [ rootLegalEntity.id, ...memberAssociationsIds, ...ownerIds ]; }; //#endregion //#region src/core/models/api/verification-status.ts var VerificationStatuses = { VALID: "valid", PENDING: "pending", INVALID: "invalid", REJECTED: "rejected" }; //#endregion //#region src/stores/globalStore/get-task-status.ts var calculateCustomerSupportTask = (capabilityProblems, rootLegalEntity) => { const supportData = rootLegalEntity.individual?.support ?? rootLegalEntity.organization?.support; const hasMissingDataErrors = capabilityProblems.LegalEntity?.[rootLegalEntity.id]?.missingData?.some((error) => CUSTOMER_SUPPORT_DATA_MISSING_ERROR_CODES.includes(error.code)); if (!supportData) return { status: TaskStatuses.DETAILS_REQUIRED, reason: "rootLegalEntityIsEmpty" }; if (hasMissingDataErrors) return { status: TaskStatuses.DETAILS_REQUIRED, reason: "dataMissingError", details: { missingData: capabilityProblems.LegalEntity?.[rootLegalEntity.id].missingData?.filter((error) => CUSTOMER_SUPPORT_DATA_MISSING_ERROR_CODES.includes(error.code)) } }; const pendingCapabilities = getPendingCapabilities(rootLegalEntity); if (pendingCapabilities.length > 0) return { status: TaskStatuses.PROCESSING, reason: "pendingCapabilities", details: { pendingCapabilities } }; if (hasAllCapabilitiesValid(rootLegalEntity)) return { status: TaskStatuses.FINISHED, reason: "allCapabilitiesValid" }; return { status: TaskStatuses.FINISHED, reason: "noIssuesFound" }; }; var getRegularTaskStatus = ({ taskType, capabilityProblems, rootLegalEntity, relevantEntityIds, ignorePendingCapabilities = false }) => { const entityType = taskType === TaskTypes.PAYIN || taskType == TaskTypes.PAYOUT ? EntityTypes.BANK_ACCOUNT : EntityTypes.LEGAL_ENTITY; if (relevantEntityIds.length === 0) return { status: TaskStatuses.EMPTY, reason: "newEntityAlwaysEmpty" }; if (relevantEntityIds.includes(rootLegalEntity.id) && isEmptyEntity(rootLegalEntity)) return { status: TaskStatuses.EMPTY, reason: "rootLegalEntityIsEmpty" }; const importantProblems = findImportantProblems(entityType, capabilityProblems, relevantEntityIds, taskType); if (importantProblems.length > 0) return importantProblems[0].statusWithReason; const pendingCapabilities = getPendingCapabilities(rootLegalEntity); if (!ignorePendingCapabilities && pendingCapabilities.length > 0) return { status: TaskStatuses.PROCESSING, reason: "pendingCapabilities", details: { pendingCapabilities } }; if (relevantEntityIds.includes(rootLegalEntity.id)) { if (hasAllCapabilitiesValid(rootLegalEntity)) return { status: TaskStatuses.FINISHED, reason: "allCapabilitiesValid" }; } return { status: TaskStatuses.FINISHED, reason: "noIssuesFound" }; }; var getDecisionMakerOverviewStatus = ({ capabilityProblems, rootLegalEntity, relevantEntityIds }) => { if (relevantEntityIds.length === 0) return { status: TaskStatuses.EMPTY, reason: "newEntityAlwaysEmpty" }; if (relevantEntityIds.includes(rootLegalEntity.id) && isEmptyEntity(rootLegalEntity)) return { status: TaskStatuses.EMPTY, reason: "rootLegalEntityIsEmpty" }; const importantProblems = findImportantProblems(EntityTypes.LEGAL_ENTITY, capabilityProblems, relevantEntityIds, TaskTypes.DECISION_MAKER_OVERVIEW); if (importantProblems.length > 0) return importantProblems[0].statusWithReason; const pendingCapabilities = getPendingCapabilities(rootLegalEntity).filter((capability) => !/.*TransferInstrument/gim.test(capability)); if (pendingCapabilities.length > 0) return { status: TaskStatuses.PROCESSING, reason: "pendingCapabilities", details: { pendingCapabilities } }; if (relevantEntityIds.includes(rootLegalEntity.id) && hasAllNonTransferInstrumentCapabilitiesValid(rootLegalEntity)) return { status: TaskStatuses.FINISHED, reason: "allCapabilitiesValid" }; return { status: TaskStatuses.FINISHED, reason: "noIssuesFound" }; }; var IMPORTANT_STATUSES = [ TaskStatuses.DETAILS_REQUIRED, TaskStatuses.DOWNLOAD, TaskStatuses.ERROR, TaskStatuses.PROCESSING ]; function findImportantProblems(type, capabilityProblems, entityIds, taskType) { if (!capabilityProblems?.[type] || !entityIds.length) return []; const problems = cloneObject(capabilityProblems); if (taskType !== TaskTypes.CUSTOMER_SUPPORT) entityIds.forEach((id) => { if (problems.LegalEntity?.[id]?.missingData) problems.LegalEntity[id].missingData = problems.LegalEntity?.[id]?.missingData?.filter((missingDataErrors) => !CUSTOMER_SUPPORT_DATA_MISSING_ERROR_CODES.includes(missingDataErrors.code)); }); return Object.entries(problems[type] ?? {}).filter(([key]) => entityIds.includes(key)).map(([, value]) => value).filter((problems) => problems.statusWithReason !== void 0 && IMPORTANT_STATUSES.includes(problems.statusWithReason.status)).sort((prev, curr) => curr.statusWithReason.status - prev.statusWithReason.status); } var getPendingCapabilities = (legalEntityResponse) => entriesOf(legalEntityResponse.capabilities ?? {}).filter(([, capability]) => capability?.verificationStatus === "pending").reduce((acc, [name]) => [...acc, name], []); var hasAllCapabilitiesResolved = (legalEntityResponse) => Object.values(legalEntityResponse?.capabilities ?? {}).every((capability) => capability.verificationStatus !== VerificationStatuses.PENDING); var hasRejectedCapabilities = (legalEntityResponse) => Object.values(legalEntityResponse?.capabilities ?? {}).some((capability) => capability.verificationStatus === VerificationStatuses.REJECTED); var hasAllCapabilitiesValid = (legalEntityResponse) => Object.values(legalEntityResponse?.capabilities ?? {}).every((capability) => capability.verificationStatus === VerificationStatuses.VALID); var hasAllNonTransferInstrumentCapabilitiesValid = (legalEntityResponse) => { for (const [name, capability] of Object.entries(legalEntityResponse?.capabilities ?? {})) if (!/.*TransferInstrument/gim.test(name) && capability.verificationStatus !== VerificationStatuses.VALID) return false; return true; }; //#endregion //#region src/stores/globalStore/calculateTaskStatus.ts var calculateTaskStatus = ({ taskIdentifier, capabilityProblems, rootLegalEntity, accountHolder, legalRepresentative, trustMembers, transferInstruments }) => { const { taskType, id } = splitTaskIdentifier(taskIdentifier); let relevantEntityIds = []; let ignorePendingCapabilities = false; switch (taskType) { case TaskTypes.INDIVIDUAL: case TaskTypes.BUSINESS_DETAILS: relevantEntityIds = [rootLegalEntity.id]; break; case TaskTypes.LEGAL_REPRESENTATIVE_DETAILS: if (!legalRepresentative) return { status: TaskStatuses.DETAILS_REQUIRED, reason: "legalRepresentativeNeeded" }; relevantEntityIds = [legalRepresentative.legalEntityId]; break; case TaskTypes.SOLE_PROPRIETOR_COMPANY: relevantEntityIds = getOwnSoleProprietorshipIdArray(rootLegalEntity); break; case TaskTypes.DECISION_MAKER_OVERVIEW: if (!hasMinRequiredDecisionMakerCount(rootLegalEntity)) return { status: TaskStatuses.DETAILS_REQUIRED, reason: "missingRequiredDecisionMakers" }; relevantEntityIds = getOwnDecisionMakersLegalEntityIds(rootLegalEntity); return getDecisionMakerOverviewStatus({ capabilityProblems, rootLegalEntity, relevantEntityIds }); case TaskTypes.DECISION_MAKER: if (!id) throw new Error("Decision maker task identifier should specify an ID"); relevantEntityIds = [id]; break; case TaskTypes.TRUST: { const trustIds = getOwnTrustLegalEntityIdAsArray(rootLegalEntity); if (accountHolder === "aTrust" && trustIds.length === 0) return { status: TaskStatuses.EMPTY, reason: "newEntityAlwaysEmpty" }; relevantEntityIds = trustIds; break; } case TaskTypes.TRUST_MEMBER_OVERVIEW: if (!hasRequiredTrustMemberCount(trustMembers)) return { status: TaskStatuses.DETAILS_REQUIRED, reason: "missingRequiredTrustMembers" }; relevantEntityIds = getOwnTrustMembersLegalEntityIds(rootLegalEntity); break; case TaskTypes.TRUST_MEMBER: case TaskTypes.TRUST_MEMBER_INDIVIDUAL: case TaskTypes.TRUST_MEMBER_COMPANY: case TaskTypes.UNINCORPORATED_PARTNERSHIP_MEMBER_COMPANY: case TaskTypes.UNINCORPORATED_PARTNERSHIP_MEMBER_INDIVIDUAL: if (!id) throw new Error("Trust member task identifier should specify an ID"); if (id === "undefinedBeneficiary") { relevantEntityIds = []; break; } relevantEntityIds = [id, ...getDirectEntityAssociations(rootLegalEntity, id).map((association) => association.legalEntityId)]; break; case TaskTypes.PAYIN: case TaskTypes.PAYOUT: { if (!id) return { status: TaskStatuses.DETAILS_REQUIRED, reason: "bankAccountNeeded" }; const matchingAccount = transferInstruments.find((ti) => ti.id === id); if (!matchingAccount) throw new Error(`Could not find transfer instrument with id ${id}`); ignorePendingCapabilities = isInstantVerifiedAccount(matchingAccount); relevantEntityIds = [id]; break; } case TaskTypes.CUSTOMER_SUPPORT: return calculateCustomerSupportTask(capabilityProblems, rootLegalEntity); case TaskTypes.UNINCORPORATED_PARTNERSHIP: { const partnerIds = getOwnUnincorporatedPartnershipLegalEntityIdAsArray(rootLegalEntity); if (accountHolder === "anUnincorporatedPartnership" && partnerIds.length === 0) return { status: TaskStatuses.EMPTY, reason: "newEntityAlwaysEmpty" }; relevantEntityIds = partnerIds; break; } case TaskTypes.UNINCORPORATED_PARTNERSHIP_MEMBER_OVERVIEW: relevantEntityIds = getOwnMembersLegalEntityIds(rootLegalEntity); if (relevantEntityIds?.length === 1) return { status: TaskStatuses.EMPTY, reason: "newEntityAlwaysEmpty" }; break; } return getRegularTaskStatus({ taskType, rootLegalEntity, capabilityProblems, relevantEntityIds, ignorePendingCapabilities }); }; /** * Splits a {@link TaskIdentifier} (e.g. `PAYOUT_ACCOUNT-abc-123`) into the task type and id */ var splitTaskIdentifier = (taskIdentifier) => { const firstDashIndex = taskIdentifier.indexOf("-"); if (firstDashIndex === -1) return { taskType: taskIdentifier }; return { taskType: taskIdentifier.slice(0, firstDashIndex), id: taskIdentifier.slice(firstDashIndex + 1) }; }; //#endregion //#region src/stores/globalStore/determineTaskIdentifiers.ts var determineTaskIdentifiers = ({ taskTypes: receivedTaskTypes, rootLegalEntity, accountHolder, capabilityProblems, trustMembers, transferInstruments, unincorporatedPartnershipMembers, legalRepresentative }) => { if (!rootLegalEntity || !capabilityProblems) return []; const taskTypes = receivedTaskTypes.map(mapReceivedTaskType); const tasks = [...taskTypes]; if (taskTypes.includes(TaskTypes.PAYIN)) transferInstruments.forEach((account) => tasks.push(`PAYIN-${account.id}`)); if (taskTypes.includes(TaskTypes.PAYOUT)) transferInstruments.forEach((account) => tasks.push(`PAYOUT-${account.id}`)); if (taskTypes.includes(TaskTypes.DECISION_MAKER_OVERVIEW)) getOwnDecisionMakersLegalEntityIds(rootLegalEntity).forEach((id) => tasks.push(`DECISION_MAKER-${id}`)); if (legalRepresentative || doesLegalEntityRequiresLegalRepresentative(rootLegalEntity)) tasks.push(TaskTypes.LEGAL_REPRESENTATIVE_DETAILS); if (accountHolder === "mySoleProprietorName") tasks.push(TaskTypes.SOLE_PROPRIETOR_COMPANY); if (accountHolder === "aTrust") { tasks.push(TaskTypes.TRUST); tasks.push(TaskTypes.TRUST_MEMBER_OVERVIEW); trustMembers.forEach((member) => { if (member.trustMemberType === "undefinedBeneficiary") { tasks.push(`TRUST_MEMBER-undefinedBeneficiary`); return; } switch (member.legalEntityType) { case "individual": tasks.push(`TRUST_MEMBER_INDIVIDUAL-${String(member.legalEntityId)}`); return; case "organization": tasks.push(`TRUST_MEMBER_COMPANY-${String(member.legalEntityId)}`); return; } }); } if (accountHolder === "anUnincorporatedPartnership") { tasks.push(TaskTypes.UNINCORPORATED_PARTNERSHIP); tasks.push(TaskTypes.UNINCORPORATED_PARTNERSHIP_MEMBER_OVERVIEW); unincorporatedPartnershipMembers?.forEach((member) => { switch (member.legalEntityType) { case "individual": tasks.push(`UNINCORPORATED_PARTNERSHIP_MEMBER_INDIVIDUAL-${String(member.legalEntityId)}`); return; case "organization": tasks.push(`UNINCORPORATED_PARTNERSHIP_MEMBER_COMPANY-${String(member.legalEntityId)}`); return; } }); } return tasks; }; var mapReceivedTaskType = (taskType) => { switch (taskType) { case TaskTypes.DECISION_MAKER: return TaskTypes.DECISION_MAKER_OVERVIEW; case TaskTypes.TRUST_MEMBER: return TaskTypes.TRUST_MEMBER_OVERVIEW; default: return taskType; } }; //#endregion //#region src/stores/globalStore/globalStore.tsx var logger = createLogger(); /** * Initializes the store. Should only be called from {@link StoreProvider}. */ var createGlobalStore = ({ rootLegalEntity: rootLegalEntityIn }) => { const rootLegalEntity = signal(rootLegalEntityIn); const taskTypes = signal([]); const accountHolder = signal(); const trustMembers = signal([]); const unincorporatedPartnershipMembers = signal([]); const rootLegalEntityCountry = computed(() => getLegalEntityCountry(rootLegalEntity.value)); const capabilities = computed(() => getCapabilities(rootLegalEntity.value)); const capabilityProblems = computed(() => getCapabilityProblems(rootLegalEntity.value, rootLegalEntityCountry.value)); const legalRepresentative = computed(() => getLegalRepresentative(rootLegalEntity.value)); const transferInstruments = computed(() => rootLegalEntity.value.transferInstruments ?? []); const tasks = computed(() => determineTaskIdentifiers({ taskTypes: taskTypes.value, rootLegalEntity: rootLegalEntity.value, accountHolder: accountHolder.value, capabilities: capabilities.value, capabilityProblems: capabilityProblems.value, legalRepresentative: legalRepresentative.value, transferInstruments: transferInstruments.value, trustMembers: trustMembers.value, unincorporatedPartnershipMembers: unincorporatedPartnershipMembers.value })); return { rootLegalEntity, taskTypes, accountHolder, trustMembers, unincorporatedPartnershipMembers, rootLegalEntityCountry, capabilities, capabilityProblems, legalRepresentative, transferInstruments, tasks, taskStatuses: computed(() => tasks.value.reduce((acc, taskIdentifier) => { try { acc[taskIdentifier] = calculateTaskStatus({ taskIdentifier, accountHolder: accountHolder.value, capabilityProblems: capabilityProblems.value, rootLegalEntity: rootLegalEntity.value, legalRepresentative: legalRepresentative.value, trustMembers: trustMembers.value, transferInstruments: transferInstruments.value }); } catch (err) { logger.error(`Could not calculate task status for ${taskIdentifier}. `, err); } return acc; }, {})), setRootLegalEntity: (newVal) => rootLegalEntity.value = newVal, setTaskTypes: (newVal) => taskTypes.value = newVal, setAccountHolder: (newVal) => accountHolder.value = newVal, setTrustMembers: (newVal) => trustMembers.value = newVal, setUnincorporatedPartnershipMembers: (newVal) => unincorporatedPartnershipMembers.value = newVal }; }; var StoreContext = createContext(null); function useGlobalStore() { const store = useContext(StoreContext); if (!store) throw new Error("`useGlobalStore()` must be called from within a `StoreContext.Provider`"); return store; } var useGetTaskStatus = () => { const taskStatusesSignal = useGlobalStore().taskStatuses; return useCallback((taskType, id) => { const taskIdentifier = id ? `${taskType}-${id}` : taskType; const status = taskStatusesSignal.value[taskIdentifier]?.status; if (status === void 0) { logger.warn(`No status for task '${taskIdentifier}', returning UNKNOWN`); return TaskStatuses.UNKNOWN; } return status; }, [taskStatusesSignal]); }; //#endregion export { splitTaskIdentifier as a, getOwnUnincorporatedPartnershipProblems as c, isInstantVerifiedAccount as d, useGlobalStore as i, isPartOfUnincorporatedPartnership as l, createGlobalStore as n, hasAllCapabilitiesResolved as o, useGetTaskStatus as r, hasRejectedCapabilities as s, StoreContext as t, getObscuredAccountNumber as u };