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 Design doc checkout: desing-doc.md

Status: refinado
Sessao de grilling: 2026-05-26, 2026-06-17


Visao Geral

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

Papeis

  • Checkout service: caller exclusivo deste endpoint; autenticado via ApiKey "internal"
  • Cart API: persiste o item, recalcula totais e marca a recomendacao como efetivada

Requisitos

REQ-01 — Autenticacao

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

Criteria de Aceite:

  1. WHEN a requisicao chega com x-api-key valida para a chave "internal" THEN o sistema SHALL processar a requisicao normalmente
  2. WHEN a requisicao chega sem x-api-key ou com valor invalido THEN o sistema SHALL retornar 401
  3. IF a requisicao contem JWT Bearer THEN o sistema SHALL ignora-lo — a autenticacao e exclusivamente por ApiKey

Padrao de implementacao: [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.

Criterios de Aceite:

  1. WHEN a requisicao e POST api/cart/{cartId}/items com body valido THEN o sistema SHALL processar a adicao do item
  2. WHEN o cartId nao e informado na rota THEN o sistema SHALL retornar 400
  3. WHEN o body nao contem recommendedItemId THEN o sistema SHALL retornar 400
  4. WHEN recommendedItemId <= 0 THEN o sistema SHALL retornar 400 com mensagem "O campo recommendedItemId deve ser um numero inteiro positivo"

Request body:

{
"recommendedItemId": 0
}

Os dados do item (productId, size, fullPrice, unitPrice, discountType, discountValue, hasEmployeeDiscount) sao resolvidos internamente a partir do registro em CartItemRecommendations (ver REQ-03). quantity e sempre 1. Nenhuma validacao de estoque e feita neste endpoint.


REQ-03 — Buscar Cart e Resolver CartRecommendedItem

Criterios de Aceite:

  1. WHEN o endpoint e chamado com cartId THEN o sistema SHALL buscar o cart pelo cartId usando ICartRepository.GetByIdWithRecommendationsAsync(cartId) (metodo novo que inclui CartItemRecommendations e CartItems)
  2. WHEN o cart nao e encontrado THEN o sistema SHALL retornar 400 com mensagem "Carrinho nao encontrado"
  3. WHEN o cart e encontrado mas SaleEcommerce == true THEN o sistema SHALL retornar 400 com mensagem "Operacao nao disponivel para este tipo de pedido"
  4. WHEN o cart e encontrado THEN o sistema SHALL buscar o CartRecommendedItem pelo recommendedItemId do body na colecao CartItemRecommendations do cart
  5. WHEN o CartRecommendedItem nao e encontrado THEN o sistema SHALL retornar 400 com mensagem "Recomendacao nao encontrada"
  6. WHEN o CartRecommendedItem e encontrado mas seu CartId nao corresponde ao cartId da rota THEN o sistema SHALL retornar 400 com mensagem "Recomendacao invalida para este carrinho"
  7. WHEN o CartRecommendedItem e encontrado mas CartItemId != null (recomendacao ja efetivada) THEN o sistema SHALL retornar 400 com mensagem "Recomendacao ja adicionada ao carrinho"
  8. WHEN o CartRecommendedItem e encontrado e pertence ao cart, THEN o sistema SHALL validar FullPrice > 0 e UnitPrice > 0; se qualquer um for <= 0, SHALL retornar 400 com mensagem "Item com preco invalido"
  9. WHEN todas as validacoes passam THEN o sistema SHALL extrair productId, size, fullPrice, unitPrice, discountType, discountValue e hasEmployeeDiscount do CartRecommendedItem e prosseguir para REQ-04

Nota: CartItemRecommendationModel nao possui StoreId. O StoreId para operacoes internas vem do CartModel.StoreId. A validacao de expiracao da janela de 60 minutos e de responsabilidade exclusiva do checkout service (task 194880).


REQ-04 — Construir, Adicionar e Marcar

User Story: Como Cart API, quero construir o CartItemsModel com os dados da recomendacao e marcar a recomendacao como efetivada, para que a consistencia de dados seja mantida e o checkout possa filtrar recomendacoes ja adicionadas.

Criterios de Aceite:

  1. WHEN todas as validacoes passam THEN o sistema SHALL construir CartItemsModel com:
    • CartId = cartId da rota
    • ProductId = CartRecommendedItem.ProductId
    • Quantity = 1
    • Size = CartRecommendedItem.Size
    • UnitPrice = CartRecommendedItem.UnitPrice
    • FullPrice = CartRecommendedItem.FullPrice
    • Discount = CartRecommendedItem.DiscountType
    • DiscountValue = CartRecommendedItem.DiscountValue
    • HasEmployeeDiscount = CartRecommendedItem.HasEmployeeDiscount
    • FromRecommendation = true
  2. WHEN o CartItemsModel e construido THEN o sistema SHALL chamar CartModel.AddItems([novoItem])
  3. WHEN os itens sao adicionados THEN o sistema SHALL chamar CartModel.CalculateTotal()
  4. WHEN os totais sao recalculados THEN o sistema SHALL atualizar CartRecommendedItem.CartItemId com o Id do CartItemsModel recem-criado
  5. WHEN a atualizacao de CartItemId e feita THEN o sistema SHALL persistir o cart atualizado e a recomendacao atualizada no banco

Nota: CartItemsModel.CalculateTotal() calcula o preco final internamente usando FullPrice, DiscountType e DiscountValue. O construtor de CartItemsModel ja define DiscountOrigin via CalculateDiscountOrigin(FullPrice, UnitPrice, DiscountValue).


REQ-05 — Persistir e Retornar

Criterios de Aceite:

  1. WHEN a persistencia e bem-sucedida THEN o sistema SHALL retornar 200 sem corpo
  2. WHEN ocorre erro inesperado na persistencia THEN o sistema SHALL retornar 500 com mensagem "Erro interno ao processar a requisicao"

Resumo de Respostas HTTP

CodigoSituacao
200Item adicionado, cart recalculado e recomendacao marcada como efetivada
400Cart nao encontrado
400Cart com SaleEcommerce == true
400recommendedItemId ausente, invalido ou <= 0 no body
400CartRecommendedItem nao encontrado para o recommendedItemId
400CartRecommendedItem nao pertence ao cartId informado
400CartRecommendedItem ja efetivado (CartItemId != null)
400FullPrice <= 0 ou UnitPrice <= 0 (item com preco invalido)
401ApiKey ausente ou invalida
500Erro interno

Fora de Escopo

  • Validacao de CartStatus — garantida pelo checkout service antes da chamada
  • Validacao de estoque (ProductsStockStores) — criterio: nao se valida estoque na adicao de item recomendado, mesmo criterio da geracao do link
  • Validacao de janela de expiracao (60min) — responsabilidade exclusiva do checkout service (task 194880)
  • Suporte a Quantity > 1 — sempre 1 neste endpoint
  • Envio de fullPrice ou price pelo caller — precos resolvidos internamente do CartItemRecommendationModel
  • Suporte a multiplos itens por chamada — um item por request
  • Logica de CRM Bonus, voucher, frete — nao aplicavel a adicao avulsa de item
  • Lock distribuido — protecao de concorrencia e responsabilidade do checkout service (task 194880)
  • Response body — o endpoint retorna apenas 200 sem corpo; o checkout service obtem os dados atualizados separadamente

Questoes Fechadas

Grilling 2026-05-14

#QuestaoDecisao
Q1Quantity no request body?Sempre 1, nao enviado
Q2fullPrice e price no request body?Removidos — resolvidos internamente do CartItemRecommendationModel
Q3Qual tabela e quais campos de preco?CartItemRecommendationModel: FullPrice, UnitPrice, DiscountType, DiscountValue
Q4Validar CartStatus?Nao — checkout service garante
Q5Validacao de estoque?Sem validacao de estoque — mesmo criterio da geracao do link
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?CartItemRecommendationModel.FullPrice — preco que o cliente viu na tela de recomendacao
Q9Validar StoreId do CartRecommendedItem vs Cart.StoreId?Removido — CartItemRecommendationModel nao possui StoreId

Grilling 2026-06-17

#QuestaoDecisao
Q10Validacao CartItemId != null na Cart API?Sim — retornar 400 "Recomendacao ja adicionada ao carrinho"
Q11Marcar FromRecommendation = true?Sim no CartItemsModel
Q12Lock distribuido na Cart API?Nao — protecao e responsabilidade do checkout service
Q13Validacao de expiracao (60min)?Nao — responsabilidade exclusiva do checkout service
Q14Validacao SaleEcommerce == true?Sim — Cart API rejeita com 400
Q15Validacao de CartStatus?Nao
Q16Response do endpoint?200 sem corpo
Q17Validar FullPrice > 0 e UnitPrice > 0?Sim — 400 "Item com preco invalido"
Q18Mapear HasEmployeeDiscount?Sim — do CartRecommendedItem.HasEmployeeDiscount
Q19Atualizar CartItemId na recomendacao?Sim — Cart API seta CartRecommendedItem.CartItemId = novoCartItem.Id
Q20Metodo para carregar recommendations?Novo metodo GetByIdWithRecommendationsAsync(int cartId) em ICartRepository

Dependencias

DependenciaStatus
ICartRepository.GetByIdWithRecommendationsAsync (novo metodo)A criar
CartItemRecommendationModel.CartItemId (FK para CartItems)Disponivel (migration 20260615185608)
CartItemsModel.FromRecommendation (coluna booleana)Disponivel (migration 20260602152257)