Skip to content

🌐 Validação Externa do Carrinho

📋 Visão Geral

O módulo de validação externa do carrinho permite integrar serviços externos ao fluxo de manipulação do carrinho, habilitando regras de negócio personalizadas que vão além das validações nativas da plataforma.

O pipeline opera em duas etapas independentes, cada uma configurada por uma URL de serviço externo via Settings:

EtapaSettingModoDescrição
Etapa 1 — Validação 🛡️cart/external/validation-urlHard-failValida se o produto pode entrar no carrinho. Rejeições bloqueiam a operação.
Etapa 2 — Valores 💰cart/external/values-urlSoft-failInjeta descontos e acréscimos (impostos, taxas) nos itens do carrinho. Falhas são registradas como warning.
Quando a URL do setting está vazia ou ausente, a etapa correspondente é silenciosamente ignorada, sem impacto no fluxo normal do carrinho.

---

🛡️ Etapa 1 — Validação de Entrada de Produtos

⏱️ Quando é chamada

A cada operação de adição (add-product) ou atualização de quantidade (update-product) de um item no carrinho.

🔄 Fluxo

1. O estado atual do carrinho é capturado em um snapshot antes da modificação 2. A quantidade solicitada é aplicada no mapa de produtos em memória 3. O serviço externo é chamado com o payload completo do carrinho 4. ✅ Se aprovado (status: ok): o fluxo continua normalmente 5. ❌ Se rejeitado (HTTP 4xx): o carrinho é restaurado ao estado anterior (rollback) e o erro é propagado

📤 Contrato de Requisição

O serviço externo recebe um POST com o seguinte payload:

1{
2 "domain": "sua-loja.com.br",
3 "action": "add-product",
4 "cart": {
5 "id": "abc123",
6 "requestedProductMap": [
7 {
8 "productId": 50,
9 "quantity": 3,
10 "code": null,
11 "upsellId": null,
12 "productPairOfferId": null
13 }
14 ],
15 "customer": { ... },
16 "addresses": [ ... ],
17 "orders": [
18 {
19 "orderId": 1,
20 "storage": { ... },
21 "items": [
22 {
23 "itemId": 1,
24 "quantity": 3,
25 "unitValue": 70.00,
26 "product": {
27 "productId": 50,
28 "name": "Camiseta Algodão Premium",
29 "sku": "TSHIRT-WHITE-G",
30 "brandName": "Marca X",
31 "active": true,
32 "stock": 100
33 }
34 }
35 ]
36 }
37 ]
38 }
39}

CampoDescrição
domainDomínio do canal de venda
actionAção que disparou a validação: add-product ou update-product
cartObjeto completo do carrinho incluindo cliente, endereços, pedidos e produtos
cart.requestedProductMapMapa de produtos com as quantidades atualizadas (fonte de verdade)
cart.ordersPedidos com itens, armazém e dados completos de produto

✅ Contrato de Resposta — Sucesso

1{
2 "status": "ok"
3}

O serviço deve retornar HTTP 200 com "status": "ok" para aprovar a operação.

❌ Contrato de Resposta — Rejeição de Negócio

1{
2 "code": "QUANTITY_LIMIT_EXCEEDED",
3 "message": "A quantidade máxima permitida por produto é 2."
4}

O serviço deve retornar HTTP 4xx (recomendado 422) com:

CampoObrigatórioDescrição
code✅ SimCódigo identificador do erro (aparece como identifier no array de erros do carrinho)
error \message \errorMessage✅ Sim (ao menos um)Mensagem descritiva do erro (aparece como description no array de erros do carrinho)
O erro propagado no carrinho terá a seguinte estrutura:

1{
2 "error": "UAPPI_STORE_CART_EXTERNAL_VALIDATION_REJECTED",
3 "entity": "cart",
4 "identifier": "QUANTITY_LIMIT_EXCEEDED",
5 "description": "A quantidade máxima permitida por produto é 2.",
6 "type": "error"
7}

⚠️ Erros de Infraestrutura Externa

Falhas de comunicação (timeout, conexão, servidor) são tratadas como hard-fail e também bloqueiam a operação:

Código InternoHTTPCenário
UAPPI_STORE_CART_EXTERNAL_VALIDATION_TIMEOUT504O serviço não respondeu no tempo limite (5s)
UAPPI_STORE_CART_EXTERNAL_VALIDATION_UNAVAILABLE502Erro de conexão ou erro 5xx no serviço
UAPPI_STORE_CART_EXTERNAL_VALIDATION_INVALID_CONTRACT502HTTP 200 mas o corpo não contém "status": "ok"
---

💰 Etapa 2 — Injeção de Valores Personalizados

⏱️ Quando é chamada

Automaticamente durante o refresh de pedidos do carrinho, após a aplicação de promoções e regras de valores customizados. É executada a cada operação que recalcula o carrinho (adição, atualização, cotação de frete, etc).

🔄 Fluxo

1. O cache do payload é invalidado para garantir quantidades atualizadas 2. O serviço externo é chamado com o payload completo do carrinho 3. Os valores retornados (descontos e acréscimos) são aplicados aos itens via DMC promocional 4. ⚠️ Em caso de falha: o erro é registrado como warning — o carrinho continua sem os valores externos

📤 Contrato de Requisição

O serviço externo recebe um POST com o mesmo payload da Etapa 1, porém sem o campo action:

1{
2 "domain": "sua-loja.com.br",
3 "cart": {
4 "id": "abc123",
5 "requestedProductMap": [ ... ],
6 "customer": { ... },
7 "addresses": [ ... ],
8 "orders": [ ... ]
9 }
10}

✅ Contrato de Resposta — Sucesso

1{
2 "status": "ok",
3 "items": [
4 {
5 "orderId": 1,
6 "productId": 50,
7 "additionType": "tax",
8 "addition": {
9 "type": "fixed",
10 "value": 150.00,
11 "label": "Imposto adicional (R$ 50,00/un)"
12 },
13 "discountType": null,
14 "discount": null
15 }
16 ],
17 "errors": []
18}

📊 Campos de items

Cada item no array items pode conter desconto, acréscimo ou ambos:

CampoTipoObrigatórioDescrição
productIdinteger✅ SimID do produto
orderIdinteger❌ NãoID do pedido (quando omitido, aplica ao produto em todos os pedidos)
discountobject❌ NãoDesconto a ser aplicado ao item
discount.typestring✅ SimTipo do cálculo: fixed (valor absoluto) ou percentage (percentual do subtotal)
discount.valuefloat✅ SimValor do desconto. Para fixed: valor em reais. Para percentage: percentual (ex: 10 = 10%)
discountTypestring❌ NãoTipo da entidade de desconto. Deve ser custom-value-rule. Fallback automático se omitido
additionobject❌ NãoAcréscimo a ser aplicado ao item
addition.typestring✅ SimTipo do cálculo: fixed ou percentage
addition.valuefloat✅ SimValor do acréscimo
addition.labelstring❌ NãoDescrição do acréscimo exibida ao usuário
additionTypestring❌ NãoTipo da entidade de acréscimo. Valores aceitos: commission, service-fee, gift-packaging, fee, tax, custom-value-rule

📊 Campos de errors

O array errors permite que o serviço externo propague mensagens de erro ou aviso para o carrinho:

CampoTipoDescrição
errorstringCódigo do erro
entitystringEntidade relacionada (ex: cart, product)
identifierstringIdentificador específico (ex: ID do produto)
descriptionstringMensagem descritiva
typestringerror ou warning

🧮 Regras de Cálculo

  • Descontos são limitados ao subtotal do item — nunca geram valores negativos
  • Acréscimos não possuem limite superior
  • Valores com type: "percentage" são calculados como: subtotal * value / 100
  • Valores com type: "fixed" são aplicados diretamente
  • Itens com value <= 0 são ignorados
  • ---

    📝 Logs de Auditoria

    Todas as chamadas externas (sucesso e falha) são registradas na tabela cart_external_validation_log com:

    CampoDescrição
    cart_idID do carrinho
    trade_policy_idPolítica comercial
    sales_channel_idCanal de venda
    stageEtapa: validation ou values
    urlURL chamada
    http_statusCódigo HTTP retornado
    request_time_msTempo de resposta em milissegundos
    requestPayload enviado (dados sensíveis mascarados)
    responseCorpo da resposta
    error_typeClassificação: connection, server, timeout, business, contract
    Registros com mais de 3 meses são removidos automaticamente.

    🔒 Mascaramento de Dados

    Os seguintes campos são mascarados antes da persistência do log:

  • cart.customer.tradeName e cart.customer.nameCorporateName — nome mascarado
  • cart.customer.email — email mascarado
  • cart.customer.phone — telefone mascarado
  • cart.customer.documents.*.document — documento mascarado
  • cart.addresses.*.recipient — nome do destinatário mascarado
  • ---

    ⚙️ Configuração

    As URLs são configuradas via Settings da loja. Quando vazias, as etapas são ignoradas:

    SettingDescrição
    cart/external/validation-urlURL do serviço de validação de entrada de produtos (Etapa 1)
    cart/external/values-urlURL do serviço de injeção de valores personalizados (Etapa 2)

    📌 Requisitos do Serviço Externo

  • Deve aceitar requisições POST com Content-Type: application/json
  • Deve responder em até 5 segundos (timeout de conexão e resposta)
  • Deve retornar Content-Type: application/json
  • Etapa 1: deve retornar {"status": "ok"} para aprovar ou HTTP 4xx para rejeitar
  • Etapa 2: deve retornar {"status": "ok", "items": [...], "errors": [...]} com os valores a aplicar
  • ---

    📌 Notas e Considerações

  • A Etapa 1 (validação) opera em modo hard-fail: qualquer falha bloqueia a operação e restaura o carrinho
  • A Etapa 2 (valores) opera em modo soft-fail: falhas são registradas como warning sem bloquear o fluxo
  • O requestedProductMap no payload sempre contém as quantidades atualizadas (fonte de verdade), mesmo quando os orders ainda refletem o estado anterior do banco
  • Os valores aplicados pela Etapa 2 são persistidos na criação do pedido como additions do tipo configurado pelo serviço externo
  • Ambas as etapas compartilham a mesma infraestrutura de logging e classificação de erros

📋 Listagem de Logs de Validação Externa do Carrinho

GET /carts/external-validation-logs

Endpoint responsável por listar logs técnicos de validação externa do carrinho

📝 Descrição

Retorna uma listagem paginada dos logs técnicos gerados pelas chamadas de validação externa do carrinho (Etapa 1 — Validação e Etapa 2 — Valores).

O endpoint é voltado ao painel administrativo e permite auditoria operacional por etapa (validation ou values), tipo de erro, status HTTP retornado pelo provedor externo e tempo de resposta.

🔐 Permissão

  • UserAuth — autenticação do usuário administrativo
  • CART_READ — escopo de permissão para leitura de carrinhos

📝 Parâmetros

🏷️ Headers Obrigatórios

HeaderValorDescrição
AuthorizationBearer {token}Token de autenticação de usuário

🔍 Campos de Condição Disponíveis

CampoTipoDescrição
idintegerFiltrar pelo ID do log
cartIdstringFiltrar pelo ID do carrinho
tradePolicyIdintegerFiltrar pela política comercial
salesChannelIdintegerFiltrar pelo canal de venda
stagestringFiltrar pela etapa (validation ou values)
urlstringFiltrar pela URL chamada no provedor externo
errorTypestringFiltrar por tipo de erro (contract, connection, timeout, business, server)
httpStatusintegerFiltrar pelo status HTTP retornado pelo provedor externo
requestTimeMsintegerFiltrar pelo tempo de resposta em milissegundos
requeststringFiltrar pelo payload registrado
responsestringFiltrar pela resposta registrada
createdAtstringFiltrar pela data de criação do log

↕️ Campos de Ordenação Disponíveis

CampoDescrição
idOrdenar pelo ID do log
stageOrdenar pela etapa
errorTypeOrdenar pelo tipo de erro
httpStatusOrdenar pelo status HTTP
requestTimeMsOrdenar pelo tempo de resposta
createdAtOrdenar pela data de criação

💡 Exemplo de Requisição

1curl --request GET \
2 --url '{{MAIN_URL}}/carts/external-validation-logs?limit=20&offset=0&condition[stage]=validation&order[0][field]=createdAt&order[0][direction]=desc' \
3 --header 'Authorization: Bearer SEU_TOKEN_AQUI'

✅ Resposta de Sucesso

HTTP Status: 200 OK

1{
2 "status": true,
3 "data": {
4 "registers": [
5 {
6 "id": 42,
7 "cartId": "ac66ca1d61577b216979169",
8 "tradePolicyId": 1,
9 "salesChannelId": 1,
10 "stage": "validation",
11 "url": "https://validator.exemplo.com/cart/validate",
12 "errorType": "business",
13 "httpStatus": 422,
14 "requestTimeMs": 187,
15 "request": {
16 "domain": "loja.exemplo.com.br",
17 "action": "add-product",
18 "cart": {
19 "id": "ac66ca1d61577b216979169",
20 "requestedProductMap": [
21 {
22 "productId": 50,
23 "quantity": 5
24 }
25 ],
26 "customer": {
27 "nameCorporateName": "J<strong><em> S</strong></em>",
28 "email": "jo<em></em>*@exemplo.com",
29 "phone": "+55<strong></strong><strong></strong>21"
30 }
31 }
32 },
33 "response": {
34 "code": "QUANTITY_LIMIT_EXCEEDED",
35 "message": "A quantidade máxima permitida por produto é 3."
36 },
37 "createdAt": "2026-09-03 14:30:00"
38 },
39 {
40 "id": 41,
41 "cartId": "ac66ca1d61577b216979169",
42 "tradePolicyId": 1,
43 "salesChannelId": 1,
44 "stage": "values",
45 "url": "https://tax.exemplo.com/cart/values",
46 "errorType": "timeout",
47 "httpStatus": null,
48 "requestTimeMs": 5003,
49 "request": {
50 "domain": "loja.exemplo.com.br",
51 "cart": {
52 "id": "ac66ca1d61577b216979169",
53 "requestedProductMap": [
54 {
55 "productId": 50,
56 "quantity": 2
57 }
58 ]
59 }
60 },
61 "response": null,
62 "createdAt": "2026-09-03 14:28:15"
63 }
64 ],
65 "pagination": {
66 "limit": 20,
67 "offset": 0,
68 "pages": 1,
69 "registers": 2
70 }
71 }
72}

📊 Campos da Resposta

CampoDescrição
idID do log
cartIdID do carrinho que gerou o log
tradePolicyIdPolítica comercial
salesChannelIdCanal de venda
stageEtapa da validação: validation (Etapa 1) ou values (Etapa 2)
urlURL do serviço externo chamado
errorTypeTipo do erro: connection, server, timeout, business, contract
httpStatusCódigo HTTP retornado (pode ser null em erros de conexão/timeout)
requestTimeMsTempo de resposta em milissegundos
requestPayload enviado ao serviço externo (dados sensíveis mascarados)
responseCorpo da resposta recebida (pode ser null)
createdAtData e hora do registro

⚠️ Possíveis erros:

Código InternoCódigo HTTPDescrição
Sem código interno específico401Token ausente, inválido ou expirado
Sem código interno específico403Usuário autenticado sem escopo CART_READ

Uappi é a melhor opção para o crescimento da sua loja!

;