Skip to content

Commit 3ed80ca

Browse files
axshaniilyamerman
andauthored
Create and Verify Documents (#280)
* added create and verify document, with tests * added industry * test * test * test Co-authored-by: ilyamerman <[email protected]>
1 parent 4392d72 commit 3ed80ca

File tree

5 files changed

+165
-58
lines changed

5 files changed

+165
-58
lines changed

resources/application.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Application, ApplicationDocument, CreateApplicationRequest, PatchApplicationRequest, UploadDocumentRequest } from "../types/application"
1+
import { Application, ApplicationDocument, CreateApplicationRequest, PatchApplicationRequest, UploadDocumentRequest, VerifyDocumentRequest } from "../types/application"
22
import { UnitResponse, Include, UnitConfig, BaseListParams, BeneficialOwner } from "../types/common"
33
import { BaseResource } from "./baseResource"
44

@@ -72,6 +72,14 @@ export class Applications extends BaseResource {
7272
public async listDocuments(applicationId: string): Promise<UnitResponse<ApplicationDocument[]>> {
7373
return this.httpGet<UnitResponse<ApplicationDocument[]>>(`/${applicationId}/documents`)
7474
}
75+
76+
public async createDocument(applicationId: string): Promise<UnitResponse<ApplicationDocument>> {
77+
return this.httpPost<UnitResponse<ApplicationDocument>>(`/${applicationId}/documents`)
78+
}
79+
80+
public async verifyDocument(request: VerifyDocumentRequest): Promise<UnitResponse<ApplicationDocument>> {
81+
return this.httpPost<UnitResponse<ApplicationDocument>>(`/${request.applicationId}/documents/${request.documentId}/verify`, {data: request.data})
82+
}
7583
}
7684

7785
export interface ApplicationListParams extends BaseListParams {

tests/applications.spec.ts

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11

22

3-
import { BusinessApplication, IndividualApplication, TrustApplication, Unit } from "../unit"
3+
import { BusinessApplication, IndividualApplication, TrustApplication, Unit, VerifyDocumentRequest } from "../unit"
44
import {
55
createIndividualApplication,
66
createBusinessApplication,
77
createTrustApplication,
8-
createIndividualApplicationWithRequiredDocument
8+
createIndividualApplicationWithRequiredDocument,
9+
createVerifyDocumentRequest,
10+
createIndividualApplicationWithSelfieVerification
911
} from "./testHelpers"
1012
import dotenv from "dotenv"
1113
import * as fs from "fs"
@@ -362,3 +364,32 @@ describe("Applications", () => {
362364
expect(documentUploadResponse.data.attributes.status).toBe("PendingReview")
363365
})
364366
})
367+
368+
369+
describe("Create Document", () => {
370+
test("Create Document for Individual Application", async () => {
371+
const applicationId = (await createIndividualApplicationWithRequiredDocument(unit)).data.id
372+
const document = (await unit.applications.createDocument(applicationId)).data
373+
374+
expect(document).not.toBeNull()
375+
expect(document?.type).toBe("document")
376+
expect(document?.attributes.documentType).toBe("ClientRequested")
377+
expect(document?.attributes.status).toBe("Required")
378+
})
379+
380+
test("Verify Document for Individual Application", async () => {
381+
const applicationId = (await createIndividualApplicationWithSelfieVerification(unit)).data.id
382+
const documents = (await unit.applications.listDocuments(applicationId)).data
383+
384+
expect(documents).not.toBeNull()
385+
const document = documents[0]
386+
expect(document.attributes.documentType).toBe("SelfieVerification")
387+
const documentId = document?.id || ""
388+
const req: VerifyDocumentRequest = createVerifyDocumentRequest(applicationId, documentId, "Tn4NxMisa")
389+
const res = await unit.applications.verifyDocument(req)
390+
expect(document?.id).toBe(res.data.id)
391+
expect(document?.attributes.description).toBe(res.data.attributes.description)
392+
expect(document?.attributes.documentType).toBe(res.data.attributes.documentType)
393+
expect(res.data.attributes.status).toBe("PendingReview")
394+
})
395+
})

tests/testHelpers.ts

Lines changed: 76 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
import { createFullName, createAddress, createPhone, createOfficer, createBusinessContact, createBeneficialOwner } from "../helpers"
2-
import { CreateBusinessApplicationRequest, CreateDepositAccountRequest, CreateIndividualApplicationRequest, CreateTrustApplicationRequest, Unit } from "../unit"
2+
import { CreateBusinessApplicationRequest, CreateDepositAccountRequest, CreateIndividualApplicationRequest, CreateTrustApplicationRequest, Unit, VerifyDocumentRequest } from "../unit"
33

44
export function createIndividualApplication(unit: Unit) {
55
const createIndividualApplication: CreateIndividualApplicationRequest = {
66
type: "individualApplication",
77
attributes: {
8-
ssn: "721074426",
9-
fullName: createFullName("Richard","Hendricks"),
8+
ssn: "123456789",
9+
fullName: createFullName("Richard", "Hendricks"),
1010
dateOfBirth: "2001-08-10",
11-
address: createAddress("20 Ingram St",null,"Forest Hills","CA","11375","US"),
11+
address: createAddress("20 Ingram St", null, "Forest Hills", "CA", "11375", "US"),
1212
13-
phone: createPhone("1","5555555555")
13+
phone: createPhone("1", "5555555555")
1414
}
1515
}
1616

@@ -22,11 +22,30 @@ export function createIndividualApplicationWithRequiredDocument(unit: Unit) {
2222
type: "individualApplication",
2323
attributes: {
2424
ssn: "000000002",
25-
fullName: createFullName("Richard","Hendricks"),
25+
fullName: createFullName("Richard", "Hendricks"),
2626
dateOfBirth: "2001-08-10",
27-
address: createAddress("20 Ingram St",null,"Forest Hills","CA","11375","US"),
27+
address: createAddress("20 Ingram St", null, "Forest Hills", "CA", "11375", "US"),
2828
29-
phone: createPhone("1","5555555555")
29+
phone: createPhone("1", "5555555555")
30+
}
31+
}
32+
33+
return unit.applications.create(createIndividualApplication)
34+
}
35+
36+
export function createIndividualApplicationWithSelfieVerification(unit: Unit) {
37+
const createIndividualApplication: CreateIndividualApplicationRequest = {
38+
type: "individualApplication",
39+
attributes: {
40+
ssn: "000000003",
41+
fullName: createFullName("Richard", "Hendricks"),
42+
dateOfBirth: "2001-08-10",
43+
address: createAddress("20 Ingram St", null, "Forest Hills", "CA", "11375", "US"),
44+
45+
phone: createPhone("1", "5555555555"),
46+
evaluationParams: {
47+
"useSelfieVerification": "ReplaceIdentification"
48+
}
3049
}
3150
}
3251

@@ -47,10 +66,10 @@ export function createBusinessApplication(unit: Unit) {
4766
createAddress("950 Allerton Street", null, "Redwood City", "CA", "94063", "US"), createPhone("1", "2025550108"), "[email protected]"),
4867
contact: createBusinessContact(createFullName("Jone", "Doe"), "[email protected]", createPhone("1", "2025550108")),
4968
beneficialOwners: [
50-
createBeneficialOwner(null,createFullName("James","Smith"),"574567625",null,null,"2012-04-05",
51-
createAddress("650 Allerton Street",null,"Redwood City","CA","94063","US"),createPhone("1","2025550127"),"[email protected]",null),
52-
createBeneficialOwner(null,createFullName("Richard","Hendricks"),"574572795",null,null,"2012-04-03",
53-
createAddress("470 Allerton Street",null,"Redwood City","CA","94063","US"),createPhone("1","2025550158"),"[email protected]",null)
69+
createBeneficialOwner(null, createFullName("James", "Smith"), "574567625", null, null, "2012-04-05",
70+
createAddress("650 Allerton Street", null, "Redwood City", "CA", "94063", "US"), createPhone("1", "2025550127"), "[email protected]", null),
71+
createBeneficialOwner(null, createFullName("Richard", "Hendricks"), "574572795", null, null, "2012-04-03",
72+
createAddress("470 Allerton Street", null, "Redwood City", "CA", "94063", "US"), createPhone("1", "2025550158"), "[email protected]", null)
5473
]
5574
}
5675
}
@@ -62,48 +81,48 @@ export function createTrustApplication(unit: Unit) {
6281
const createTrustApplication: CreateTrustApplicationRequest = {
6382
"type": "trustApplication",
6483
"attributes": {
65-
"name": "Trust me Inc.",
66-
"stateOfIncorporation": "CA",
67-
"revocability": "Revocable",
68-
"sourceOfFunds": "Salary",
69-
"taxId": "123456789",
70-
"trustees": [
71-
{
72-
"fullName": createFullName("Richard", "Hendricks"),
73-
"dateOfBirth": "2000-01-01",
74-
"ssn": "000000002",
75-
"email": "[email protected]",
76-
"phone": createPhone("1", "2025550108"),
77-
"address": createAddress("5230 Newell Rd", null, "Palo Alto", "CA", "94303", "US")
78-
}
79-
],
80-
"contact": {
81-
"fullName": createFullName("Jared", "Dunn"),
82-
"email": "[email protected]",
83-
"phone": createPhone("1", "2025550108"),
84-
"address": createAddress("5230 Newell Rd", null, "Palo Alto", "CA", "94303", "US")
85-
},
86-
"grantor": {
87-
"fullName": createFullName("Jared", "Dunn"),
88-
"dateOfBirth": "2000-01-01",
89-
"ssn": "000000003",
90-
"email": "[email protected]",
91-
"phone": createPhone("1", "2025550108"),
92-
"address": createAddress("5230 Newell Rd", null, "Palo Alto", "CA", "94303", "US")
93-
},
94-
"tags": {
95-
"test": "test1"
96-
},
97-
"beneficiaries": [
98-
{
99-
"fullName": createFullName("Dinesh", "Chugtai"),
100-
"dateOfBirth": "2000-01-01"
84+
"name": "Trust me Inc.",
85+
"stateOfIncorporation": "CA",
86+
"revocability": "Revocable",
87+
"sourceOfFunds": "Salary",
88+
"taxId": "123456789",
89+
"trustees": [
90+
{
91+
"fullName": createFullName("Richard", "Hendricks"),
92+
"dateOfBirth": "2000-01-01",
93+
"ssn": "000000002",
94+
"email": "[email protected]",
95+
"phone": createPhone("1", "2025550108"),
96+
"address": createAddress("5230 Newell Rd", null, "Palo Alto", "CA", "94303", "US")
97+
}
98+
],
99+
"contact": {
100+
"fullName": createFullName("Jared", "Dunn"),
101+
"email": "[email protected]",
102+
"phone": createPhone("1", "2025550108"),
103+
"address": createAddress("5230 Newell Rd", null, "Palo Alto", "CA", "94303", "US")
101104
},
102-
{
103-
"fullName": createFullName("Gilfoyle", "Unknown"),
104-
"dateOfBirth": "2000-01-01"
105-
}
106-
]
105+
"grantor": {
106+
"fullName": createFullName("Jared", "Dunn"),
107+
"dateOfBirth": "2000-01-01",
108+
"ssn": "000000003",
109+
"email": "[email protected]",
110+
"phone": createPhone("1", "2025550108"),
111+
"address": createAddress("5230 Newell Rd", null, "Palo Alto", "CA", "94303", "US")
112+
},
113+
"tags": {
114+
"test": "test1"
115+
},
116+
"beneficiaries": [
117+
{
118+
"fullName": createFullName("Dinesh", "Chugtai"),
119+
"dateOfBirth": "2000-01-01"
120+
},
121+
{
122+
"fullName": createFullName("Gilfoyle", "Unknown"),
123+
"dateOfBirth": "2000-01-01"
124+
}
125+
]
107126
}
108127
}
109128

@@ -150,4 +169,8 @@ export async function createIndividualAccount(unit: Unit) {
150169
export async function createBussinessAccount(unit: Unit) {
151170
const customerId = await createBusinessCustomer(unit)
152171
return createAccount(customerId ? customerId : "", unit)
172+
}
173+
174+
export function createVerifyDocumentRequest(applicationId: string, documentId: string, jobId: string): VerifyDocumentRequest {
175+
return { applicationId, documentId, data: { type: "selfieVerification", attributes: { jobId } } }
153176
}

types/application.ts

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Address, BeneficialOwner, BusinessContact, FullName, Officer, Phone, State, Relationship, DeviceFingerprint, Agent, RelationshipsArray, Beneficiary, Grantor, TrustContact, Trustee, UnimplementedRelationships, UnimplementedFields } from "./common"
1+
import { Address, BeneficialOwner, BusinessContact, FullName, Officer, Phone, State, Relationship, DeviceFingerprint, Agent, RelationshipsArray, Beneficiary, Grantor, TrustContact, Trustee, UnimplementedRelationships, UnimplementedFields, EvaluationParams, Industry } from "./common"
22

33
/**
44
* see [Application Statuses](https://docs.unit.co/applications/#application-statuses).
@@ -147,6 +147,11 @@ export interface IndividualApplication extends BaseApplication {
147147
* Optional. Indicates if the individual is a sole proprietor who is doing business under a different name, if specified.
148148
*/
149149
dba?: string
150+
151+
/**
152+
*
153+
*/
154+
150155
} & BaseApplicationAttributes
151156

152157
relationships: BaseApplicationRelationships
@@ -249,7 +254,10 @@ export type DocumentType =
249254
"AddressVerification" | //An individual's document to verify address. Document may be a utility bill, bank statement, lease agreement or current pay stub.
250255
"SocialSecurityCard" | //An individual's social security card.
251256
"CertificateOfIncorporation" | //A business's certificate of incorporation.
252-
"EmployerIdentificationNumberConfirmation" //A business's EIN confirmation document (either IRS form 147c or IRS form CP-575).
257+
"EmployerIdentificationNumberConfirmation" | //A business's EIN confirmation document (either IRS form 147c or IRS form CP-575).
258+
"PowerOfAttorney" |
259+
"ClientRequested" |
260+
"SelfieVerification"
253261

254262
export type ReasonCode =
255263
"PoorQuality" |
@@ -457,6 +465,16 @@ export interface CreateIndividualApplicationRequest {
457465
* Optional. The details of the person that will act as the agent that has power of attorney.
458466
*/
459467
powerOfAttorneyAgent?: Agent
468+
469+
/**
470+
* If the individual is a sole proprietor, specify the business industry here.
471+
*/
472+
industry?: Industry
473+
474+
/**
475+
* Optional. Evaluation Params for this entity.
476+
*/
477+
evaluationParams?: EvaluationParams
460478
}
461479
}
462480

@@ -597,3 +615,15 @@ export interface PatchApplicationRequest {
597615
}
598616
}
599617
}
618+
619+
export interface VerifyDocumentRequest {
620+
applicationId: string
621+
documentId: string
622+
623+
data: {
624+
type: "selfieVerification"
625+
attributes: {
626+
jobId: string
627+
}
628+
}
629+
}

types/common.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,18 @@ export interface HealthcareAmounts {
386386
totalHealthcareAmount: number
387387
}
388388

389+
export interface EvaluationParams {
390+
/**
391+
* Optional. Decide when to request a selfie verification document
392+
*/
393+
useSelfieVerification?: "Never" | "ReplaceIdentification"
394+
395+
/**
396+
* Optional, default to false. Decide whether to require an ID verification regardless of evaluation response
397+
*/
398+
requireIdVerification?: boolean
399+
}
400+
389401
export type Grantor = BaseContactAttributes
390402

391403
export type Beneficiary = Pick<BaseContactAttributes, "fullName" | "dateOfBirth">
@@ -399,6 +411,9 @@ export interface TrustContact extends Pick<BaseContactAttributes, "fullName" | "
399411
jwtSubject?: string
400412
}
401413

414+
export type Industry = "Retail" | "Wholesale" | "Restaurants" | "Hospitals" | "Construction" | "Insurance" | "Unions" | "RealEstate" |
415+
"FreelanceProfessional" | "OtherProfessionalServices" | "OnlineRetailer" | "OtherEducationServices"
416+
402417
export type Direction = "Credit" | "Debit"
403418

404419
export type CardNetwork = "Visa" | "Interlink" | "Accel" | "Allpoint" | "Other"

0 commit comments

Comments
 (0)