import {z} from 'zod' export type AccountCreateAttributes = {name: string, account_type: 'CARD', subtype: 'DEBIT' | 'DAILYPAY', details: {token: string, first_name: string, last_name: string, expiration_month: string, expiration_year: string, address_line_one: string, address_line_two?: string | undefined, address_city: string, address_state: string, address_zip_code: string, address_country: string, issuer: string}} | {name: string, account_type: 'DEPOSITORY', subtype: 'SAVINGS' | 'CHECKING', details: {first_name: string, last_name: string, routing_number: string, account_number: string}}; export type PersonIdentifier = {type: 'people', id: string}; export type PersonRelationship = {data: PersonIdentifier}; export type AccountRelationships = {person: PersonRelationship}; export type AccountCreateResource = {type: 'accounts', attributes: AccountCreateAttributes, relationships: AccountRelationships}; export type AccountCreateData = {data: AccountCreateResource}; export type CreateApiRestAccountsArgs = {body: AccountCreateData}; export type CreateApiRestAccountsOptions = {baseUrl?: string | undefined, oauthUserToken?: string | undefined, fetch?: typeof fetch | undefined}; export const currency = z.string(); export const transferDestinationCapability = z.object({schedule: z.enum(["WITHIN_THIRTY_MINUTES", "NEXT_BUSINESS_DAY"]), fee: z.number().int(), currency: currency}); export const accountAttributes = z.union([z.object({verification_status: z.enum(["VERIFICATION_PENDING", "VERIFICATION_FAILED", "VERIFIED"]), balances: z.object({available: z.number().int().gte(0).nullable(), current: z.number().int().gte(0).nullable(), currency: z.string()}), capabilities: z.object({transfer_destination: z.array(transferDestinationCapability)}), name: z.string(), account_type: z.literal("CARD"), subtype: z.enum(["DEBIT", "DAILYPAY"]), details: z.object({last_four: z.string(), issuer: z.string(), first_name: z.string(), last_name: z.string(), expiration_month: z.string(), expiration_year: z.string()})}), z.object({verification_status: z.enum(["VERIFICATION_PENDING", "VERIFICATION_FAILED", "VERIFIED"]), balances: z.object({available: z.number().int().gte(0).nullable(), current: z.number().int().gte(0).nullable(), currency: z.string()}), capabilities: z.object({transfer_destination: z.array(transferDestinationCapability)}), name: z.string(), account_type: z.literal("EARNINGS_BALANCE"), subtype: z.literal("ODP"), details: z.object({})}), z.object({verification_status: z.enum(["VERIFICATION_PENDING", "VERIFICATION_FAILED", "VERIFIED"]), balances: z.object({available: z.number().int().gte(0).nullable(), current: z.number().int().gte(0).nullable(), currency: z.string()}), capabilities: z.object({transfer_destination: z.array(transferDestinationCapability)}), name: z.string(), account_type: z.literal("DEPOSITORY"), subtype: z.enum(["SAVINGS", "CHECKING"]), details: z.object({first_name: z.string(), last_name: z.string(), routing_number: z.string(), account_number: z.string()})})]); export const accountLink = z.string(); export const accountLinks = z.object({self: accountLink}); export const personIdentifier = z.object({type: z.literal("people"), id: z.string()}); export const personRelationship = z.object({data: personIdentifier}); export const accountRelationships = z.object({person: personRelationship}); export const accountResource = z.object({id: z.string(), type: z.literal("accounts"), attributes: accountAttributes, links: accountLinks, relationships: accountRelationships}); export const accountData = z.object({data: accountResource}); export const validateResponse = async ( schema: { readonly '~standard': { readonly validate: (value: unknown) => ({ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }) | Promise<{ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }> } }, value: unknown ): Promise => { const result = await schema['~standard'].validate(value) if (result.issues !== undefined) { const detail = result.issues .map(({ message, path }) => path?.length ? `${path.map((segment) => String(typeof segment === 'object' ? segment.key : segment)).join('.')}: ${message}` : message ) .join('; ') throw new Error(`Response does not match the schema: ${detail}`, { cause: { issues: result.issues } }) } return result.value }; /** * Create an Account object * * POST /rest/accounts */ export const createApiRestAccounts = async (args: CreateApiRestAccountsArgs, options?: CreateApiRestAccountsOptions) => { const {baseUrl = "https://api.dailypay.com", fetch: fetchFn = fetch, oauthUserToken} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('content-type', "application/vnd.api+json") headers.set('accept', "application/vnd.api+json") if (oauthUserToken !== undefined) headers.set('authorization', `Bearer ${oauthUserToken}`) const url = `${baseUrl.replace(/\/+$/, '')}/rest/accounts${query.size > 0 ? `?${query}` : ''}` const res = await fetchFn(url, {method: "POST", headers, body: JSON.stringify(args.body)}) if (!res.ok) throw new Error(`Request failed with status ${res.status}`, {cause: {status: res.status, body: await res.text()}}) return validateResponse(accountData, await res.json()) };