Skip to content

Instantly share code, notes, and snippets.

@IvanMMM
Last active February 16, 2023 09:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save IvanMMM/40aa50998778ad7e1601750aab7f0979 to your computer and use it in GitHub Desktop.
Save IvanMMM/40aa50998778ad7e1601750aab7f0979 to your computer and use it in GitHub Desktop.
Generated by XState Viz: https://xstate.js.org/viz
const REVIEW_STATUS = {
PENDING: "pending",
PRECHECKED: "prechecked",
QUEUED: "queued",
HOLD: "onHold",
COMPLETED: "completed",
};
const REVIEW_RESULT = {
GOOD: "GREEN",
BAD: "RED",
};
const REJECT_TYPE = {
TRY_AGAIN: "RETRY",
FINAL: "FINAL",
};
const WEBHOOK_TYPE = {
// When an applicant is created.
CREATED: "applicantCreated",
// When a user uploaded all the required documents and the applicant's status changed to pending.
PENDING: "applicantPending",
// Processing of the applicant is paused for an agreed reason.
HOLD: "applicantOnHold",
// When verification is completed. Contains the verification result
REVIEWED: "applicantReviewed",
// Applicant has been reset: applicant status changed to init and all documents were set as inactive.
RESET: "applicantReset",
};
const KYC_RESULT = {
NONE: "NONE",
SEND: "SEND",
DENY: "DENY",
ACCEPT: "ACCEPT",
RE_VERIFY: "RE_VERIFY",
ADDITIONAL_DOCS_REQUIRED: "ADDITIONAL_DOCS_REQUIRED",
MANUAL_REVIEW: "MANUAL_REVIEW",
KYC_RESTRICTION: "KYC_RESTRICTION",
};
const SERVICE_TYPES = {
IDENTITY: "identity",
ADDRESS: "address",
};
actions = {
initApplicant: async (content) => {
await Promise.all([
sumSubApi.updateApplicant(content.applicantId, content.user),
sumSubApi.updateApplicantData(content.applicantId, content.user),
]);
},
updateEventData: assign({
applicantId: (context, event) => event.applicantId,
levelName: (context, event) => event.levelName,
}),
updateUserData: assign({
data: (context, event) => {
const info = event.data && event.data.info;
const questionnaires = event && event.data && event.data.questionnaires;
return {info, questionnaires};
},
}),
resetUserData: assign({
data: () => ({info: {}, questionnaires: []}),
}),
notifySend: (content) => {
if (content.id === SERVICE_TYPES.ADDRESS) {
dataLayer.kycAddressSent(content.user);
}
},
notifyAdditionalDocsRequired: (content) => {
if (content.id === SERVICE_TYPES.IDENTITY) {
dataLayer.kycIdentityDocsRequired(content.user);
}
},
notifyManualReview: (content) => {
if (content.id === SERVICE_TYPES.IDENTITY) {
notifications.kycManualReview(content.user, {
name: content.user.fullName,
});
}
},
notifyDeny: (content) => {
if (content.id === SERVICE_TYPES.IDENTITY) {
notifications.kycDeny(content.user, {
name: content.user.fullName,
});
}
if (content.id === SERVICE_TYPES.ADDRESS) {
notifications.kycAddressDeny(content.user, {
name: content.user.fullName,
});
}
},
notifyAccept: (content) => {
if (content.id === SERVICE_TYPES.IDENTITY) {
dataLayer.kycIdentityAccept(content.user);
// TODO Add check for phone number exists
dataLayer.kycPhoneAccept(content.user);
dataLayer.verification(content.user);
notifications.kycAccepted(content.user, {
name: content.user.fullName,
});
}
if (content.id === SERVICE_TYPES.ADDRESS) {
dataLayer.kycAddressAccept(content.user);
}
},
}
const guards = {
isTransitioned: (context) => context.isTransitioned,
isAccept: (context, event) => true,
isRetry: (context, event) => true,
isDeny: (context, event) => true,
};
const actions2 = {
initApplicant: async (content) => {
await Promise.all([
sumSubApi.updateApplicant(content.applicantId, content.user),
sumSubApi.updateApplicantData(content.applicantId, content.user),
]);
},
updateEventData: assign({
applicantId: (context, event) => event.applicantId,
levelName: (context, event) => event.levelName,
}),
updateUserData: assign({
data: (context, event) => {
const info = event.data && event.data.info;
const questionnaires = event && event.data && event.data.questionnaires;
return {info, questionnaires};
},
}),
notifySend: (content) => {
if (content.id === SERVICE_TYPES.ADDRESS) {
dataLayer.kycAddressSent(content.user);
}
},
notifyAdditionalDocsRequired: (content) => {
if (content.id === SERVICE_TYPES.IDENTITY) {
dataLayer.kycIdentityDocsRequired(content.user);
}
},
notifyManualReview: (content) => {
if (content.id === SERVICE_TYPES.IDENTITY) {
notifications.kycManualReview(content.user, {
name: content.user.fullName,
});
}
},
notifyDeny: (content) => {
if (content.id === SERVICE_TYPES.IDENTITY) {
notifications.kycDeny(content.user, {
name: content.user.fullName,
});
}
if (content.id === SERVICE_TYPES.ADDRESS) {
notifications.kycAddressDeny(content.user, {
name: content.user.fullName,
});
}
},
notifyAccept: (content) => {
if (content.id === SERVICE_TYPES.IDENTITY) {
dataLayer.kycIdentityAccept(content.user);
// TODO Add check for phone number exists
dataLayer.kycPhoneAccept(content.user);
dataLayer.verification(content.user);
notifications.kycAccepted(content.user, {
name: content.user.fullName,
});
}
if (content.id === SERVICE_TYPES.ADDRESS) {
dataLayer.kycAddressAccept(content.user);
}
},
};
function createMachineWithContext(id, user, data = {}) {
return Machine(
{
predictableActionArguments: true,
id,
initial: "NONE",
context: {
id,
user,
data,
applicantId: undefined,
levelName: undefined,
},
states: {
[KYC_RESULT.NONE]: {
entry: [actions.updateEventData, actions.resetUserData],
on: {
// User initially created
[WEBHOOK_TYPE.CREATED]: {
target: KYC_RESULT.NONE,
actions: [actions.updateEventData, actions.initApplicant],
},
// User uploaded documents first time
[WEBHOOK_TYPE.PENDING]: {
target: KYC_RESULT.SEND,
actions: [actions.notifySend],
},
// User was reset again
[WEBHOOK_TYPE.RESET]: {
target: KYC_RESULT.NONE,
},
},
},
[KYC_RESULT.SEND]: {
entry: [actions.updateEventData, actions.updateUserData],
on: {
// User was reviewed by sumsub
[WEBHOOK_TYPE.REVIEWED]: [
// User was accepted after providing documents
{
target: KYC_RESULT.ACCEPT,
cond: "isAccept",
actions: [actions.notifyAccept],
},
// User was rejected after providing documents
{
target: KYC_RESULT.DENY,
cond: "isDeny",
actions: [actions.notifyDeny],
},
// User was asked for additional documents
{
target: KYC_RESULT.ADDITIONAL_DOCS_REQUIRED,
cond: "isRetry",
actions: [actions.notifyAdditionalDocsRequired],
},
],
// User was switched to manual review after providing docs
[WEBHOOK_TYPE.HOLD]: {
target: KYC_RESULT.MANUAL_REVIEW,
actions: [actions.notifyManualReview],
},
// User verification levels reset
[WEBHOOK_TYPE.RESET]: {
target: KYC_RESULT.NONE,
},
},
},
[KYC_RESULT.MANUAL_REVIEW]: {
entry: [actions.updateEventData, actions.updateUserData],
on: {
// User was reviewed by compliance
[WEBHOOK_TYPE.REVIEWED]: [
{
target: KYC_RESULT.ACCEPT,
cond: "isAccept",
actions: [actions.notifyAccept],
},
{
target: KYC_RESULT.DENY,
cond: "isDeny",
actions: [actions.notifyDeny],
},
{
target: KYC_RESULT.ADDITIONAL_DOCS_REQUIRED,
cond: "isRetry",
actions: [actions.notifyAdditionalDocsRequired],
},
],
// User verification levels reset
[WEBHOOK_TYPE.RESET]: {
target: KYC_RESULT.NONE,
},
},
},
[KYC_RESULT.ACCEPT]: {
entry: [actions.updateEventData, actions.updateUserData],
on: {
// User was re-reviewed
[WEBHOOK_TYPE.REVIEWED]: [
// User blocked because of PEP or some other rejection
{
target: KYC_RESULT.DENY,
cond: "isDeny",
actions: [actions.notifyDeny],
},
// Additional docs was requested
{
target: KYC_RESULT.ADDITIONAL_DOCS_REQUIRED,
cond: "isRetry",
actions: [actions.notifyAdditionalDocsRequired],
},
],
// User was switched to manual review after successful verification
[WEBHOOK_TYPE.HOLD]: {
target: KYC_RESULT.MANUAL_REVIEW,
actions: [actions.notifyManualReview],
},
// User verification levels reset
[WEBHOOK_TYPE.RESET]: {
target: KYC_RESULT.NONE,
},
},
},
[KYC_RESULT.DENY]: {
entry: [actions.updateEventData, actions.updateUserData],
// User was switched to manual review after final deny
on: {
[WEBHOOK_TYPE.HOLD]: {
target: KYC_RESULT.MANUAL_REVIEW,
actions: [actions.notifyManualReview],
},
// User verification levels reset
[WEBHOOK_TYPE.RESET]: {
target: KYC_RESULT.NONE,
},
},
},
[KYC_RESULT.ADDITIONAL_DOCS_REQUIRED]: {
entry: [actions.updateEventData, actions.updateUserData],
on: {
// User re-uploaded documents
[WEBHOOK_TYPE.PENDING]: {
target: KYC_RESULT.SEND,
actions: [actions.notifySend],
},
// User was switched to manual review after requesting additional docs
[WEBHOOK_TYPE.HOLD]: {
target: KYC_RESULT.MANUAL_REVIEW,
actions: [actions.notifyManualReview],
},
// User verification levels reset
[WEBHOOK_TYPE.RESET]: {
target: KYC_RESULT.NONE,
},
},
},
},
},
{
actions,
guards,
},
);
}
const m = createMachineWithContext("identify", {});
interpret(m);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment