Skip to main content

Requisitos — Cart API: Endpoint para Adicionar Item a Cart Existente

Task: task-194881-cart-api-add-item.md Contexto: CONTEXT Contexto US: US 194871 — MVP Upsell (Pedido) Pesquisa de precos: pesquisa-add-recommended-item.md

Status: refinado
Sessão de grilling: 2026-05-26


Visão Geral

O checkout service precisa persistir um CartRecommendedItem aceito pelo cliente como CartItem no cart existente. A Cart API não possui hoje um endpoint para adicionar item a cart existente — esta feature cria esse endpoint interno.

Papéis

  • Checkout service: caller exclusivo deste endpoint; autenticado via ApiKey "internal"
  • Cart API: persiste o item, valida estoque e recalcula totais

Requisitos

REQ-01 — Autenticação

User Story: Como checkout service, quero chamar a Cart API sem JWT, para que eu possa adicionar itens sem depender de contexto de usuário.

Critérios de Aceite:

  1. WHEN a requisição chega com x-api-key válida para a chave "internal" THEN o sistema SHALL processar a requisição normalmente
  2. WHEN a requisição chega sem x-api-key ou com valor inválido THEN o sistema SHALL retornar 401
  3. IF a requisição contém JWT Bearer THEN o sistema SHALL ignorá-lo — a autenticação é exclusivamente por ApiKey

Padrão de implementação: [AllowAnonymous] + [UseApiKey("internal")] — idêntico ao POST api/cart/assistant na Cart API.


REQ-02 — Contrato do Endpoint

User Story: Como checkout service, quero um endpoint POST api/cart/{cartId}/items, para que eu possa adicionar um item a um cart existente informando apenas o recommendedItemId.

Critérios de Aceite:

  1. WHEN a requisição é POST api/cart/{cartId}/items com body válido THEN o sistema SHALL processar a adição do item
  2. WHEN o cartId não é informado na rota THEN o sistema SHALL retornar 400
  3. WHEN o body não contém recommendedItemId THEN o sistema SHALL retornar 400

Request body:

{
"recommendedItemId": 0
}

Os dados do item (productId, size, discountType, discountValue) são resolvidos internamente a partir do registro em CartRecommendedItems (ver REQ-03).
quantity não é enviado — é sempre 1.
fullPrice e price (CURRENT) não são enviados — recuperados do banco (ver REQ-04).


REQ-03 — Buscar Cart e Resolver CartRecommendedItem

Critérios de Aceite:

  1. WHEN o endpoint é chamado com cartId THEN o sistema SHALL buscar o cart pelo cartId
  2. WHEN o cart não é encontrado THEN o sistema SHALL retornar 400 com mensagem indicando cart não encontrado
  3. WHEN o cart é encontrado THEN o sistema SHALL buscar o CartRecommendedItem pelo recommendedItemId do body
  4. WHEN o CartRecommendedItem não é encontrado THEN o sistema SHALL retornar 400 com mensagem "Recomendação não encontrada"
  5. WHEN o CartRecommendedItem é encontrado mas seu CartId não corresponde ao cartId da rota THEN o sistema SHALL retornar 400 com mensagem "Recomendação inválida para este carrinho"
  6. WHEN o CartRecommendedItem é encontrado e pertence ao cart, mas seu StoreId difere do StoreId do cart THEN o sistema SHALL retornar 400 com mensagem "Loja da recomendação não coincide com a loja do carrinho"
  7. WHEN todas as validações passam THEN o sistema SHALL extrair productId, size, discountType e discountValue do CartRecommendedItem e usar o StoreId do cart para as etapas seguintes

REQ-04 — Validar Estoque e Recuperar Preços

User Story: Como Cart API, quero buscar FullPrice e Price (CURRENT) diretamente de ProductsStockStores, para que o caller não precise enviar dados de preço e a fonte de verdade permaneça no banco.

Critérios de Aceite:

  1. WHEN o cart é encontrado THEN o sistema SHALL consultar ProductsStockStores usando productId + size + StoreId (onde StoreId vem do cart)
  2. WHEN o registro não existe em ProductsStockStores THEN o sistema SHALL retornar 400 indicando produto sem estoque na loja
  3. WHEN o registro existe mas Quantity < 1 THEN o sistema SHALL retornar 400 indicando produto sem estoque na loja
  4. WHEN o registro existe e Quantity >= 1 THEN o sistema SHALL usar PriceFull como FULL e Price como CURRENT para construir o item

Reutiliza GetProductStockAsync(sku, storeId, size) do IProductRepository — mesmo método usado no path CreateCartAssistantCommand.
A query filtra por productId + size + storeIdstoreId vem do CartModel carregado em REQ-03.


REQ-05 — Construir e Adicionar o CartItem

User Story: Como Cart API, quero construir o CartItemsModel com as mesmas regras do fluxo de criação de cart, para que a consistência de dados seja mantida.

Critérios de Aceite:

  1. WHEN o produto tem estoque THEN o sistema SHALL construir CartItemsModel com:
    • CartId = cartId da rota
    • ProductId = productId resolvido do CartRecommendedItem (REQ-03)
    • Quantity = 1
    • Size = size resolvido do CartRecommendedItem (REQ-03)
    • UnitPrice = FINAL calculado a partir de FullPrice e discountValue/discountType
    • FullPrice = PriceFull do banco (REQ-04)
    • Discount = discountType resolvido do CartRecommendedItem (REQ-03)
    • DiscountValue = discountValue resolvido do CartRecommendedItem (REQ-03)
  2. WHEN UnitPrice calculado resultar em valor <= 0 THEN o sistema SHALL retornar 400 indicando item sem preço válido
  3. WHEN o CartItemsModel é construído THEN o sistema SHALL chamar CartModel.AddItems([novoItem])
  4. WHEN os itens são adicionados THEN o sistema SHALL chamar CartModel.CalculateTotal()

Cálculo de FINAL: base sempre é FullPrice; discountValue == 0FINAL = CURRENT; discountType == PercentageFINAL = FullPrice × (1 − pct/100); discountType == ValueFINAL = FullPrice − discountValue. Idêntico ao CommandToCartModelHandler.


REQ-06 — Persistir e Retornar

Critérios de Aceite:

  1. WHEN os totais são recalculados THEN o sistema SHALL persistir o cart atualizado no banco
  2. WHEN a persistência é bem-sucedida THEN o sistema SHALL retornar 200
  3. WHEN ocorre erro inesperado na persistência THEN o sistema SHALL retornar 500

Resumo de Respostas HTTP

CódigoSituação
200Item adicionado e cart persistido com sucesso
400Cart não encontrado
400Produto não encontrado em ProductsStockStores para productId + size + storeId
400Produto com Quantity < 1 (sem estoque)
400UnitPrice calculado <= 0 (item sem preço válido)
400recommendedItemId ausente no body
400CartRecommendedItem não encontrado para o recommendedItemId
400CartRecommendedItem não pertence ao cartId informado
400StoreId do CartRecommendedItem diverge do StoreId do cart
401ApiKey ausente ou inválida
500Erro inesperado

Fora de Escopo

  • Validação de CartStatus — garantida pelo checkout service antes da chamada
  • Suporte a Quantity > 1 — sempre 1 neste endpoint
  • Envio de fullPrice ou price pelo caller — preços sempre do banco
  • Suporte a múltiplos itens por chamada — um item por request
  • Lógica de CRM Bonus, voucher, frete — não aplicável a adição avulsa de item

Questões Fechadas (grilling 2026-05-14)

#QuestãoDecisão
Q1Quantity no request body?Sempre 1, não enviado
Q2fullPrice e price no request body?Removidos — recuperados do banco
Q3Qual tabela e quais campos de preço?ProductsStockStores.PriceFull (FULL) e Price (CURRENT)
Q4Validar CartStatus?Não — checkout service garante
Q5Validação de estoque: existência ou quantidade?Quantity >= 1 usando productId + size + storeId
Q6Body do endpoint?{ "recommendedItemId": 0 } — apenas o ID do registro em CartRecommendedItems
Q7Fonte do productId e size?Resolvidos do CartRecommendedItem pela Cart API (REQ-03)
Q8Fonte do FullPrice?PriceFull de ProductStockStores (preço atual, não o FullPrice do CartRecommendedItem)
Q9Validar StoreId do CartRecommendedItem vs Cart.StoreId?Sim — devem coincidir