SelectwinDOCS
Checkouts

Sessões de checkout

Uma Checkout Session é um carrinho de compra hospedado pela Selectwin: você cria a sessão pela API, recebe uma URL de checkout pronta e a entrega ao comprador (redirect, e-mail, SMS ou QR Code). O com

Uma Checkout Session é um carrinho de compra hospedado pela Selectwin: você cria a sessão pela API, recebe uma URL de checkout pronta e a entrega ao comprador (redirect, e-mail, SMS ou QR Code). O comprador preenche os dados e paga na página hospedada, e você confirma o resultado por webhook — sem construir um checkout próprio nem tocar em dados de cartão.

POST · GET · PUT · DELETE /v1/checkouts/sessions

Diferente de um Payment Link (uma URL reutilizável, sem comprador fixo), a session representa uma intenção de compra específica: itens já precificados pelo backend, cliente/endereço opcionais e um externalTransactionId para reconciliar com o seu ERP.

Como funciona

Quando você cria uma sessão, o servidor monta um carrinho hospedado e devolve uma url pronta. O fluxo é:

  1. Você cria a sessão com os lineItems (o que vender) e, opcionalmente, cliente, cupons, métodos de pagamento e URLs de redirect.
  2. O backend resolve o preço de cada item server-side (nunca confia no preço enviado pelo cliente — veja Modelo de precificação) e recalcula os totais.
  3. A API devolve o objeto checkout.session com um id público (ses_…) e a url hospedada.
  4. Você entrega a url ao comprador (redirect, e-mail, SMS, QR Code).
  5. O comprador paga na página da Selectwin; a sessão gera uma transação (cobrança).
  6. Você reage ao webhook checkout.session.completed para liberar o pedido — não faça polling.
Checkout SessionPayment Link
NaturezaUma intenção de compra específicaUma URL reutilizável
CompradorConhecido/esperado (snapshot na sessão)Qualquer um que abrir o link
ItenslineItems[] precificados na criaçãoitems[] (variantes) fixos no link
ReconciliaçãoexternalTransactionId (1:1 com o seu pedido)Pelo id do link / transação gerada
Quando usarCarrinho montado na hora para um pedido"Um link, vários compradores"

Modelo de precificação (autoritativo no backend)

O preço nunca é confiado cegamente ao cliente da API. Cada item de lineItems[] é resolvido no servidor de duas formas:

  • Catálogo — informe o id de uma variante (var_…). O backend lê o preço da variante. Envie apenas id + quantity; opcionalmente sobrescreva com unitPrice (em centavos).
  • Ad-hoc — sem variante de catálogo: informe name e unitPrice (centavos). Útil para serviços avulsos. Item sem preço → checkoutSessionItemPriceRequired; id que não resolve → checkoutSessionItemInvalid.

O carrinho é agnóstico ao tipo de pagamento: pode misturar itens oneTime e variantes recurring. Quando há item recorrente, a sessão vira assinatura no momento da cobrança, lendo billingType/cycles/trialInterval da própria variante — você não envia esses campos.

Valores em centavos

unitPrice e totals.* são inteiros em centavos (49700 = R$ 497,00). totals.amount já vem líquido do desconto. Veja Convenções.

Quando usar (e quando não usar)

Use Checkout Sessions quando você monta um carrinho específico para um pedido (no seu site, app ou ERP) e quer cobrar numa página hospedada, sem lidar com PCI/cartão nem construir o checkout. A sessão carrega o pedido inteiro (itens, cliente, cupons) e fecha em 1:1 com o seu externalTransactionId.

Considere outra abordagem quando:

  • Você quer um link fixo, reutilizável por muitos compradores → use Payment Links.
  • Você já tem um checkout próprio e só precisa processar o pagamento no backend → use Transações diretamente.

Ciclo de vida (status)

statusSignificadoTerminal?
activeCriada e válida; aguardando/recebendo o comprador.não
pendingPagamento iniciado, aguardando confirmação.não
completedPagamento aprovado.sim
abandonedMarcada como abandonada (cron de timeout).sim
expiredPassou de expiresAt ou cancelada via DELETE.sim

Em status terminal a url redireciona para uma página de estado em vez de renderizar o checkout.

Requisição

Headers

CabeçalhoUso
SelectKeyObrigatório. Sua chave de API. Produção começa com sl_live_; sandbox com sl_test_. Nunca use Authorization: Bearer/Basic. Veja Autenticação.
Content-Type: application/jsonObrigatório em POST e PUT (requisições com corpo).
X-Idempotency-KeyRecomendado em POST. Garante que repetir a mesma criação (ex.: após timeout de rede) não gere sessões duplicadas. Veja Idempotência.

Criar — POST /v1/checkouts/sessions

Itens resolvidos pelo catálogo (var_…):

curl -X POST "https://api.selectwin.io/v1/checkouts/sessions" \
  -H "SelectKey: sl_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \
  -H "Content-Type: application/json" \
  -H "X-Idempotency-Key: 1f8d2c6a-7b9e-4a1d-8c3f-0e2b4d6f8a10" \
  -d '{
    "ambient": "live",
    "externalTransactionId": "pedido-7781",
    "lineItems": [
      { "id": "var_main_product", "quantity": 1 },
      { "id": "var_orderbump_book", "quantity": 1, "isOrderbump": true }
    ],
    "customer": { "email": "[email protected]", "fullName": "Maria Silva" },
    "coupons": [{ "code": "DESCONTO10" }],
    "redirectUrls": { "paid": "https://seller.com/obrigado" },
    "metadata": { "erpOrderId": "9921" }
  }'

A resposta 201 é o objeto checkout.session (veja Resposta). Redirecione o comprador para url.

Item ad-hoc (fora do catálogo — exige name + unitPrice):

{ "lineItems": [{ "name": "Consultoria 1h", "quantity": 2, "unitPrice": 25000 }] }

Atualizar — PUT /v1/checkouts/sessions/{id}

O PUT faz merge parcial, com recálculo seguro de totais, e só funciona em status não-terminal (active/pending) — senão checkoutSessionNotEditable (422):

  • Campos simples (customer, address, metadata, redirectUrls, externalTransactionId, expiresAt) atualizam sem recalcular o carrinho.
  • Enviar lineItems re-precifica itens e totais e zera os cupons — reenvie coupons no mesmo PUT para reaplicá-los.
  • Enviar coupons sem lineItems re-precifica os itens atuais para recalcular o desconto.
curl -X PUT "https://api.selectwin.io/v1/checkouts/sessions/ses_a1b2c3d4e5f6789012ab" \
  -H "SelectKey: sl_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \
  -H "Content-Type: application/json" \
  -d '{
    "lineItems": [{ "id": "var_main_product", "quantity": 2 }],
    "coupons": [{ "code": "DESCONTO10" }]
  }'

Cancelar — DELETE /v1/checkouts/sessions/{id}

O DELETE é um soft-cancel: marca status=expired e preserva a linha (os dados de funil/abandono continuam disponíveis). Ele rejeita com checkoutSessionNotCancelable (422) se a sessão já estiver completed ou tiver gerado uma transação — essas são mantidas para reconciliação.

curl -X DELETE "https://api.selectwin.io/v1/checkouts/sessions/ses_a1b2c3d4e5f6789012ab" \
  -H "SelectKey: sl_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ"

Listar / Consultar — GET /v1/checkouts/sessions[/{id}]

A listagem é paginada e aceita filtros (status, externalTransactionId, datas). Use o GET por {id} para reconciliação pontual:

# Listar sessões ativas de um pedido específico
curl "https://api.selectwin.io/v1/checkouts/sessions?status=active&externalTransactionId=pedido-7781&limit=20" \
  -H "SelectKey: sl_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ"

# Consultar uma sessão
curl "https://api.selectwin.io/v1/checkouts/sessions/ses_a1b2c3d4e5f6789012ab" \
  -H "SelectKey: sl_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ"

Resposta

Objeto checkout.session como retornado por criar, consultar e atualizar:

{
  "object": "checkout.session",
  "id": "ses_a1b2c3d4e5f6789012ab",
  "url": "https://checkout.selectwin.io/p/live/ses_a1b2c3d4e5f6789012ab",
  "status": "active",
  "ambient": "live",
  "source": "api",
  "currency": "BRL",

  "lineItems": [
    { "id": "var_main_product", "name": "Curso Premium", "quantity": 1, "unitPrice": 49700,
      "currency": "BRL", "isOrderbump": false, "isUpsell": false,
      "metadata": { "sku": "ABC-123" }, "externalReference": "erp-ref-9" }
  ],
  "totals": { "amount": 46523, "discount": 5167, "shipping": 0, "tax": 0 },
  "coupons": [{ "code": "DESCONTO10", "type": "percentage", "value": 10 }],

  "customer": {
    "email": "[email protected]", "fullName": "Maria Silva",
    "document": { "number": "12345678909", "type": "cpf" },
    "telephone": { "countryCode": "55", "areaCode": "11", "number": "999998888" }
  },
  "address": {
    "postcode": "01310-100", "street": "Av. Paulista", "number": "1000",
    "district": "Bela Vista", "city": "São Paulo", "state": "SP", "country": "BR"
  },

  "paymentMethods": ["pix", "credit", "billet"],
  "paymentSelection": null,
  "redirectUrls": {
    "paid": "https://seller.com/obrigado",
    "refused": "https://seller.com/recusado",
    "pending": "https://seller.com/processando"
  },

  "utm": { "source": "newsletter", "campaign": "junho" },
  "metadata": { "erpOrderId": "9921" },
  "customInputs": null,
  "integrationType": "shopify",
  "integrationId": "cmp_shop_123",
  "externalTransactionId": "pedido-7781",

  "firstInitialization": true,
  "online": false,
  "expiresAt": "2026-06-12T12:00:00.000Z",
  "createdAt": "2026-06-08T12:00:00.000Z",
  "updatedAt": "2026-06-08T12:00:00.000Z"
}
CampoDescrição
idIdentificador público (ses_…), opaco. Use em GET/PUT/DELETE e na reconciliação.
urlA URL hospedada. Entregue ao comprador. Formato {domínio}/p/{ambient}/{id}.
statusEstado da sessão (ver Ciclo de vida).
ambientlive ou sandbox.
currencyMoeda da sessão (ISO 4217), ex.: BRL.
lineItemsItens já com preço resolvido pelo backend.
totalsTotais recalculados pelo backend (centavos). amount já é líquido do desconto.
couponsCupons aplicados/validados.
customer / addressSnapshot do comprador (parcial até ele preencher; é um objeto JSON, ainda não cria registro).
paymentMethodsMétodos habilitados (do corpo ou do template global da empresa).
redirectUrlsURLs de pós-pagamento (do corpo ou do template global).
metadata / utm / externalTransactionIdEcoam o que você enviou (reconciliação).
firstInitializationtrue enquanto o comprador não abriu a url.
expiresAtExpiração (default 48h se omitido).

Configuração (paymentMethods / redirectUrls)

Você pode enviar paymentMethods e redirectUrls no corpo da sessão. Quando omitidos, o backend usa o template de checkout global da empresa (isGlobal) para os métodos habilitados e as URLs de redirect; o redirectUrls do corpo sobrescreve por sessão. Sem template e sem corpo, o default é ["pix", "credit", "billet"].

Cupons

Envie coupons: [{ "code": "…" }] por código. O backend valida a elegibilidade (datas, limite de uso, mínimo de carrinho, itens permitidos) e calcula o desconto, refletindo em totals.discount/totals.amount e em coupons[]. Cupom inexistente → couponNotFound; inelegível → erro de cupom específico (ex.: couponExpired, couponMinCartAmount). Veja Cupons.

Webhooks

EventoQuando
checkout.session.completedO comprador concluiu o pagamento na sessão. Use isto (não polling) para liberar o pedido.

O caminho recomendado de confirmação é webhook + a transação correspondente — veja Proibição de Polling e Verificando Assinaturas de Webhook.

Erros

Os endpoints de checkout retornam o envelope de erro padrão; trate sempre pelo error.code, nunca pela message. Veja Tratamento de Erros e o Catálogo de Códigos de Erro.

error.codeHTTPQuando ocorre
invalidParameters400Corpo/parâmetro inválido (campos detalhados em error.params).
checkoutSessionItemPriceRequired400Item ad-hoc sem unitPrice (item fora do catálogo precisa de name + unitPrice).
checkoutSessionItemInvalid400lineItems[].id não resolve para uma variante válida.
checkoutSessionNotEditable422PUT numa sessão em status terminal (completed/abandoned/expired).
checkoutSessionNotCancelable422DELETE numa sessão já completed ou que já gerou transação.
couponNotFound404Cupom informado por código não existe.
unauthorized401SelectKey ausente, inválida ou revogada.
forbidden403Autenticado, mas sem permissão para a operação.
tooManyRequests429Limite de requisições excedido — respeite error.retryAfterMinutes.
serverError500Erro interno — repita com backoff.

Boas práticas

  1. Resolva preço pelo catálogo (var_…) sempre que possível; use ad-hoc só para itens fora do catálogo.
  2. Use externalTransactionId para reconciliar — ele também é aceito como filtro (GET /v1/checkouts/sessions?externalTransactionId=pedido-7781).
  3. Reaja a checkout.session.completed em vez de fazer polling no GET.
  4. Reenvie coupons ao alterar lineItems num PUT, já que mudar o carrinho zera os descontos.
  5. Defina expiresAt se a janela default de 48h não fizer sentido para o seu fluxo.
  6. Guarde o id (ses_…), não a url — o id é a chave estável para consultar, atualizar e cancelar.

Veja também

How is this guide?

On this page