From 1474781c082d4abd962199228a7b8f3a01e4f6ad Mon Sep 17 00:00:00 2001 From: MOHAN Date: Sun, 29 Mar 2026 17:54:23 +0530 Subject: [PATCH] feat: add typed store integration endpoints for status and holiday hours --- .../04-store-integration-audit.md | 27 +++ docs/developer-portal/04-stores.md | 8 + docs/openapi/openapi.json | 124 +++++++++++++ postman/Uber_Wrapper.postman_collection.json | 169 ++++++++++++++++++ src/config/uberEndpoints.js | 2 + src/modules/proxy/proxy.controller.js | 55 ++++++ src/modules/proxy/proxy.service.js | 74 ++++++++ src/routes/proxy.routes.js | 98 ++++++++++ 8 files changed, 557 insertions(+) create mode 100644 docs/developer-portal/04-store-integration-audit.md diff --git a/docs/developer-portal/04-store-integration-audit.md b/docs/developer-portal/04-store-integration-audit.md new file mode 100644 index 0000000..4a350c0 --- /dev/null +++ b/docs/developer-portal/04-store-integration-audit.md @@ -0,0 +1,27 @@ +# 04 Store Integration Audit + +Source checked: Uber Eats "Store Integration" section shared by you. + +## Implemented Now + +- Retrieve all stores: + - `GET /api/v1/uber/stores` +- Retrieve store by ID: + - `GET /api/v1/uber/stores/{storeId}` +- Retrieve and set online/offline store status: + - `GET /api/v1/uber/stores/{storeId}/status` + - `POST /api/v1/uber/stores/{storeId}/status` +- Retrieve and set date-specific holiday hours: + - `GET /api/v1/uber/stores/{storeId}/holiday-hours` + - `POST /api/v1/uber/stores/{storeId}/holiday-hours` + +## Existing Before + +- Store hours update route (`PUT /api/v1/uber/stores/hours`) +- Provisioning routes and store discovery via merchant auth flow + +## Pending + +- Exact status payload schema hardening once official request examples are finalized +- Extended store settings endpoints not yet included in shared doc dump + diff --git a/docs/developer-portal/04-stores.md b/docs/developer-portal/04-stores.md index 9046ff1..a9f519e 100644 --- a/docs/developer-portal/04-stores.md +++ b/docs/developer-portal/04-stores.md @@ -7,3 +7,11 @@ Uber Eats store-focused integrations: - Holiday/special hours - Operational metadata sync +Typed routes available: + +- `GET /api/v1/uber/stores` +- `GET /api/v1/uber/stores/{storeId}` +- `GET /api/v1/uber/stores/{storeId}/status` +- `POST /api/v1/uber/stores/{storeId}/status` +- `GET /api/v1/uber/stores/{storeId}/holiday-hours` +- `POST /api/v1/uber/stores/{storeId}/holiday-hours` diff --git a/docs/openapi/openapi.json b/docs/openapi/openapi.json index 69df0bf..81b551e 100644 --- a/docs/openapi/openapi.json +++ b/docs/openapi/openapi.json @@ -280,6 +280,19 @@ } } }, + "/api/v1/uber/stores": { + "get": { + "summary": "Retrieve all stores provisioned to developer account", + "tags": [ + "Uber Stores" + ], + "responses": { + "200": { + "description": "Stores retrieved" + } + } + } + }, "/api/v1/uber/stores/provisionable": { "get": { "summary": "Retrieve stores associated with merchant OAuth token", @@ -303,6 +316,117 @@ } } }, + "/api/v1/uber/stores/{storeId}": { + "get": { + "summary": "Retrieve store details by store ID", + "tags": [ + "Uber Stores" + ], + "parameters": [ + { + "in": "path", + "name": "storeId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Store retrieved" + } + } + } + }, + "/api/v1/uber/stores/{storeId}/status": { + "get": { + "summary": "Retrieve store online/offline status", + "tags": [ + "Uber Stores" + ], + "parameters": [ + { + "in": "path", + "name": "storeId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Store status retrieved" + } + } + }, + "post": { + "summary": "Set store online/offline status", + "tags": [ + "Uber Stores" + ], + "parameters": [ + { + "in": "path", + "name": "storeId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Store status updated" + } + } + } + }, + "/api/v1/uber/stores/{storeId}/holiday-hours": { + "get": { + "summary": "Retrieve store holiday hours", + "tags": [ + "Uber Stores" + ], + "parameters": [ + { + "in": "path", + "name": "storeId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Holiday hours retrieved" + } + } + }, + "post": { + "summary": "Set store holiday hours", + "tags": [ + "Uber Stores" + ], + "parameters": [ + { + "in": "path", + "name": "storeId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Holiday hours updated" + } + } + } + }, "/api/v1/uber/orders/{orderId}/action": { "post": { "summary": "Trigger order action (accept, deny, ready, cancel)", diff --git a/postman/Uber_Wrapper.postman_collection.json b/postman/Uber_Wrapper.postman_collection.json index faf18b3..e23692a 100644 --- a/postman/Uber_Wrapper.postman_collection.json +++ b/postman/Uber_Wrapper.postman_collection.json @@ -230,6 +230,175 @@ } } }, + { + "name": "List Stores (App)", + "request": { + "method": "GET", + "header": [ + { + "key": "x-api-key", + "value": "{{apiKey}}" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v1/uber/stores", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v1", + "uber", + "stores" + ] + } + } + }, + { + "name": "Get Store By ID", + "request": { + "method": "GET", + "header": [ + { + "key": "x-api-key", + "value": "{{apiKey}}" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v1/uber/stores/{{storeId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v1", + "uber", + "stores", + "{{storeId}}" + ] + } + } + }, + { + "name": "Get Store Status", + "request": { + "method": "GET", + "header": [ + { + "key": "x-api-key", + "value": "{{apiKey}}" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v1/uber/stores/{{storeId}}/status", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v1", + "uber", + "stores", + "{{storeId}}", + "status" + ] + } + } + }, + { + "name": "Set Store Status", + "request": { + "method": "POST", + "header": [ + { + "key": "x-api-key", + "value": "{{apiKey}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"status\": \"OFFLINE\",\n \"reason\": \"maintenance\"\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/v1/uber/stores/{{storeId}}/status", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v1", + "uber", + "stores", + "{{storeId}}", + "status" + ] + } + } + }, + { + "name": "Get Holiday Hours", + "request": { + "method": "GET", + "header": [ + { + "key": "x-api-key", + "value": "{{apiKey}}" + } + ], + "url": { + "raw": "{{baseUrl}}/api/v1/uber/stores/{{storeId}}/holiday-hours", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v1", + "uber", + "stores", + "{{storeId}}", + "holiday-hours" + ] + } + } + }, + { + "name": "Set Holiday Hours", + "request": { + "method": "POST", + "header": [ + { + "key": "x-api-key", + "value": "{{apiKey}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"holiday_hours\": []\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/v1/uber/stores/{{storeId}}/holiday-hours", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "v1", + "uber", + "stores", + "{{storeId}}", + "holiday-hours" + ] + } + } + }, { "name": "Create POS Data (Provision)", "request": { diff --git a/src/config/uberEndpoints.js b/src/config/uberEndpoints.js index a42f55b..9a22e22 100644 --- a/src/config/uberEndpoints.js +++ b/src/config/uberEndpoints.js @@ -14,6 +14,8 @@ module.exports = { stores: { list: "/v1/eats/stores", getById: "/v1/eats/stores/{storeId}", + status: "/v1/eats/stores/{storeId}/status", + holidayHours: "/v1/eats/stores/{storeId}/holiday_hours", updateHours: "/v1/eats/stores/{storeId}/hours", inventory: "/v1/eats/stores/{storeId}/inventory", posData: "/v1/eats/stores/{storeId}/pos_data" diff --git a/src/modules/proxy/proxy.controller.js b/src/modules/proxy/proxy.controller.js index 34649ae..89d86e3 100644 --- a/src/modules/proxy/proxy.controller.js +++ b/src/modules/proxy/proxy.controller.js @@ -115,6 +115,55 @@ async function listProvisionableStores(req, res) { 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 schema = z.object({ merchantId: z.string().min(1), @@ -164,6 +213,12 @@ module.exports = { orderAction, updateHours, listProvisionableStores, + listStores, + getStoreById, + getStoreStatus, + setStoreStatus, + getHolidayHours, + setHolidayHours, createPosData, patchPosData, deletePosData diff --git a/src/modules/proxy/proxy.service.js b/src/modules/proxy/proxy.service.js index b7b51c5..c3e9ccb 100644 --- a/src/modules/proxy/proxy.service.js +++ b/src/modules/proxy/proxy.service.js @@ -215,6 +215,74 @@ async function listProvisionableStores({ merchantId, query }) { }); } +async function listStores({ query }) { + return callUberApi({ + method: "GET", + uberPath: uberEndpoints.stores.list, + query, + wrapperRoute: "/api/v1/uber/stores", + authMode: "app", + scopes: AUTH_SCOPES.STORE + }); +} + +async function getStoreById({ storeId }) { + const uberPath = interpolatePath(uberEndpoints.stores.getById, { storeId }); + return callUberApi({ + method: "GET", + uberPath, + wrapperRoute: "/api/v1/uber/stores/:storeId", + authMode: "app", + scopes: AUTH_SCOPES.STORE + }); +} + +async function getStoreStatus({ storeId }) { + const uberPath = interpolatePath(uberEndpoints.stores.status, { storeId }); + return callUberApi({ + method: "GET", + uberPath, + wrapperRoute: "/api/v1/uber/stores/:storeId/status", + authMode: "app", + scopes: AUTH_SCOPES.STORE + }); +} + +async function setStoreStatus({ storeId, payload }) { + const uberPath = interpolatePath(uberEndpoints.stores.status, { storeId }); + return callUberApi({ + method: "POST", + uberPath, + body: payload, + wrapperRoute: "/api/v1/uber/stores/:storeId/status", + authMode: "app", + scopes: AUTH_SCOPES.STORE_STATUS_WRITE + }); +} + +async function getHolidayHours({ storeId }) { + const uberPath = interpolatePath(uberEndpoints.stores.holidayHours, { storeId }); + return callUberApi({ + method: "GET", + uberPath, + wrapperRoute: "/api/v1/uber/stores/:storeId/holiday-hours", + authMode: "app", + scopes: AUTH_SCOPES.STORE + }); +} + +async function setHolidayHours({ storeId, payload }) { + const uberPath = interpolatePath(uberEndpoints.stores.holidayHours, { storeId }); + return callUberApi({ + method: "POST", + uberPath, + body: payload, + wrapperRoute: "/api/v1/uber/stores/:storeId/holiday-hours", + authMode: "app", + scopes: AUTH_SCOPES.STORE + }); +} + async function createPosData({ merchantId, storeId, payload }) { const uberPath = interpolatePath(uberEndpoints.stores.posData, { storeId }); return callUberApi({ @@ -262,6 +330,12 @@ module.exports = { orderAction, updateStoreHours, listProvisionableStores, + listStores, + getStoreById, + getStoreStatus, + setStoreStatus, + getHolidayHours, + setHolidayHours, createPosData, patchPosData, deletePosData diff --git a/src/routes/proxy.routes.js b/src/routes/proxy.routes.js index 5304eba..d980e41 100644 --- a/src/routes/proxy.routes.js +++ b/src/routes/proxy.routes.js @@ -69,6 +69,19 @@ router.get("/uber/menu", asyncHandler(controller.getMenu)); */ router.get("/uber/orders", asyncHandler(controller.listOrders)); +/** + * @openapi + * /api/v1/uber/stores: + * get: + * summary: Retrieve all stores provisioned to developer account + * tags: + * - Uber Stores + * responses: + * 200: + * description: Stores retrieved + */ +router.get("/uber/stores", asyncHandler(controller.listStores)); + /** * @openapi * /api/v1/uber/stores/provisionable: @@ -88,6 +101,91 @@ router.get("/uber/orders", asyncHandler(controller.listOrders)); */ router.get("/uber/stores/provisionable", asyncHandler(controller.listProvisionableStores)); +/** + * @openapi + * /api/v1/uber/stores/{storeId}: + * get: + * summary: Retrieve store details by store ID + * tags: + * - Uber Stores + * parameters: + * - in: path + * name: storeId + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Store retrieved + */ +router.get("/uber/stores/:storeId", asyncHandler(controller.getStoreById)); + +/** + * @openapi + * /api/v1/uber/stores/{storeId}/status: + * get: + * summary: Retrieve store online/offline status + * tags: + * - Uber Stores + * parameters: + * - in: path + * name: storeId + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Store status retrieved + * post: + * summary: Set store online/offline status + * tags: + * - Uber Stores + * parameters: + * - in: path + * name: storeId + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Store status updated + */ +router.get("/uber/stores/:storeId/status", asyncHandler(controller.getStoreStatus)); +router.post("/uber/stores/:storeId/status", asyncHandler(controller.setStoreStatus)); + +/** + * @openapi + * /api/v1/uber/stores/{storeId}/holiday-hours: + * get: + * summary: Retrieve store holiday hours + * tags: + * - Uber Stores + * parameters: + * - in: path + * name: storeId + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Holiday hours retrieved + * post: + * summary: Set store holiday hours + * tags: + * - Uber Stores + * parameters: + * - in: path + * name: storeId + * required: true + * schema: + * type: string + * responses: + * 200: + * description: Holiday hours updated + */ +router.get("/uber/stores/:storeId/holiday-hours", asyncHandler(controller.getHolidayHours)); +router.post("/uber/stores/:storeId/holiday-hours", asyncHandler(controller.setHolidayHours)); + /** * @openapi * /api/v1/uber/orders/{orderId}/action: