import {z} from 'zod' export type StoreId = string; export type SourceExternalIdentifiers = {id?: string | undefined, friendlyId?: string | undefined, source?: string | undefined, sourceType?: ('POINT_OF_SALE' | 'ORDERING_MARKETPLACE' | 'AGGREGATOR' | 'CUSTOMER_INTERACTION') | undefined, sourceExternalIdentifiers?: SourceExternalIdentifiers | undefined} | null; export type OrderExternalIdentifiers = {id: string, friendlyId: string, source?: (string | null) | undefined, sourceType?: ('POINT_OF_SALE' | 'ORDERING_MARKETPLACE' | 'AGGREGATOR' | 'CUSTOMER_INTERACTION' | null) | undefined, sourceExternalIdentifiers?: SourceExternalIdentifiers | undefined}; export type ItemModifier = {quantity: number, skuPrice?: (number | null) | undefined, id?: (string | null) | undefined, lineItemId?: (string | null) | undefined, skuId?: (string | null) | undefined, name?: (string | null) | undefined, price?: (number | null) | undefined, groupName?: (string | null) | undefined, groupId?: (string | null) | undefined, stationId?: (string | null) | undefined, modifiers?: (Array | null) | undefined}; export type Item = {quantity: number, skuPrice?: (number | null) | undefined, id?: (string | null) | undefined, lineItemId?: (string | null) | undefined, skuId?: (string | null) | undefined, name?: (string | null) | undefined, note?: (string | null) | undefined, categoryId?: (string | null) | undefined, internalCategoryId?: (string | null) | undefined, categoryName?: (string | null) | undefined, internalCategoryName?: (string | null) | undefined, stationId?: (string | null) | undefined, price?: (number | null) | undefined, modifiers?: (Array | null) | undefined}; export type PersonalIdentifiers = {taxIdentificationNumber?: (string | null) | undefined, serviceProviderId?: (string | null) | undefined} | null; export type Person = {name?: (string | null) | undefined, phone?: (string | null) | undefined, phoneCode?: (string | null) | undefined, email?: (string | null) | undefined, personalIdentifiers?: PersonalIdentifiers | undefined} | null; export type Courier = {name?: (string | null) | undefined, phone?: (string | null) | undefined, phoneCode?: (string | null) | undefined, email?: (string | null) | undefined, personalIdentifiers?: PersonalIdentifiers | undefined} | null; export type Location = {latitude: number, longitude: number} | null; export type Address = {fullAddress?: (string | null) | undefined, postalCode?: string | undefined, city?: string | undefined, state?: string | undefined, countryCode?: string | undefined, addressLines?: (Array | null) | undefined, linesOfAddress?: (Array | null) | undefined, location?: Location | undefined} | null; export type SignatureRequirement = {enabled?: boolean | undefined, collectSignerName?: boolean | undefined, collectSignerRelationship?: boolean | undefined} | null; export type PictureRequirement = {enabled?: boolean | undefined} | null; export type VerificationRequirements = {signatureRequirement?: SignatureRequirement | undefined, pictureRequirement?: PictureRequirement | undefined} | null; export type DropoffInstructions = {dropoffType?: ('MEET_AT_DOOR' | 'LEAVE_AT_DOOR' | 'MEET_IN_LOBBY') | undefined, verificationRequirements?: VerificationRequirements | undefined} | null; export type DeliveryInfo = {courier?: Courier | undefined, destination?: Address | undefined, licensePlate?: (string | null) | undefined, makeModel?: (string | null) | undefined, lastKnownLocation?: Location | undefined, dropoffInstructions?: DropoffInstructions | undefined, note?: (string | null) | undefined} | null; export type OrderTotal = {subtotal: number, claimedSubtotal?: (number | null) | undefined, discount?: (number | null) | undefined, tax?: (number | null) | undefined, tip?: (number | null) | undefined, deliveryFee?: (number | null) | undefined, total?: (number | null) | undefined, couponCode?: (string | null) | undefined}; export type SimpleFinanceLine = {subType: 'VALUE' | 'TAX' | 'VALUE_WITH_TAX' | 'VAT', name: string, value: number}; export type CompositeFinanceLine = {breakdown: Array}; export type FinancialData = {foodSales: CompositeFinanceLine, feeForRestaurantProvidedDelivery?: (CompositeFinanceLine | null) | undefined, restaurantFundedDiscount?: (CompositeFinanceLine | null) | undefined, voucherDiscount?: (CompositeFinanceLine | null) | undefined, promotionDiscount?: (CompositeFinanceLine | null) | undefined, operatorDiscount?: (CompositeFinanceLine | null) | undefined, loyaltyDiscount?: (CompositeFinanceLine | null) | undefined, tipForRestaurant?: (CompositeFinanceLine | null) | undefined, adjustments?: (CompositeFinanceLine | null) | undefined, packingFee?: (CompositeFinanceLine | null) | undefined, bagFee?: (CompositeFinanceLine | null) | undefined, serviceProviderDiscount?: (CompositeFinanceLine | null) | undefined, tipForServiceProviderCourier?: (CompositeFinanceLine | null) | undefined, feeForServiceProviderDelivery?: (CompositeFinanceLine | null) | undefined, smallOrderFee?: (CompositeFinanceLine | null) | undefined, serviceFee?: (CompositeFinanceLine | null) | undefined, otherFee?: (CompositeFinanceLine | null) | undefined, netPayout?: (CompositeFinanceLine | null) | undefined, couponCodes?: (Array | null) | undefined}; export type CustomerPaymentV2 = {customerPaymentDue?: (number | null) | undefined, customerPrepayment?: (number | null) | undefined, customerAmountToReturn?: (number | null) | undefined, paymentDueToRestaurant?: (number | null) | undefined} | null; export type Payout = {payoutFromServiceProvider?: (number | null) | undefined, payoutFrom3rdParty?: (number | null) | undefined, cashPayout?: (number | null) | undefined} | null; export type ServiceProviderCharge = {commission?: CompositeFinanceLine | undefined, processingFee?: CompositeFinanceLine | undefined, salesTaxWithheld?: CompositeFinanceLine | undefined, deliveryFeeForRestaurant?: CompositeFinanceLine | undefined, advertisingFee?: CompositeFinanceLine | undefined, chargesAdjustments?: CompositeFinanceLine | undefined, otherFees?: CompositeFinanceLine | undefined} | null; export type OrderTotalV2 = {customerTotal: FinancialData, customerPayment?: (CustomerPaymentV2 | null) | undefined, payout?: (Payout | null) | undefined, serviceProviderCharge?: (ServiceProviderCharge | null) | undefined} | null; export type CardInfo = {paymentNetwork: 'MASTERCARD' | 'MASTERCARD_MAESTRO' | 'VISA' | 'AMEX' | 'DINERS' | 'ELO' | 'HIPERCARD' | 'BANRICOMPRAS' | 'NUGO' | 'GOODCARD' | 'ELO_MAIS' | 'GREENCARD' | 'VEROCARD' | 'COOPER' | 'NUTRICARD' | 'VR' | 'SODEXO' | 'OTHER', type: 'CREDIT' | 'DEBIT' | 'VOUCHER' | 'OTHER'} | null; export type RecordProviderType = 'ALLINPAY' | 'CHECKOUT' | 'CSS' | 'MERCADO_PAGO' | 'OLIVENETWORKS' | 'SHOUQIANBA' | 'STRIPE' | 'UNIONPAY' | 'VAN_DAOU' | 'VAN_JTNET' | 'VAN_KICC' | 'VAN_KIS' | 'VAN_KOVAN' | 'VAN_KSNET' | 'VAN_NICE' | 'VAN_SMARTRO' | 'UNKNOWN'; export type RecordPaymentType = 'ACH_CREDIT' | 'ACH_DEBIT' | 'ACSS_DEBIT' | 'ALIPAY_MINIAPP' | 'ALI_PAY' | 'BACS_DEBIT' | 'BANK_TRANSFER' | 'BECS_DEBIT' | 'BOLETO' | 'CARD' | 'CARD_PRESENT' | 'CASH' | 'CHECK' | 'CHECKOUT_SESSION' | 'ETC' | 'EXTERNAL_CASH_BALANCE' | 'GIFT_CARD' | 'INTERNAL_CASH_BALANCE' | 'KAKAO_PAY' | 'LIABILITY_BALANCE' | 'LINKED_BANK_ACCOUNT' | 'NAVER_PAY' | 'PIX' | 'QRCODE' | 'QRCODE_ALIPAY' | 'QRCODE_WECHAT' | 'SEPA_DEBIT' | 'SPEI_BANK_TRANSFER' | 'WALLET' | 'WECHAT_PAY' | 'UNKNOWN'; export type CardBrandType = 'UNKNOWN' | 'AMEX' | 'DINERS_CLUB' | 'DISCOVER' | 'JCB' | 'MASTERCARD' | 'UNION_PAY' | 'VISA' | 'GIROCARD' | 'EFTPOS_AU' | 'INTERAC'; export type CardFundingType = 'UNKNOWN' | 'CREDIT' | 'DEBIT' | 'PREPAID'; export type CardWalletType = 'UNKNOWN' | 'APPLE_PAY' | 'GOOGLE_PAY'; export type PaymentDetailsCard = {brandType?: CardBrandType | undefined, expiration?: ({year?: number | undefined, month?: number | undefined}) | undefined, fundingType?: CardFundingType | undefined, walletType?: CardWalletType | undefined, lastFour?: string | undefined, walletLastFour?: string | undefined, authorizationCode?: string | undefined, applicationPreferredName?: string | undefined, fingerprint?: string | undefined, readMethod?: string | undefined}; export type MandateStatus = 'ACTIVE' | 'INACTIVE' | 'PENDING'; export type PaymentDetailsBacs = {mandateStatus?: MandateStatus | undefined, lastFour?: string | undefined, sortCode?: string | undefined, mandateId?: string | undefined, reference?: string | undefined, url?: string | undefined}; export type PaymentDetailsAcss = {mandateStatus?: MandateStatus | undefined, acceptedAt?: string | undefined, lastFour?: string | undefined, transitNumber?: string | undefined, institutionNumber?: string | undefined, bankName?: string | undefined, mandateId?: string | undefined, transactionType?: string | undefined, intervalDescription?: string | undefined}; export type PaymentDetailsBecs = {mandateStatus?: MandateStatus | undefined, lastFour?: string | undefined, bsbNumber?: string | undefined, mandateId?: string | undefined, url?: string | undefined}; export type PaymentDetailsSepa = {mandateStatus?: MandateStatus | undefined, lastFour?: string | undefined, branchCode?: string | undefined, bankCode?: string | undefined, countryCode?: string | undefined, mandateId?: string | undefined, reference?: string | undefined, url?: string | undefined}; export type AccountHolderType = 'INDIVIDUAL' | 'COMPANY'; export type AccountType = 'CHECKING' | 'SAVINGS'; export type PaymentDetailsAch = {accountHolderType?: AccountHolderType | undefined, accountType?: AccountType | undefined, mandateStatus?: MandateStatus | undefined, lastFour?: string | undefined, routingNumber?: string | undefined, bankName?: string | undefined, mandateId?: string | undefined}; export type PaymentRecord = {otterPaymentRecordId?: string | undefined, recordProviderType?: RecordProviderType | undefined, recordPaymentType?: RecordPaymentType | undefined, paymentRecordId?: string | undefined, payerId?: string | undefined, balanceTransactionId?: string | undefined, paymentDetails?: (PaymentDetailsCard | PaymentDetailsBacs | PaymentDetailsAcss | PaymentDetailsBecs | PaymentDetailsSepa | PaymentDetailsAch) | undefined}; export type LoyaltyInfo = {hasMembershipPass?: boolean | undefined} | null; export type CustomerPayment = {value: number, processingStatus: 'COLLECTABLE' | 'PROCESSED', paymentMethod: 'CASH' | 'CARD' | 'UNKNOWN' | 'OTHER' | 'CHEQUE' | 'GIFT_CARD', paymentAuthorizer?: ('AMEX' | 'APPLE_PAY_AMEX' | 'APPLE_PAY_MASTERCARD' | 'APPLE_PAY_VISA' | 'BANRICOMPRAS' | 'BANRICOMPRAS_DEBIT' | 'CARNET' | 'CHEF_CARD' | 'DEBIT' | 'DINERS' | 'DISCOVER' | 'ELO' | 'ELO_DEBIT' | 'GER_CC_CREDITO' | 'GOODCARD' | 'GOOGLE_PAY_ELO' | 'GOOGLE_PAY_MASTERCARD' | 'GOOGLE_PAY_VISA' | 'HIPERCARD' | 'IFOOD_CORP' | 'LOOP_CLUB' | 'MASTERCARD' | 'MASTERCARD_DEBIT' | 'MASTERCARD_MAESTRO' | 'MOVILE_PAY' | 'MOVILE_PAY_AMEX' | 'MOVILE_PAY_DINERS' | 'MOVILE_PAY_ELO' | 'MOVILE_PAY_HIPERCARD' | 'MOVILE_PAY_MASTERCARD' | 'MOVILE_PAY_VISA' | 'NUGO' | 'NUTRICARD_REFEICAO_E_ALIMENTACAO' | 'OTHER_TYPE' | 'PAYPAL' | 'PIX' | 'PSE' | 'QR_CODE' | 'RAPPI_PAY' | 'TERMINAL_BANCARIA' | 'UNKNOWN_TYPE' | 'VALE_ALELO' | 'VALE_BEN_VIS' | 'VALE_COOPER_CARD' | 'VALE_GREEN_CARD_CARD' | 'VALE_GREEN_CARD_PAPEL' | 'VALE_REFEISUL' | 'VALE_SODEXO' | 'VALE_TICKET_RESTAURANTE' | 'VALE_VEROCARD' | 'VALE_VR_SMART' | 'VERDECARD' | 'VISA' | 'VISA_DEBIT' | 'VISA_ELECTORN' | null) | undefined, cardInfo?: CardInfo | undefined, externalPaymentType?: (string | null) | undefined, paymentRecords?: (Array | null) | undefined, loyaltyInfo?: LoyaltyInfo | undefined}; export type FulfillmentInfo = {pickupTime?: (string | null) | undefined, estimatedPickupTime?: (string | null) | undefined, deliveryTime?: (string | null) | undefined, fulfillmentMode?: ('DELIVERY' | 'RESTAURANT_DELIVERY' | 'PICKUP' | 'DINE_IN' | 'DRIVE_THROUGH') | undefined, schedulingType?: ('ASAP' | 'FIXED_TIME' | null) | undefined, courierStatus?: ('COURIER_ASSIGNED' | 'COURIER_ON_ROUTE_TO_PICKUP' | 'COURIER_ARRIVED' | 'COURIER_PICKED_UP_FOOD' | 'COURIER_COMPLETED' | null) | undefined, tableIdentifier?: (string | null) | undefined} | null; export type PromotionDetails = {externalId?: string | undefined, name?: string | undefined, value?: number | undefined} | null; export type PreparationTime = {estimatedPreparationTime?: string | undefined} | null; export type Order = {externalIdentifiers: OrderExternalIdentifiers, currencyCode: string, status: 'NEW_ORDER' | 'CONFIRMED' | 'PICKED_UP' | 'CANCELED' | 'FULFILLED' | 'PREPARED' | 'REJECTED' | 'PREVISIT_COMPLETED' | 'PREVISIT_NO_SHOW' | 'UNKNOWN', items?: Array | undefined, orderedAt?: (string | null) | undefined, customer?: Person | undefined, customerNote?: (string | null) | undefined, deliveryInfo?: DeliveryInfo | undefined, orderTotal?: (OrderTotal | null) | undefined, orderTotalV2?: (OrderTotalV2 | null) | undefined, customerPayments?: (Array | null) | undefined, fulfillmentInfo?: FulfillmentInfo | undefined, promotionsDetails?: (Array | null) | undefined, preparationTime?: PreparationTime | undefined, cutleryRequest?: ('CUTLERY_REQUESTED' | 'CUTLERY_NOT_NEEDED' | null) | undefined}; export type CreateApiV1OrdersArgs = {'X-Store-Id': StoreId, body: Order}; export type CreateApiV1OrdersOptions = {baseUrl?: string | undefined, OAuth20?: string | undefined, fetch?: typeof fetch | undefined}; export const sourceExternalIdentifiers: z.ZodType = z.object({id: z.string().optional(), friendlyId: z.string().optional(), source: z.string().optional(), sourceType: z.enum(["POINT_OF_SALE", "ORDERING_MARKETPLACE", "AGGREGATOR", "CUSTOMER_INTERACTION"]).optional(), sourceExternalIdentifiers: z.lazy(() => sourceExternalIdentifiers.optional())}).nullable(); export const orderExternalIdentifiers = z.object({id: z.string(), friendlyId: z.string(), source: z.string().nullable().optional(), sourceType: z.enum(["POINT_OF_SALE", "ORDERING_MARKETPLACE", "AGGREGATOR", "CUSTOMER_INTERACTION"]).nullable().optional(), sourceExternalIdentifiers: sourceExternalIdentifiers.optional()}); export const orderReference = z.object({externalIdentifiers: orderExternalIdentifiers, storeId: z.string()}); 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 order * * POST /v1/orders */ export const createApiV1Orders = async (args: CreateApiV1OrdersArgs, options?: CreateApiV1OrdersOptions) => { const {baseUrl = "https://partners.tryotter.com", fetch: fetchFn = fetch, OAuth20} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('content-type', "application/json") headers.set('accept', "application/json") if (args['X-Store-Id'] != null) headers.set("X-Store-Id", String(args['X-Store-Id'])) if (OAuth20 !== undefined) headers.set('authorization', `Bearer ${OAuth20}`) const url = `${baseUrl.replace(/\/+$/, '')}/v1/orders${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(orderReference, await res.json()) };