import {z} from 'zod' export type RailProduct = 'us' | 'swift' | 'eu' | 'uae' | 'uk' | 'br' | 'mx' | 'co' | 'ar' | 'au' | 'ng' | 'ca' | 'dk' | 'no' | 'pl' | 'tr' | 'il' | 'sa' | 'bo' | 'cl' | 'cr' | 'do' | 'gt' | 'hn' | 'jm' | 'eg' | 'sl' | 'tz' | 'ug' | 'waemu' | 'zm' | 'bd' | 'cn' | 'hk' | 'in' | 'id' | 'jp' | 'my' | 'pk' | 'ph' | 'kr' | 'th' | 'vn' | 'card'; export type GetApiBanksProductArgs = {product: RailProduct, 'X-Hevn-Account'?: (string | null) | undefined}; export type GetApiBanksProductOptions = {baseUrl: string, HTTPBearer?: string | undefined, fetch?: typeof fetch | undefined}; export const railProduct = z.enum(["us", "swift", "eu", "uae", "uk", "br", "mx", "co", "ar", "au", "ng", "ca", "dk", "no", "pl", "tr", "il", "sa", "bo", "cl", "cr", "do", "gt", "hn", "jm", "eg", "sl", "tz", "ug", "waemu", "zm", "bd", "cn", "hk", "in", "id", "jp", "my", "pk", "ph", "kr", "th", "vn", "card"]); export const bankDepositCurrency = z.enum(["USD", "EUR", "USDC", "EURC", "RD", "USDC_ARB"]); export const fiatPaymentType = z.enum(["sepa", "swift", "ach", "uaefts", "fedwire", "rtp", "fednow", "pix", "spei", "pse", "breb", "transfers_3_0", "ukfps", "npp", "nip", "bdt_local", "bdt_ewallet", "bob_local", "brl_local", "cad_eft", "clp_local", "cny_alipay", "cny_wechatpay", "cop_local", "crc_local", "dkk_local", "dop_local", "dop_ewallet", "egp_local", "gtq_local", "gtq_ewallet", "hkd_fps", "hnl_local", "idr_local", "idr_ewallet", "ils_local", "inr_local", "inr_upi", "jmd_local", "jpy_local", "krw_local", "myr_local", "nok_local", "php_local", "php_ewallet", "pkr_raast", "pkr_ewallet", "pln_local", "sar_local", "sle_local", "thb_local", "try_fast", "tzs_local", "ugx_local", "vnd_local", "xof_local", "zmw_local"]); export const railStatus = z.enum(["preparing", "active", "suspended", "closed"]); export const accountKey = z.enum(["USDC", "EURC", "RD", "USDC_ARB", "FDIC_USD", "FDIC_EUR", "KYC_CARDS"]); export const requisitePixKeyType = z.enum(["phone", "email", "tax_id", "random"]); export const requisiteAccountKind = z.enum(["checking", "savings", "deposit", "salary", "other"]); export const requisiteFieldsOutput = z.object({iban: z.string().nullable(), bic: z.string().nullable(), accountNumber: z.string().nullable(), routingNumber: z.string().nullable(), pixKey: z.string().nullable(), pixKeyType: requisitePixKeyType.nullable(), paymentKey: z.string().nullable(), financialInstitutionId: z.string().nullable(), branchNumber: z.string().nullable(), accountKind: requisiteAccountKind.nullable(), phoneNumber: z.string().nullable()}); export const requisiteHolderType = z.enum(["individual", "business"]); export const countryCode = z.enum(["AF", "AX", "AL", "DZ", "AS", "AD", "AO", "AI", "AQ", "AG", "AR", "AM", "AW", "AU", "AT", "AZ", "BS", "BH", "BD", "BB", "BY", "BE", "BZ", "BJ", "BM", "BT", "BO", "BQ", "BA", "BW", "BV", "BR", "IO", "BN", "BG", "BF", "BI", "CV", "KH", "CM", "CA", "KY", "CF", "TD", "CL", "CN", "CX", "CC", "CO", "KM", "CG", "CD", "CK", "CR", "CI", "HR", "CU", "CW", "CY", "CZ", "DK", "DJ", "DM", "DO", "EC", "EG", "SV", "GQ", "ER", "EE", "SZ", "ET", "FK", "FO", "FJ", "FI", "FR", "GF", "PF", "TF", "GA", "GM", "GE", "DE", "GH", "GI", "GR", "GL", "GD", "GP", "GU", "GT", "GG", "GN", "GW", "GY", "HT", "HM", "VA", "HN", "HK", "HU", "IS", "IN", "ID", "IR", "IQ", "IE", "IM", "IL", "IT", "JM", "JP", "JE", "JO", "KZ", "KE", "KI", "KP", "KR", "XK", "KW", "KG", "LA", "LV", "LB", "LS", "LR", "LY", "LI", "LT", "LU", "MO", "MG", "MW", "MY", "MV", "ML", "MT", "MH", "MQ", "MR", "MU", "YT", "MX", "FM", "MD", "MC", "MN", "ME", "MS", "MA", "MZ", "MM", "NA", "NR", "NP", "NL", "NC", "NZ", "NI", "NE", "NG", "NU", "NF", "MK", "MP", "NO", "OM", "PK", "PW", "PS", "PA", "PG", "PY", "PE", "PH", "PN", "PL", "PT", "PR", "QA", "RE", "RO", "RU", "RW", "BL", "SH", "KN", "LC", "MF", "PM", "VC", "WS", "SM", "ST", "SA", "SN", "RS", "SC", "SL", "SG", "SX", "SK", "SI", "SB", "SO", "ZA", "GS", "SS", "ES", "LK", "SD", "SR", "SJ", "SE", "CH", "SY", "TW", "TJ", "TZ", "TH", "TL", "TG", "TK", "TO", "TT", "TN", "TR", "TM", "TC", "TV", "UG", "UA", "AE", "GB", "US", "UM", "UY", "UZ", "VU", "VE", "VN", "VG", "VI", "WF", "EH", "YE", "ZM", "ZW"]); export const userAddressOutput = z.object({streetAddress: z.string().nullable(), addressLine2: z.string().nullable(), city: z.string().nullable(), state: z.string().nullable(), country: countryCode.nullable(), zip: z.string().nullable()}); export const requisiteIdType = z.enum(["passport", "national_id", "foreigner_id", "temporary_permit", "minor_id", "driving_license", "resident_card", "tax_id", "other"]); export const requisiteHolderOutput = z.object({type: requisiteHolderType, firstName: z.string().nullable(), lastName: z.string().nullable(), businessName: z.string().nullable(), address: userAddressOutput.nullable(), idType: requisiteIdType.nullable(), idNumber: z.string().nullable(), taxId: z.string().nullable(), nationality: z.string().nullable()}); export const requisiteState = z.enum(["active", "pending", "closed"]); export const railRequisitesView = z.object({fields: requisiteFieldsOutput, holder: requisiteHolderOutput, bankName: z.string().nullable().optional(), paymentReference: z.string().nullable().optional(), state: requisiteState}); export const railBlockerView = z.object({code: z.string(), field: z.string(), message: z.string(), url: z.string().nullable().optional()}); export const railView = z.object({product: railProduct, rail: z.string(), currency: z.string(), railCurrency: bankDepositCurrency, method: fiatPaymentType, opened: z.boolean(), status: railStatus.nullable().optional(), feeBps: z.number().int(), supportedAccounts: z.array(accountKey), destinationAccount: accountKey.nullable().optional(), minimumDeposit: z.string().nullable().optional(), id: z.string().nullable().optional(), requisites: railRequisitesView.nullable().optional(), blockers: z.array(railBlockerView).optional()}); 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 }; /** * Retrieve one virtual account * * GET /banks/{product} */ export const getApiBanksProduct = async (args: GetApiBanksProductArgs, options: GetApiBanksProductOptions) => { const {baseUrl, fetch: fetchFn = fetch, HTTPBearer} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('accept', "application/json") if (args['X-Hevn-Account'] != null) headers.set("X-Hevn-Account", String(args['X-Hevn-Account'])) if (HTTPBearer !== undefined) headers.set('authorization', `Bearer ${HTTPBearer}`) const url = `${baseUrl.replace(/\/+$/, '')}/banks/${encodeURIComponent(String(args.product))}${query.size > 0 ? `?${query}` : ''}` const res = await fetchFn(url, {method: "GET", headers}) if (!res.ok) throw new Error(`Request failed with status ${res.status}`, {cause: {status: res.status, body: await res.text()}}) return validateResponse(railView, await res.json()) };