Introdução

A API do DePix App permite criar e gerenciar checkouts Pix programaticamente. É a mesma API que alimenta o plugin do BTCPay Server e a Área do Lojista.

URL base

https://depix-backend.vercel.app

Formato

Todos os requests e responses usam JSON (Content-Type: application/json). Valores monetários são sempre em centavos (inteiros). Exemplo: R$ 10,00 = 1000.

Para começar, crie uma conta, ative sua conta de lojista na Área do Lojista e gere uma API key.

Autenticação

Use uma API key no header Authorization em todos os requests autenticados.

Header
Authorization: Bearer sk_live_<sua-chave>

Tipos de chave

PrefixoTipoComportamento
sk_live_LiveCheckouts reais. Dinheiro de verdade.
sk_test_TestCheckouts de teste. Nenhum dinheiro real é movimentado.

Para gerenciar suas chaves (criar, listar, revogar), acesse a Área do Lojista em depixapp.com/#merchant. Máximo de 5 chaves live e 5 chaves test ativas por conta.

Guarde sua chave com segurança. Ela só é exibida uma vez no momento da criação. Se perdê-la, revogue e gere uma nova.

Erros

Erros seguem o formato abaixo. O campo errorMessage é sempre legível para humanos.

Resposta de erro
{
  "response": {
    "errorMessage": "Dados inválidos.",
    "errors": [                         // presente em erros de validação
      { "field": "amount", "message": "Obrigatório." }
    ]
  }
}
StatusSignificado
400Dados inválidos — verifique os campos.
401API key ausente, inválida ou revogada.
403Sem permissão. Conta de lojista não configurada, ou chave sem acesso ao recurso.
404Recurso não encontrado.
409Conflito de estado — operação não permitida no status atual.
429Rate limit excedido. Aguarde e tente novamente.
500Erro interno no servidor. Tente novamente em alguns instantes.
503Serviço indisponível — plataforma em manutenção ou provedor Pix temporariamente fora do ar.

Criar checkout

Cria um novo checkout Pix. Retorna o QR code e a URL de pagamento para exibir ao seu cliente.

POST /api/checkouts

Parâmetros

CampoTipoDescrição
amountintegerobrigatórioValor em centavos. Mínimo: 500 (R$ 5,00). Máximo: 300000 (R$ 3.000,00).
descriptionstringopcionalDescrição do pedido. Máximo 500 caracteres. Exibida na página de pagamento.
expires_inintegeropcionalTempo de expiração em segundos. Padrão: 1200 (20min). Mínimo: 300 (5min). Máximo: 1200 (20min).
image_urlstringopcionalURL HTTPS da imagem do produto. Exibida na página de pagamento.
callback_urlstringopcionalURL HTTPS que recebe os webhooks do checkout.
redirect_urlstringopcionalURL para redirecionar o cliente após o pagamento.
metadataobjectopcionalDados adicionais do seu sistema (order_id, user_id, etc.). Máximo 4KB. Devolvido nos webhooks.

Exemplo

curl -X POST https://depix-backend.vercel.app/api/checkouts \
  -H "Authorization: Bearer sk_live_<sua-chave>" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 2990,
    "description": "Camiseta tamanho M",
    "expires_in": 900,
    "callback_url": "https://minha-loja.com/webhook/depix",
    "metadata": { "order_id": "ORD-123" }
  }'
const res = await fetch("https://depix-backend.vercel.app/api/checkouts", {
  method: "POST",
  headers: {
    "Authorization": "Bearer sk_live_<sua-chave>",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    amount: 2990,
    description: "Camiseta tamanho M",
    expires_in: 900,
    callback_url: "https://minha-loja.com/webhook/depix",
    metadata: { order_id: "ORD-123" },
  }),
});
const data = await res.json();
console.log(data.id, data.payment_url);
import requests

resp = requests.post(
    "https://depix-backend.vercel.app/api/checkouts",
    headers={"Authorization": "Bearer sk_live_<sua-chave>"},
    json={
        "amount": 2990,
        "description": "Camiseta tamanho M",
        "expires_in": 900,
        "callback_url": "https://minha-loja.com/webhook/depix",
        "metadata": {"order_id": "ORD-123"},
    },
)
data = resp.json()
print(data["id"], data["payment_url"])
$ch = curl_init("https://depix-backend.vercel.app/api/checkouts");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer sk_live_<sua-chave>",
        "Content-Type: application/json",
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "amount" => 2990,
        "description" => "Camiseta tamanho M",
        "expires_in" => 900,
        "callback_url" => "https://minha-loja.com/webhook/depix",
        "metadata" => ["order_id" => "ORD-123"],
    ]),
]);
$response = curl_exec($ch);
$data = json_decode($response, true);
echo $data["id"] . " " . $data["payment_url"];
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer sk_live_<sua-chave>");

var payload = new {
    amount = 2990,
    description = "Camiseta tamanho M",
    expires_in = 900,
    callback_url = "https://minha-loja.com/webhook/depix",
    metadata = new { order_id = "ORD-123" }
};

var res = await client.PostAsync(
    "https://depix-backend.vercel.app/api/checkouts",
    new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json")
);
var json = await res.Content.ReadAsStringAsync();
Console.WriteLine(json);
body := `{"amount":2990,"description":"Camiseta tamanho M","expires_in":900,"callback_url":"https://minha-loja.com/webhook/depix","metadata":{"order_id":"ORD-123"}}`

req, _ := http.NewRequest("POST", "https://depix-backend.vercel.app/api/checkouts", strings.NewReader(body))
req.Header.Set("Authorization", "Bearer sk_live_<sua-chave>")
req.Header.Set("Content-Type", "application/json")

resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
io.Copy(os.Stdout, resp.Body)
require "net/http"
require "json"

uri = URI("https://depix-backend.vercel.app/api/checkouts")
req = Net::HTTP::Post.new(uri, {
  "Authorization" => "Bearer sk_live_<sua-chave>",
  "Content-Type" => "application/json",
})
req.body = { amount: 2990, description: "Camiseta tamanho M", expires_in: 900,
             callback_url: "https://minha-loja.com/webhook/depix",
             metadata: { order_id: "ORD-123" } }.to_json

res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
puts JSON.parse(res.body)
HttpClient client = HttpClient.newHttpClient();
String json = """
    {"amount":2990,"description":"Camiseta tamanho M","expires_in":900,
     "callback_url":"https://minha-loja.com/webhook/depix",
     "metadata":{"order_id":"ORD-123"}}""";

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://depix-backend.vercel.app/api/checkouts"))
    .header("Authorization", "Bearer sk_live_<sua-chave>")
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString(json))
    .build();

HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
Resposta — 201 Created
{
  "id":          "chk_01jxxxxxxxxxxxxxxxxxxxxxx",
  "status":      "pending",
  "amount":      2990,
  "description": "Camiseta tamanho M",
  "image_url":   null,
  "expires_at":  "2025-06-01T15:30:00.000Z",
  "is_live":     true,
  "payment_url": "https://pay.depixapp.com/chk_01jxxxxxxxxxxxxxxxxxxxxxx",
  "pix": {
    "qr_code": "00020126580014br.gov.bcb.pix..."    // payload EMV para QR code
  }
}
💡 Exiba o payment_url ao seu cliente ou gere um QR code a partir do pix.qr_code. O QR code é compatível com qualquer app de banco.

Consultar checkout

Retorna os detalhes de um checkout específico.

GET /api/checkouts/:id
curl
curl https://depix-backend.vercel.app/api/checkouts/chk_01jxxxxxxxxxxxxxxxxxxxxxx \
  -H "Authorization: Bearer sk_live_<sua-chave>"
Resposta — 200 OK
{
  "checkout": {
    "id":             "chk_01jxxxxxxxxxxxxxxxxxxxxxx",
    "status":         "completed",     // pending | processing | completed | cancelled | expired
    "amount":         2990,
    "description":    "Camiseta tamanho M",
    "image_url":      null,
    "callback_url":   "https://minha-loja.com/webhook/depix",
    "redirect_url":   null,
    "metadata":       { "order_id": "ORD-123" },
    "expires_at":     "2025-06-01T15:30:00.000Z",
    "is_live":        true,
    "created_at":     "2025-06-01T15:00:00.000Z",
    "processing_at":  "2025-06-01T15:02:00.000Z",
    "completed_at":   "2025-06-01T15:22:00.000Z",
    "cancelled_at":   null,
    "blockchain_tx_id": "abc123...def456"  // txid Liquid (presente quando completed)
  }
}

Status possíveis

StatusSignificado
pendingAguardando pagamento.
processingPix recebido, processando conversão para DePix.
completedPagamento confirmado. DePix na carteira do merchant.
cancelledCancelado pelo merchant.
expiredPrazo de pagamento expirou.

Listar checkouts

Lista os checkouts do merchant com filtros e paginação.

GET /api/checkouts

Query params (todos opcionais)

ParâmetroDescrição
statusFiltrar por status: pending, completed, cancelled, expired.
product_idFiltrar por produto. Ex: prd_xxx.
fromData de início (ISO 8601). Ex: 2025-06-01T00:00:00Z.
toData de fim (ISO 8601).
qBusca por ID ou descrição.
limitNúmero de resultados por página. Padrão: 50. Máximo: 100.
offsetPaginação. Padrão: 0.
curl
curl "https://depix-backend.vercel.app/api/checkouts?status=completed&limit=20" \
  -H "Authorization: Bearer sk_live_<sua-chave>"
Resposta — 200 OK
{
  "checkouts": [
    {
      "id":            "chk_01jxxxxxxxxxxxxxxxxxxxxxx",
      "status":        "completed",
      "amount":        2990,
      "description":   "Camiseta tamanho M",
      "product_name":  "Camiseta Preta",    // null se checkout não vier de um produto
      "metadata":      "{\"order_id\":\"42\"}",  // string JSON, null se ausente
      "created_at":    "2025-06-01T15:00:00.000Z",
      "processing_at": "2025-06-01T15:02:14.000Z",
      "expires_at":    "2025-06-01T15:30:00.000Z",
      "is_live":       true
    }
  ],
  "stats": {
    "total":            47,
    "pending":          2,
    "completed":        40,
    "completed_amount":  189500    // centavos — R$ 1.895,00
  },
  "limit":  20,
  "offset": 0
}

Cancelar checkout

Cancela um checkout pendente. Só é possível cancelar checkouts com status pending.

POST /api/checkouts/:id/cancel
curl
curl -X POST https://depix-backend.vercel.app/api/checkouts/chk_01jxxxxxxxxxxxxxxxxxxxxxx/cancel \
  -H "Authorization: Bearer sk_live_<sua-chave>"
Resposta — 200 OK
{ "success": true }

Criar produto

Cria um novo produto com valor fixo. Cada produto gera um link de pagamento permanente que pode ser compartilhado com seus clientes.

POST /api/products

Parâmetros

CampoTipoDescrição
namestringobrigatórioNome do produto exibido na UI. 2-80 caracteres.
slugstringopcionalIdentificador na URL. Se omitido, é gerado automaticamente a partir do name. Letras minúsculas, números e hífens. 2-60 caracteres. Não pode começar/terminar com hífen.
amountintegerobrigatórioValor em centavos. Mínimo: 500. Máximo: 300000.
descriptionstringopcionalDescrição do produto. Máximo 500 caracteres.
image_urlstringopcionalURL HTTPS da imagem do produto.
callback_urlstringopcionalURL HTTPS para webhooks. Sobrescreve o default do merchant.
redirect_urlstringopcionalURL de redirecionamento. Sobrescreve o default do merchant.
metadataobjectopcionalDados adicionais. Máximo 4KB. Incluído nos webhooks dos checkouts gerados.
expires_inintegeropcionalTempo de expiração dos checkouts em segundos. Padrão: 1200 (20min). Mínimo: 300 (5min). Máximo: 1200 (20min).

Exemplo

curl -X POST https://depix-backend.vercel.app/api/products \
  -H "Authorization: Bearer sk_live_<sua-chave>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Camiseta M",
    "slug": "camiseta-m",
    "amount": 2990,
    "description": "Camiseta tamanho M"
  }'
const res = await fetch("https://depix-backend.vercel.app/api/products", {
  method: "POST",
  headers: {
    "Authorization": "Bearer sk_live_<sua-chave>",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "Camiseta M",
    slug: "camiseta-m",
    amount: 2990,
    description: "Camiseta tamanho M",
  }),
});
const data = await res.json();
console.log(data.product.payment_url);
import requests

resp = requests.post(
    "https://depix-backend.vercel.app/api/products",
    headers={"Authorization": "Bearer sk_live_<sua-chave>"},
    json={
        "name": "Camiseta M",
        "slug": "camiseta-m",
        "amount": 2990,
        "description": "Camiseta tamanho M",
    },
)
data = resp.json()
print(data["product"]["payment_url"])
$ch = curl_init("https://depix-backend.vercel.app/api/products");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer sk_live_<sua-chave>",
        "Content-Type: application/json",
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "name" => "Camiseta M",
        "slug" => "camiseta-m",
        "amount" => 2990,
        "description" => "Camiseta tamanho M",
    ]),
]);
$response = curl_exec($ch);
$data = json_decode($response, true);
echo $data["product"]["payment_url"];
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer sk_live_<sua-chave>");

var payload = new { name = "Camiseta M", slug = "camiseta-m", amount = 2990, description = "Camiseta tamanho M" };

var res = await client.PostAsync(
    "https://depix-backend.vercel.app/api/products",
    new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json")
);
Console.WriteLine(await res.Content.ReadAsStringAsync());
body := `{"name":"Camiseta M","slug":"camiseta-m","amount":2990,"description":"Camiseta tamanho M"}`

req, _ := http.NewRequest("POST", "https://depix-backend.vercel.app/api/products", strings.NewReader(body))
req.Header.Set("Authorization", "Bearer sk_live_<sua-chave>")
req.Header.Set("Content-Type", "application/json")

resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
io.Copy(os.Stdout, resp.Body)
require "net/http"
require "json"

uri = URI("https://depix-backend.vercel.app/api/products")
req = Net::HTTP::Post.new(uri, {
  "Authorization" => "Bearer sk_live_<sua-chave>",
  "Content-Type" => "application/json",
})
req.body = { name: "Camiseta M", slug: "camiseta-m", amount: 2990, description: "Camiseta tamanho M" }.to_json

res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
puts JSON.parse(res.body)
HttpClient client = HttpClient.newHttpClient();
String json = """
    {"name":"Camiseta M","slug":"camiseta-m","amount":2990,"description":"Camiseta tamanho M"}""";

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://depix-backend.vercel.app/api/products"))
    .header("Authorization", "Bearer sk_live_<sua-chave>")
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString(json))
    .build();

HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
Resposta — 201 Created
{
  "product": {
    "id":           "prd_xxx",
    "name":         "Camiseta M",
    "slug":         "camiseta-m",
    "amount":       2990,
    "description":  "Camiseta tamanho M",
    "image_url":    null,
    "callback_url": null,
    "redirect_url": null,
    "metadata":     null,
    "expires_in":   1200,
    "active":       true,
    "is_live":      true,
    "payment_url":  "https://pay.depixapp.com/joao/camiseta-m"
  }
}

Listar produtos

Lista os produtos do merchant com filtros e paginação.

GET /api/products

Query params (todos opcionais)

ParâmetroDescrição
activeFiltrar por status: 1 (ativos) ou 0 (inativos).
qBusca por nome, slug ou descrição.
limitNúmero de resultados. Padrão: 50. Máximo: 100.
offsetPaginação. Padrão: 0.
curl
curl "https://depix-backend.vercel.app/api/products?active=1" \
  -H "Authorization: Bearer sk_live_<sua-chave>"
Resposta — 200 OK
{
  "products": [
    {
      "id":          "prd_xxx",
      "name":        "Camiseta M",
      "slug":        "camiseta-m",
      "amount":      2990,
      "description": "Camiseta tamanho M",
      "active":      true,
      "is_live":     true,
      "payment_url": "https://pay.depixapp.com/joao/camiseta-m"
    }
  ],
  "stats": {
    "total":  5,
    "active": 4
  },
  "limit":  50,
  "offset": 0
}

Consultar produto

Retorna os detalhes de um produto específico, incluindo estatísticas de checkouts.

GET /api/products/:id
curl
curl https://depix-backend.vercel.app/api/products/prd_xxx \
  -H "Authorization: Bearer sk_live_<sua-chave>"
Resposta — 200 OK
{
  "product": {
    "id":           "prd_xxx",
    "name":         "Camiseta M",
    "slug":         "camiseta-m",
    "amount":       2990,
    "description":  "Camiseta tamanho M",
    "image_url":    null,
    "callback_url": null,
    "redirect_url": null,
    "metadata":     null,
    "expires_in":   1200,
    "active":       true,
    "is_live":      true,
    "payment_url":  "https://pay.depixapp.com/joao/camiseta-m",
    "created_at":   "2025-06-01T00:00:00.000Z"
  }
}

Editar produto

Atualiza um ou mais campos de um produto existente. Envie apenas os campos que deseja alterar.

PATCH /api/products/:id

Parâmetros (todos opcionais)

CampoTipoDescrição
namestringNovo nome do produto. 2-80 caracteres.
slugstringNovo identificador na URL. Mesmas regras da criação.
amountintegerNovo valor em centavos.
descriptionstringNova descrição.
image_urlstringNova URL de imagem.
callback_urlstringNova URL de webhook.
redirect_urlstringNova URL de redirecionamento.
metadataobjectNovos dados adicionais.
expires_inintegerNovo tempo de expiração dos checkouts. Mínimo: 300 (5min). Máximo: 1200 (20min).
curl
curl -X PATCH https://depix-backend.vercel.app/api/products/prd_xxx \
  -H "Authorization: Bearer sk_live_<sua-chave>" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 3490, "description": "Camiseta tamanho M - Edição Especial" }'
Resposta — 200 OK
{
  "product": {
    "id":           "prd_xxx",
    "name":         "Camiseta M",
    "slug":         "camiseta-m",
    "amount":       3490,
    "description":  "Camiseta tamanho M - Edição Especial",
    "active":       true,
    "is_live":      true,
    "payment_url":  "https://pay.depixapp.com/joao/camiseta-m"
  }
}

Ativar / Desativar produto

Ativa ou desativa um produto. Produtos inativos retornam erro 404 quando acessados pelo link de pagamento.

POST /api/products/:id/activate
POST /api/products/:id/deactivate
curl — ativar
curl -X POST https://depix-backend.vercel.app/api/products/prd_xxx/activate \
  -H "Authorization: Bearer sk_live_<sua-chave>"
curl — desativar
curl -X POST https://depix-backend.vercel.app/api/products/prd_xxx/deactivate \
  -H "Authorization: Bearer sk_live_<sua-chave>"
Resposta — 200 OK
{ "success": true }

Checkouts do produto

Lista os checkouts gerados a partir de um produto específico. Aceita os mesmos filtros da listagem geral de checkouts.

GET /api/products/:id/checkouts
curl
curl "https://depix-backend.vercel.app/api/products/prd_xxx/checkouts?status=completed" \
  -H "Authorization: Bearer sk_live_<sua-chave>"

A resposta segue o mesmo formato da listagem de checkouts.

Produto público

Retorna os dados públicos de um produto ativo. Não requer autenticação.

GET /api/products/:id/public
curl
curl https://depix-backend.vercel.app/api/products/prd_xxx/public
Resposta — 200 OK
{
  "product": {
    "id":          "prd_xxx",
    "name":        "Camiseta M",
    "slug":        "camiseta-m",
    "amount":      2990,
    "description": "Camiseta tamanho M",
    "image_url":   null
  },
  "merchant": {
    "name":     "Loja do Joao",
    "username": "joao"
  }
}

Checkout do produto

Cria um checkout a partir de um produto ativo. Não requer autenticação. O valor é herdado do produto.

POST /api/products/:id/checkout
curl
curl -X POST https://depix-backend.vercel.app/api/products/prd_xxx/checkout

A resposta segue o mesmo formato do criar checkout (status 201).

Página do merchant

Retorna os dados públicos do merchant. Não requer autenticação.

GET /api/merchants/:username/public
curl
curl https://depix-backend.vercel.app/api/merchants/joao/public
Resposta — 200 OK
{
  "merchant": {
    "name":     "Loja do Joao",
    "username": "joao"
  }
}

Checkout do merchant

Cria um checkout com valor customizado a partir da página do merchant. Não requer autenticação.

POST /api/merchants/:username/checkout

Parâmetros

CampoTipoDescrição
amountintegerobrigatórioValor em centavos. Mínimo: 500. Máximo: 300000.
curl
curl -X POST https://depix-backend.vercel.app/api/merchants/joao/checkout \
  -H "Content-Type: application/json" \
  -d '{ "amount": 5000 }'

A resposta segue o mesmo formato do criar checkout (status 201).

Webhooks

Quando o status de um checkout muda, a API envia um POST para o callback_url que você informou ao criar o checkout (ou configurado no produto/merchant).

Como funciona

  • O request é enviado com timeout de 5 segundos.
  • Se falhar, a API tenta novamente até 2 vezes: após 1 minuto e após 10 minutos (3 tentativas no total).
  • Sua endpoint deve responder com status 2xx para confirmar recebimento.
  • O callback_url precisa ser HTTPS e de acesso público (sem IPs privados).

Eventos

checkout.processing

Disparado quando o pagamento Pix é recebido e a conversão está sendo processada.

{
  "event": "checkout.processing",
  "data": {
    "id":             "chk_01jxxxxxxxxxxxxxxxxxxxxxx",
    "status":         "processing",
    "amount":         2990,
    "processing_at":  "2025-06-01T15:02:00.000Z",
    "metadata":       { "order_id": "ORD-123" }
  }
}

checkout.completed

Disparado quando o pagamento é confirmado e o DePix chega na carteira do merchant.

{
  "event": "checkout.completed",
  "data": {
    "id":            "chk_01jxxxxxxxxxxxxxxxxxxxxxx",
    "status":        "completed",
    "amount":        2990,
    "completed_at":  "2025-06-01T15:22:00.000Z",
    "metadata":      { "order_id": "ORD-123" }
  }
}

checkout.cancelled

Disparado quando o merchant cancela o checkout via API.

{
  "event": "checkout.cancelled",
  "data": {
    "id":            "chk_01jxxxxxxxxxxxxxxxxxxxxxx",
    "status":        "cancelled",
    "amount":        2990,
    "cancelled_at":  "2025-06-01T15:05:00.000Z",
    "metadata":      { "order_id": "ORD-123" }
  }
}

checkout.expired

Disparado quando o checkout expira sem receber pagamento.

{
  "event": "checkout.expired",
  "data": {
    "id":          "chk_01jxxxxxxxxxxxxxxxxxxxxxx",
    "status":      "expired",
    "amount":      2990,
    "expires_at":  "2025-06-01T15:30:00.000Z",
    "metadata":    { "order_id": "ORD-123" }
  }
}

Verificar assinatura

Cada webhook vem com um header X-DePix-Signature. Sempre valide a assinatura antes de processar o evento — isso garante que o request veio da API do DePix App e não de terceiros.

Formato do header

X-DePix-Signature: t=1717257600,v1=abc123def456...
  • t — timestamp Unix do envio (segundos).
  • v1 — assinatura HMAC-SHA256 em hexadecimal.

Como validar

A assinatura é calculada sobre a string timestamp.payload usando o Webhook Secret da sua conta (disponível na Área do Lojista).

Bash
# Calcular a assinatura esperada
EXPECTED=$(echo -n "${TIMESTAMP}.${RAW_BODY}" | \
  openssl dgst -sha256 -hmac "${WEBHOOK_SECRET}" | awk '{print $2}')

# Comparar com o v1 recebido
if [ "$EXPECTED" = "$RECEIVED_V1" ]; then
  echo "Assinatura válida"
fi
Node.js
import crypto from "node:crypto";

function verifyWebhook(rawBody, sigHeader, secret) {
  const parts = Object.fromEntries(
    sigHeader.split(",").map(p => p.split("=", 2))
  );
  const timestamp = parts["t"];
  const received  = parts["v1"];

  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");

  // Use timingSafeEqual to prevent timing attacks
  const a = Buffer.from(expected, "hex");
  const b = Buffer.from(received, "hex");
  if (a.length !== b.length) return false;
  return crypto.timingSafeEqual(a, b);
}

// Exemplo com Express
app.post("/webhook/depix", express.raw({ type: "application/json" }), (req, res) => {
  const sig = req.headers["x-depix-signature"];
  if (!verifyWebhook(req.body.toString(), sig, process.env.DEPIX_WEBHOOK_SECRET)) {
    return res.status(401).send("Assinatura inválida");
  }
  const { event, data } = JSON.parse(req.body);
  // processa o evento...
  res.sendStatus(200);
});
Python
import hmac, hashlib

def verify_webhook(raw_body: str, sig_header: str, secret: str) -> bool:
    parts = dict(p.split("=", 1) for p in sig_header.split(","))
    timestamp = parts["t"]
    received = parts["v1"]
    expected = hmac.new(
        secret.encode(),
        f"{timestamp}.{raw_body}".encode(),
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, received)
PHP
function verifyWebhook(string $rawBody, string $sigHeader, string $secret): bool {
    $parts = [];
    foreach (explode(",", $sigHeader) as $pair) {
        [$k, $v] = explode("=", $pair, 2);
        $parts[$k] = $v;
    }
    $expected = hash_hmac("sha256", $parts["t"] . "." . $rawBody, $secret);
    return hash_equals($expected, $parts["v1"]);
}
C#
static bool VerifyWebhook(string rawBody, string sigHeader, string secret) {
    var parts = sigHeader.Split(',')
        .ToDictionary(p => p.Split('=', 2)[0], p => p.Split('=', 2)[1]);
    using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
    var expected = Convert.ToHexString(
        hmac.ComputeHash(Encoding.UTF8.GetBytes($"{parts["t"]}.{rawBody}"))
    ).ToLower();
    return CryptographicOperations.FixedTimeEquals(
        Encoding.UTF8.GetBytes(expected),
        Encoding.UTF8.GetBytes(parts["v1"])
    );
}
Go
func verifyWebhook(rawBody, sigHeader, secret string) bool {
    parts := make(map[string]string)
    for _, p := range strings.Split(sigHeader, ",") {
        kv := strings.SplitN(p, "=", 2)
        parts[kv[0]] = kv[1]
    }
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write([]byte(parts["t"] + "." + rawBody))
    expected := hex.EncodeToString(mac.Sum(nil))
    return hmac.Equal([]byte(expected), []byte(parts["v1"]))
}
Ruby
def verify_webhook(raw_body, sig_header, secret)
  parts = sig_header.split(",").to_h { |p| p.split("=", 2) }
  expected = OpenSSL::HMAC.hexdigest("sha256", secret, "#{parts['t']}.#{raw_body}")
  Rack::Utils.secure_compare(expected, parts["v1"])
end
Java
static boolean verifyWebhook(String rawBody, String sigHeader, String secret)
        throws Exception {
    Map<String, String> parts = new HashMap<>();
    for (String p : sigHeader.split(",")) {
        String[] kv = p.split("=", 2);
        parts.put(kv[0], kv[1]);
    }
    Mac mac = Mac.getInstance("HmacSHA256");
    mac.init(new SecretKeySpec(secret.getBytes(), "HmacSHA256"));
    String expected = HexFormat.of().formatHex(
        mac.doFinal((parts.get("t") + "." + rawBody).getBytes())
    );
    return MessageDigest.isEqual(expected.getBytes(), parts.get("v1").getBytes());
}
Leia o body como raw bytes (antes do parsing JSON). Qualquer reformatação invalida a assinatura.

Sandbox

Use chaves do tipo sk_test_... para testar sem movimentar dinheiro real. Checkouts criados com chave test nunca geram Pix real e ficam isolados dos checkouts de produção.

Diferenças do modo test

  • O campo is_live retorna false.
  • O QR code gerado não é um Pix válido — não pode ser pago num app de banco.
  • Use o endpoint /simulate-payment para marcar o checkout como pago.
  • Webhooks são enviados normalmente — ótimo para testar sua integração de ponta a ponta.
O sandbox é independente de produção. Você pode criar, simular e cancelar checkouts de teste sem risco.

Simular pagamento

Marca um checkout de teste como pago. Só funciona com chaves sk_test_. Dispara o webhook checkout.completed normalmente.

POST /api/checkouts/:id/simulate-payment
curl
# 1. Crie um checkout de teste
curl -X POST https://depix-backend.vercel.app/api/checkouts \
  -H "Authorization: Bearer sk_test_<sua-chave>" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 1000, "callback_url": "https://minha-loja.com/webhook" }'

# 2. Simule o pagamento
curl -X POST https://depix-backend.vercel.app/api/checkouts/chk_01jxxxxxxxxxxxxxxxxxxxxxx/simulate-payment \
  -H "Authorization: Bearer sk_test_<sua-chave>"
Resposta — 200 OK
{ "success": true }

Após a simulação, seu callback_url receberá o evento checkout.completed em alguns segundos — exatamente como num pagamento real.

Verificar chave (GET /api/me)

Retorna as informações do merchant autenticado. Útil para verificar se a API key é válida e consultar dados da conta.

GET /api/me
curl
curl https://depix-backend.vercel.app/api/me \
  -H "Authorization: Bearer sk_live_<sua-chave>"
Resposta — 200 OK
{
  "merchant_id": "mrc_xxx",
  "name":        "Loja do Joao",
  "username":    "joao",
  "is_live":     true,
  "created_at":  "2025-06-01T00:00:00.000Z"
}

Rate limits

A API aplica limites de requisições para garantir estabilidade e proteger contra abusos.

EndpointLimiteEscopo
POST /api/checkouts30 / minpor IP
GET /api/checkout-page/:id30 / minpor IP (público)
GET /api/pay/:id60 / minpor IP (público)
POST /api/pay/:id/simulate5 / minpor IP (público, só sandbox)
POST /api/merchants/:username/checkout10 / minpor IP (público)
POST /api/products/:id/checkout10 / minpor IP (público)
GET /api/products/:id/public30 / minpor IP (público)
GET /api/merchants/:username/public30 / minpor IP (público)
Por merchant (API key)Configurávelaplicado após auth, em cima do limite por IP
  • Para requests autenticados com API key, um rate limit adicional é aplicado por merchant (configurável — fale com o suporte se precisar de aumento).
  • Para endpoints públicos (sem auth), o rate limit é aplicado apenas por IP.
  • Quando o limite é atingido, a API retorna status 429 com a mensagem "Muitas requisições. Tente novamente em 1 minuto." — aguarde ~60s antes de tentar de novo.
Se você precisa de limites maiores para sua integração, entre em contato pelo suporte.