feat: implement order fulfillment api v1.0.0 delivery endpoints and webhook compatibility
This commit is contained in:
parent
d4042fb656
commit
53e5bd970c
46
docs/developer-portal/06-order-api-1-0-0-audit.md
Normal file
46
docs/developer-portal/06-order-api-1-0-0-audit.md
Normal file
@ -0,0 +1,46 @@
|
||||
# 06 Order API 1.0.0 Audit
|
||||
|
||||
Source checked: "Order Fulfillment API (1.0.0)" shared by you.
|
||||
|
||||
## Implemented Now (Dedicated Wrapper Namespace)
|
||||
|
||||
- Get Order Details:
|
||||
- `GET /api/v1/uber/delivery-order/orders/{orderId}`
|
||||
- upstream: `/v1/delivery/order/{order_id}`
|
||||
- List Orders Details:
|
||||
- `GET /api/v1/uber/delivery-order/stores/{storeId}/orders`
|
||||
- upstream: `/v1/delivery/store/{store_id}/orders`
|
||||
- Accept Order:
|
||||
- `POST /api/v1/uber/delivery-order/orders/{orderId}/accept`
|
||||
- Deny Order:
|
||||
- `POST /api/v1/uber/delivery-order/orders/{orderId}/deny`
|
||||
- Cancel Order:
|
||||
- `POST /api/v1/uber/delivery-order/orders/{orderId}/cancel`
|
||||
- Mark Order Ready:
|
||||
- `POST /api/v1/uber/delivery-order/orders/{orderId}/ready`
|
||||
- Adjust Order Price:
|
||||
- `POST /api/v1/uber/delivery-order/orders/{orderId}/adjust-price`
|
||||
- Update Order Ready Time:
|
||||
- `POST /api/v1/uber/delivery-order/orders/{orderId}/update-ready-time`
|
||||
- Resolve Fulfillment Issues:
|
||||
- `POST /api/v1/uber/delivery-order/orders/{orderId}/resolve-fulfillment-issues`
|
||||
- Get Replacement Recommendations:
|
||||
- `POST /api/v1/uber/delivery-order/replacement-recommendations`
|
||||
|
||||
## Validation Added
|
||||
|
||||
- adjust-price reasons enum and `custom_reason` required when reason is `OTHER`
|
||||
- update-ready-time requires `ready_for_pickup_time`
|
||||
- deny/cancel reason object shape validation
|
||||
- list orders page size max enforced (`<= 50`)
|
||||
|
||||
## Existing Before
|
||||
|
||||
- Legacy order routes under `/api/v1/uber/orders...`
|
||||
- Retail fulfillment helpers and related webhooks
|
||||
|
||||
## Pending
|
||||
|
||||
- Response normalization nuances for `204 No Content` cancel semantics if required by consuming clients
|
||||
- Deeper typed schemas for webhook metadata and fulfillment issue object variants
|
||||
|
||||
@ -26,3 +26,16 @@ Retail fulfillment guidance:
|
||||
- Read customer preference (`REPLACE_FOR_ME`, `SUBSTITUTE_ME`, `REMOVE_ITEM`) from order details.
|
||||
- Update issue states via fulfillment endpoint (`FOUND_ITEM`, `PARTIAL_AVAILABILITY`, `OUT_OF_ITEM`).
|
||||
- On `orders.fulfillment_issues.resolved` webhook, fetch latest order and continue resolution.
|
||||
|
||||
Order API 1.0.0 coverage (delivery namespace):
|
||||
|
||||
- `GET /api/v1/uber/delivery-order/orders/{orderId}`
|
||||
- `GET /api/v1/uber/delivery-order/stores/{storeId}/orders`
|
||||
- `POST /api/v1/uber/delivery-order/orders/{orderId}/accept`
|
||||
- `POST /api/v1/uber/delivery-order/orders/{orderId}/deny`
|
||||
- `POST /api/v1/uber/delivery-order/orders/{orderId}/cancel`
|
||||
- `POST /api/v1/uber/delivery-order/orders/{orderId}/ready`
|
||||
- `POST /api/v1/uber/delivery-order/orders/{orderId}/adjust-price`
|
||||
- `POST /api/v1/uber/delivery-order/orders/{orderId}/update-ready-time`
|
||||
- `POST /api/v1/uber/delivery-order/orders/{orderId}/resolve-fulfillment-issues`
|
||||
- `POST /api/v1/uber/delivery-order/replacement-recommendations`
|
||||
|
||||
@ -22,6 +22,7 @@ Common event types handled:
|
||||
- `orders.release`
|
||||
- `orders.scheduled.notification`
|
||||
- `orders.cancel`
|
||||
- `delivery.state_changed`
|
||||
- `store.provisioned`
|
||||
- `store.deprovisioned`
|
||||
- `store.status.changed`
|
||||
|
||||
@ -840,6 +840,156 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/uber/delivery-order/orders/{orderId}": {
|
||||
"get": {
|
||||
"summary": "Order API 1.0.0 - Get order details",
|
||||
"tags": [
|
||||
"Uber Delivery Order v1"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"in": "path",
|
||||
"name": "orderId",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Order details retrieved"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/uber/delivery-order/stores/{storeId}/orders": {
|
||||
"get": {
|
||||
"summary": "Order API 1.0.0 - List store orders with details",
|
||||
"tags": [
|
||||
"Uber Delivery Order v1"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"in": "path",
|
||||
"name": "storeId",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Orders listed"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/uber/delivery-order/orders/{orderId}/accept": {
|
||||
"post": {
|
||||
"summary": "Order API 1.0.0 - Accept order",
|
||||
"tags": [
|
||||
"Uber Delivery Order v1"
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Order accepted"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/uber/delivery-order/orders/{orderId}/deny": {
|
||||
"post": {
|
||||
"summary": "Order API 1.0.0 - Deny order",
|
||||
"tags": [
|
||||
"Uber Delivery Order v1"
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Order denied"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/uber/delivery-order/orders/{orderId}/cancel": {
|
||||
"post": {
|
||||
"summary": "Order API 1.0.0 - Cancel order",
|
||||
"tags": [
|
||||
"Uber Delivery Order v1"
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Order canceled"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/uber/delivery-order/orders/{orderId}/ready": {
|
||||
"post": {
|
||||
"summary": "Order API 1.0.0 - Mark order ready",
|
||||
"tags": [
|
||||
"Uber Delivery Order v1"
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Order ready"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/uber/delivery-order/orders/{orderId}/adjust-price": {
|
||||
"post": {
|
||||
"summary": "Order API 1.0.0 - Adjust order price",
|
||||
"tags": [
|
||||
"Uber Delivery Order v1"
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Order price adjusted"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/uber/delivery-order/orders/{orderId}/update-ready-time": {
|
||||
"post": {
|
||||
"summary": "Order API 1.0.0 - Update order ready time",
|
||||
"tags": [
|
||||
"Uber Delivery Order v1"
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Order ready time updated"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/uber/delivery-order/orders/{orderId}/resolve-fulfillment-issues": {
|
||||
"post": {
|
||||
"summary": "Order API 1.0.0 - Resolve fulfillment issues",
|
||||
"tags": [
|
||||
"Uber Delivery Order v1"
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Fulfillment issue resolution submitted"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/uber/delivery-order/replacement-recommendations": {
|
||||
"post": {
|
||||
"summary": "Order API 1.0.0 - Get replacement recommendations",
|
||||
"tags": [
|
||||
"Uber Delivery Order v1"
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Replacement recommendations returned"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/uber/reporting/fetch": {
|
||||
"post": {
|
||||
"summary": "Fetch Uber reporting CSV with retries and header-based parsing",
|
||||
|
||||
@ -516,6 +516,349 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Delivery Order API - Get Order Details",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [
|
||||
{
|
||||
"key": "x-api-key",
|
||||
"value": "{{apiKey}}"
|
||||
}
|
||||
],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/api/v1/uber/delivery-order/orders/{{orderId}}?expand=carts,deliveries,payment",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"api",
|
||||
"v1",
|
||||
"uber",
|
||||
"delivery-order",
|
||||
"orders",
|
||||
"{{orderId}}"
|
||||
],
|
||||
"query": [
|
||||
{
|
||||
"key": "expand",
|
||||
"value": "carts,deliveries,payment"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Delivery Order API - List Store Orders",
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"header": [
|
||||
{
|
||||
"key": "x-api-key",
|
||||
"value": "{{apiKey}}"
|
||||
}
|
||||
],
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/api/v1/uber/delivery-order/stores/{{storeId}}/orders?page_size=50",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"api",
|
||||
"v1",
|
||||
"uber",
|
||||
"delivery-order",
|
||||
"stores",
|
||||
"{{storeId}}",
|
||||
"orders"
|
||||
],
|
||||
"query": [
|
||||
{
|
||||
"key": "page_size",
|
||||
"value": "50"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Delivery Order API - Accept",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"key": "x-api-key",
|
||||
"value": "{{apiKey}}"
|
||||
},
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"ready_for_pickup_time\": \"2026-04-01T18:50:05.000Z\",\n \"accepted_by\": \"John Smith\"\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/api/v1/uber/delivery-order/orders/{{orderId}}/accept",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"api",
|
||||
"v1",
|
||||
"uber",
|
||||
"delivery-order",
|
||||
"orders",
|
||||
"{{orderId}}",
|
||||
"accept"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Delivery Order API - Deny",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"key": "x-api-key",
|
||||
"value": "{{apiKey}}"
|
||||
},
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"deny_reason\": {\n \"type\": \"ITEM_ISSUE\",\n \"info\": \"Item is not available\"\n }\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/api/v1/uber/delivery-order/orders/{{orderId}}/deny",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"api",
|
||||
"v1",
|
||||
"uber",
|
||||
"delivery-order",
|
||||
"orders",
|
||||
"{{orderId}}",
|
||||
"deny"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Delivery Order API - Cancel",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"key": "x-api-key",
|
||||
"value": "{{apiKey}}"
|
||||
},
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"cancellation_reason\": {\n \"type\": \"ITEM_ISSUE\",\n \"info\": \"Item sold out\"\n }\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/api/v1/uber/delivery-order/orders/{{orderId}}/cancel",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"api",
|
||||
"v1",
|
||||
"uber",
|
||||
"delivery-order",
|
||||
"orders",
|
||||
"{{orderId}}",
|
||||
"cancel"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Delivery Order API - Ready",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"key": "x-api-key",
|
||||
"value": "{{apiKey}}"
|
||||
},
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/api/v1/uber/delivery-order/orders/{{orderId}}/ready",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"api",
|
||||
"v1",
|
||||
"uber",
|
||||
"delivery-order",
|
||||
"orders",
|
||||
"{{orderId}}",
|
||||
"ready"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Delivery Order API - Adjust Price",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"key": "x-api-key",
|
||||
"value": "{{apiKey}}"
|
||||
},
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"amount_e5\": -500000,\n \"reason\": \"ITEM_SOLD_OUT\"\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/api/v1/uber/delivery-order/orders/{{orderId}}/adjust-price",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"api",
|
||||
"v1",
|
||||
"uber",
|
||||
"delivery-order",
|
||||
"orders",
|
||||
"{{orderId}}",
|
||||
"adjust-price"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Delivery Order API - Update Ready Time",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"key": "x-api-key",
|
||||
"value": "{{apiKey}}"
|
||||
},
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"ready_for_pickup_time\": \"2026-04-01T18:50:05.000Z\"\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/api/v1/uber/delivery-order/orders/{{orderId}}/update-ready-time",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"api",
|
||||
"v1",
|
||||
"uber",
|
||||
"delivery-order",
|
||||
"orders",
|
||||
"{{orderId}}",
|
||||
"update-ready-time"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Delivery Order API - Resolve Fulfillment Issues",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"key": "x-api-key",
|
||||
"value": "{{apiKey}}"
|
||||
},
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"fulfillment_issues\": [\n {}\n ]\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/api/v1/uber/delivery-order/orders/{{orderId}}/resolve-fulfillment-issues",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"api",
|
||||
"v1",
|
||||
"uber",
|
||||
"delivery-order",
|
||||
"orders",
|
||||
"{{orderId}}",
|
||||
"resolve-fulfillment-issues"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Delivery Order API - Replacement Recommendations",
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"key": "x-api-key",
|
||||
"value": "{{apiKey}}"
|
||||
},
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"id\": \"3679399\",\n \"order_id\": \"{{orderId}}\",\n \"store_id\": \"{{storeId}}\"\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{baseUrl}}/api/v1/uber/delivery-order/replacement-recommendations",
|
||||
"host": [
|
||||
"{{baseUrl}}"
|
||||
],
|
||||
"path": [
|
||||
"api",
|
||||
"v1",
|
||||
"uber",
|
||||
"delivery-order",
|
||||
"replacement-recommendations"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Get Store By ID",
|
||||
"request": {
|
||||
|
||||
@ -31,6 +31,18 @@ module.exports = {
|
||||
updatePrepTime: "/v1/delivery/store/{storeId}/update-store-prep-time",
|
||||
updateFulfillmentConfig: "/v1/delivery/store/{storeId}/update-fulfillment-configuration"
|
||||
},
|
||||
deliveryOrder: {
|
||||
getById: "/v1/delivery/order/{orderId}",
|
||||
listByStore: "/v1/delivery/store/{storeId}/orders",
|
||||
accept: "/v1/delivery/order/{orderId}/accept",
|
||||
deny: "/v1/delivery/order/{orderId}/deny",
|
||||
cancel: "/v1/delivery/order/{orderId}/cancel",
|
||||
ready: "/v1/delivery/order/{orderId}/ready",
|
||||
adjustPrice: "/v1/delivery/order/{orderId}/adjust-price",
|
||||
updateReadyTime: "/v1/delivery/order/{orderId}/update-ready-time",
|
||||
resolveFulfillmentIssues: "/v1/delivery/order/{orderId}/resolve-fulfillment-issues",
|
||||
replacementRecommendations: "/v1/delivery/get-replacement-recommendations"
|
||||
},
|
||||
webhooks: {
|
||||
events: "/v1/eats/stores/{storeId}/event_feed"
|
||||
}
|
||||
|
||||
@ -444,6 +444,159 @@ async function deliveryUpdateFulfillmentConfig(req, res) {
|
||||
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 });
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
genericProxy,
|
||||
upsertMenu,
|
||||
@ -473,5 +626,15 @@ module.exports = {
|
||||
deliveryGetStoreStatus,
|
||||
deliverySetStoreStatus,
|
||||
deliveryUpdatePrepTime,
|
||||
deliveryUpdateFulfillmentConfig
|
||||
deliveryUpdateFulfillmentConfig,
|
||||
deliveryGetOrderDetails,
|
||||
deliveryListOrders,
|
||||
deliveryAcceptOrder,
|
||||
deliveryDenyOrder,
|
||||
deliveryCancelOrder,
|
||||
deliveryMarkOrderReady,
|
||||
deliveryAdjustOrderPrice,
|
||||
deliveryUpdateReadyTime,
|
||||
deliveryResolveFulfillmentIssues,
|
||||
deliveryGetReplacementRecommendations
|
||||
};
|
||||
|
||||
@ -470,6 +470,125 @@ async function deliveryUpdateFulfillmentConfig({ storeId, payload }) {
|
||||
});
|
||||
}
|
||||
|
||||
async function deliveryGetOrderDetails({ orderId, query }) {
|
||||
const uberPath = interpolatePath(uberEndpoints.deliveryOrder.getById, { orderId });
|
||||
return callUberApi({
|
||||
method: "GET",
|
||||
uberPath,
|
||||
query,
|
||||
wrapperRoute: "/api/v1/uber/delivery-order/orders/:orderId",
|
||||
authMode: "app",
|
||||
scopes: AUTH_SCOPES.ORDER
|
||||
});
|
||||
}
|
||||
|
||||
async function deliveryListOrders({ storeId, query }) {
|
||||
const uberPath = interpolatePath(uberEndpoints.deliveryOrder.listByStore, { storeId });
|
||||
return callUberApi({
|
||||
method: "GET",
|
||||
uberPath,
|
||||
query,
|
||||
wrapperRoute: "/api/v1/uber/delivery-order/stores/:storeId/orders",
|
||||
authMode: "app",
|
||||
scopes: AUTH_SCOPES.ORDER
|
||||
});
|
||||
}
|
||||
|
||||
async function deliveryAcceptOrder({ orderId, payload }) {
|
||||
const uberPath = interpolatePath(uberEndpoints.deliveryOrder.accept, { orderId });
|
||||
return callUberApi({
|
||||
method: "POST",
|
||||
uberPath,
|
||||
body: payload || {},
|
||||
wrapperRoute: "/api/v1/uber/delivery-order/orders/:orderId/accept",
|
||||
authMode: "app",
|
||||
scopes: AUTH_SCOPES.ORDER
|
||||
});
|
||||
}
|
||||
|
||||
async function deliveryDenyOrder({ orderId, payload }) {
|
||||
const uberPath = interpolatePath(uberEndpoints.deliveryOrder.deny, { orderId });
|
||||
return callUberApi({
|
||||
method: "POST",
|
||||
uberPath,
|
||||
body: payload,
|
||||
wrapperRoute: "/api/v1/uber/delivery-order/orders/:orderId/deny",
|
||||
authMode: "app",
|
||||
scopes: AUTH_SCOPES.ORDER
|
||||
});
|
||||
}
|
||||
|
||||
async function deliveryCancelOrder({ orderId, payload }) {
|
||||
const uberPath = interpolatePath(uberEndpoints.deliveryOrder.cancel, { orderId });
|
||||
return callUberApi({
|
||||
method: "POST",
|
||||
uberPath,
|
||||
body: payload,
|
||||
wrapperRoute: "/api/v1/uber/delivery-order/orders/:orderId/cancel",
|
||||
authMode: "app",
|
||||
scopes: AUTH_SCOPES.ORDER
|
||||
});
|
||||
}
|
||||
|
||||
async function deliveryMarkOrderReady({ orderId, payload }) {
|
||||
const uberPath = interpolatePath(uberEndpoints.deliveryOrder.ready, { orderId });
|
||||
return callUberApi({
|
||||
method: "POST",
|
||||
uberPath,
|
||||
body: payload || {},
|
||||
wrapperRoute: "/api/v1/uber/delivery-order/orders/:orderId/ready",
|
||||
authMode: "app",
|
||||
scopes: AUTH_SCOPES.ORDER
|
||||
});
|
||||
}
|
||||
|
||||
async function deliveryAdjustOrderPrice({ orderId, payload }) {
|
||||
const uberPath = interpolatePath(uberEndpoints.deliveryOrder.adjustPrice, { orderId });
|
||||
return callUberApi({
|
||||
method: "POST",
|
||||
uberPath,
|
||||
body: payload,
|
||||
wrapperRoute: "/api/v1/uber/delivery-order/orders/:orderId/adjust-price",
|
||||
authMode: "app",
|
||||
scopes: AUTH_SCOPES.ORDER
|
||||
});
|
||||
}
|
||||
|
||||
async function deliveryUpdateReadyTime({ orderId, payload }) {
|
||||
const uberPath = interpolatePath(uberEndpoints.deliveryOrder.updateReadyTime, { orderId });
|
||||
return callUberApi({
|
||||
method: "POST",
|
||||
uberPath,
|
||||
body: payload,
|
||||
wrapperRoute: "/api/v1/uber/delivery-order/orders/:orderId/update-ready-time",
|
||||
authMode: "app",
|
||||
scopes: AUTH_SCOPES.ORDER
|
||||
});
|
||||
}
|
||||
|
||||
async function deliveryResolveFulfillmentIssues({ orderId, payload }) {
|
||||
const uberPath = interpolatePath(uberEndpoints.deliveryOrder.resolveFulfillmentIssues, { orderId });
|
||||
return callUberApi({
|
||||
method: "POST",
|
||||
uberPath,
|
||||
body: payload,
|
||||
wrapperRoute: "/api/v1/uber/delivery-order/orders/:orderId/resolve-fulfillment-issues",
|
||||
authMode: "app",
|
||||
scopes: AUTH_SCOPES.ORDER
|
||||
});
|
||||
}
|
||||
|
||||
async function deliveryGetReplacementRecommendations({ payload }) {
|
||||
return callUberApi({
|
||||
method: "POST",
|
||||
uberPath: uberEndpoints.deliveryOrder.replacementRecommendations,
|
||||
body: payload,
|
||||
wrapperRoute: "/api/v1/uber/delivery-order/replacement-recommendations",
|
||||
authMode: "app",
|
||||
scopes: AUTH_SCOPES.ORDER
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
genericProxy,
|
||||
menuUpsert,
|
||||
@ -499,5 +618,15 @@ module.exports = {
|
||||
deliveryGetStoreStatus,
|
||||
deliverySetStoreStatus,
|
||||
deliveryUpdatePrepTime,
|
||||
deliveryUpdateFulfillmentConfig
|
||||
deliveryUpdateFulfillmentConfig,
|
||||
deliveryGetOrderDetails,
|
||||
deliveryListOrders,
|
||||
deliveryAcceptOrder,
|
||||
deliveryDenyOrder,
|
||||
deliveryCancelOrder,
|
||||
deliveryMarkOrderReady,
|
||||
deliveryAdjustOrderPrice,
|
||||
deliveryUpdateReadyTime,
|
||||
deliveryResolveFulfillmentIssues,
|
||||
deliveryGetReplacementRecommendations
|
||||
};
|
||||
|
||||
@ -105,7 +105,12 @@ async function handleUberWebhook(req, res) {
|
||||
|
||||
const merchantId = req.query.merchantId || req.body?.merchant_id || null;
|
||||
const eventType = req.body?.event_type || req.body?.type || "unknown";
|
||||
const resourceId = req.body?.resource_id || req.body?.order_id || req.body?.order?.id || null;
|
||||
const resourceId =
|
||||
req.body?.resource_id ||
|
||||
req.body?.meta?.resource_id ||
|
||||
req.body?.order_id ||
|
||||
req.body?.order?.id ||
|
||||
null;
|
||||
const resourceHref = req.body?.resource_href || null;
|
||||
const dedupeKey = buildDedupeKey(verification.signature, req);
|
||||
|
||||
|
||||
@ -481,4 +481,173 @@ router.post(
|
||||
asyncHandler(controller.deliveryUpdateFulfillmentConfig)
|
||||
);
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /api/v1/uber/delivery-order/orders/{orderId}:
|
||||
* get:
|
||||
* summary: Order API 1.0.0 - Get order details
|
||||
* tags:
|
||||
* - Uber Delivery Order v1
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: orderId
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Order details retrieved
|
||||
*/
|
||||
router.get("/uber/delivery-order/orders/:orderId", asyncHandler(controller.deliveryGetOrderDetails));
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /api/v1/uber/delivery-order/stores/{storeId}/orders:
|
||||
* get:
|
||||
* summary: Order API 1.0.0 - List store orders with details
|
||||
* tags:
|
||||
* - Uber Delivery Order v1
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: storeId
|
||||
* required: true
|
||||
* schema:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Orders listed
|
||||
*/
|
||||
router.get(
|
||||
"/uber/delivery-order/stores/:storeId/orders",
|
||||
asyncHandler(controller.deliveryListOrders)
|
||||
);
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /api/v1/uber/delivery-order/orders/{orderId}/accept:
|
||||
* post:
|
||||
* summary: Order API 1.0.0 - Accept order
|
||||
* tags:
|
||||
* - Uber Delivery Order v1
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Order accepted
|
||||
*/
|
||||
router.post(
|
||||
"/uber/delivery-order/orders/:orderId/accept",
|
||||
asyncHandler(controller.deliveryAcceptOrder)
|
||||
);
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /api/v1/uber/delivery-order/orders/{orderId}/deny:
|
||||
* post:
|
||||
* summary: Order API 1.0.0 - Deny order
|
||||
* tags:
|
||||
* - Uber Delivery Order v1
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Order denied
|
||||
*/
|
||||
router.post(
|
||||
"/uber/delivery-order/orders/:orderId/deny",
|
||||
asyncHandler(controller.deliveryDenyOrder)
|
||||
);
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /api/v1/uber/delivery-order/orders/{orderId}/cancel:
|
||||
* post:
|
||||
* summary: Order API 1.0.0 - Cancel order
|
||||
* tags:
|
||||
* - Uber Delivery Order v1
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Order canceled
|
||||
*/
|
||||
router.post(
|
||||
"/uber/delivery-order/orders/:orderId/cancel",
|
||||
asyncHandler(controller.deliveryCancelOrder)
|
||||
);
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /api/v1/uber/delivery-order/orders/{orderId}/ready:
|
||||
* post:
|
||||
* summary: Order API 1.0.0 - Mark order ready
|
||||
* tags:
|
||||
* - Uber Delivery Order v1
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Order ready
|
||||
*/
|
||||
router.post(
|
||||
"/uber/delivery-order/orders/:orderId/ready",
|
||||
asyncHandler(controller.deliveryMarkOrderReady)
|
||||
);
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /api/v1/uber/delivery-order/orders/{orderId}/adjust-price:
|
||||
* post:
|
||||
* summary: Order API 1.0.0 - Adjust order price
|
||||
* tags:
|
||||
* - Uber Delivery Order v1
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Order price adjusted
|
||||
*/
|
||||
router.post(
|
||||
"/uber/delivery-order/orders/:orderId/adjust-price",
|
||||
asyncHandler(controller.deliveryAdjustOrderPrice)
|
||||
);
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /api/v1/uber/delivery-order/orders/{orderId}/update-ready-time:
|
||||
* post:
|
||||
* summary: Order API 1.0.0 - Update order ready time
|
||||
* tags:
|
||||
* - Uber Delivery Order v1
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Order ready time updated
|
||||
*/
|
||||
router.post(
|
||||
"/uber/delivery-order/orders/:orderId/update-ready-time",
|
||||
asyncHandler(controller.deliveryUpdateReadyTime)
|
||||
);
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /api/v1/uber/delivery-order/orders/{orderId}/resolve-fulfillment-issues:
|
||||
* post:
|
||||
* summary: Order API 1.0.0 - Resolve fulfillment issues
|
||||
* tags:
|
||||
* - Uber Delivery Order v1
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Fulfillment issue resolution submitted
|
||||
*/
|
||||
router.post(
|
||||
"/uber/delivery-order/orders/:orderId/resolve-fulfillment-issues",
|
||||
asyncHandler(controller.deliveryResolveFulfillmentIssues)
|
||||
);
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /api/v1/uber/delivery-order/replacement-recommendations:
|
||||
* post:
|
||||
* summary: Order API 1.0.0 - Get replacement recommendations
|
||||
* tags:
|
||||
* - Uber Delivery Order v1
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Replacement recommendations returned
|
||||
*/
|
||||
router.post(
|
||||
"/uber/delivery-order/replacement-recommendations",
|
||||
asyncHandler(controller.deliveryGetReplacementRecommendations)
|
||||
);
|
||||
|
||||
module.exports = router;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user