Autenticação

Gere suas credenciais no painel em Desenvolvedores > API Keys. Você receberá duas chaves:

ChaveFormatoUsoPermissões
Secret Key sk_live_... Apenas no backend Acesso total: criar, consultar, listar, configurar splits
Public Key pk_live_... Frontend (browser) Somente leitura: consultar e listar transações

Envie a chave no header x-api-key em todas as requisições:

# Backend (Secret Key - acesso total)
curl -H "x-api-key: sk_live_sua_chave_secreta" \
  https://api.velanipagamentos.com.br/api/v1/api-gateway/v1/transactions

# Frontend (Public Key - somente consulta)
curl -H "x-api-key: pk_live_sua_chave_publica" \
  https://api.velanipagamentos.com.br/api/v1/api-gateway/v1/transactions/uuid
⚠️ Importante: Nunca exponha sua Secret Key no frontend ou código público. Use a Public Key para consultas no browser.

Erros e HTTP Codes

CodeSignificadoQuando ocorre
200SucessoRequisição processada com sucesso
400Bad RequestDados inválidos ou campos obrigatórios ausentes
401UnauthorizedAPI Key inválida, expirada ou ausente
404Not FoundTransação ou recurso não encontrado
429Rate LimitedLimite de 5 requisições/segundo excedido
500Server ErrorErro interno (contate o suporte)

Formato de Erro

{
  "success": false,
  "error": {
    "message": "Valor mínimo: R$ 1,00 (100 centavos)",
    "statusCode": 400
  }
}

Status das Transações

StatusDescrição
pendingAguardando pagamento PIX (QR Code gerado)
processingPagamento em processamento pelo banco
paidPagamento confirmado — produto pode ser liberado
failedFalha no pagamento
expiredQR Code expirou (30 minutos)
refundedValor devolvido ao cliente

Criar Transação PIX

POST /v1/transactions

Cria uma cobrança PIX. Retorna o QR Code para o cliente pagar.

Headers

HeaderTipoDescrição
x-api-key string obrigatório Sua Secret Key (sk_live_...)
x-idempotency-key string opcional Chave única para evitar cobranças duplicatas

Body (JSON)

CampoTipoDescrição
amount integer obrigatório Valor em centavos. Ex: R$ 100,00 = 10000. Mínimo: 100
paymentMethod string obrigatório Método de pagamento: "pix"
customer object obrigatório Dados do cliente (veja abaixo)
customer.name string obrigatório Nome completo do cliente
customer.email string opcional E-mail do cliente
customer.phone string opcional Telefone com DDD
customer.document object opcional { "type": "cpf", "number": "12345678900" }
items array opcional [{ "title": "Produto", "unitPrice": 10000, "quantity": 1 }]
split array opcional novo Split inline: [{ "email": "parceiro@email.com", "percentage": "10" }]
postbackUrl string opcional URL HTTPS para webhook de confirmação
externalId string opcional Seu ID interno para referência
description string opcional Descrição da cobrança
tracking object opcional UTMs do clique para atribuição de campanha na UTMify: { "utm_source", "utm_campaign", "utm_medium", "utm_content", "utm_term", "src", "sck" }. Se omitido, a venda é enviada à UTMify sem origem.

Exemplo

curl -X POST https://api.velanipagamentos.com.br/api/v1/api-gateway/v1/transactions \
  -H "Content-Type: application/json" \
  -H "x-api-key: sk_live_sua_chave_secreta" \
  -d '{
  "paymentMethod": "pix",
  "amount": 10000,
  "customer": {
    "name": "João Silva",
    "email": "joao@email.com",
    "phone": "11999999999",
    "document": { "type": "cpf", "number": "12345678900" }
  },
  "items": [
    { "title": "Assinatura Premium", "unitPrice": 10000, "quantity": 1 }
  ],
  "postbackUrl": "https://meusite.com/webhook/velani",
  "externalId": "pedido-123",
  "tracking": {
    "utm_source": "facebook",
    "utm_campaign": "campanha-verao",
    "utm_medium": "cpc",
    "utm_content": "criativo-a"
  }
}'

Resposta (200)

{
  "success": true,
  "data": {
    "id": "uuid-da-transacao",
    "status": "pending",
    "amount": 10000,
    "paymentMethod": "pix",
    "customer": {
      "name": "João Silva",
      "email": "joao@email.com"
    },
    "pixQrCode": "00020126580014br.gov.bcb.pix...",
    "pixQrCodeImage": "https://api.velanipagamentos.com.br/qrcode/xxx.png",
    "expiresAt": "2026-01-15T11:00:00.000Z",
    "createdAt": "2026-01-15T10:30:00.000Z"
  },
  "meta": {
    "timestamp": "2026-01-15T10:30:00.000Z",
    "version": "v1"
  }
}

Consultar Transação

GET /v1/transactions/:id

Consulta status e dados de uma transação. Aceita Secret Key ou Public Key.

curl https://api.velanipagamentos.com.br/api/v1/api-gateway/v1/transactions/uuid \
  -H "x-api-key: sk_live_sua_chave_secreta"

Resposta

{
  "success": true,
  "data": {
    "id": "uuid-da-transacao",
    "externalId": "pedido-123",
    "status": "paid",
    "amount": 10000,
    "paymentMethod": "pix",
    "customerName": "João Silva",
    "customerEmail": "joao@email.com",
    "paidAt": "2026-01-15T10:35:00.000Z",
    "expiresAt": "2026-01-15T11:00:00.000Z",
    "createdAt": "2026-01-15T10:30:00.000Z"
  }
}

Listar Transações

GET /v1/transactions?page=1&limit=20

Lista suas transações com paginação. Aceita Secret Key ou Public Key.

Query ParamTipoPadrãoDescrição
pageinteger1Número da página
limitinteger20Itens por página (máx: 100)

Saques (Withdrawals) novo

A API de saques permite que sellers solicitem transferências PIX diretamente via API, sem precisar acessar o painel. Por padrão, os saques são criados como PENDING e processados após aprovação manual do administrador. Se o admin ativar a auto-aprovação de saque via API para a sua conta, o saque é processado e enviado na hora (veja abaixo).

⚠️ Pré-requisitos obrigatórios:
1. Saque via API habilitado — precisa ser ativado pelo admin em Usuários > (seu usuário) > Ações > Saque via API
2. Destino do saque — configurar uma conta bancária cadastrada em Financeiro > Conta Bancária, OU enviar uma chave PIX externa diretamente na requisição (pixKey, pixKeyType, holderName, holderDocument).
3. CPF/CNPJ do titularobrigatório em todo saque. As adquirentes validam o documento contra o dono da chave PIX. No modo PIX externo, envie holderDocument. No modo conta bancária, cadastre o CPF/CNPJ no seu perfil (ou na conta bancária) — sem um documento válido a requisição é recusada com 400.
4. IP na whitelist — o IP que faz a requisição deve estar cadastrado em Desenvolvedores > IPs Autorizados
ℹ️ Dois modos de destino:
Modo padrão — envie apenas amount; o saque é direcionado para a conta bancária cadastrada do seller. O CPF/CNPJ do titular é obtido do cadastro (conta bancária ou perfil) e precisa estar preenchido e válido.
Modo PIX externo — envie amount + pixKey + pixKeyType + holderName + holderDocument; o saque é direcionado para a chave PIX informada (independente da conta bancária cadastrada). Funciona com todas as adquirentes configuradas na plataforma.
⚡ Auto-aprovação de saque via API (opcional):
Quando o admin ativa Auto-aprovar saque para a sua conta (em Usuários > (seu usuário) > Ações > ↳ Auto-aprovar saque, logo abaixo de Saque via API), cada saque criado via POST /v1/withdrawals é processado e enviado imediatamente, sem passar por aprovação manual — o retorno já vem com status: "PAID" ou "PROCESSING" (e o campo autoApproved: true). Requisitos: saque via API habilitado, senha de saque configurada na plataforma e CPF/CNPJ válido do titular. Se algum requisito faltar, o saque cai no fluxo normal e fica PENDING para aprovação manual. Sem esse recurso ativo, todo saque continua PENDING.
ℹ️ Como funciona a taxa de saque:
O campo amount é exatamente o que você receberá. A taxa de saque é cobrada por cima do valor solicitado e debitada separadamente do saldo. Exemplo: se você solicitar R$ 50,00 e a taxa for R$ 2,00, serão debitados R$ 52,00 do saldo e você receberá R$ 50,00.

Status dos Saques

StatusDescrição
PENDINGAguardando aprovação do administrador
PROCESSINGEnviado à adquirente, aguardando confirmação (aparece na auto-aprovação)
PAIDSaque aprovado e PIX enviado para sua conta
REJECTEDSaque reprovado pelo admin (ver campo rejectionReason)
FAILEDRecusado pela adquirente — o valor é devolvido ao saldo automaticamente
UNDER_REVIEWEnvio sem confirmação (timeout) — em conferência; sem reenvio nem estorno até resolver

Criar Saque

POST /v1/withdrawals

Solicita um saque. O valor solicitado + taxa é debitado imediatamente do saldo disponível. O saque fica como PENDING até aprovação manual pelo admin.

Headers

HeaderTipoDescrição
x-api-key string obrigatório Sua Secret Key (sk_live_...). Public Key não é aceita aqui.
Content-Type string obrigatório application/json

Body (JSON)

📄 CPF/CNPJ do titular é sempre necessário. No modo PIX externo (com pixKey), envie no campo holderDocument abaixo. No modo conta bancária (só amount), o documento não vai no body mas precisa estar cadastrado e válido no seu perfil ou na conta bancária — sem ele o saque é recusado com 400. Os badges condicional abaixo se referem ao que vai no corpo da requisição, não à obrigatoriedade do documento em si.
CampoTipoDescrição
amount integer obrigatório Valor a receber em centavos. Ex: R$ 50,00 = 5000. Mínimo: 100 (R$ 1,00). Min/máx adicionais são definidos pela plataforma.
pixKey string opcional Chave PIX de destino. Quando presente, ativa o modo PIX externo e os 3 campos abaixo passam a ser obrigatórios. Sem este campo, o saque vai para a conta bancária cadastrada do seller.
pixKeyType enum condicional Obrigatório quando pixKey é enviado. Valores: CPF · CNPJ · EMAIL · PHONE · RANDOM
holderName string condicional Nome completo do titular da chave PIX. Obrigatório quando pixKey é enviado. Apenas letras, espaços e pontuação simples (. , - '). Mín. 3, máx. 120 caracteres.
holderDocument string condicional CPF (11 dígitos) ou CNPJ (14 dígitos) do titular, somente números. Obrigatório quando pixKey é enviado. No modo conta bancária (sem pixKey) o documento não vai no body, mas precisa estar cadastrado e válido no seu perfil ou na conta bancária — o saque é recusado se não houver.
postbackUrl string opcional URL HTTPS que recebe o webhook deste saque (withdrawal.paid / withdrawal.rejected). Se ausente, o webhook vai para os endpoints configurados em Desenvolvedores > Webhooks que escutam esses eventos.
externalReference string opcional Chave de idempotência (recomendado; pode também ser enviada no header x-idempotency-key). 6–64 caracteres: letras, números, hífen, underscore. Única por conta. Retry com a mesma chave retorna o MESMO saque (campo idempotentReplay: true na resposta) em vez de criar — e debitar — outro. Use sempre que sua integração tiver retry automático/timeout.

Validação de pixKey por tipo

pixKeyTypeFormato exigidoExemplo
CPFExatamente 11 dígitos. Validado com algoritmo de dígitos verificadores."12345678909"
CNPJExatamente 14 dígitos. Validado com algoritmo de dígitos verificadores."12345678000190"
EMAILE-mail válido, máx. 100 caracteres. Convertido para minúsculas."joao@email.com"
PHONE10 a 13 dígitos. Números BR sem código de país recebem prefixo +55 automaticamente."+5511999998888"
RANDOMUUID v4 — 32 hex sem hífens, ou 36 chars no formato 8-4-4-4-12."550e8400-e29b-41d4-a716-446655440000"

Exemplo 1 — Saque para conta bancária cadastrada

curl -X POST https://api.velanipagamentos.com.br/api/v1/api-gateway/v1/withdrawals \
  -H "Content-Type: application/json" \
  -H "x-api-key: sk_live_sua_chave_secreta" \
  -d '{ "amount": 5000 }'

Exemplo 2 — Saque para chave PIX externa

curl -X POST https://api.velanipagamentos.com.br/api/v1/api-gateway/v1/withdrawals \
  -H "Content-Type: application/json" \
  -H "x-api-key: sk_live_sua_chave_secreta" \
  -d '{
    "amount": 5000,
    "pixKey": "joao@email.com",
    "pixKeyType": "EMAIL",
    "holderName": "João da Silva",
    "holderDocument": "12345678909"
  }'

Resposta (200) — Modo padrão (conta bancária)

{
  "success": true,
  "data": {
    "id": "uuid-do-saque",
    "amount": 50.00,
    "fee": 2.00,
    "netAmount": 48.00,
    "status": "PENDING",
    "destination": "BANK_ACCOUNT",
    "pixKey": null,
    "pixKeyType": null,
    "holderName": null,
    "holderDocument": null,
    "createdAt": "2026-05-04T12:00:00.000Z",
    "updatedAt": "2026-05-04T12:00:00.000Z",
    "paidAt": null,
    "rejectionReason": null,
    "autoApproved": false
  }
}
/* amount      = total debitado do saldo (bruto solicitado)
   fee         = taxa da plataforma cobrada
   netAmount   = valor líquido que será creditado no PIX (amount − fee)
   autoApproved = true quando a auto-aprovação processou o saque na hora (status vem "PAID"/"PROCESSING") */

Resposta (200) — Modo PIX externo

{
  "success": true,
  "data": {
    "id": "uuid-do-saque",
    "amount": 50.00,
    "fee": 2.00,
    "netAmount": 48.00,
    "status": "PENDING",
    "destination": "EXTERNAL_PIX",
    "pixKey": "joao@email.com",
    "pixKeyType": "EMAIL",
    "holderName": "João da Silva",
    "holderDocument": "12345678909",
    "createdAt": "2026-05-04T12:00:00.000Z",
    "updatedAt": "2026-05-04T12:00:00.000Z",
    "paidAt": null,
    "rejectionReason": null,
    "autoApproved": false
  }
}
⚡ Resposta com auto-aprovação ativa: quando o admin habilitou Auto-aprovar saque via API para a conta, o mesmo endpoint devolve o saque já processado — "status": "PAID" (ou "PROCESSING" enquanto a adquirente confirma) e "autoApproved": true, com paidAt preenchido. Nenhuma ação manual é necessária. Se a adquirente recusar, o status volta "FAILED" e o valor é devolvido ao saldo automaticamente.
ℹ️ Segurança — IP Whitelist: O IP da requisição deve estar cadastrado em Desenvolvedores > IPs Autorizados. Requisições de IPs não cadastrados retornam 401 Unauthorized. Você pode adicionar até 10 IPs por conta.
🛡️ Validações anti-injection (modo PIX externo): os campos pixKey, holderName e holderDocument são validados contra:
  • caracteres de controle (newline, null byte, tab) — bloqueia log forging;
  • caracteres invisíveis (zero-width, RTL/LTR override, BOM) — bloqueia homoglyph attacks;
  • tags HTML (<, >) e padrões script-like (javascript:, vbscript:, data:, <script>, on*=);
  • backticks e template literals (`, ${});
  • tamanhos máximos: pixKey 140, holderName 120, holderDocument 18.
Inputs que falham qualquer validação retornam 400 Bad Request com mensagem específica.

Erros comuns

HTTPMensagemCausa
400É necessário um CPF/CNPJ válido do titular para saque via API...Modo conta bancária sem CPF/CNPJ válido no perfil nem na conta bancária. Cadastre o documento ou use o modo PIX externo com holderDocument.
400Para saque em chave PIX externa, os campos são obrigatórios: pixKeyType, holderName...Você enviou pixKey mas faltou algum dos demais campos do holder.
400pixKey do tipo CPF deve conter exatamente 11 dígitosFormato da chave PIX não bate com o pixKeyType declarado.
400CPF/CNPJ informado em holderDocument é inválidoDocumento falhou na validação de dígitos verificadores.
400Nome do titular inválidoO holderName contém caracteres fora da whitelist (números, símbolos, etc.).
400Campo X contém caracteres de controle não permitidosAlgum campo trouxe newline, null byte ou outro caractere de controle.
400Saldo insuficienteSaldo disponível menor que amount.
401IP não autorizado para esta contaAdicione o IP em Desenvolvedores > IPs Autorizados.
403Saque via API não está habilitadoPedir ao admin para habilitar em Usuários > (seu usuário) > Ações > Saque via API.

Listar Saques

GET /v1/withdrawals

Lista todos os saques da conta com paginação. Aceita Secret Key ou Public Key.

Query ParamTipoPadrãoDescrição
pageinteger1Número da página
limitinteger20Itens por página (máx: 100)
curl "https://api.velanipagamentos.com.br/api/v1/api-gateway/v1/withdrawals?page=1&limit=20" \
  -H "x-api-key: sk_live_sua_chave_secreta"

Resposta (200)

{
  "success": true,
  "data": [
    {
      "id": "uuid-do-saque",
      "amount": 52.00,
      "fee": 2.00,
      "netAmount": 50.00,
      "status": "PENDING",
      "createdAt": "2026-04-13T12:00:00.000Z",
      "paidAt": null,
      "rejectionReason": null
    }
  ],
  "meta": {
    "total": 5,
    "page": 1,
    "limit": 20
  }
}

Consultar Saque

GET /v1/withdrawals/:id

Consulta detalhes de um saque específico. Aceita Secret Key ou Public Key.

curl "https://api.velanipagamentos.com.br/api/v1/api-gateway/v1/withdrawals/uuid-do-saque" \
  -H "x-api-key: sk_live_sua_chave_secreta"

Resposta (200)

{
  "success": true,
  "data": {
    "id": "uuid-do-saque",
    "amount": 52.00,
    "fee": 2.00,
    "netAmount": 50.00,
    "status": "PAID",
    "createdAt": "2026-04-13T12:00:00.000Z",
    "updatedAt": "2026-04-13T14:30:00.000Z",
    "paidAt": "2026-04-13T14:30:00.000Z",
    "rejectionReason": null
  }
}

Split de Pagamento novo

O Split de Pagamento permite dividir automaticamente o valor de cada venda com parceiros, afiliados ou sócios. Ao criar uma transação, envie o campo split com os destinatários e porcentagens. Quando o pagamento é confirmado, os valores são creditados automaticamente.

Como funciona:
1. Ao criar a transação, envie o campo split com e-mail e porcentagem de cada destinatário
2. O cliente paga normalmente via PIX
3. Após a taxa da plataforma, o valor líquido é dividido automaticamente
4. Parceiro recebe sua porcentagem e você recebe o restante — tudo automático
ℹ️ Regras: A soma de todos os splits não pode ultrapassar 100%. O destinatário precisa ter uma conta na plataforma Velani Pagamentos.

Split na Transação

Envie o campo split ao criar uma transação para dividir o valor automaticamente:

POST /v1/transactions com split

Exemplo com Split Inline

curl -X POST https://api.velanipagamentos.com.br/api/v1/api-gateway/v1/transactions \
  -H "Content-Type: application/json" \
  -H "x-api-key: sk_live_sua_chave_secreta" \
  -d '{
  "paymentMethod": "pix",
  "amount": 10000,
  "customer": {
    "name": "João Silva",
    "email": "joao@email.com"
  },
  "split": [
    { "email": "parceiro@email.com", "percentage": "10" },
    { "email": "afiliado@email.com", "percentage": "5" }
  ],
  "postbackUrl": "https://meusite.com/webhook",
  "externalId": "pedido-456"
}'

Campos do Split

CampoTipoDescrição
split[].email string obrigatório E-mail do destinatário (deve ter conta na plataforma)
split[].percentage string obrigatório Porcentagem do valor líquido. Ex: "10" = 10%

Exemplo de divisão:

Venda: R$ 100,00
Taxa plataforma (6%): - R$ 6,00
Valor líquido: R$ 94,00

Split parceiro (10%): R$ 9,40 → creditado para parceiro@email.com
Split afiliado (5%):  R$ 4,70 → creditado para afiliado@email.com
Você recebe (85%):    R$ 79,90

Webhooks

Receba notificações em tempo real quando transações mudam de status. Configure via painel (Desenvolvedores > Webhooks) ou envie postbackUrl na transação.

Payload do Webhook

{
  "event": "transaction.paid",
  "timestamp": "2026-01-15T10:35:00.000Z",
  "data": {
    "id": "uuid-da-transacao",
    "externalId": "pedido-123",
    "amount": 10000,
    "status": "paid",
    "paymentMethod": "pix",
    "customerName": "João Silva",
    "paidAt": "2026-01-15T10:35:00.000Z"
  },
  "signature": "hmac-sha256-do-payload",
  "version": "1.0",
  "source": "velani-gateway"
}

Eventos disponíveis

EventoCategoriaDescrição
transaction.paidPIXPagamento PIX confirmado
transaction.createdPIXCobrança PIX criada
transaction.failedPIXTransação falhou ou expirou
transaction.refundedPIXTransação reembolsada
withdrawal.paidSaqueSaque aprovado e PIX enviado para sua conta
withdrawal.rejectedSaqueSaque reprovado pelo administrador

Payload — withdrawal.paid / withdrawal.rejected

{
  "event": "withdrawal.paid",
  "timestamp": "2026-04-13T14:30:00.000Z",
  "data": {
    "id": "uuid-do-saque",
    "amount": 52.00,
    "fee": 2.00,
    "netAmount": 50.00,
    "status": "PAID",
    "paidAt": "2026-04-13T14:30:00.000Z",
    "rejectionReason": null,
    "createdAt": "2026-04-13T12:00:00.000Z",
    "updatedAt": "2026-04-13T14:30:00.000Z"
  },
  "signature": "hmac-sha256-do-payload",
  "version": "1.0",
  "source": "velani-gateway"
}

/* Para withdrawal.rejected:
   "event": "withdrawal.rejected",
   "data.status": "REJECTED",
   "data.paidAt": null,
   "data.rejectionReason": "Dados bancários inválidos" */

Validação de Assinatura (HMAC-SHA256)

const crypto = require('crypto');

function validateWebhook(payload, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(JSON.stringify(payload.data))
    .digest('hex');
  return expected === signature;
}

// Express.js
app.post('/webhook/velani', (req, res) => {
  const { event, data, signature } = req.body;
  
  if (!validateWebhook(req.body, signature, 'whsec_seu_secret')) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  if (event === 'transaction.paid') {
    // Pagamento confirmado! Liberar produto.
    console.log(`Pago: ${data.id} - R$ ${(data.amount/100).toFixed(2)}`);
  }

  res.status(200).json({ received: true });
});
ℹ️ Retries: Se o servidor não retornar HTTP 200, reenviaremos o webhook até 3 vezes com intervalos de 30s, 2min e 10min.

Exemplo Completo

Fluxo de integração ponta a ponta com Node.js + Express:

1. Criar cobrança PIX (com split)

const axios = require('axios');

const API_KEY = 'sk_live_sua_chave_secreta';
const BASE_URL = 'https://api.velanipagamentos.com.br/api/v1/api-gateway';

app.post('/criar-pagamento', async (req, res) => {
  const { nome, email, valor } = req.body;

  const { data } = await axios.post(`${BASE_URL}/v1/transactions`, {
    paymentMethod: 'pix',
    amount: Math.round(valor * 100),
    customer: { name: nome, email },
    items: [{ title: 'Meu Produto', unitPrice: Math.round(valor * 100), quantity: 1 }],
    split: [
      { email: 'parceiro@email.com', percentage: '10' }
    ],
    postbackUrl: 'https://meusite.com/webhook/velani',
    externalId: `pedido-${Date.now()}`,
  }, {
    headers: { 'x-api-key': API_KEY },
  });

  res.json({
    transactionId: data.data.id,
    pixCode: data.data.pixQrCode,
    pixImage: data.data.pixQrCodeImage,
    expiresAt: data.data.expiresAt,
  });
});

2. Receber webhook de pagamento

app.post('/webhook/velani', (req, res) => {
  const { event, data, signature } = req.body;

  // Validar assinatura (recomendado)
  // if (!validateWebhook(req.body, signature, 'whsec_...')) { ... }

  if (event === 'transaction.paid') {
    console.log('Pagamento confirmado:', data.id);
    console.log('Valor:', (data.amount / 100).toFixed(2));
    console.log('Pedido:', data.externalId);
    // Liberar acesso ao produto/serviço
    liberarAcesso(data.externalId);
  }

  res.status(200).json({ received: true });
});

3. Exibir QR Code no frontend

<div id="pix-container">
  <h2>Escaneie o QR Code para pagar</h2>
  <img id="qr-image" src="" alt="QR Code PIX" />
  <p>Ou copie o código PIX:</p>
  <input id="pix-code" type="text" readonly />
  <button onclick="navigator.clipboard.writeText(
    document.getElementById('pix-code').value
  )">Copiar</button>
</div>

<script>
async function checkPayment(transactionId) {
  const res = await fetch(
    `/api/check-payment/${transactionId}`
  );
  const data = await res.json();
  if (data.status === 'paid') {
    window.location.href = '/obrigado';
  } else {
    setTimeout(() => checkPayment(transactionId), 3000);
  }
}
</script>

Velani Pagamentos © 2026 · Voltar ao painel · Testar Transação