# Atomenta API - Store Management & External Guide (Pedi Foods Edition) Este documento é o guia definitivo e completo para parceiros e aplicações externas (como pedifoods.com.br) que consomem a API da plataforma Atomenta para gestão de lojas. **Esta API reflete 100% das funcionalidades do painel administrativo.** ## 🔐 Autenticação e Headers Todas as chamadas devem usar autenticação híbrida. ### Headers Obrigatórios: - **Accept**: `application/json` - **Atomenta-Token**: `550e8400-e29b-41d4-a716-446655440008` (Escopo: Store Management) - **Authorization**: `Bearer ` --- ## 🏬 Gestão de Identidade e Lojas ### 1. Listar Lojas do Usuário **GET** `/api/store/list` - Retorna todas as lojas vinculadas ao token do usuário. ### 2. Informações Completas da Loja (Settings) **GET** `/api/store/:storeId/info` - Retorna a configuração completa da loja, incluindo dados bancários, horários e flags de pagamento. **Resposta Exemplo:** ```json { "error": false, "result": { "isOpen": true, "statusLabel": "Aberto Agora", "isManualOpen": true, "fantasyName": "Pizzaria do Arantes", "razaoSocial": "Loverde Co LTDA", "document": "12.345.678/0001-99", "documentType": "CNPJ", "email": "loja@loverde.com.br", "phone": "11988776655", "paymentMethods": { "paymentOnDelivery": true, "acceptCash": true, "acceptCreditVisa": true, "acceptCreditMaster": true, "acceptCreditElo": true, "acceptVoucherAlelo": true, "acceptPix": true }, "bankInfo": { "bankName": "Banco do Brasil", "accountType": "Corrente", "agency": "1234", "account": "56789-0", "pixKey": "12345678000199" }, "openingHours": { "monday": [{"open": "18:00", "close": "23:00"}], "friday": [{"open": "11:00", "close": "15:00"}, {"open": "18:00", "close": "00:00"}] }, "address": { "street": "Av. Brasil, 1000", "latitude": -23.55052, "longitude": -46.633308 } } } ``` --- ## 📊 Dashboard Real-time (Portal Parity) ### 3. Dashboard e Monitoramento **GET** `/api/store/:storeId/dashboard` **Resposta Detalhada (100% Parity):** ```json { "error": false, "result": { "orderFlow": { "urgencys": [ { "id": "ORD-123", "clientName": "Daniel", "minutes": 45, "status": "PREPARING" } ], "received": [ /* Pedidos Pendentes */ ], "inProgress": [ /* Pedidos Em Preparo */ ], "readyTo": [ /* Pedidos Prontos para Entrega/Retirada */ ], "inRoute": [ /* Pedidos em Rota */ ], "delivered": [ /* Pedidos Finalizados na Sessão */ ] }, "payments": { "totalSelledToday": 2450.00, "growthToday": 15.5, "avgTicket": 65.20, "growthPeriod": 8.5 }, "alerts": { "pendingOver3Minutes": 2, "delayedInProgress": 1 } } } ``` --- ## 🍴 Catálogo e Menu (Complex Payloads) ### 3.1 Catálogo Consolidado (App) **GET** `/api/store/:storeId/catalog` **Resposta:** ```json { "error": false, "code": "STORE_CATALOG_RETRIEVED", "result": [ { "id": "cat_lanches", "name": "Lanches", "isPizzaCategory": false, "products": [ { "id": "prod_xburger", "type": "prepared", "name": "X-Burger", "description": "Pão, carne e queijo", "image": "/uploads/products/xburger.jpg", "price": "25.00", "addons": "yes", "addonGroups": [] } ] }, { "id": "cat_pizzas", "name": "Pizzas", "isPizzaCategory": true, "pizzaConfig": { "sizes": [{ "id": "broto", "name": "Broto", "slices": 4, "maxFlavors": 1 }], "doughs": [{ "id": "tradicional", "name": "Tradicional", "active": true }], "crusts": [{ "id": "catupiry", "name": "Catupiry", "active": true, "priceModifier": "10.00" }] }, "products": [ { "id": "prod_pizza_calabresa", "type": "pizza", "name": "Pizza Calabresa", "description": "Molho, mussarela e calabresa", "image": "/uploads/products/pizza-calabresa.jpg", "price": "0.00", "pizzaPrices": { "broto": "35.00", "grande": "55.00" }, "addons": "yes", "addonGroups": [] } ] } ] } ``` **Regra de identificação de pizza:** - Item pizza: `products[].type === "pizza"`. - Configuração de montagem vem em `category.pizzaConfig`. - Preço por tamanho vem em `product.pizzaPrices`. ### 4. Listar Categorias do Catálogo **GET** `/api/store/:storeId/catalog/categories` **Resposta:** ```json { "error": false, "code": "CATEGORIES_RETRIEVED", "result": [ { "id": "cat_1", "name": "Pizzas", "isActive": true } ], "message": "Categorias recuperadas com sucesso" } ``` ### 5. Criar Categoria (Ex.: Pizza) **POST** `/api/store/:storeId/catalog/categories` ```json { "name": "Pizzas Salgadas", "isPizzaCategory": true, "pizzaConfig": { "sizes": [ { "id": "small", "name": "Broto", "slices": 4, "maxFlavors": 1 }, { "id": "large", "name": "Grande", "slices": 8, "maxFlavors": 3 } ], "doughs": [ { "id": "tradicional", "name": "Tradicional", "price": 0 } ], "crusts": [ { "id": "catupiry", "name": "Borda de Catupiry", "price": 10.00 } ] } } ``` ### 6. Atualizar Categoria **PUT** `/api/store/:storeId/catalog/categories/:id` ### 7. Excluir Categoria **DELETE** `/api/store/:storeId/catalog/categories/:id` ### 8. Produtos Tipo Combo ou Pizza **POST** `/api/store/:storeId/catalog/products` ```json { "type": "pizza", "name": "Pizza de Calabresa", "categoryId": "cat_pizzas", "pizzaPrices": { "small": "35.00", "large": "55.00" }, "addonGroups": [ { "name": "Remover Ingredientes", "minSelectors": 0, "maxSelectors": 5, "items": [ { "name": "Sem Cebola", "price": 0 } ] } ] } ``` --- ## 📦 Histórico e Detalhes do Pedido ### 9. Histórico com Filtros **GET** `/api/store/:storeId/orders/history?start=2024-01-01&end=2024-01-31&q=Ana` **Objeto de Pedido Completo (result.list[0]):** ```json { "id": "order_guid", "shortId": "1234", "clientName": "Arantes Loverde", "totalValue": 120.50, "netValue": 112.40, "pediFoodsFee": 1.00, "status": "COMPLETED", "createdAt": "2024-01-08T15:00:00Z", "confirmOtp": "12345678", "deliveryEstimate": "35-50 min", "deliveryTypeLabel": "Entrega Própria", "timeline": [ { "status": "PENDING", "time": "15:00:00", "message": "Pedido criado" }, { "status": "ACCEPTED", "time": "15:02:00", "message": "Chef aceitou o pedido" }, { "status": "COMPLETED", "time": "15:45:00", "message": "Entregue ao cliente" } ], "items": [ { "name": "Pizza Grande", "price": 55.00, "choices": ["Borda Catupiry", "Meio Calabresa", "Meio Mussarela"] } ] } ``` > [!IMPORTANT] > O campo `confirmOtp` (8 dígitos) é o código que o motoboy deve utilizar para iniciar a confirmação. O `customerOtp` (4 dígitos do cliente) **NÃO** é retornado via API por questões de segurança. ``` ### 10. Detalhes de um Pedido Individual **GET** `/api/store/:storeId/orders/:orderId` Retorna o objeto completo do pedido (parity 100% com o painel). --- ## 💰 Financeiro Detalhado ### 11. Resumo de Performance e Gateway **GET** `/api/store/:storeId/financial/summary` **Resposta:** ```json { "error": false, "result": { "performance": { "totalSelledAllTime": 150230.50, "avgTicket": 72.00 }, "gateway": { "balance": { "balance": 1500.00, "available": 1200.00 }, "transfers": [ { "id": "TRANS-1", "date": "2024-01-05", "value": 500.00, "status": "DONE" } ] } } } ``` --- ## ⭐ Reviews e Avaliações ### 12. Métricas e Respostas **GET** `/api/store/:storeId/reviews` **Resposta:** ```json { "error": false, "result": { "metrics": { "averageRate": "4.1", "totalReviews": 260, "starsDistribution": { "5": 121, "4": 60, "3": 23, "2": 15, "1": 39 }, "itemFeedback": { "flavor": 207, "ingredients": 94, "packaging": 35, "temperature": 29, "appearance": 23 }, "improvementFeedback": { "quantity": 128, "temperature": 89, "packaging": 42, "ingredients": 23, "flavor": 13 }, "deliveryFeedback": { "positive": 102, "negative": 60 } }, "list": [ { "id": "rev_1", "clientName": "Ana", "rate": 5, "message": "Melhor pizza!", "reply": "Obrigado Ana!", "repliedAt": "2024-01-08T16:00:00Z" } ] } } ``` ### 9. Contestar Avaliação **POST** `/api/store/:storeId/reviews/:reviewId/dispute` ```json { "reason": "Cliente agressivo e mensagem falsa sobre o produto." } ``` --- ## 🖼️ Upload de Ativos e Galeria **POST** `/api/store/:storeId/assets/upload` (Form-data: `file`) -> Retorna URL absoluta. **GET** `/api/store/:storeId/assets` -> Retorna lista de URLs absolutas de todas as imagens da loja (logo, capa e produtos). --- ## 🏷️ Reordenação de Catálogo **POST** `/api/store/:storeId/catalog/products/reorder` (Payload: `{ "ids": ["p1", "p2", ...] }`) **POST** `/api/store/:storeId/catalog/categories/reorder` (Payload: `{ "ids": ["c1", "c2", ...] }`) --- ## 🛵 Fluxo de Entrega Pedi Foods (2-Step OTP) | Campo | Tipo | Descrição | |---|---|---| | `otp` | String | Legacy OTP (4 digits). | | `confirmOtp` | String | **Novo:** Código de 8 dígitos para o motoboy. (Use este para o fluxo de 2 etapas) | | `deliveryEstimate` | String | Tempo estimado de entrega (ex: "30-45 min"). | | `pediFoodsFee` | Number | Taxa de serviço Pedi Foods calculada. | ## 8. Entrega e Confirmação (Motoboy Flow) Novos endpoints públicos para confirmação de entrega em duas etapas. ### 8.1. Página de Confirmação `GET /delivery/confirm/:orderId` Renderiza a página para o motoboy iniciar o processo. ### 8.2. Validar Motoboy (Passo 1) `POST /delivery/confirm/:orderId/validate-motoboy` Valida o código de 8 dígitos (`confirmOtp`). **Payload:** ```json { "otp": "12345678" } ``` ### 8.3. Validar Cliente (Passo 2) `POST /delivery/confirm/:orderId/validate-customer` Valida o código de 4 dígitos (`customerOtp`) e finaliza o pedido (`COMPLETED`). **Payload:** ```json { "otp": "1234" } ``` Para garantir a segurança da entrega, o sistema utiliza um fluxo de confirmação em duas etapas via página pública: 1. **URL de Confirmação**: `https://atomenta.loverde.com.br/delivery/confirm/:orderRealId` 2. **Passo 1 (Motoboy)**: O entregador insere o `confirmOtp` (8 dígitos) visível no painel da loja. 3. **Passo 2 (Cliente)**: Após validar o código do motoboy, o sistema solicita o `customerOtp` (4 dígitos) que o cliente possui em seu app/notificação. 4. **Finalização**: A validação bem-sucedida do segundo código altera o status do pedido para `COMPLETED` automaticamente. Endpoints utilizados pela página pública (CORS habilitado para domínios parceiros): - `POST /delivery/confirm/:orderId/validate-motoboy` { "otp": "String(8)" } - `POST /delivery/confirm/:orderId/validate-customer` { "otp": "String(4)" } --- ## 🛒 Checkout e Criação de Pedido ### 10.0 Validar Endereço de Entrega (Checkout) **POST** `/api/store/:storeId/delivery/validate-address` Endpoint para validar cobertura de entrega no checkout, quando o cliente troca o endereço antes do pagamento. **Payload:** ```json { "address": { "street": "Avenida das Andorinhas", "number": "477", "neighborhood": "Jardim Andorinhas", "city": "Campinas", "state": "SP", "zip": "13101-400", "lat": -22.918, "lng": -47.01813 } } ``` **Resposta Exemplo:** ```json { "error": false, "code": "DELIVERY_ADDRESS_VALIDATED", "result": { "deliveryAllowed": true, "reasonCode": "DELIVERY_ALLOWED", "reasonMessage": "Endereço válido para entrega", "deliveryMode": "KM", "distance": 1.3, "deliveryFee": 8, "deliveryTime": "35 min", "sameCity": false, "matchedKmRange": { "from": 1, "to": 2, "price": 8, "time": 35 }, "matchedNeighborhood": null } } ``` **Regras:** - `KM`: aprova somente até o limite da última faixa + `0.5 km` de tolerância. - `NEIGHBORHOOD`: reprova se a cidade do endereço for diferente da cidade da loja. - `NEIGHBORHOOD`: aprova com bairro configurado ou com fallback de mesma cidade. ### 10. Criar Novo Pedido (Public) **POST** `/api/public/orders` Este endpoint é utilizado pelo front-end de checkout (Pedi Foods) para criar o pedido no sistema da loja. **Payload:** ```json { "storeId": "store_123456", "userId": "user_guid_or_guest", "clientName": "João Silva", "clientPhone": "11999999999", "deliveryType": "DELIVERY", // ou "PICKUP" "address": { "street": "Rua das Flores", "number": "123", "neighborhood": "Centro", "city": "São Paulo", "state": "SP", "zip": "01000-000", "complement": "Apt 10", "lat": -23.55, "lng": -46.63 }, "paymentMethod": "PIX", // "CREDIT_CARD", "DEBIT_CARD", "CASH" "itemsTotal": 50.00, "deliveryFee": 5.00, "serviceFee": 0.00, "discount": 0.00, "total": 55.00, "items": [ { "id": "prod_1", "name": "Pizza", "price": 50.00, "qty": 1, "image": "...", "addons": [ { "addonId": "addon_bacon", "name": "Bacon", "price": 4.00, "qty": 2 } ] } ] } ``` **Regras para `items[].addons`:** - `qty` do adicional é suportado (ex.: 2x bacon/ovo). - Se `qty` não for enviado ou vier inválido, a API assume `1`. - `qty` mínimo efetivo é `1`. **Resposta Sucesso:** ```json { "error": false, "result": { "id": "order_guid", "shortId": "1234", "status": "PAYMENT_PENDING", "paymentStatus": "PENDING", "paymentMethod": "PIX", "paymentPayload": { "copyPaste": "000201010212...", "qrCodeImage": "iVBORw0KGgoAAAANSUhEUgAA...", "expirationDate": "2026-02-14T22:30:00.000Z" }, "payment": { "method": "PIX", "status": "PENDING", "pix": { "copyPaste": "000201010212...", "qrCodeImage": "iVBORw0KGgoAAAANSUhEUgAA...", "expirationDate": "2026-02-14T22:30:00.000Z" } } } } ``` **Observação de segurança:** o Atomenta não retorna links de checkout/fatura do Asaas no response de criação de pedido. ### 10.1 Realtime de Status de Pedido (Socket.IO) Para atualização em tempo real do checkout/app (sem depender de push), usar Socket.IO. **Socket endpoint:** - `ws://:/socket.io/` (usar `wss://` em produção com TLS) **Auth no handshake:** - Enviar JWT no campo `auth.token`. - Exemplo: `Bearer `. **Evento para o cliente:** - `order_update` **Payload típico do evento:** ```json { "id": "order_guid", "shortId": "1234", "storeId": "store_123456", "userId": "user_guid", "status": "CONFIRMED", "paymentStatus": "CONFIRMED", "updatedAt": "2026-02-15T20:10:00.000Z" } ``` ### 10.2 Fallback de Status (Polling) Caso não haja conexão socket, consultar status por HTTP até estado final. **Endpoint:** - `GET /api/public/orders/:orderId` **Recomendação de intervalo:** - 0-60s: `3s` - 60-180s: `5s` - acima de 180s: `10s` - parar em status final (`COMPLETED`, `CANCELED`, `REFUNDED`). ### 11. Listar Lojas (Public App) **GET** `/api/public/stores` Endpoint utilizado pelo aplicativo Pedi Foods para listar todas as lojas disponíveis para o cliente. **Resposta Sucesso:** ```json { "error": false, "result": [ { "id": "company_guid", "storeId": "store_123456", "name": "Pizzaria do Arantes", "logo": "https://...", "cover": "https://...", "category": "Restaurante", "isOpen": true, "statusLabel": "Aberto Agora", "rating": 4.8, "deliveryTime": "30-45 min", "deliveryFee": 5.00 } ] } ``` --- ## 📝 Gestão de Contratos (Store) ### 12. Verificar Status do Contrato **GET** `/api/store/:storeId/contract/status` Retorna o ID e o status do contrato mais recente da loja. **Resposta Exemplo:** ```json { "error": false, "result": { "id": "contract_guid", "status": "draft" // ou "signed" } } ``` ### 13. Assinar Contrato **POST** `/api/store/:storeId/contract/sign` Assina o contrato draft atual da loja. **Payload:** ```json { "method": "typed", "typedName": "João Silva", // Obrigatório se method === "typed" "signatureDataUrl": "data:image/png;base64,..." // Obrigatório se method === "draw" } ``` ### 14. Visualizar PDF do Contrato **GET** `/api/store/contract/:contractId/pdf` Retorna o stream do arquivo PDF do contrato (draft ou assinado). Autor: Daniel Arantes Loverde