Payments
This commit is contained in:
@@ -75,7 +75,8 @@ Used when interacting with a specific store (Catalog, Checkout).
|
||||
```
|
||||
|
||||
**Fluxo:**
|
||||
- Passo 1: enviar `email` + `phoneNumber` para receber código por email.<OTP>`.
|
||||
- Passo 1: enviar `email` + `phoneNumber` para receber código por email.
|
||||
- Passo 2: enviar `email` + `phoneNumber` e o OTP no header `Authorization: Bearer <OTP>`.
|
||||
|
||||
**Response:**
|
||||
|
||||
@@ -115,14 +116,16 @@ Used when interacting with a specific store (Catalog, Checkout).
|
||||
"phoneNumber": "+5511999999999",
|
||||
"favorites": ["store_abc...", "store_xyz..."],
|
||||
"address_book": [],
|
||||
"behavioral_stats": {
|
||||
"total_orders": 5,
|
||||
"avg_ticket_size": 45.00
|
||||
}
|
||||
"verified": true,
|
||||
"biometricsEnabled": false,
|
||||
"createdAt": "2026-01-21T22:37:56.472Z",
|
||||
"updatedAt": "2026-02-13T19:00:00.000Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Campos internos não retornados para o app:** `keyHash`, `strongHash`, `source`, `isShadow`, `profile_metrics`, `behavioral_stats`, `otpHash`, `otpHashParams`, `otpExpiresAt`.
|
||||
|
||||
### 4. Update Profile
|
||||
**POST** `/api/customer/:id`
|
||||
|
||||
@@ -137,14 +140,56 @@ Used when interacting with a specific store (Catalog, Checkout).
|
||||
{
|
||||
"name": "Daniel A. Loverde", // Fields to update
|
||||
"phoneNumber": "+5511988888888",
|
||||
"biometricsEnabled": true
|
||||
"biometricsEnabled": true,
|
||||
"address_book": [
|
||||
{
|
||||
"label": "Casa",
|
||||
"type": "residential",
|
||||
"address": "Avenida das Andorinhas",
|
||||
"number": "477",
|
||||
"neighborhood": "Jardim Andorinhas",
|
||||
"city": "Campinas",
|
||||
"state": "SP",
|
||||
"zipCode": "13101-400",
|
||||
"country": "Brasil",
|
||||
"complement": "Apto 78",
|
||||
"lat_long": [-22.9064, -47.0616]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Regra de coordenadas (`address_book.lat_long`):**
|
||||
|
||||
- Se existir `zipCode` válido e houver cache do CEP no Atomenta (`/api/public/cep/:cep`) para o mesmo usuário, a API valida o ponto enviado.
|
||||
- Se não houver cache local, o Atomenta consulta a AwesomeAPI e atualiza o cache antes de validar.
|
||||
- Se `lat_long` estiver ausente/inválido, a API preenche com as coordenadas do CEP cacheado.
|
||||
- Se a distância entre `lat_long` enviado e o ponto do CEP for maior que `1.5 km`, a API substitui `lat_long` pelo ponto do CEP cacheado.
|
||||
- Objetivo: evitar discrepâncias grandes entre endereço e coordenada salva no perfil.
|
||||
|
||||
---
|
||||
|
||||
## 🏠 App Home Screen
|
||||
|
||||
### List Categories
|
||||
**GET** `/api/public/categories`
|
||||
|
||||
**Headers:**
|
||||
- `Accept: application/json`
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"error": false,
|
||||
"result": [
|
||||
{ "id": "all", "name": "Todas", "icon": "🏪" },
|
||||
{ "id": "lanches", "name": "Lanches", "icon": "🍔" },
|
||||
{ "id": "pizza", "name": "Pizza", "icon": "🍕" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### List Stores (Smart Listing)
|
||||
**GET** `/api/app/stores`
|
||||
|
||||
@@ -244,7 +289,7 @@ Lists stores based on user location, filtered by distance.
|
||||
### 2. Full Store Info
|
||||
**GET** `/api/store/:storeId/info`
|
||||
|
||||
Returns detailed info including operating hours and accepted payment methods.
|
||||
Retorna versão pública para app (sem dados administrativos/financeiros da loja).
|
||||
|
||||
**Response:**
|
||||
|
||||
@@ -254,30 +299,29 @@ Returns detailed info including operating hours and accepted payment methods.
|
||||
"code": "STORE_INFO_RETRIEVED",
|
||||
"result": {
|
||||
"isOpen": true,
|
||||
"statusLabel": "Aberto",
|
||||
"fantasyName": "Mc Donalds",
|
||||
"specialty": "Lanches",
|
||||
"minOrder": 15,
|
||||
"deliveryTime": "30-45 min",
|
||||
"logo": "https://...",
|
||||
"cover": "https://...",
|
||||
"paymentMethods": {
|
||||
"paymentOnDelivery": true,
|
||||
"paymentOnPickup": true,
|
||||
"acceptCash": true,
|
||||
"acceptPix": true,
|
||||
"acceptCreditCard": true, // Resumo Geral
|
||||
"acceptDebitCard": true, // Resumo Geral
|
||||
"brands": {
|
||||
"credit": {
|
||||
"visa": true,
|
||||
"master": true,
|
||||
"elo": true,
|
||||
"amex": false,
|
||||
"hipercard": false
|
||||
},
|
||||
"debit": {
|
||||
"visa": true,
|
||||
"master": true,
|
||||
"elo": true
|
||||
}
|
||||
}
|
||||
"acceptPix": true
|
||||
},
|
||||
"openingHours": { ... },
|
||||
"address": { ... }
|
||||
"openingHours": { "...": "..." },
|
||||
"address": {
|
||||
"street": "Rua X",
|
||||
"neighborhood": "Centro",
|
||||
"city": "Campinas",
|
||||
"state": "SP",
|
||||
"zipcode": "13000-000",
|
||||
"latitude": -22.9,
|
||||
"longitude": -47.0
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -285,7 +329,7 @@ Returns detailed info including operating hours and accepted payment methods.
|
||||
### 3. Product Catalog
|
||||
**GET** `/api/store/:storeId/catalog`
|
||||
|
||||
Returns categories with their respective products.
|
||||
Retorna categorias com produtos, incluindo diferenciação explícita de pizza.
|
||||
|
||||
**Response:**
|
||||
|
||||
@@ -294,15 +338,45 @@ Returns categories with their respective products.
|
||||
"error": false,
|
||||
"result": [
|
||||
{
|
||||
"id": "cat_1...",
|
||||
"name": "Burgers",
|
||||
"id": "cat_lanches",
|
||||
"name": "Lanches",
|
||||
"isPizzaCategory": false,
|
||||
"products": [
|
||||
{
|
||||
"id": "prod_1...",
|
||||
"name": "Big Mac",
|
||||
"price": 25.90,
|
||||
"image": "https://...",
|
||||
"addonGroups": [...]
|
||||
"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": []
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -310,7 +384,59 @@ Returns categories with their respective products.
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Create Order (Checkout)
|
||||
**Como identificar pizza no app:**
|
||||
- Produto pizza: `products[].type === "pizza"`.
|
||||
- Configuração de montagem/preço por tamanho: `category.isPizzaCategory === true` + `category.pizzaConfig` + `product.pizzaPrices`.
|
||||
|
||||
### 4. Validate Delivery Address (Checkout)
|
||||
**POST** `/api/store/:storeId/delivery/validate-address`
|
||||
|
||||
Use este endpoint ao trocar endereço na tela de pagamento para validar cobertura antes de finalizar.
|
||||
|
||||
**Body:**
|
||||
|
||||
```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 de validação:**
|
||||
- `KM`: valida distância até o limite da última faixa + tolerância de `0.5 km`.
|
||||
- `NEIGHBORHOOD`: reprova se cidade do endereço for diferente da cidade da loja.
|
||||
- `NEIGHBORHOOD`: aprova se bairro configurado bater, ou se for mesma cidade (fallback).
|
||||
|
||||
### 5. Create Order (Checkout)
|
||||
**POST** `/api/store/:storeId/orders`
|
||||
|
||||
**Body:**
|
||||
@@ -330,9 +456,17 @@ Returns categories with their respective products.
|
||||
"items": [
|
||||
{
|
||||
"productId": "prod_123",
|
||||
"name": "X-Burger",
|
||||
"qty": 2,
|
||||
"price": 25.00,
|
||||
"addons": []
|
||||
"addons": [
|
||||
{
|
||||
"addonId": "addon_egg",
|
||||
"name": "Ovo",
|
||||
"price": 2.50,
|
||||
"qty": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"total": 50.00,
|
||||
@@ -346,6 +480,12 @@ Returns categories with their respective products.
|
||||
}
|
||||
```
|
||||
|
||||
**Regras para `items[].addons`:**
|
||||
|
||||
- `qty` do adicional é aceito no backend (ex.: `2 ovos`).
|
||||
- Se `qty` não for enviado ou vier inválido, a API assume `1`.
|
||||
- `qty` mínimo efetivo é `1`.
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
@@ -354,8 +494,74 @@ Returns categories with their respective products.
|
||||
"code": "ORDER_CREATED",
|
||||
"result": {
|
||||
"id": "ord_987...",
|
||||
"status": "created",
|
||||
"paymentPayload": "https://www.asaas.com/i/..." // Link para pagamento (Pix/Boleto)
|
||||
"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 mais links de checkout/fatura do Asaas no payload de criação do pedido.
|
||||
|
||||
### 6. Realtime de Status do Pedido (Socket.IO)
|
||||
Use este canal para acompanhar mudança de status do pedido em tempo real (sem depender de push notification).
|
||||
|
||||
**Socket Endpoint:**
|
||||
- `ws://<host>:<port>/socket.io/` (em produção, usar `wss://` se houver TLS no balanceador)
|
||||
|
||||
**Auth no handshake:**
|
||||
- Enviar o JWT do customer em `auth.token` (mesmo token do `Authorization: Bearer ...` das APIs HTTP).
|
||||
|
||||
**Exemplo de conexão (conceitual):**
|
||||
|
||||
```json
|
||||
{
|
||||
"auth": {
|
||||
"token": "Bearer <Users_JWT_Token>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Eventos recebidos pelo app:**
|
||||
- `order_update`: enviado quando o pedido muda (pagamento aprovado, aceito pela loja, saiu para entrega, concluído, cancelado).
|
||||
|
||||
**Payload típico de `order_update`:**
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "ord_987...",
|
||||
"shortId": "1234",
|
||||
"storeId": "store_123...",
|
||||
"userId": "cust_uuid...",
|
||||
"status": "CONFIRMED",
|
||||
"paymentStatus": "CONFIRMED",
|
||||
"updatedAt": "2026-02-15T20:10:00.000Z"
|
||||
}
|
||||
```
|
||||
|
||||
### 7. Fallback oficial (Polling)
|
||||
Se o socket cair, o app deve continuar consultando o status até estado final.
|
||||
|
||||
**Endpoint:**
|
||||
- `GET /api/public/orders/:orderId`
|
||||
|
||||
**Sugestão de estratégia:**
|
||||
- primeiros 60s: a cada `3s`
|
||||
- próximos 2min: a cada `5s`
|
||||
- depois: a cada `10s`
|
||||
- parar em status final (`COMPLETED`, `CANCELED`, `REFUNDED`) ou ao sair da tela.
|
||||
|
||||
Reference in New Issue
Block a user