import {z} from 'zod' export type EncryptedPaymentDataVersion = 'EC_v1'; export type WalletType = 'apple_pay'; export type WalletPaymentMethod = {display_name?: string | undefined, network?: string | undefined, type?: string | undefined}; export type EncryptedPaymentData = {digital_signature?: string | undefined, encrypted_payload_text: string, key_hash: string, public_key: string, version?: EncryptedPaymentDataVersion | undefined, wallet_type?: WalletType | undefined, wallet_transaction_id?: string | undefined, payment_method?: WalletPaymentMethod | undefined}; export type EncryptedCardAttributes = {encrypted_payment_data: EncryptedPaymentData}; export type EncryptedCardResourceRequest = {data: {attributes: EncryptedCardAttributes}}; export type CreateApiCardsArgs = {body: EncryptedCardResourceRequest}; export type CreateApiCardsOptions = {baseUrl?: string | undefined, bearerAuth?: string | undefined, fetch?: typeof fetch | undefined}; export const address = z.object({address1: z.string().max(255).optional(), address2: z.string().max(255).optional(), address3: z.string().max(255).optional(), address4: z.string().max(255).optional(), city: z.string().max(255).optional(), region: z.string().max(255).optional(), postal_code: z.string().max(10).optional(), country: z.string().min(2).max(3).optional()}); export const cardholder = z.object({name: z.string().min(1).max(255).optional(), company: z.string().max(255).optional(), phone: z.string().max(16).optional(), email: z.string().optional(), address: address.optional()}); export const tokenType = z.enum(["dpan", "mpan"]); export const walletType = z.literal("apple_pay"); export const enrollmentSource = z.enum(["backbook", "frontbook"]); export const enrichedCardAttributes = z.object({card_properties: z.object({card_number_length: z.number().int().optional(), card_segment_type: z.string().optional(), level_2: z.boolean().optional(), level_3: z.boolean().optional(), virtual_card: z.boolean().optional(), prepaid_card: z.boolean().optional(), product_name: z.string().optional(), issuer_bin: z.string().optional(), country_letter_code: z.string().optional(), country_name: z.string().optional(), country_numeric: z.number().int().optional()}).optional(), card_capabilities: z.object({reloadable: z.boolean().optional(), hsa: z.boolean().optional(), fsa: z.boolean().optional(), ebt: z.boolean().optional()}).optional(), bank: z.object({issuer_name: z.string().optional(), issuer_phone_number: z.string().optional(), issuer_website: z.string().optional()}).optional(), additional_card_brands: z.array(z.object({card_brand: z.string()})).optional(), interchange: z.object({regulated: z.string().optional()}).optional()}); export const paymentDataType = z.enum(["3DSecure", "MerchantToken"]); export const cryptogramAttributes = z.object({type: z.enum(["TAVV", "DTVV"]).optional(), value: z.string().optional(), eci: z.string().optional()}); export const walletPaymentMethod = z.object({display_name: z.string().min(1).max(255).optional(), network: z.string().min(1).max(255).optional(), type: z.string().min(1).max(255).optional()}); export const walletDetails = z.object({currency_code: z.string().min(3).max(3).optional(), amount: z.number().int().gte(0).optional(), device_manufacturer_identifier: z.string().optional(), payment_data_type: paymentDataType.optional(), cryptogram: cryptogramAttributes.optional(), merchant_token_identifier: z.string().optional(), payment_method: walletPaymentMethod.optional(), transaction_id: z.string().optional()}); export const cardResourceAttributes = z.object({pan: z.string().min(14).max(19).optional(), cvc_status: z.string().min(3).max(10), exp_month: z.number().int().gte(1).lte(12), exp_year: z.number().int().gte(0).lte(99), cardholder: cardholder.optional(), token_type: tokenType, wallet_type: walletType, pan_alias: z.string(), cvc_alias: z.string().min(30).max(35).optional(), bin: z.string().min(6).max(8), first8: z.string().min(8).max(8), last4: z.string().min(4).max(4), card_fingerprint: z.string(), created_at: z.string(), updated_at: z.string(), card_brand: z.string(), card_type: z.string(), enrollment_source: enrollmentSource.optional(), enriched_attributes: enrichedCardAttributes.optional(), wallet_details: walletDetails}); export const observability = z.object({trace_id: z.string(), client_id: z.string(), vault_id: z.string(), account_id: z.string(), fingerprint: z.string()}); export const metadataResponse = z.object({observability: observability}); export const cardResourceResponse = z.object({data: z.object({id: z.string(), type: z.string(), attributes: cardResourceAttributes}), metadata: metadataResponse}); 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 a card * * POST /cards */ export const createApiCards = async (args: CreateApiCardsArgs, options?: CreateApiCardsOptions) => { const {baseUrl = "https://sandbox.vgsapi.com", fetch: fetchFn = fetch, bearerAuth} = 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 (bearerAuth !== undefined) headers.set('authorization', `Bearer ${bearerAuth}`) const url = `${baseUrl.replace(/\/+$/, '')}/cards${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(cardResourceResponse, await res.json()) };