Pular para o conteúdo principal

Pedidos (Orders)

Endpoints para criar, pagar e acompanhar pedidos. Integração de pagamento via Asaas (Pix, cartão e boleto).

Conceitos

  • Login obrigatório — pedidos só podem ser criados por usuários autenticados. O userId é vinculado automaticamente.
  • Multi-seller — um pedido pode conter itens de múltiplos sellers. Cada item rastreia o sellerProfileId para cálculo de comissão.
  • Snapshot de preços — todos os valores (preço, custo base, taxas, comissão) são congelados no momento da criação. Alterações futuras no catálogo não afetam pedidos existentes.
  • Ownership — todos os endpoints verificam que o pedido pertence ao usuário autenticado (ou admin).
  • Pagamento — integração com Asaas. Suporta Pix, cartão e boleto. Múltiplas tentativas por pedido. Suporta reembolso total/parcial e cancelamento com estorno automático. Exige CPF/CNPJ do comprador (ver Atualizar Perfil).
  • Fulfillment por item — cada item tem seu próprio fulfillmentStatus e trackingCode, pois itens podem ser produzidos/enviados por fabricantes diferentes. O status da Order é derivado dos itens.

Status do Pedido

CampoNívelValores
paymentStatusOrderpending, approved, rejected, refunded, cancelled
fulfillmentStatusItempending, in_production, shipped, delivered, returned
fulfillmentStatusOrderDerivado dos itens (automático)

:::info Derivação do fulfillment da Order

  • Todos delivered → Order delivered
  • Todos shipped ou delivered → Order shipped
  • Algum returned → Order returned
  • Algum in_production → Order in_production
  • Caso contrário → Order pending :::

Transições de paymentStatus

paymentStatus atualAções permitidasEndpoint
pendingRetry, cancelar, simular (dev)POST /pay, POST /cancel, POST /dev-pay
approvedReembolsar (total/parcial), cancelar (com refund automático)POST /refund, POST /cancel
rejectedRetry pagamento, cancelarPOST /pay, POST /cancel
refundedNenhuma (terminal)
cancelledNenhuma (terminal)

:::warning Estados terminais refunded e cancelled não permitem nenhuma ação. Tentativas retornam erro 400. :::


Fluxo Completo de Compra


1. Criar Pedido + Iniciar Pagamento (Checkout)

POST /orders

Requer autenticação. Em uma única chamada: cria o pedido, faz snapshot de preços, cria customer + payment no Asaas, e retorna initPoint pronto para redirect.

{
"fullName": "João Silva",
"phone": "+5511999999999",
"shipping": {
"cep": "01310100",
"street": "Av Paulista",
"number": "1000",
"complement": "Apto 42",
"neighborhood": "Bela Vista",
"city": "São Paulo",
"state": "SP",
"country": "BR"
},
"items": [
{
"sellerProductId": "uuid-do-seller-product",
"variantId": "uuid-do-seller-product-variant",
"quantity": 2,
"selectedOptions": { "color": "black" }
}
],
"shippingCents": 1590,
"discountCents": 0,
"shippingMethodId": "sedex",
"carrier": "Correios",
"serviceCode": "04014"
}
CampoRegra
emailNão enviado no body — usa automaticamente o email do usuário autenticado
fullNameOpcional, max 200 chars. Se omitido, usa user.name. Útil para entrega em nome de outra pessoa
phoneOpcional, formato E.164 (+5511999999999). Se omitido, usa user.whatsapp
shippingObrigatório (endereço de entrega)
shipping.cep8-10 chars
shipping.state2 chars (UF)
itemsMínimo 1 item
items[].quantity1 a 100
items[].selectedOptionsObrigatório quando a variant tem allowedOptions

:::danger Exige CPF/CNPJ do usuário Se o usuário não tem documentNumber salvo, este endpoint retorna erro. Colete e envie via PATCH /users/me antes do checkout. Ver Atualizar Perfil. :::

Response (201): OrderResponse completo com initPoint preenchido.

Frontend:

const order = await api.createOrder(checkoutData);
window.location.href = order.initPoint;

2. Retry de Pagamento

POST /orders/{orderId}/pay

Cria novo payment no Asaas para um pedido existente. Use quando o initPoint anterior expirou ou ficou inválido.

Pré-condição: paymentStatus deve ser pending ou rejected.

:::info Na maioria dos casos, não é necessário O mesmo initPoint permite múltiplas tentativas no Asaas (cartão rejeitado → tentar outro). Só chame /pay quando o link em si parar de funcionar. :::

Response
{
"orderId": "uuid",
"initPoint": "https://www.asaas.com/i/pay_xxxx",
"sandboxInitPoint": null,
"preferenceId": "202809963-920c288b-..."
}

3. Simular Pagamento (Dev Only)

POST /orders/{orderId}/dev-pay

Disponível apenas em ENVIRONMENT=local/development/dev. Simula pagamento aprovado sem chamar o Asaas — útil quando não há tunnel configurado.

Pré-condição: paymentStatus em pending ou rejected.

Response: OrderResponse com paymentStatus: "approved".


4. Consultar Pedido

GET /orders/{orderId}

Requer autenticação (dono do pedido ou admin).

:::tip Fluxo após redirect do Asaas Depois que o Asaas redireciona para /order/success, o frontend deve consultar este endpoint para confirmar o status real do pagamento. O webhook é a fonte de verdade, não os query params do redirect. :::

Para pedidos em pending/rejected, o initPoint vem preenchido (reconstruído a partir do providerPreferenceId salvo). Para pedidos pagos/cancelados, initPoint é null.


5. Listar Pedidos

Meus pedidos

GET /orders/me?skip=0&limit=100

Filtros opcionais: payment_status, fulfillment_status, search, created_at, created_from, created_to.

Todos (admin)

GET /orders?skip=0&limit=100

Filtros adicionais: user_id, email.

Pedidos do seller

GET /orders/seller/me

Pedidos completos que contêm itens do seller.

GET /orders/seller/items?skip=0&limit=100

Apenas os itens do seller (mais leve, ideal para dashboard do seller).


6. Atualizar Fulfillment de Item (Admin)

PATCH /orders/items/{itemId}/fulfillment
{
"fulfillmentStatus": "shipped",
"trackingCode": "BR123456789"
}

Valores: pending, in_production, shipped, delivered, returned.

O fulfillmentStatus da Order pai é recalculado automaticamente.

Exemplo de fluxo com 2 itens

1. Pedido criado → Item A: pending, Item B: pending → Order: pending
2. PATCH A → A: in_production → Order: in_production
3. PATCH A (tracking) → A: shipped, B: pending → Order: in_production
4. PATCH B → A: shipped, B: shipped → Order: shipped
5. PATCH A + B → ambos delivered → Order: delivered

7. Reembolsar Pedido (Admin)

POST /orders/{orderId}/refund

Reembolso total ou parcial via API do Asaas. Suporta múltiplos reembolsos parciais desde que a soma não ultrapasse o total original.

Pré-condição: paymentStatus == "approved" com providerPaymentId real (não simulado).

{ "amountCents": 5600 }
CampoObrigatórioDescrição
amountCentsNãoValor a reembolsar (centavos). Se omitido/null → reembolso total

Comportamento

TipoBodypaymentStatus apósQuando usar
TotalSem body ou amountCents: nullrefundedCancelar pedido inteiro
ParcialamountCents: 2800Mantém approvedDevolver item específico
Parcial (último)Soma ≥ totalrefundedÚltimo refund atinge o total
Exemplo — reembolso de itens individuais
Pedido: R$ 71,90 (produtos: R$ 56,00 + frete: R$ 15,90)

1. Reembolsar item A (R$ 28,00):
POST /orders/{id}/refund { "amountCents": 2800 }
→ status: "partial_refund", totalRefundedCents: 2800
→ paymentStatus continua "approved"

2. Reembolsar item B (R$ 28,00):
POST /orders/{id}/refund { "amountCents": 2800 }
→ totalRefundedCents: 5600 (continua approved)

3. Reembolsar frete (R$ 15,90) — total atingido:
POST /orders/{id}/refund { "amountCents": 1590 }
→ status: "refunded", totalRefundedCents: 7190
→ paymentStatus → "refunded"

Response:

{
"orderId": "uuid",
"refundId": "1009042015",
"amountCents": 2800,
"totalRefundedCents": 2800,
"status": "partial_refund"
}

8. Cancelar Pedido (Admin)

POST /orders/{orderId}/cancel

Cancela o pedido. Se já foi pago, emite refund total no Asaas automaticamente.

Pré-condição: paymentStatus não pode ser cancelled nem refunded.

Estado atualAção no AsaasStatus final
approvedRefund total automáticorefunded
pending (com payment no Asaas)Cancel no Asaascancelled
pending/rejected (sem Asaas)Nenhumacancelled

:::tip Cancelamento com reembolso parcial Para descontar frete ou reter parte do valor: chame /refund com amountCents primeiro, depois /cancel. :::


Fórmula de Ganhos do Artista

Calculada no momento do pedido (snapshot):

platform_fee = price × platformFeePercent / 100
profit = price − baseCost − platform_fee
artist_earnings = profit × artistRoyaltyPercent / 100

Os percentuais (platformFeePercent, artistRoyaltyPercent) vêm do ProductType da variante e são configuráveis por produto.


Schemas de Resposta

OrderResponse

JSON completo
{
"id": "uuid",
"orderNumber": 1001,
"email": "cliente@email.com",
"fullName": "João Silva",
"phone": "+5511999999999",
"userId": "uuid",
"shippingCep": "01310100",
"shippingStreet": "Av Paulista",
"shippingNumber": "1000",
"shippingComplement": "Apto 42",
"shippingNeighborhood": "Bela Vista",
"shippingCity": "São Paulo",
"shippingState": "SP",
"shippingCountry": "BR",
"paymentStatus": "pending",
"fulfillmentStatus": "pending",
"subtotalCents": 9980,
"shippingCents": 1590,
"discountCents": 0,
"totalCents": 11570,
"items": [
{
"id": "uuid",
"sellerProductId": "uuid",
"productVariantId": "uuid",
"sellerProfileId": "uuid",
"artworkId": "uuid",
"quantity": 2,
"unitPriceCents": 4990,
"lineTotalCents": 9980,
"title": "Caneca Arte Tropical",
"imageUrl": "https://cdn.../render.webp",
"optionLabel": "350ml · Brilhante · Preto",
"variantAssets": { "size": "350ml", "finish": "glossy" },
"selectedOptions": { "color": "black" },
"sellerName": "Arte do João",
"sellerSlug": "arte-do-joao",
"productTypeName": "Caneca Cerâmica",
"productionDays": 3,
"packagingDays": 1,
"fulfillmentStatus": "pending",
"trackingCode": null,
"shippedAt": null,
"deliveredAt": null
}
],
"payment": {
"id": "uuid",
"paymentProvider": "asaas",
"providerPreferenceId": "202809963-920c288b-...",
"providerPaymentId": null,
"status": "pending",
"amountCents": 11570,
"paidAt": null,
"createdAt": "2026-04-02T10:00:00Z"
},
"shipment": null,
"initPoint": "https://sandbox.asaas.com/i/pay_xxxx",
"sandboxInitPoint": null,
"createdAt": "2026-04-02T10:00:00Z",
"updatedAt": "2026-04-02T10:00:00Z",
"paidAt": null,
"shippedAt": null,
"deliveredAt": null
}

:::info initPoint e sandboxInitPoint Preenchidos apenas no POST /orders (criação). Em consultas posteriores (GET /orders/{id}) vêm null — o frontend não precisa deles depois do redirect inicial. :::

OrderListResponse

{
"orders": ["...OrderResponse[]"],
"total": 42,
"skip": 0,
"limit": 100
}

SellerOrderItemResponse

Retornado em GET /orders/seller/items. Mais leve — só o necessário para o dashboard do seller.

{
"id": "uuid",
"orderId": "uuid",
"orderNumber": 1001,
"quantity": 2,
"unitPriceCents": 4990,
"lineTotalCents": 9980,
"artistEarningsCents": 1247,
"title": "Caneca Arte Tropical",
"imageUrl": "https://cdn.../render.webp",
"paymentStatus": "approved",
"fulfillmentStatus": "pending",
"orderCreatedAt": "2026-04-02T10:00:00Z"
}