const { z } = require("zod"); const proxyService = require("./proxy.service"); const genericSchema = z.object({ merchantId: z.string().min(1).optional(), method: z.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]).default("GET"), path: z.string().min(1), authMode: z.enum(["app", "merchant"]).optional(), scopes: z.string().optional(), query: z.record(z.string(), z.any()).optional(), body: z.any().optional() }); async function genericProxy(req, res) { const payload = genericSchema.parse(req.body); const data = await proxyService.genericProxy(payload); return res.json({ success: true, data }); } async function upsertMenu(req, res) { const schema = z.object({ merchantId: z.string().min(1), storeId: z.string().min(1), menu: z.any() }); const payload = schema.parse(req.body); const data = await proxyService.menuUpsert({ merchantId: payload.merchantId, storeId: payload.storeId, payload: payload.menu }); return res.json({ success: true, data }); } async function getMenu(req, res) { const schema = z.object({ merchantId: z.string().min(1), storeId: z.string().min(1), menu_type: z .enum([ "MENU_TYPE_FULFILLMENT_DELIVERY", "MENU_TYPE_FULFILLMENT_PICK_UP", "MENU_TYPE_FULFILLMENT_DINE_IN" ]) .optional(), menuType: z .enum([ "MENU_TYPE_FULFILLMENT_DELIVERY", "MENU_TYPE_FULFILLMENT_PICK_UP", "MENU_TYPE_FULFILLMENT_DINE_IN" ]) .optional() }); const payload = schema.parse(req.query); const data = await proxyService.menuGet({ merchantId: payload.merchantId, storeId: payload.storeId, menuType: payload.menu_type || payload.menuType }); return res.json({ success: true, data }); } async function replaceMenu(req, res) { const schema = z.object({ merchantId: z.string().min(1), storeId: z.string().min(1), menu: z.any() }); const payload = schema.parse(req.body); const data = await proxyService.menuReplace({ merchantId: payload.merchantId, storeId: payload.storeId, payload: payload.menu }); return res.json({ success: true, data }); } async function updateMenuItems(req, res) { const schema = z.object({ merchantId: z.string().min(1), storeId: z.string().min(1), items: z.array(z.any()).min(1) }); const payload = schema.parse(req.body); const data = await proxyService.updateMenuItems({ merchantId: payload.merchantId, storeId: payload.storeId, payload: { items: payload.items } }); return res.json({ success: true, data }); } async function listOrders(req, res) { const schema = z.object({ merchantId: z.string().min(1), storeId: z.string().min(1) }); const payload = schema.parse(req.query); const data = await proxyService.ordersList({ merchantId: payload.merchantId, storeId: payload.storeId, query: req.query }); return res.json({ success: true, data }); } async function getOrderById(req, res) { const data = await proxyService.getOrderById({ orderId: req.params.orderId }); return res.json({ success: true, data }); } async function orderAction(req, res) { const schema = z.object({ merchantId: z.string().min(1), action: z.enum(["accept", "deny", "ready", "cancel", "resolve"]), payload: z.any().optional() }); const parsed = schema.parse(req.body); const data = await proxyService.orderAction({ merchantId: parsed.merchantId, orderId: req.params.orderId, action: parsed.action, payload: parsed.payload }); return res.json({ success: true, data }); } async function resolveFulfillmentIssues(req, res) { const quantityObject = z .object({ in_sellable_unit: z.any().optional(), in_priceable_unit: z.any().optional() }) .optional(); const fulfillmentIssueSchema = z.object({ cart_item_id: z.string().optional(), issue_type: z.enum([ "FOUND_ITEM", "PARTIAL_AVAILABILITY", "OUT_OF_ITEM" ]), action_type: z .enum([ "REPLACE_FOR_ME", "SUBSTITUTE_ME", "REMOVE_ITEM", "ALTERNATIVE_ITEM", "SUBSTITUTION_REJECTED" ]) .optional(), item_substitute: z.any().optional(), item_availability: z .object({ items_available: quantityObject }) .optional() }); const schema = z.object({ issues: z.array(fulfillmentIssueSchema).min(1) }); const payload = schema.parse(req.body || {}); const data = await proxyService.resolveFulfillmentIssues({ orderId: req.params.orderId, payload }); return res.json({ success: true, data }); } async function markOrderReady(req, res) { const data = await proxyService.markOrderReady({ orderId: req.params.orderId, payload: req.body || {} }); return res.json({ success: true, data }); } async function updateHours(req, res) { const schema = z.object({ merchantId: z.string().min(1), storeId: z.string().min(1), hours: z.any() }); const payload = schema.parse(req.body); const data = await proxyService.updateStoreHours({ merchantId: payload.merchantId, storeId: payload.storeId, payload: payload.hours }); return res.json({ success: true, data }); } async function listProvisionableStores(req, res) { const schema = z.object({ merchantId: z.string().min(1) }); const payload = schema.parse(req.query); const data = await proxyService.listProvisionableStores({ merchantId: payload.merchantId, query: req.query }); return res.json({ success: true, data }); } async function listStores(req, res) { const data = await proxyService.listStores({ query: req.query }); return res.json({ success: true, data }); } async function getStoreById(req, res) { const data = await proxyService.getStoreById({ storeId: req.params.storeId }); return res.json({ success: true, data }); } async function getStoreStatus(req, res) { const data = await proxyService.getStoreStatus({ storeId: req.params.storeId }); return res.json({ success: true, data }); } async function setStoreStatus(req, res) { const schema = z.object({ status: z.string().optional(), reason: z.string().optional() }); const payload = schema.partial().parse(req.body || {}); const data = await proxyService.setStoreStatus({ storeId: req.params.storeId, payload }); return res.json({ success: true, data }); } async function getHolidayHours(req, res) { const data = await proxyService.getHolidayHours({ storeId: req.params.storeId }); return res.json({ success: true, data }); } async function setHolidayHours(req, res) { const data = await proxyService.setHolidayHours({ storeId: req.params.storeId, payload: req.body || {} }); return res.json({ success: true, data }); } async function createPosData(req, res) { const allowedCustomerRequestsSchema = z .object({ allow_single_use_items_requests: z.boolean().optional(), allow_special_instruction_requests: z.boolean().optional() }) .optional(); const webhooksConfigSchema = z .object({ order_release_webhooks: z.record(z.string(), z.any()).optional(), schedule_order_webhooks: z.record(z.string(), z.any()).optional(), delivery_status_webhooks: z.record(z.string(), z.any()).optional(), webhooks_version: z.string().optional() }) .optional(); const posDataSchema = z.object({ allowed_customer_requests: allowedCustomerRequestsSchema, integrator_brand_id: z.string().optional(), integrator_store_id: z.string().optional(), is_order_manager: z.boolean().optional(), merchant_store_id: z.string().optional(), require_manual_acceptance: z.boolean().optional(), store_configuration_data: z.string().optional(), webhooks_config: webhooksConfigSchema, integration_enabled: z.boolean().optional() }); const schema = z.object({ merchantId: z.string().min(1), posData: posDataSchema }); const payload = schema.parse(req.body); const data = await proxyService.createPosData({ merchantId: payload.merchantId, storeId: req.params.storeId, payload: payload.posData }); return res.json({ success: true, data }); } async function getPosData(req, res) { const data = await proxyService.getPosData({ storeId: req.params.storeId }); return res.json({ success: true, data }); } async function patchPosData(req, res) { const allowedCustomerRequestsSchema = z .object({ allow_single_use_items_requests: z.boolean().optional(), allow_special_instruction_requests: z.boolean().optional() }) .optional(); const webhooksConfigSchema = z .object({ order_release_webhooks: z.record(z.string(), z.any()).optional(), schedule_order_webhooks: z.record(z.string(), z.any()).optional(), delivery_status_webhooks: z.record(z.string(), z.any()).optional(), webhooks_version: z.string().optional() }) .optional(); const posDataSchema = z .object({ allowed_customer_requests: allowedCustomerRequestsSchema, integrator_brand_id: z.string().optional(), integrator_store_id: z.string().optional(), is_order_manager: z.boolean().optional(), merchant_store_id: z.string().optional(), require_manual_acceptance: z.boolean().optional(), store_configuration_data: z.string().optional(), webhooks_config: webhooksConfigSchema, integration_enabled: z.boolean().optional() }) .refine((value) => Object.keys(value).length > 0, { message: "posData must include at least one updatable field" }); const schema = z.object({ posData: posDataSchema }); const payload = schema.parse(req.body); const data = await proxyService.patchPosData({ storeId: req.params.storeId, payload: payload.posData }); return res.json({ success: true, data }); } async function deletePosData(req, res) { const data = await proxyService.deletePosData({ storeId: req.params.storeId }); return res.json({ success: true, data }); } async function deliveryListStores(req, res) { const schema = z.object({ next_page_token: z.string().optional(), page_size: z.coerce.number().int().min(1).max(200).optional() }); const query = schema.parse(req.query || {}); const data = await proxyService.deliveryListStores({ query }); return res.json({ success: true, data }); } async function deliveryGetStoreDetails(req, res) { const schema = z.object({ expand: z.string().optional() }); const query = schema.parse(req.query || {}); const data = await proxyService.deliveryGetStoreDetails({ storeId: req.params.storeId, query }); return res.json({ success: true, data }); } async function deliveryUpdateStore(req, res) { const contactSchema = z .object({ email: z.string().optional(), name: z.string().optional(), phone_number: z.string().optional() }) .optional(); const locationSchema = z .object({ latitude: z.number().optional(), longitude: z.number().optional(), street_address_line_one: z.string().optional(), street_address_line_two: z.string().optional(), city: z.string().optional(), country: z.string().optional(), postal_code: z.union([z.string(), z.number()]).optional(), unit_number: z.union([z.string(), z.number()]).optional(), business_name: z.string().optional() }) .optional(); const schema = z.object({ contact: contactSchema, location: locationSchema, pickup_instructions: z.string().optional() }); const payload = schema .refine((value) => Object.keys(value).length > 0, { message: "At least one of contact/location/pickup_instructions is required" }) .parse(req.body || {}); const data = await proxyService.deliveryUpdateStore({ storeId: req.params.storeId, payload }); return res.json({ success: true, data }); } async function deliveryGetStoreStatus(req, res) { const data = await proxyService.deliveryGetStoreStatus({ storeId: req.params.storeId }); return res.json({ success: true, data }); } async function deliverySetStoreStatus(req, res) { const schema = z.object({ status: z.enum(["ONLINE", "OFFLINE"]), is_offline_until: z.string().optional(), reason: z.string().optional() }); const payload = schema.parse(req.body || {}); const data = await proxyService.deliverySetStoreStatus({ storeId: req.params.storeId, payload }); return res.json({ success: true, data }); } async function deliveryUpdatePrepTime(req, res) { const schema = z.object({ default_prep_time: z.coerce.number().int().min(0).max(10800) }); const payload = schema.parse(req.body || {}); const data = await proxyService.deliveryUpdatePrepTime({ storeId: req.params.storeId, payload }); return res.json({ success: true, data }); } async function deliveryUpdateFulfillmentConfig(req, res) { const schema = z.object({ override_config: z.record(z.string(), z.any()) }); const payload = schema.parse(req.body || {}); const data = await proxyService.deliveryUpdateFulfillmentConfig({ storeId: req.params.storeId, payload }); return res.json({ success: true, data }); } async function deliveryGetOrderDetails(req, res) { const schema = z.object({ expand: z.string().optional() }); const query = schema.parse(req.query || {}); const data = await proxyService.deliveryGetOrderDetails({ orderId: req.params.orderId, query }); return res.json({ success: true, data }); } async function deliveryListOrders(req, res) { const schema = z.object({ expand: z.string().optional(), state: z.string().optional(), status: z.string().optional(), start_time: z.string().optional(), end_time: z.string().optional(), next_page_token: z.string().optional(), page_size: z.coerce.number().int().min(1).max(50).optional() }); const query = schema.parse(req.query || {}); const data = await proxyService.deliveryListOrders({ storeId: req.params.storeId, query }); return res.json({ success: true, data }); } async function deliveryAcceptOrder(req, res) { const schema = z.object({ ready_for_pickup_time: z.string().optional(), external_reference_id: z.string().optional(), accepted_by: z.string().optional(), order_pickup_instructions: z.string().optional() }); const payload = schema.parse(req.body || {}); const data = await proxyService.deliveryAcceptOrder({ orderId: req.params.orderId, payload }); return res.json({ success: true, data }); } async function deliveryDenyOrder(req, res) { const denyReasonSchema = z.object({ info: z.string().optional(), type: z.string().min(1), client_error_code: z.string().optional(), item_metadata: z.record(z.string(), z.any()).optional() }); const schema = z.object({ deny_reason: denyReasonSchema }); const payload = schema.parse(req.body || {}); const data = await proxyService.deliveryDenyOrder({ orderId: req.params.orderId, payload }); return res.json({ success: true, data }); } async function deliveryCancelOrder(req, res) { const cancelReasonSchema = z.object({ info: z.string().optional(), type: z.string().min(1), client_error_code: z.string().optional(), item_metadata: z.record(z.string(), z.any()).optional() }); const schema = z.object({ cancellation_reason: cancelReasonSchema }); const payload = schema.parse(req.body || {}); const data = await proxyService.deliveryCancelOrder({ orderId: req.params.orderId, payload }); return res.json({ success: true, data }); } async function deliveryMarkOrderReady(req, res) { const data = await proxyService.deliveryMarkOrderReady({ orderId: req.params.orderId, payload: req.body || {} }); return res.json({ success: true, data }); } async function deliveryAdjustOrderPrice(req, res) { const schema = z .object({ amount_e5: z.coerce.number(), tax_rate: z.string().optional(), reason: z.enum([ "REQUESTED_ADD_ONS", "BIGGER_SIZE", "NEW_ITEM_ADDED", "ITEM_SOLD_OUT", "REMOVED_ITEM", "ADD_ON_UNAVAILABLE", "OTHER" ]), custom_reason: z.string().optional() }) .refine((value) => value.reason !== "OTHER" || Boolean(value.custom_reason), { message: "custom_reason is required when reason is OTHER" }); const payload = schema.parse(req.body || {}); const data = await proxyService.deliveryAdjustOrderPrice({ orderId: req.params.orderId, payload }); return res.json({ success: true, data }); } async function deliveryUpdateReadyTime(req, res) { const schema = z.object({ ready_for_pickup_time: z.string().min(1) }); const payload = schema.parse(req.body || {}); const data = await proxyService.deliveryUpdateReadyTime({ orderId: req.params.orderId, payload }); return res.json({ success: true, data }); } async function deliveryResolveFulfillmentIssues(req, res) { const schema = z.object({ fulfillment_issues: z.array(z.any()).min(1) }); const payload = schema.parse(req.body || {}); const data = await proxyService.deliveryResolveFulfillmentIssues({ orderId: req.params.orderId, payload }); return res.json({ success: true, data }); } async function deliveryGetReplacementRecommendations(req, res) { const schema = z.object({ id: z.string().min(1), order_id: z.string().min(1), store_id: z.string().min(1) }); const payload = schema.parse(req.body || {}); const data = await proxyService.deliveryGetReplacementRecommendations({ payload }); return res.json({ success: true, data }); } async function deliveryUpdatePartnerCount(req, res) { const schema = z.object({ delivery_partner_count: z.coerce.number().int().min(2).max(5) }); const payload = schema.parse(req.body || {}); const data = await proxyService.deliveryUpdatePartnerCount({ orderId: req.params.orderId, payload }); return res.json({ success: true, data }); } async function deliveryByocIngestCourierLocation(req, res) { const locationEventSchema = z.object({ timestamp: z.union([z.number(), z.string()]).optional(), latitude: z.number().optional(), longitude: z.number().optional(), accuracy_meters: z.number().optional(), speed_mps: z.number().optional(), heading_degrees: z.number().optional() }); const schema = z.object({ location_request: z.object({ order_workflow_uuid: z.string().uuid(), restaurant_uuid: z.string().uuid(), is_batched_order: z.boolean().optional(), location_events: z.array(locationEventSchema) }) }); const payload = schema.parse(req.body || {}); const data = await proxyService.deliveryByocIngestCourierLocation({ payload }); return res.json({ success: true, data }); } async function deliveryCreatePromotion(req, res) { const budgetSchema = z .object({ unlimited_budget: z.boolean().optional(), amount_e5: z.coerce.number().optional() }) .optional(); const promotionSchema = z.object({ start_time: z.string(), end_time: z.string(), external_promotion_id: z.string().optional(), user_group: z.enum(["ALL_CUSTOMERS", "FIRST_TIME_CUSTOMERS"]).optional(), allow_unlimited_apply: z.boolean().optional(), currency_code: z.string().optional(), budget: budgetSchema, promo_type: z.string(), promotion_discount: z.record(z.string(), z.any()).optional(), promotion_customization: z.record(z.string(), z.any()).optional() }); const payload = promotionSchema.parse(req.body || {}); const data = await proxyService.deliveryCreatePromotion({ storeId: req.params.storeId, payload }); return res.json({ success: true, data }); } async function deliveryRevokePromotion(req, res) { const data = await proxyService.deliveryRevokePromotion({ promotionId: req.params.promotionId }); return res.json({ success: true, data }); } async function deliveryGetPromotion(req, res) { const data = await proxyService.deliveryGetPromotion({ promotionId: req.params.promotionId }); return res.json({ success: true, data }); } async function deliveryListPromotions(req, res) { const schema = z.object({ state: z.enum(["active", "pending", "completed", "revoked", "expired", "deleted"]).optional(), start_time: z.string().optional(), end_time: z.string().optional() }); const query = schema.parse(req.query || {}); const data = await proxyService.deliveryListPromotions({ storeId: req.params.storeId, query }); return res.json({ success: true, data }); } module.exports = { genericProxy, upsertMenu, replaceMenu, updateMenuItems, getMenu, listOrders, getOrderById, orderAction, resolveFulfillmentIssues, markOrderReady, updateHours, listProvisionableStores, listStores, getStoreById, getStoreStatus, setStoreStatus, getHolidayHours, setHolidayHours, createPosData, getPosData, patchPosData, deletePosData, deliveryListStores, deliveryGetStoreDetails, deliveryUpdateStore, deliveryGetStoreStatus, deliverySetStoreStatus, deliveryUpdatePrepTime, deliveryUpdateFulfillmentConfig, deliveryGetOrderDetails, deliveryListOrders, deliveryAcceptOrder, deliveryDenyOrder, deliveryCancelOrder, deliveryMarkOrderReady, deliveryAdjustOrderPrice, deliveryUpdateReadyTime, deliveryResolveFulfillmentIssues, deliveryGetReplacementRecommendations, deliveryUpdatePartnerCount, deliveryByocIngestCourierLocation, deliveryCreatePromotion, deliveryRevokePromotion, deliveryGetPromotion, deliveryListPromotions };