Plasma

Checkout

Crie links de checkout a partir do carrinho da sua loja, com variante e quantidade.

Visão Geral

A API de Checkout cria o link de pagamento do seu carrinho. O comprador abre o link, paga com Pix, cartão ou boleto, e você recebe o pedido por webhook.

Todo valor monetário da API é em centavos inteiros, em campos terminados em Cents. 15000 é R$ 150,00.

Para produtos do catálogo, use lineItems: o preço vem do catálogo e não do seu payload, então ninguém consegue forjar o valor cobrado.

Endpoints

MétodoEndpointAuthDescrição
POST/api/sdk/checkout/create-link🔒 SDKCriar link de checkout
GET/api/sdk/checkout/:token🌐 PúblicoAbre a sessão (é a rota que a página de checkout usa)

Não use GET /api/sdk/checkout/:token para conferir pagamento. É a rota que abre a página de checkout: enquanto a sessão está pendente, cada chamada conta como um novo início de checkout nos pixels de marketing da loja. E o status completed dela quer dizer pedido criado, não pago. Quem confirma pagamento é o webhook order.paid, ou a API de Pedidos.


POST /api/sdk/checkout/create-link

Os itens vão por uma de três formas. Mandar mais de uma, ou nenhuma, devolve 400 VALIDATION_ERROR.

FormaQuando usarPreço
lineItemsCarrinho com produtos do catálogo, com variante e quantidadeDo catálogo
productIdsAtalho: uma unidade de cada produto, sem varianteDo catálogo
itemsO que não está no catálogo, como uma consultoriaInformado por você

Body

CampoTipoObrigatórioDescrição
lineItemsarray❌*Linhas do carrinho, do catálogo
lineItems[].productIdstring✅Id do produto
lineItems[].variantIdstring❌Id da variante. Obrigatório quando o produto tem variantes; omita quando não tem
lineItems[].quantityinteger❌Unidades, de 1 a 9999 (padrão: 1)
productIdsstring[]❌*Uma unidade de cada id. Recusa produto com variantes
itemsarray❌*Itens fora do catálogo
items[].titlestring✅Título do item (max 200)
items[].priceCentsinteger✅Preço unitário em centavos inteiros. 15000 é R$ 150,00
items[].quantityinteger❌Quantidade (padrão: 1, max: 9999)
items[].imagestring❌URL da imagem
items[].descriptionstring❌Descrição (max 1000)
items[].skustring❌SKU do item
items[].variantTitlestring❌Rótulo exibido no resumo, como P / Azul
successUrlstring❌URL de redirecionamento depois de finalizar
cancelUrlstring❌URL de redirecionamento se o comprador desistir
customerobject❌Pré-preencher email, name e phone
currencystring❌Moeda (padrão: BRL)
expiresIninteger❌Expiração em minutos (5 a 10080, padrão 1440)
shippingCentsinteger❌Frete em centavos inteiros
couponCodestring❌Código de cupom (max 50)
metadataobject❌Seus dados (max 4KB). Volta em todo aviso de pedido

Um mesmo checkout aceita até 50 linhas. É limite do carrinho: o catálogo da loja não tem limite de produtos.

Regras do carrinho (lineItems)

  • Produto com variantes: mande o variantId da variante escolhida, e ele precisa ser uma variante daquele produto. Sem ele, 400 VARIANT_REQUIRED, com os ids válidos em variantIds. Variante de outro produto devolve 400 VARIANT_NOT_FOUND.
  • Produto sem variantes: vai sem variantId.
  • Estoque nunca impede uma venda. O link é criado mesmo com estoque zerado, e o pagamento baixa o estoque mesmo que ele fique negativo.

Exemplo

curl -X POST "https://api.plasmacheckout.com/api/sdk/checkout/create-link" \
  -H "X-PLASMA-Public-Key: pk_live_xxxxxxxxxxxxxxxxxxxx" \
  -H "X-PLASMA-Secret-Key: sk_live_xxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "lineItems": [
      { "productId": "cmu1splash0001", "variantId": "cmu1var10ml", "quantity": 2 },
      { "productId": "cmu1caneca0001", "quantity": 1 }
    ],
    "successUrl": "https://meusite.com/obrigado",
    "cancelUrl": "https://meusite.com/carrinho",
    "metadata": { "pedidoDaLoja": "Y-123" }
  }'
import { PlasmaSDK } from '@plasmacheckout/sdk';

const plasma = new PlasmaSDK({
  apiKey: process.env.PLASMA_PUBLIC_KEY!,
  secretKey: process.env.PLASMA_SECRET_KEY!,
});

const { data } = await plasma.checkout.createLink({
  lineItems: [
    { productId: 'cmu1splash0001', variantId: 'cmu1var10ml', quantity: 2 },
    { productId: 'cmu1caneca0001', quantity: 1 },
  ],
  successUrl: 'https://meusite.com/obrigado',
  cancelUrl: 'https://meusite.com/carrinho',
  metadata: { pedidoDaLoja: 'Y-123' },
});

// Guarde data.checkoutToken no seu pedido antes de redirecionar.
console.log(data.checkoutUrl);
const response = await fetch(
  'https://api.plasmacheckout.com/api/sdk/checkout/create-link',
  {
    method: 'POST',
    headers: {
      'X-PLASMA-Public-Key': process.env.PLASMA_PUBLIC_KEY,
      'X-PLASMA-Secret-Key': process.env.PLASMA_SECRET_KEY,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      lineItems: [
        { productId: 'cmu1splash0001', variantId: 'cmu1var10ml', quantity: 2 },
        { productId: 'cmu1caneca0001', quantity: 1 },
      ],
      successUrl: 'https://meusite.com/obrigado',
      cancelUrl: 'https://meusite.com/carrinho',
    }),
  }
);

const { data } = await response.json();
console.log(data.checkoutUrl); // URL para redirecionar o comprador

Resposta (201 Created)

{
  "data": {
    "id": "cmswltiti0005rv05rpvt7an1",
    "checkoutUrl": "https://pay.minhaloja.com.br/c/c08b6109-4c32-46ac-9a8a-bb72b7ec71de",
    "checkoutToken": "c08b6109-4c32-46ac-9a8a-bb72b7ec71de",
    "status": "pending",
    "expiresAt": "2026-10-05T12:00:00.000Z",
    "items": [
      { "productId": "cmu1splash0001", "variantId": "cmu1var10ml", "title": "Body Splash Lavanda",
        "priceCents": 4990, "quantity": 2, "image": "https://cdn.minhaloja.com/lavanda.jpg", "variantTitle": "10ml" },
      { "productId": "cmu1caneca0001", "variantId": null, "title": "Caneca",
        "priceCents": 3990, "quantity": 1, "image": null, "variantTitle": null }
    ],
    "totals": {
      "subtotalCents": 13970,
      "shippingCents": 0,
      "discountCents": 0,
      "totalCents": 13970
    },
    "currency": "BRL",
    "customer": null,
    "urls": {
      "checkout": "https://pay.minhaloja.com.br/c/c08b6109-4c32-46ac-9a8a-bb72b7ec71de",
      "success": "https://meusite.com/obrigado",
      "cancel": "https://meusite.com/carrinho"
    },
    "usedFallback": false
  },
  "message": "Checkout link created successfully"
}

O que fazer com a resposta

  1. Guarde data.checkoutToken junto do pedido na sua base. Todo aviso de pedido traz esse token em data.checkoutToken: é ele que liga o pagamento a este carrinho.
  2. Redirecione para data.checkoutUrl exatamente como veio. O Plasma já resolve o domínio próprio da loja e, enquanto o SSL dele não está ativo, usa o domínio de fallback (usedFallback: true). Nunca remonte a URL a partir do token.

Fluxo do Checkout

O seu backend cria o link com as chaves de API e guarda o checkoutToken.

Redirecionar

O comprador vai para o checkoutUrl e paga.

Pedido criado

Saem order.created e checkout.completed. O pedido existe, sem pagamento ainda: um Pix gerado e nunca pago também passa por aqui.

Pagamento

Sai order.paid quando o gateway confirma. É o único aviso que libera o pedido. Se o gateway cancelar ou devolver o pagamento, sai order.cancelled ou order.refunded. Pedido que nunca recebe order.paid não foi pago, como um cartão recusado ou um Pix que ninguém pagou.

Volta para o seu site

O comprador volta para a successUrl, às vezes antes do order.paid. A página de obrigado lê o estado do pedido na sua base.


Expiração

O campo expiresIn é especificado em minutos:

ValorTempo
Mínimo5 minutos
Padrão1440 minutos (24 horas)
Máximo10080 minutos (7 dias)

O valor é em minutos, não em segundos. 60 = 1 hora, 1440 = 24 horas.


Moedas Suportadas

CódigoMoeda
BRLReal Brasileiro
USDDólar Americano
EUREuro
ARSPeso Argentino
CLPPeso Chileno
COPPeso Colombiano
MXNPeso Mexicano
PENSol Peruano
UYUPeso Uruguaio

Integração Frontend

Botão de Compra

BotaoFinalizar.jsx
function BotaoFinalizar({ carrinho }) {
  const [loading, setLoading] = useState(false);

  async function finalizar() {
    setLoading(true);

    // carrinho: [{ productId, variantId, quantity }]
    const response = await fetch('/api/criar-checkout', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ lineItems: carrinho }),
    });

    const { checkoutUrl } = await response.json();
    window.location.href = checkoutUrl;
  }

  return (
    <button onClick={finalizar} disabled={loading}>
      {loading ? 'Carregando...' : 'Finalizar compra'}
    </button>
  );
}

Backend (Next.js API Route)

app/api/criar-checkout/route.ts
import { NextResponse } from 'next/server';
import { PlasmaSDK, PlasmaError } from '@plasmacheckout/sdk';

const plasma = new PlasmaSDK({
  apiKey: process.env.PLASMA_PUBLIC_KEY!,
  secretKey: process.env.PLASMA_SECRET_KEY!,
});

export async function POST(request: Request) {
  const { lineItems } = await request.json();

  try {
    const { data } = await plasma.checkout.createLink({
      lineItems,
      successUrl: `${process.env.NEXT_PUBLIC_URL}/obrigado`,
      cancelUrl: `${process.env.NEXT_PUBLIC_URL}/carrinho`,
    });

    await salvarPedidoPendente({ checkoutToken: data.checkoutToken, lineItems });

    return NextResponse.json({ checkoutUrl: data.checkoutUrl });
  } catch (error) {
    if (error instanceof PlasmaError) {
      return NextResponse.json({ code: error.code, message: error.message }, { status: error.statusCode });
    }
    throw error;
  }
}

Nunca exponha suas API Keys no frontend. Use uma rota no backend para intermediar.


Erros Comuns

CódigoErroSolução
400VALIDATION_ERRORMande só uma forma de itens (lineItems, productIds ou items)
400VARIANT_REQUIREDO produto tem variantes: mande o variantId (os válidos vêm em variantIds)
400VARIANT_NOT_FOUNDA variante não é daquele produto, ou o produto não tem variantes
400PRODUCTS_NOT_FOUNDId inexistente, inativo ou de outra loja (lista em missingIds)
400INVALID_LINE_ITEMSLinha malformada; a mensagem diz qual
400TOO_MANY_ITEMSMais de 50 linhas no mesmo checkout
400INVALID_EXPIRATIONexpiresIn deve ser entre 5 e 10080 minutos
400INVALID_CURRENCYUse uma moeda suportada
400INVALID_METADATAmetadata deve ser objeto com no máximo 4KB

Não existe erro de estoque esgotado: estoque nunca impede uma venda.


Webhooks Disparados

Uma venda feita no checkout gera estes avisos, todos com o pedido inteiro em data (itens com variante, valores em centavos, o seu checkoutToken e o seu metadata):

  1. order.created e checkout.completed: o pedido nasceu no checkout, ainda sem pagamento.
  2. order.paid: o gateway confirmou o pagamento (Pix, cartão ou boleto). Libere o pedido.
  3. order.cancelled: o gateway informou o cancelamento do pagamento.
  4. order.refunded: o pagamento foi devolvido.

Cartão recusado e Pix que ninguém pagou não geram aviso próprio: o pedido simplesmente nunca recebe order.paid. Para saber o status atual, consulte a API de Pedidos.

Webhooks

Formato do aviso, assinatura e retentativas