import {z} from 'zod' export type ContinueRequest = {payment_method: string}; export type CreateApiPaymentsMerchantDataContinueArgs = {merchant_data: string, body: ContinueRequest}; export type CreateApiPaymentsMerchantDataContinueOptions = {baseUrl?: string | undefined, Passcode?: string | undefined, fetch?: typeof fetch | undefined}; export const custom = z.object({ref1: z.string().max(256).optional(), ref2: z.string().max(256).optional(), ref3: z.string().max(256).optional(), ref4: z.string().max(256).optional(), ref5: z.string().max(256).optional()}); export const cvdResult = z.number().int(); export const avsResponse = z.object({id: z.enum(["0", "5", "9", "A", "B", "C", "D", "E", "G", "I", "M", "N", "P", "R", "S", "U", "W", "X", "Y", "Z"]).optional(), message: z.string().optional(), processed: z.boolean().optional()}); export const sourceData = z.object({td_token_status: z.string().min(1).max(1).optional()}); export const processorTokenResponse = z.object({payment_token: z.string().min(15).max(19).optional(), reporting_token: z.string().max(23).optional(), status: z.enum(["success", "fail"]).optional(), source: z.string().min(4).max(12).optional(), source_data: sourceData.optional()}); export const cavvResult = z.string(); export const cardPurchaseResponse = z.object({card_type: z.enum(["AM", "DI", "JB", "MC", "NN", "VI", "PV", "MD"]).max(2).optional(), last_four: z.string().max(4).optional(), card_bin: z.string().min(6).max(8).optional(), cvd_result: cvdResult.optional(), eci: z.number().int().optional(), address_match: z.number().int().optional(), postal_result: z.number().int().optional(), avs: avsResponse.optional(), processor_token: processorTokenResponse.optional(), cavv_result: cavvResult.optional(), status_id: z.number().int().optional(), status: z.enum(["1", "2", "3", "4"]).optional(), error_id: z.number().int().optional(), error: z.enum(["1", "2", "3"]).optional()}); export const schema3DSecureResponse = z.object({status: z.string(), downgraded: z.boolean().optional(), reason_merchant: z.string().optional(), reason_cardholder: z.string().optional(), method_url: z.string().optional()}); export const link = z.object({ref: z.string().optional(), href: z.string().max(256).optional(), method: z.string().optional()}); export const cardOnFileResponse = z.object({type: z.enum(["not_card_on_file", "first_installment", "subsequent_installment", "first_recurring", "subsequent_recurring", "subsequent_customer_initiated", "subsequent_unscheduled"]).optional(), series_id: z.number().optional()}); export const paymentResponse = z.object({id: z.string().max(9).optional(), authorizing_merchant_id: z.number().int().optional(), approved: z.string().optional(), message_id: z.number().int().optional(), message: z.string().max(256).optional(), auth_code: z.string().max(32).optional(), created: z.string().optional(), order_number: z.string().max(32).optional(), type: z.enum(["P", "R", "VP", "VR", "PA", "PAC"]).max(16).optional(), risk_score: z.number().optional(), amount: z.number().optional(), payment_method: z.enum(["card", "token", "payment_profile", "cash", "cheque", "apple_pay", "google_pay"]).optional(), custom: custom.optional(), card: cardPurchaseResponse.optional(), "3d_secure": schema3DSecureResponse.optional(), merchant_data: z.string().max(32).optional(), contents: z.string().optional(), links: z.array(link).optional(), card_on_file: cardOnFileResponse.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 }; /** * 3D Secure payments * * POST /payments/{merchant_data}/continue * * Also accepts auth: Sub-Merchant-Id */ export const createApiPaymentsMerchantDataContinue = async (args: CreateApiPaymentsMerchantDataContinueArgs, options?: CreateApiPaymentsMerchantDataContinueOptions) => { const {baseUrl = "https://api.na.bambora.com/v1", fetch: fetchFn = fetch, Passcode} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('content-type', "application/json") headers.set('accept', "application/json") if (Passcode !== undefined) headers.set("Authorization", Passcode) const url = `${baseUrl.replace(/\/+$/, '')}/payments/${encodeURIComponent(String(args.merchant_data))}/continue${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(paymentResponse, await res.json()) };