feat: align integration configuration endpoints with api suite v1.0.0

This commit is contained in:
MOHAN 2026-03-29 18:11:41 +05:30
parent fe524a719d
commit 54565d9b6d
7 changed files with 190 additions and 29 deletions

View File

@ -9,6 +9,8 @@ Source checked: Uber Eats "Integration Configuration Flows" section shared by yo
- Uses merchant token (`authorization_code` / `eats.pos_provisioning`) - Uses merchant token (`authorization_code` / `eats.pos_provisioning`)
- Store activation via POS data: - Store activation via POS data:
- `POST /api/v1/uber/stores/{storeId}/pos-data` - `POST /api/v1/uber/stores/{storeId}/pos-data`
- Retrieve integration configuration:
- `GET /api/v1/uber/stores/{storeId}/pos-data`
- Store integration update/deactivation: - Store integration update/deactivation:
- `PATCH /api/v1/uber/stores/{storeId}/pos-data` - `PATCH /api/v1/uber/stores/{storeId}/pos-data`
- Store de-provision: - Store de-provision:
@ -26,6 +28,5 @@ Source checked: Uber Eats "Integration Configuration Flows" section shared by yo
## Pending ## Pending
- Strong store mapping workflow (location-data assisted matching UI flow) - Strong store mapping workflow (location-data assisted matching UI flow)
- Typed POS data schema once exact request fields are finalized from endpoint reference - Strict nested schema validation for deeper webhook config object variants
- Automated post-provisioning follow-up actions (e.g., mandatory menu upload jobs) - Automated post-provisioning follow-up actions (e.g., mandatory menu upload jobs)

View File

@ -0,0 +1,36 @@
# 03 Integration Configuration Suite 1.0.0 Audit
Source checked: "Uber Eats Integration Activation & Configuration API Suite (1.0.0)" shared by you.
## Endpoint Coverage
- Activate Integration:
- `POST /api/v1/uber/stores/{storeId}/pos-data`
- Uses merchant OAuth token (`eats.pos_provisioning`) via `merchantId`
- Update Integration Configuration:
- `PATCH /api/v1/uber/stores/{storeId}/pos-data`
- Uses app token with `eats.store` scope
- Retrieve Integration Configuration:
- `GET /api/v1/uber/stores/{storeId}/pos-data`
- Uses app token with `eats.store` scope
- Remove Integration Configuration:
- `DELETE /api/v1/uber/stores/{storeId}/pos-data`
- Uses app token with `eats.store` scope
## Schema Alignment
- Added typed request schema fields for `pos_data`:
- `allowed_customer_requests`
- `integrator_brand_id`
- `integrator_store_id`
- `is_order_manager`
- `merchant_store_id`
- `require_manual_acceptance`
- `store_configuration_data`
- `webhooks_config`
- `integration_enabled` (PATCH)
## Pending
- Add stricter nested object typing for all `webhooks_config` variants when final examples are shared.

View File

@ -629,6 +629,27 @@
} }
} }
}, },
"get": {
"summary": "Retrieve integration configuration for selected store (GET /pos_data)",
"tags": [
"Uber Provisioning"
],
"parameters": [
{
"in": "path",
"name": "storeId",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Store integration configuration retrieved"
}
}
},
"patch": { "patch": {
"summary": "Update integration settings for selected store (PATCH /pos_data)", "summary": "Update integration settings for selected store (PATCH /pos_data)",
"tags": [ "tags": [

View File

@ -662,7 +662,7 @@
], ],
"body": { "body": {
"mode": "raw", "mode": "raw",
"raw": "{\n \"merchantId\": \"{{merchantId}}\",\n \"posData\": {\n \"partner_store_id\": \"POS_STORE_001\",\n \"integration_enabled\": true,\n \"store_configuration_data\": {}\n }\n}" "raw": "{\n \"merchantId\": \"{{merchantId}}\",\n \"posData\": {\n \"allowed_customer_requests\": {\n \"allow_single_use_items_requests\": false,\n \"allow_special_instruction_requests\": false\n },\n \"integrator_brand_id\": \"app-brand-1jj9th32\",\n \"integrator_store_id\": \"app-store-001\",\n \"is_order_manager\": true,\n \"merchant_store_id\": \"UberStore1\",\n \"require_manual_acceptance\": false,\n \"store_configuration_data\": \"v1-config\",\n \"webhooks_config\": {\n \"webhooks_version\": \"1.0.0\"\n }\n }\n}"
}, },
"url": { "url": {
"raw": "{{baseUrl}}/api/v1/uber/stores/{{storeId}}/pos-data", "raw": "{{baseUrl}}/api/v1/uber/stores/{{storeId}}/pos-data",
@ -680,6 +680,32 @@
} }
} }
}, },
{
"name": "Get POS Data",
"request": {
"method": "GET",
"header": [
{
"key": "x-api-key",
"value": "{{apiKey}}"
}
],
"url": {
"raw": "{{baseUrl}}/api/v1/uber/stores/{{storeId}}/pos-data",
"host": [
"{{baseUrl}}"
],
"path": [
"api",
"v1",
"uber",
"stores",
"{{storeId}}",
"pos-data"
]
}
}
},
{ {
"name": "Patch POS Data (Enable/Disable)", "name": "Patch POS Data (Enable/Disable)",
"request": { "request": {
@ -696,7 +722,7 @@
], ],
"body": { "body": {
"mode": "raw", "mode": "raw",
"raw": "{\n \"merchantId\": \"{{merchantId}}\",\n \"posData\": {\n \"integration_enabled\": false\n }\n}" "raw": "{\n \"posData\": {\n \"integration_enabled\": false,\n \"require_manual_acceptance\": false\n }\n}"
}, },
"url": { "url": {
"raw": "{{baseUrl}}/api/v1/uber/stores/{{storeId}}/pos-data", "raw": "{{baseUrl}}/api/v1/uber/stores/{{storeId}}/pos-data",
@ -722,16 +748,8 @@
{ {
"key": "x-api-key", "key": "x-api-key",
"value": "{{apiKey}}" "value": "{{apiKey}}"
},
{
"key": "Content-Type",
"value": "application/json"
} }
], ],
"body": {
"mode": "raw",
"raw": "{\n \"merchantId\": \"{{merchantId}}\"\n}"
},
"url": { "url": {
"raw": "{{baseUrl}}/api/v1/uber/stores/{{storeId}}/pos-data", "raw": "{{baseUrl}}/api/v1/uber/stores/{{storeId}}/pos-data",
"host": [ "host": [

View File

@ -239,9 +239,37 @@ async function setHolidayHours(req, res) {
} }
async function createPosData(req, res) { 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({ const schema = z.object({
merchantId: z.string().min(1), merchantId: z.string().min(1),
posData: z.any() posData: posDataSchema
}); });
const payload = schema.parse(req.body); const payload = schema.parse(req.body);
const data = await proxyService.createPosData({ const data = await proxyService.createPosData({
@ -252,14 +280,51 @@ async function createPosData(req, res) {
return res.json({ success: true, data }); 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) { 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({ const schema = z.object({
merchantId: z.string().min(1), posData: posDataSchema
posData: z.any()
}); });
const payload = schema.parse(req.body); const payload = schema.parse(req.body);
const data = await proxyService.patchPosData({ const data = await proxyService.patchPosData({
merchantId: payload.merchantId,
storeId: req.params.storeId, storeId: req.params.storeId,
payload: payload.posData payload: payload.posData
}); });
@ -267,12 +332,7 @@ async function patchPosData(req, res) {
} }
async function deletePosData(req, res) { async function deletePosData(req, res) {
const schema = z.object({
merchantId: z.string().min(1)
});
const payload = schema.parse(req.body);
const data = await proxyService.deletePosData({ const data = await proxyService.deletePosData({
merchantId: payload.merchantId,
storeId: req.params.storeId storeId: req.params.storeId
}); });
return res.json({ success: true, data }); return res.json({ success: true, data });
@ -298,6 +358,7 @@ module.exports = {
getHolidayHours, getHolidayHours,
setHolidayHours, setHolidayHours,
createPosData, createPosData,
getPosData,
patchPosData, patchPosData,
deletePosData deletePosData
}; };

View File

@ -354,28 +354,37 @@ async function createPosData({ merchantId, storeId, payload }) {
}); });
} }
async function patchPosData({ merchantId, storeId, payload }) { async function getPosData({ storeId }) {
const uberPath = interpolatePath(uberEndpoints.stores.posData, { storeId });
return callUberApi({
method: "GET",
uberPath,
wrapperRoute: "/api/v1/uber/stores/:storeId/pos-data",
authMode: "app",
scopes: AUTH_SCOPES.STORE
});
}
async function patchPosData({ storeId, payload }) {
const uberPath = interpolatePath(uberEndpoints.stores.posData, { storeId }); const uberPath = interpolatePath(uberEndpoints.stores.posData, { storeId });
return callUberApi({ return callUberApi({
merchantId,
method: "PATCH", method: "PATCH",
uberPath, uberPath,
body: payload, body: payload,
wrapperRoute: "/api/v1/uber/stores/:storeId/pos-data", wrapperRoute: "/api/v1/uber/stores/:storeId/pos-data",
authMode: "merchant", authMode: "app",
scopes: AUTH_SCOPES.POS_PROVISIONING scopes: AUTH_SCOPES.STORE
}); });
} }
async function deletePosData({ merchantId, storeId }) { async function deletePosData({ storeId }) {
const uberPath = interpolatePath(uberEndpoints.stores.posData, { storeId }); const uberPath = interpolatePath(uberEndpoints.stores.posData, { storeId });
return callUberApi({ return callUberApi({
merchantId,
method: "DELETE", method: "DELETE",
uberPath, uberPath,
wrapperRoute: "/api/v1/uber/stores/:storeId/pos-data", wrapperRoute: "/api/v1/uber/stores/:storeId/pos-data",
authMode: "merchant", authMode: "app",
scopes: AUTH_SCOPES.POS_PROVISIONING scopes: AUTH_SCOPES.STORE
}); });
} }
@ -399,6 +408,7 @@ module.exports = {
getHolidayHours, getHolidayHours,
setHolidayHours, setHolidayHours,
createPosData, createPosData,
getPosData,
patchPosData, patchPosData,
deletePosData deletePosData
}; };

View File

@ -307,6 +307,19 @@ router.put("/uber/stores/hours", asyncHandler(controller.updateHours));
* responses: * responses:
* 200: * 200:
* description: Store provisioned * description: Store provisioned
* get:
* summary: Retrieve integration configuration for selected store (GET /pos_data)
* tags:
* - Uber Provisioning
* parameters:
* - in: path
* name: storeId
* required: true
* schema:
* type: string
* responses:
* 200:
* description: Store integration configuration retrieved
* patch: * patch:
* summary: Update integration settings for selected store (PATCH /pos_data) * summary: Update integration settings for selected store (PATCH /pos_data)
* tags: * tags:
@ -335,6 +348,7 @@ router.put("/uber/stores/hours", asyncHandler(controller.updateHours));
* description: Store integration removed * description: Store integration removed
*/ */
router.post("/uber/stores/:storeId/pos-data", asyncHandler(controller.createPosData)); router.post("/uber/stores/:storeId/pos-data", asyncHandler(controller.createPosData));
router.get("/uber/stores/:storeId/pos-data", asyncHandler(controller.getPosData));
router.patch("/uber/stores/:storeId/pos-data", asyncHandler(controller.patchPosData)); router.patch("/uber/stores/:storeId/pos-data", asyncHandler(controller.patchPosData));
router.delete("/uber/stores/:storeId/pos-data", asyncHandler(controller.deletePosData)); router.delete("/uber/stores/:storeId/pos-data", asyncHandler(controller.deletePosData));