import {z} from 'zod' export type StoreId = string; export type GetApiManagerOrderV1OrdersArgs = {'X-Store-Id': StoreId, limit: string, token?: string | undefined, minDateTime?: string | undefined, maxDateTime?: string | undefined}; export type GetApiManagerOrderV1OrdersOptions = {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 itemModifier: z.ZodType = z.object({quantity: z.number().int().gte(1).lte(1000), skuPrice: z.number().nullable().optional(), id: z.string().nullable().optional(), lineItemId: z.string().nullable().optional(), skuId: z.string().nullable().optional(), name: z.string().nullable().optional(), price: z.number().nullable().optional(), groupName: z.string().nullable().optional(), groupId: z.string().nullable().optional(), stationId: z.string().nullable().optional(), modifiers: z.array(z.lazy(() => itemModifier)).nullable().optional()}); export const item = z.object({quantity: z.number().int().gte(1).lte(1000), skuPrice: z.number().nullable().optional(), id: z.string().nullable().optional(), lineItemId: z.string().nullable().optional(), skuId: z.string().nullable().optional(), name: z.string().nullable().optional(), note: z.string().nullable().optional(), categoryId: z.string().nullable().optional(), internalCategoryId: z.string().nullable().optional(), categoryName: z.string().nullable().optional(), internalCategoryName: z.string().nullable().optional(), stationId: z.string().nullable().optional(), price: z.number().nullable().optional(), modifiers: z.array(itemModifier).nullable().optional()}); export const personalIdentifiers = z.object({taxIdentificationNumber: z.string().nullable().optional(), serviceProviderId: z.string().nullable().optional()}).nullable(); export const person = z.object({name: z.string().max(255).nullable().optional(), phone: z.string().max(25).nullable().optional(), phoneCode: z.string().max(25).nullable().optional(), email: z.string().nullable().optional(), personalIdentifiers: personalIdentifiers.optional()}).nullable(); export const courier = z.object({name: z.string().nullable().optional(), phone: z.string().nullable().optional(), phoneCode: z.string().nullable().optional(), email: z.string().nullable().optional(), personalIdentifiers: personalIdentifiers.optional()}).nullable(); export const location = z.object({latitude: z.number(), longitude: z.number()}).nullable(); export const address = z.object({fullAddress: z.string().nullable().optional(), postalCode: z.string().optional(), city: z.string().optional(), state: z.string().optional(), countryCode: z.string().optional(), addressLines: z.array(z.string()).nullable().optional(), linesOfAddress: z.array(z.string()).nullable().optional(), location: location.optional()}).nullable(); export const signatureRequirement = z.object({enabled: z.boolean().optional(), collectSignerName: z.boolean().optional(), collectSignerRelationship: z.boolean().optional()}).nullable(); export const pictureRequirement = z.object({enabled: z.boolean().optional()}).nullable(); export const verificationRequirements = z.object({signatureRequirement: signatureRequirement.optional(), pictureRequirement: pictureRequirement.optional()}).nullable(); export const dropoffInstructions = z.object({dropoffType: z.enum(["MEET_AT_DOOR", "LEAVE_AT_DOOR", "MEET_IN_LOBBY"]).optional(), verificationRequirements: verificationRequirements.optional()}).nullable(); export const deliveryInfo = z.object({courier: courier.optional(), destination: address.optional(), licensePlate: z.string().nullable().optional(), makeModel: z.string().nullable().optional(), lastKnownLocation: location.optional(), dropoffInstructions: dropoffInstructions.optional(), note: z.string().nullable().optional()}).nullable(); export const orderTotal = z.object({subtotal: z.number(), claimedSubtotal: z.number().nullable().optional(), discount: z.number().nullable().optional(), tax: z.number().nullable().optional(), tip: z.number().nullable().optional(), deliveryFee: z.number().nullable().optional(), total: z.number().nullable().optional(), couponCode: z.string().nullable().optional()}); export const simpleFinanceLine = z.object({subType: z.enum(["VALUE", "TAX", "VALUE_WITH_TAX", "VAT"]), name: z.string(), value: z.number()}); export const compositeFinanceLine = z.object({breakdown: z.array(simpleFinanceLine)}); export const financialData = z.object({foodSales: compositeFinanceLine, feeForRestaurantProvidedDelivery: compositeFinanceLine.nullable().optional(), restaurantFundedDiscount: compositeFinanceLine.nullable().optional(), voucherDiscount: compositeFinanceLine.nullable().optional(), promotionDiscount: compositeFinanceLine.nullable().optional(), operatorDiscount: compositeFinanceLine.nullable().optional(), loyaltyDiscount: compositeFinanceLine.nullable().optional(), tipForRestaurant: compositeFinanceLine.nullable().optional(), adjustments: compositeFinanceLine.nullable().optional(), packingFee: compositeFinanceLine.nullable().optional(), bagFee: compositeFinanceLine.nullable().optional(), serviceProviderDiscount: compositeFinanceLine.nullable().optional(), tipForServiceProviderCourier: compositeFinanceLine.nullable().optional(), feeForServiceProviderDelivery: compositeFinanceLine.nullable().optional(), smallOrderFee: compositeFinanceLine.nullable().optional(), serviceFee: compositeFinanceLine.nullable().optional(), otherFee: compositeFinanceLine.nullable().optional(), netPayout: compositeFinanceLine.nullable().optional(), couponCodes: z.array(z.string()).nullable().optional()}); export const customerPaymentV2 = z.object({customerPaymentDue: z.number().nullable().optional(), customerPrepayment: z.number().nullable().optional(), customerAmountToReturn: z.number().nullable().optional(), paymentDueToRestaurant: z.number().nullable().optional()}).nullable(); export const payout = z.object({payoutFromServiceProvider: z.number().nullable().optional(), payoutFrom3rdParty: z.number().nullable().optional(), cashPayout: z.number().nullable().optional()}).nullable(); export const serviceProviderCharge = z.object({commission: compositeFinanceLine.optional(), processingFee: compositeFinanceLine.optional(), salesTaxWithheld: compositeFinanceLine.optional(), deliveryFeeForRestaurant: compositeFinanceLine.optional(), advertisingFee: compositeFinanceLine.optional(), chargesAdjustments: compositeFinanceLine.optional(), otherFees: compositeFinanceLine.optional()}).nullable(); export const orderTotalV2 = z.object({customerTotal: financialData, customerPayment: customerPaymentV2.nullable().optional(), payout: payout.nullable().optional(), serviceProviderCharge: serviceProviderCharge.nullable().optional()}).nullable(); export const cardInfo = z.object({paymentNetwork: z.enum(["MASTERCARD", "MASTERCARD_MAESTRO", "VISA", "AMEX", "DINERS", "ELO", "HIPERCARD", "BANRICOMPRAS", "NUGO", "GOODCARD", "ELO_MAIS", "GREENCARD", "VEROCARD", "COOPER", "NUTRICARD", "VR", "SODEXO", "OTHER"]), type: z.enum(["CREDIT", "DEBIT", "VOUCHER", "OTHER"])}).nullable(); export const recordProviderType = z.enum(["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 const recordPaymentType = z.enum(["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 const cardBrandType = z.enum(["UNKNOWN", "AMEX", "DINERS_CLUB", "DISCOVER", "JCB", "MASTERCARD", "UNION_PAY", "VISA", "GIROCARD", "EFTPOS_AU", "INTERAC"]); export const cardFundingType = z.enum(["UNKNOWN", "CREDIT", "DEBIT", "PREPAID"]); export const cardWalletType = z.enum(["UNKNOWN", "APPLE_PAY", "GOOGLE_PAY"]); export const paymentDetailsCard = z.object({brandType: cardBrandType.optional(), expiration: z.object({year: z.number().int().optional(), month: z.number().int().optional()}).optional(), fundingType: cardFundingType.optional(), walletType: cardWalletType.optional(), lastFour: z.string().optional(), walletLastFour: z.string().optional(), authorizationCode: z.string().optional(), applicationPreferredName: z.string().optional(), fingerprint: z.string().optional(), readMethod: z.string().optional()}); export const mandateStatus = z.enum(["ACTIVE", "INACTIVE", "PENDING"]); export const paymentDetailsBacs = z.object({mandateStatus: mandateStatus.optional(), lastFour: z.string().optional(), sortCode: z.string().optional(), mandateId: z.string().optional(), reference: z.string().optional(), url: z.string().optional()}); export const paymentDetailsAcss = z.object({mandateStatus: mandateStatus.optional(), acceptedAt: z.string().optional(), lastFour: z.string().optional(), transitNumber: z.string().optional(), institutionNumber: z.string().optional(), bankName: z.string().optional(), mandateId: z.string().optional(), transactionType: z.string().optional(), intervalDescription: z.string().optional()}); export const paymentDetailsBecs = z.object({mandateStatus: mandateStatus.optional(), lastFour: z.string().optional(), bsbNumber: z.string().optional(), mandateId: z.string().optional(), url: z.string().optional()}); export const paymentDetailsSepa = z.object({mandateStatus: mandateStatus.optional(), lastFour: z.string().optional(), branchCode: z.string().optional(), bankCode: z.string().optional(), countryCode: z.string().optional(), mandateId: z.string().optional(), reference: z.string().optional(), url: z.string().optional()}); export const accountHolderType = z.enum(["INDIVIDUAL", "COMPANY"]); export const accountType = z.enum(["CHECKING", "SAVINGS"]); export const paymentDetailsAch = z.object({accountHolderType: accountHolderType.optional(), accountType: accountType.optional(), mandateStatus: mandateStatus.optional(), lastFour: z.string().optional(), routingNumber: z.string().optional(), bankName: z.string().optional(), mandateId: z.string().optional()}); export const paymentRecord = z.object({otterPaymentRecordId: z.string().optional(), recordProviderType: recordProviderType.optional(), recordPaymentType: recordPaymentType.optional(), paymentRecordId: z.string().optional(), payerId: z.string().optional(), balanceTransactionId: z.string().optional(), paymentDetails: z.union([paymentDetailsCard, paymentDetailsBacs, paymentDetailsAcss, paymentDetailsBecs, paymentDetailsSepa, paymentDetailsAch]).optional()}); export const loyaltyInfo = z.object({hasMembershipPass: z.boolean().optional()}).nullable(); export const customerPayment = z.object({value: z.number(), processingStatus: z.enum(["COLLECTABLE", "PROCESSED"]), paymentMethod: z.enum(["CASH", "CARD", "UNKNOWN", "OTHER", "CHEQUE", "GIFT_CARD"]), paymentAuthorizer: z.enum(["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"]).nullable().optional(), cardInfo: cardInfo.optional(), externalPaymentType: z.string().nullable().optional(), paymentRecords: z.array(paymentRecord).nullable().optional(), loyaltyInfo: loyaltyInfo.optional()}); export const fulfillmentInfo = z.object({pickupTime: z.string().nullable().optional(), estimatedPickupTime: z.string().nullable().optional(), deliveryTime: z.string().nullable().optional(), fulfillmentMode: z.enum(["DELIVERY", "RESTAURANT_DELIVERY", "PICKUP", "DINE_IN", "DRIVE_THROUGH"]).optional(), schedulingType: z.enum(["ASAP", "FIXED_TIME"]).nullable().optional(), courierStatus: z.enum(["COURIER_ASSIGNED", "COURIER_ON_ROUTE_TO_PICKUP", "COURIER_ARRIVED", "COURIER_PICKED_UP_FOOD", "COURIER_COMPLETED"]).nullable().optional(), tableIdentifier: z.string().nullable().optional()}).nullable(); export const promotionDetails = z.object({externalId: z.string().optional(), name: z.string().optional(), value: z.number().optional()}).nullable(); export const preparationTime = z.object({estimatedPreparationTime: z.string().optional()}).nullable(); export const order = z.object({externalIdentifiers: orderExternalIdentifiers, currencyCode: z.string().min(3).max(3), status: z.enum(["NEW_ORDER", "CONFIRMED", "PICKED_UP", "CANCELED", "FULFILLED", "PREPARED", "REJECTED", "PREVISIT_COMPLETED", "PREVISIT_NO_SHOW", "UNKNOWN"]), items: z.array(item).optional(), orderedAt: z.string().nullable().optional(), customer: person.optional(), customerNote: z.string().nullable().optional(), deliveryInfo: deliveryInfo.optional(), orderTotal: orderTotal.nullable().optional(), orderTotalV2: orderTotalV2.nullable().optional(), customerPayments: z.array(customerPayment).nullable().optional(), fulfillmentInfo: fulfillmentInfo.optional(), promotionsDetails: z.array(promotionDetails).nullable().optional(), preparationTime: preparationTime.optional(), cutleryRequest: z.enum(["CUTLERY_REQUESTED", "CUTLERY_NOT_NEEDED"]).nullable().optional()}); export const orderFeed = z.object({orders: z.array(order), offsetToken: z.string().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 }; /** * Fetch order feed for a store * * GET /manager/order/v1/orders */ export const getApiManagerOrderV1Orders = async (args: GetApiManagerOrderV1OrdersArgs, options?: GetApiManagerOrderV1OrdersOptions) => { const {baseUrl = "https://partners.tryotter.com", fetch: fetchFn = fetch, OAuth20} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('accept', "application/json") if (args['X-Store-Id'] != null) headers.set("X-Store-Id", String(args['X-Store-Id'])) if (args.limit != null) query.append("limit", String(args.limit)) if (args.token != null) query.append("token", String(args.token)) if (args.minDateTime != null) query.append("minDateTime", String(args.minDateTime)) if (args.maxDateTime != null) query.append("maxDateTime", String(args.maxDateTime)) if (OAuth20 !== undefined) headers.set('authorization', `Bearer ${OAuth20}`) const url = `${baseUrl.replace(/\/+$/, '')}/manager/order/v1/orders${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(orderFeed, await res.json()) };