diff --git a/resources/application.ts b/resources/application.ts index 0b3552ae..1ca704c2 100644 --- a/resources/application.ts +++ b/resources/application.ts @@ -1,21 +1,15 @@ import axios, { AxiosResponse } from 'axios' import { Application, ApplicationDocument, CreateApplicationRequest } from '../types/application'; import { UnitResponse, Include, UnitError } from '../types/core'; +import { BaseResource } from './baseResource'; -export class Applications { - private token: string; - private basePath = 'https://api.s.unit.sh'; - private resourcePath = '/applications' +export class Applications extends BaseResource{ - constructor(token: string) { - this.token = token + constructor(token: string, basePath: string) { + super(token, basePath + '/applications'); } public async list(params: ApplicationListParams): Promise | UnitError> { - var headers = { - 'Authorization': `Bearer ${this.token}` - }; - var parameters = { 'page[limit]': (params.limit ? params.limit : 100), 'page[offset]': (params.offset ? params.offset : 0), @@ -24,39 +18,24 @@ export class Applications { ...(params.tags && { 'filter[tags]': params.tags }), 'sort': params.sort ? params.sort : '-createdAt' } - var res = await axios.get>(`${this.basePath + this.resourcePath}`, { headers: headers, params: parameters }) - .then(r => r.data) - .catch(error => { return error.response.data }) - - return res + return this.httpGet>('', { params: parameters }) } public async create(request: CreateApplicationRequest): Promise | UnitError> { var headers = { - 'Authorization': `Bearer ${this.token}`, 'Content-Type': 'application/vnd.api+json' }; - var res = await axios.post | UnitError>(`${this.basePath + this.resourcePath}`, { data: request }, { headers }) - .then(r => r.data) - .catch(error => { return error.response.data }) - - return res + return this.httpPost>('', { data: request }, { headers }) } - public async get(id: number): Promise & Include | UnitError> { - var headers = { - 'Authorization': `Bearer ${this.token}` - }; - - var path = `${this.basePath + this.resourcePath}/${id}` - - var res = await axios.get & Include | UnitError>(path, { headers }) - .then(r => r.data) - .catch(error => { return error.response.data }) + public async get(applicationId: number): Promise & Include | UnitError> { + return this.httpGet & Include>(`/${applicationId}`) + } - return res + public async listDocuments(applicationId: number): Promise | UnitError> { + return this.httpGet>(`/${applicationId}/documents`) } } @@ -96,4 +75,4 @@ interface ApplicationListParams { * default: sort=-createdAt */ sort?: string -} \ No newline at end of file +} diff --git a/resources/baseResource.ts b/resources/baseResource.ts new file mode 100644 index 00000000..e3a2e814 --- /dev/null +++ b/resources/baseResource.ts @@ -0,0 +1,56 @@ +import axios from "axios" +import { UnitError } from "../types/core" + +export class BaseResource { + private resourcePath: string + private headers: {}; + + constructor(token: string, resourcePath: string) { + this.resourcePath = resourcePath + + this.headers = { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/vnd.api+json' + }; + } + + protected async httpGet(path: string, config?: { headers?: object, params?: object }) : Promise { + + var conf = { + headers: config?.headers ? { ...this.headers, ...config?.headers } : this.headers, + ...(config?.params && { params: (config.params)}) + } + + return await axios.get(this.resourcePath + path, conf) + .then(r => r.data) + .catch(error => { return error.response.data }) + } + + protected async httpPatch(path: string, data: object, config?: { headers?: object, params?: object }) : Promise { + var conf = { + headers: config?.headers ? { ...this.headers, ...config?.headers } : this.headers, + ...(config?.params && { params: (config.params) }) + } + + return await axios.patch(this.resourcePath + path, data, conf) + .then(r => r.data) + .catch(error => { return error.response.data }) + } + + protected async httpPost(path: string, data: object, config?: { headers?: object, params?: object }) : Promise{ + var conf = { + headers: config?.headers ? { ...this.headers, ...config?.headers } : this.headers, + ...(config?.params && { params: (config.params) }) + } + + return await axios.post(this.resourcePath + path, data, conf) + .then(r => r.data) + .catch(error => { return error.response.data }) + } + + protected async httpDelete(path: string) : Promise { + return await axios.delete(this.resourcePath + path) + .then(r => r.data) + .catch(error => { return error.response.data }) + } +} \ No newline at end of file diff --git a/resources/customer.ts b/resources/customer.ts new file mode 100644 index 00000000..61085a2f --- /dev/null +++ b/resources/customer.ts @@ -0,0 +1,75 @@ +import { UnitResponse, UnitError} from "../types/core"; +import { Customer, PatchBusinessCustomerRequest, PatchIndividualCustomerRequest } from "../types/customer"; +import { BaseResource } from "./baseResource"; + +export class Customers extends BaseResource { + + constructor(token: string, basePath: string) { + super(token, basePath + '/customers') + } + + public async updateIndividual(request: PatchIndividualCustomerRequest) { + return this.httpPatch>(`/${request.customerId}`, { data: request.data }) + } + + public async updateBusiness(request: PatchBusinessCustomerRequest) { + return this.httpPatch>(`/${request.customerId}`, { data: request.data }) + } + + public async get(customerId: number): Promise | UnitError> { + + return this.httpGet>(`/${customerId}`) + } + + public async list(params?: CustomersListParams): Promise | UnitError> { + + var parameters = { + 'page[limit]': (params?.limit ? params.limit : 100), + 'page[offset]': (params?.offset ? params.offset : 0), + ...(params?.query && { 'filter[query]': params.query }), + ...(params?.email && { 'filter[email]': params.email }), + ...(params?.tags && { 'filter[tags]': params.tags }), + 'sort': params?.sort ? params.sort : '-createdAt' + } + + return this.httpGet>('', { params: parameters }) + } +} + +interface CustomersListParams { + /** + * Maximum number of resources that will be returned. Maximum is 1000 resources. See Pagination. + * default: 100 + */ + limit?: number, + + /** + * Number of resources to skip. See Pagination. + * default: 0 + */ + offset?: number, + + /** + * Optional. Search term according to the Full-Text Search Rules. + * default: empty + */ + query?: string, + + /** + * Optional. Filter customers by email address (case sensitive). + * default: empty + */ + email?: string, + + /** + * Optional. Filter customers by Tags. + * default: empty + */ + tags?: Object, + + /** + * Optional. sort=createdAt for ascending order or sort=-createdAt (leading minus sign) for descending order. + * default: sort=-createdAt + */ + sort?: string +} diff --git a/resources/unit.ts b/resources/unit.ts index 6c3571bd..abcfeb2f 100644 --- a/resources/unit.ts +++ b/resources/unit.ts @@ -1,12 +1,13 @@ import { Applications } from './application' +import { Customers } from './customer'; export class Unit { - private token: string; public applications: Applications + public customers: Customers - constructor(token: string) { - this.token = token; - this.applications = new Applications(token); + constructor(token: string, basePath: string) { + this.applications = new Applications(token, basePath); + this.customers = new Customers(token, basePath) } } diff --git a/types/application.ts b/types/application.ts index 87394605..db8b565b 100644 --- a/types/application.ts +++ b/types/application.ts @@ -1,4 +1,4 @@ -import { Address, BeneficialOwner, BusinessContact, FullName, Officer, Phone, State } from "./core" +import { Address, BeneficialOwner, BusinessContact, FullName, Officer, Phone, State, Relationship } from "./core" export type ApplicationStatus = "AwaitingDocuments" | //Certain documents are required for the process to continue. You may upload them via Upload Document. @@ -7,11 +7,6 @@ export type ApplicationStatus = "Approved" | //The application was approved. A Customer resource was created. "Denied" //The application was denied. A Customer resource will not be created. -/** - * More about [Relationship](https://developers.unit.co/#relationships) - */ -export type Relationship = null | [] | { type: string, id: string } | Array<{ type: string, id: string }> - export interface Application { /** * Identifier of the application resource. @@ -21,7 +16,7 @@ export interface Application { /** * Type of the application resource. */ - type: string + type: 'IndividualApplication' | 'BusinessApplication' /** * The relationships object describes the relationship between the current resource and other resources. @@ -131,6 +126,7 @@ export interface IndividualApplication extends Application { export interface BusinessApplication extends Application { type: 'BusinessApplication', + attributes: { /** * One of AwaitingDocuments, PendingReview, Approved, Pending, or Denied, see Application Statuses. @@ -196,7 +192,7 @@ export interface BusinessApplication extends Application { /** * Array of beneficial owners of the business. Beneficial Owner is anyone with more than 25% ownership. Beneficial Owners would need to go over KYC process and provide documents. */ - beneficialOwners: Array + beneficialOwners: BeneficialOwner[] /** * See [Tags](https://developers.unit.co/#tags). @@ -244,7 +240,7 @@ export interface ApplicationDocument { type: "document" /** - * representing the document’s data. + * Representing the document’s data. */ attributes: { /** @@ -307,11 +303,11 @@ export interface CreateApplicationRequest { export interface CreateIndividualApplicationRequest extends CreateApplicationRequest { type: 'individualApplication' - attributes: - { + + attributes: { /** - * SSN of the individual (numbers only). Either an SSN or a passport number is required. - */ + * SSN of the individual (numbers only). Either an SSN or a passport number is required. + */ ssn?: string /** @@ -384,6 +380,7 @@ export interface CreateIndividualApplicationRequest extends CreateApplicationReq export interface CreateBusinessApplicationRequest extends CreateApplicationRequest { type: 'businessApplication' + attributes: { /** * Name of the business. diff --git a/types/core.ts b/types/core.ts index 916989df..46bc8039 100644 --- a/types/core.ts +++ b/types/core.ts @@ -201,11 +201,6 @@ export interface AuthorizedUser { */ fullName: FullName, - /** - * The authorized user's username. - */ - username: string, - /** * The authorized user's email address. */ @@ -251,6 +246,11 @@ export interface Coordinates { latitude: number } +/** + * More about [Relationship](https://developers.unit.co/#relationships) + */ +export type Relationship = null | [] | { type: string, id: string } | Array<{ type: string, id: string }> + export interface UnitResponse { data: T } @@ -266,4 +266,93 @@ export interface UnitError { detail?: string, details?: string }] -} \ No newline at end of file +} + +export function createAddress(street: string, street2: string | null , city: string, state: State | null, postalCode: string, country: string): Address { + return { + street, + ...(street2 && { street2 }), + city, + ...(state && { state }), + postalCode, + country + } +} + +export function createFullName(first: string, last: string) { + return { + first, + last + } +} + +export function createPhone(countryCode: string, number: string) { + return { + countryCode, + number + } +} + +export function createOfficer(status: Status | null, fullName: FullName, title: Title | null, ssn: string | null, passport: string | null, + nationality: string | null, dateOfBirth: string, address: Address, phone: Phone, email: string) { + return { + ...(status && { status }), + fullName, + ...(title && { title }), + ...(ssn && { ssn }), + ...(passport && { passport }), + ...(nationality && { nationality }), + dateOfBirth, + address, + phone, + email, + } +} + +export function createBeneficialOwner(status: Status | null, fullName: FullName, ssn: string | null, passport: string | null, + nationality: string | null, dateOfBirth: string, address: Address, phone: Phone, email: string, percentage: string | null){ + return { + ...(status && { status }), + fullName, + ...(ssn && { ssn }), + ...(passport && { passport }), + ...(nationality && { nationality }), + dateOfBirth, + address, + phone, + email, + ...(percentage && { percentage }), + } +} + +export function createBusinessContact(fullName: FullName, email: string, phone: Phone) { + return { + fullName, + email, + phone + } +} + +export function createAuthorizedUser(fullName: FullName, email: string, phone: Phone) { + return { + fullName, + email, + phone + } +} + +export function createCounterparty(routingNumber: string, accountNumber: string, accountType: string, name: string){ + return { + routingNumber, + accountNumber, + accountType, + name + } +} + +export function createCoordinates(longitude: number, latitude: number) { + return { + longitude, + latitude + } +} diff --git a/types/customer.ts b/types/customer.ts new file mode 100644 index 00000000..6eea366e --- /dev/null +++ b/types/customer.ts @@ -0,0 +1,238 @@ +import { Address, AuthorizedUser, BusinessContact, FullName, Phone, Relationship, State } from "./core" + + +export interface Customer { + /** + * Identifier of the individual resource. + */ + id: string + + /** + * Type of the resource. + */ + type: 'IndividualCustomer' | 'BusinessCustomer' + + /** + * Describes relationships between the customer resource, the Org it belongs to, and the Application it was created by. + */ + relationships: { + /** + * The Org of the individual. + */ + org: Relationship + + /** + * The Application that created this individual. + */ + application: Relationship + } +} + +export interface IndividualCustomer extends Customer { + /** + * Type of the resource, the value is always individualCustomer. + */ + type: 'IndividualCustomer' + + /** + * Representing the individual data. + */ + attributes: { + /** + * Date only. The date the resource was created. + * RFC3339 format. For more information: https://en.wikipedia.org/wiki/ISO_8601#RFCs + */ + createdAt: string + + /** + * SSN of the individual (numbers only). Either ssn or passport will be populated. + */ + ssn?: string + + /** + * Passport number of the individual. .Either ssn or passport will be populated. + */ + passport?: string + + /** + * Required on passport only. Two letters representing the individual nationality. + * ISO31661 - Alpha2 format. For more information: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 + */ + nationality: string + + /** + * Full name of the individual. + */ + fullName: FullName + + /** + * Date only. + * RFC3339 format. For more information: https://en.wikipedia.org/wiki/ISO_8601#RFCs + */ + dateOfBirth: string + + /** + * Address of the individual. + */ + address: Address + + /** + * Phone of the individual. + */ + phone: Phone + + /** + * Email address of the individual. + */ + email: string + + /** + * See [Tags](https://developers.unit.co/#tags). + */ + tags: object + } +} + +export interface BusinessCustomer extends Customer { + /** + * Type of the resource, the value is always businessCustomer. + */ + type: 'BusinessCustomer' + + /** + * Representing the business data. + */ + attributes: { + /** + * Date only. The date the resource was created. + * RFC3339 format. For more information: https://en.wikipedia.org/wiki/ISO_8601#RFCs + */ + createdAt: string + + /** + * Name of the business. + */ + name: string + + /** + * Optional. “Doing business as”. + */ + dba?: string + + /** + * Address of the business. + */ + address: Address + + /** + * Phone of the business. + */ + phone: Phone + + /** + * Two letters representing a US state. + */ + stateOfIncorporation: State + + /** + * Business EIN (numbers only). + */ + ein: string + + /** + * One of "Corporation" or "LLC". + */ + entityType: "Corporation" | "LLC" + + /** + * Primary contact of the business. + */ + contact: BusinessContact + + /** + * Array of authorized users. + * An authorized user is someone who can participate in the One Time Password(OTP) authentication process. + * + */ + authorizedUsers: AuthorizedUser[] + + /** + * See [Tags](https://developers.unit.co/#tags). + * Inherited from the application tags(see [Tag Inheritance](https://developers.unit.co/#tag-inheritance)). + */ + tags: Object + + } +} + + +export interface PatchIndividualCustomerRequest { + customerId: number, + + data: { + type: 'individualCustomer' + + attributes: { + /** + * Address of the individual. To modify or add specify the new address. + */ + address?: Address + + /** + * Phone of the individual. To modify or add specify the new phone number. + */ + phone?: Phone + + /** + * Email address of the individual. To modify or add specify the new email address. + */ + email?: string + + /** + * If the individual is a sole proprietor who is doing business under a different name. + * To modify or add specify the new dba name. + */ + dba?: string + + /** + * See (Updating Tags)[https://developers.unit.co/#tags]. + */ + tags?: object + } + } +} + +export interface PatchBusinessCustomerRequest { + customerId: number, + + data: { + type: 'businessCustomer', + + attributes: { + /** + * Address of the business. To modify specify the new address. + */ + address?: Address + + /** + * Phone of the business. To modify specify the new phone number. + */ + phone?: Phone + + /** + * Primary contact of the business. + */ + contact?: BusinessContact + + /** + * Array of authorized users. The provided array items will replace the existing ones. + */ + authorizedUsers?: AuthorizedUser[] + + /** + * See (Updating Tags)[https://developers.unit.co/#tags]. + */ + tags?: object + } + } +} \ No newline at end of file diff --git a/types/depositAccount.ts b/types/depositAccount.ts new file mode 100644 index 00000000..5c86140f --- /dev/null +++ b/types/depositAccount.ts @@ -0,0 +1,85 @@ +import { Relationship } from "./core"; + +export interface DepositAccount { + /** + * Identifier of the deposit account resource. + */ + id: string + + /** + * Type of the resource, the value is always depositAccount. + */ + type: 'depositAccount' + + /** + * Representing the deposit account data. + */ + attributes: { + /** + * Date only. The date the resource was created. + * RFC3339 format. For more information: https://en.wikipedia.org/wiki/ISO_8601#RFCs + */ + createdAt: string + + /** + * Name of the account holder. + */ + name: string + + /** + * The name of the deposit product. + */ + depositProduct: string + + /** + * Routing number of account. + */ + routingNumber: string + + /** + * Account number, together with the routingNumber forms the identifier of the account on the ACH network. + */ + accountNumber: string + + /** + * Currency of the account. + * Note: the currency is currently always set to USD. The balance is represented in cents. + */ + currency: string + + /** + * The balance amount (in cents). + */ + balance: number + + /** + * The hold amount (in cents). + */ + hold: number + + /** + * The available balance for spending (in cents). + */ + available: number + + /** + * See [Tags](https://developers.unit.co/#tags). + */ + tags: Object + + /** + * Status of the account, either Open or Closed. + */ + status: string + } + + /** + * Describes relationships between the deposit account resource and the customer. + */ + relationships: { + /** + * The customer. + */ + customer: Relationship + } +} \ No newline at end of file