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:
- WHEN a requisição chega com
x-api-keyválida para a chave"internal"THEN o sistema SHALL processar a requisição normalmente - WHEN a requisição chega sem
x-api-keyou com valor inválido THEN o sistema SHALL retornar401 - 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:
- WHEN a requisição é
POST api/cart/{cartId}/itemscom body válido THEN o sistema SHALL processar a adição do item - WHEN o
cartIdnão é informado na rota THEN o sistema SHALL retornar400 - WHEN o body não contém
recommendedItemIdTHEN o sistema SHALL retornar400
Request body:
{
"recommendedItemId": 0
}
Os dados do item (
productId,size,discountType,discountValue) são resolvidos internamente a partir do registro emCartRecommendedItems(ver REQ-03).
quantitynão é enviado — é sempre1.
fullPriceeprice(CURRENT) não são enviados — recuperados do banco (ver REQ-04).
REQ-03 — Buscar Cart e Resolver CartRecommendedItem
Critérios de Aceite:
- WHEN o endpoint é chamado com
cartIdTHEN o sistema SHALL buscar o cart pelocartId - WHEN o cart não é encontrado THEN o sistema SHALL retornar
400com mensagem indicando cart não encontrado - WHEN o cart é encontrado THEN o sistema SHALL buscar o
CartRecommendedItempelorecommendedItemIddo body - WHEN o
CartRecommendedItemnão é encontrado THEN o sistema SHALL retornar400com mensagem"Recomendação não encontrada" - WHEN o
CartRecommendedItemé encontrado mas seuCartIdnão corresponde aocartIdda rota THEN o sistema SHALL retornar400com mensagem"Recomendação inválida para este carrinho" - WHEN o
CartRecommendedItemé encontrado e pertence ao cart, mas seuStoreIddifere doStoreIddo cart THEN o sistema SHALL retornar400com mensagem"Loja da recomendação não coincide com a loja do carrinho" - WHEN todas as validações passam THEN o sistema SHALL extrair
productId,size,discountTypeediscountValuedoCartRecommendedIteme usar oStoreIddo 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:
- WHEN o cart é encontrado THEN o sistema SHALL consultar
ProductsStockStoresusandoproductId + size + StoreId(ondeStoreIdvem do cart) - WHEN o registro não existe em
ProductsStockStoresTHEN o sistema SHALL retornar400indicando produto sem estoque na loja - WHEN o registro existe mas
Quantity < 1THEN o sistema SHALL retornar400indicando produto sem estoque na loja - WHEN o registro existe e
Quantity >= 1THEN o sistema SHALL usarPriceFullcomoFULLePricecomoCURRENTpara construir o item
Reutiliza
GetProductStockAsync(sku, storeId, size)doIProductRepository— mesmo método usado no pathCreateCartAssistantCommand.
A query filtra porproductId + size + storeId—storeIdvem doCartModelcarregado 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:
- WHEN o produto tem estoque THEN o sistema SHALL construir
CartItemsModelcom:CartId=cartIdda rotaProductId=productIdresolvido doCartRecommendedItem(REQ-03)Quantity=1Size=sizeresolvido doCartRecommendedItem(REQ-03)UnitPrice=FINALcalculado a partir deFullPriceediscountValue/discountTypeFullPrice=PriceFulldo banco (REQ-04)Discount=discountTyperesolvido doCartRecommendedItem(REQ-03)DiscountValue=discountValueresolvido doCartRecommendedItem(REQ-03)
- WHEN
UnitPricecalculado resultar em valor<= 0THEN o sistema SHALL retornar400indicando item sem preço válido - WHEN o
CartItemsModelé construído THEN o sistema SHALL chamarCartModel.AddItems([novoItem]) - WHEN os itens são adicionados THEN o sistema SHALL chamar
CartModel.CalculateTotal()
Cálculo de
FINAL: base sempre éFullPrice;discountValue == 0→FINAL = CURRENT;discountType == Percentage→FINAL = FullPrice × (1 − pct/100);discountType == Value→FINAL = FullPrice − discountValue. Idêntico aoCommandToCartModelHandler.
REQ-06 — Persistir e Retornar
Critérios de Aceite:
- WHEN os totais são recalculados THEN o sistema SHALL persistir o cart atualizado no banco
- WHEN a persistência é bem-sucedida THEN o sistema SHALL retornar
200 - WHEN ocorre erro inesperado na persistência THEN o sistema SHALL retornar
500
Resumo de Respostas HTTP
| Código | Situação |
|---|---|
200 | Item adicionado e cart persistido com sucesso |
400 | Cart não encontrado |
400 | Produto não encontrado em ProductsStockStores para productId + size + storeId |
400 | Produto com Quantity < 1 (sem estoque) |
400 | UnitPrice calculado <= 0 (item sem preço válido) |
400 | recommendedItemId ausente no body |
400 | CartRecommendedItem não encontrado para o recommendedItemId |
400 | CartRecommendedItem não pertence ao cartId informado |
400 | StoreId do CartRecommendedItem diverge do StoreId do cart |
401 | ApiKey ausente ou inválida |
500 | Erro inesperado |
Fora de Escopo
- Validação de
CartStatus— garantida pelo checkout service antes da chamada - Suporte a
Quantity > 1— sempre1neste endpoint - Envio de
fullPriceoupricepelo 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ão | Decisão |
|---|---|---|
| Q1 | Quantity no request body? | Sempre 1, não enviado |
| Q2 | fullPrice e price no request body? | Removidos — recuperados do banco |
| Q3 | Qual tabela e quais campos de preço? | ProductsStockStores.PriceFull (FULL) e Price (CURRENT) |
| Q4 | Validar CartStatus? | Não — checkout service garante |
| Q5 | Validação de estoque: existência ou quantidade? | Quantity >= 1 usando productId + size + storeId |
| 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? | PriceFull de ProductStockStores (preço atual, não o FullPrice do CartRecommendedItem) |
| Q9 | Validar StoreId do CartRecommendedItem vs Cart.StoreId? | Sim — devem coincidir |