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:
- WHEN a requisicao chega com
x-api-keyvalida para a chave"internal"THEN o sistema SHALL processar a requisicao normalmente - WHEN a requisicao chega sem
x-api-keyou com valor invalido THEN o sistema SHALL retornar401 - 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:
- WHEN a requisicao e
POST api/cart/{cartId}/itemscom body valido THEN o sistema SHALL processar a adicao do item - WHEN o
cartIdnao e informado na rota THEN o sistema SHALL retornar400 - WHEN o body nao contem
recommendedItemIdTHEN o sistema SHALL retornar400 - WHEN
recommendedItemId <= 0THEN o sistema SHALL retornar400com 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 emCartItemRecommendations(ver REQ-03).quantitye sempre1. Nenhuma validacao de estoque e feita neste endpoint.
REQ-03 — Buscar Cart e Resolver CartRecommendedItem
Criterios de Aceite:
- WHEN o endpoint e chamado com
cartIdTHEN o sistema SHALL buscar o cart pelocartIdusandoICartRepository.GetByIdWithRecommendationsAsync(cartId)(metodo novo que incluiCartItemRecommendationseCartItems) - WHEN o cart nao e encontrado THEN o sistema SHALL retornar
400com mensagem"Carrinho nao encontrado" - WHEN o cart e encontrado mas
SaleEcommerce == trueTHEN o sistema SHALL retornar400com mensagem"Operacao nao disponivel para este tipo de pedido" - WHEN o cart e encontrado THEN o sistema SHALL buscar o
CartRecommendedItempelorecommendedItemIddo body na colecaoCartItemRecommendationsdo cart - WHEN o
CartRecommendedItemnao e encontrado THEN o sistema SHALL retornar400com mensagem"Recomendacao nao encontrada" - WHEN o
CartRecommendedIteme encontrado mas seuCartIdnao corresponde aocartIdda rota THEN o sistema SHALL retornar400com mensagem"Recomendacao invalida para este carrinho" - WHEN o
CartRecommendedIteme encontrado masCartItemId != null(recomendacao ja efetivada) THEN o sistema SHALL retornar400com mensagem"Recomendacao ja adicionada ao carrinho" - WHEN o
CartRecommendedIteme encontrado e pertence ao cart, THEN o sistema SHALL validarFullPrice > 0eUnitPrice > 0; se qualquer um for<= 0, SHALL retornar400com mensagem"Item com preco invalido" - WHEN todas as validacoes passam THEN o sistema SHALL extrair
productId,size,fullPrice,unitPrice,discountType,discountValueehasEmployeeDiscountdoCartRecommendedIteme prosseguir para REQ-04
Nota:
CartItemRecommendationModelnao possuiStoreId. OStoreIdpara operacoes internas vem doCartModel.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:
- WHEN todas as validacoes passam THEN o sistema SHALL construir
CartItemsModelcom:CartId=cartIdda rotaProductId=CartRecommendedItem.ProductIdQuantity=1Size=CartRecommendedItem.SizeUnitPrice=CartRecommendedItem.UnitPriceFullPrice=CartRecommendedItem.FullPriceDiscount=CartRecommendedItem.DiscountTypeDiscountValue=CartRecommendedItem.DiscountValueHasEmployeeDiscount=CartRecommendedItem.HasEmployeeDiscountFromRecommendation=true
- WHEN o
CartItemsModele construido THEN o sistema SHALL chamarCartModel.AddItems([novoItem]) - WHEN os itens sao adicionados THEN o sistema SHALL chamar
CartModel.CalculateTotal() - WHEN os totais sao recalculados THEN o sistema SHALL atualizar
CartRecommendedItem.CartItemIdcom oIddoCartItemsModelrecem-criado - WHEN a atualizacao de
CartItemIde feita THEN o sistema SHALL persistir o cart atualizado e a recomendacao atualizada no banco
Nota:
CartItemsModel.CalculateTotal()calcula o preco final internamente usandoFullPrice,DiscountTypeeDiscountValue. O construtor deCartItemsModelja defineDiscountOriginviaCalculateDiscountOrigin(FullPrice, UnitPrice, DiscountValue).
REQ-05 — Persistir e Retornar
Criterios de Aceite:
- WHEN a persistencia e bem-sucedida THEN o sistema SHALL retornar
200sem corpo - WHEN ocorre erro inesperado na persistencia THEN o sistema SHALL retornar
500com mensagem"Erro interno ao processar a requisicao"
Resumo de Respostas HTTP
| Codigo | Situacao |
|---|---|
200 | Item adicionado, cart recalculado e recomendacao marcada como efetivada |
400 | Cart nao encontrado |
400 | Cart com SaleEcommerce == true |
400 | recommendedItemId ausente, invalido ou <= 0 no body |
400 | CartRecommendedItem nao encontrado para o recommendedItemId |
400 | CartRecommendedItem nao pertence ao cartId informado |
400 | CartRecommendedItem ja efetivado (CartItemId != null) |
400 | FullPrice <= 0 ou UnitPrice <= 0 (item com preco invalido) |
401 | ApiKey ausente ou invalida |
500 | Erro 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— sempre1neste endpoint - Envio de
fullPriceoupricepelo caller — precos resolvidos internamente doCartItemRecommendationModel - 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
200sem corpo; o checkout service obtem os dados atualizados separadamente
Questoes Fechadas
Grilling 2026-05-14
| # | Questao | Decisao |
|---|---|---|
| Q1 | Quantity no request body? | Sempre 1, nao enviado |
| Q2 | fullPrice e price no request body? | Removidos — resolvidos internamente do CartItemRecommendationModel |
| Q3 | Qual tabela e quais campos de preco? | CartItemRecommendationModel: FullPrice, UnitPrice, DiscountType, DiscountValue |
| Q4 | Validar CartStatus? | Nao — checkout service garante |
| Q5 | Validacao de estoque? | Sem validacao de estoque — mesmo criterio da geracao do link |
| Q6 | Body do endpoint? | { "recommendedItemId": 0 } — apenas o ID do registro em CartRecommendedItems |
| Q7 | Fonte do productId e size? | Resolvidos do CartRecommendedItem pela Cart API (REQ-03) |
| Q8 | Fonte do FullPrice? | CartItemRecommendationModel.FullPrice — preco que o cliente viu na tela de recomendacao |
| Q9 | Validar StoreId do CartRecommendedItem vs Cart.StoreId? | Removido — CartItemRecommendationModel nao possui StoreId |
Grilling 2026-06-17
| # | Questao | Decisao |
|---|---|---|
| Q10 | Validacao CartItemId != null na Cart API? | Sim — retornar 400 "Recomendacao ja adicionada ao carrinho" |
| Q11 | Marcar FromRecommendation = true? | Sim no CartItemsModel |
| Q12 | Lock distribuido na Cart API? | Nao — protecao e responsabilidade do checkout service |
| Q13 | Validacao de expiracao (60min)? | Nao — responsabilidade exclusiva do checkout service |
| Q14 | Validacao SaleEcommerce == true? | Sim — Cart API rejeita com 400 |
| Q15 | Validacao de CartStatus? | Nao |
| Q16 | Response do endpoint? | 200 sem corpo |
| Q17 | Validar FullPrice > 0 e UnitPrice > 0? | Sim — 400 "Item com preco invalido" |
| Q18 | Mapear HasEmployeeDiscount? | Sim — do CartRecommendedItem.HasEmployeeDiscount |
| Q19 | Atualizar CartItemId na recomendacao? | Sim — Cart API seta CartRecommendedItem.CartItemId = novoCartItem.Id |
| Q20 | Metodo para carregar recommendations? | Novo metodo GetByIdWithRecommendationsAsync(int cartId) em ICartRepository |
Dependencias
| Dependencia | Status |
|---|---|
ICartRepository.GetByIdWithRecommendationsAsync (novo metodo) | A criar |
CartItemRecommendationModel.CartItemId (FK para CartItems) | Disponivel (migration 20260615185608) |
CartItemsModel.FromRecommendation (coluna booleana) | Disponivel (migration 20260602152257) |