Pular para o conteúdo
LeonaPayLeonaPayDevelopers
Primeiros passosReferenciais da APIIntegraçõesv2
Internacional · BRL, MXN, EUR, COP, ARS e USD

Cartão de débito

Receba pagamentos com Cartão de débito usando o endpoint unificado da LeonaPay.

Visão geral

Use POST /api/v2/payments com method: DEBIT_CARD. O valor deve ser positivo e a moeda deve corresponder ao mercado.
CampoValor
MétodoDEBIT_CARD
MercadoInternacional
MoedasBRL, MXN, EUR, COP, ARS e USD
ConfirmaçãoSíncrona ou por webhook

Criar pagamento Cartão de débito

Envie uma referência única do seu pedido. Campos específicos do método aparecem abaixo.
Não envie paymentMethodToken para iniciar os campos seguros. Use clientSecret para montar número, validade e CVV separadamente na sua interface. sessionToken e checkoutUrl permanecem apenas para compatibilidade temporária. Antes da captura, a LeonaPay pode corrigir o método conforme o tipo real do cartão, se a configuração da empresa e as condições comerciais permitirem.
curl --request POST \
+  --url https://api.leonapay.com.br/api/v2/payments \
+  --header 'Accept: application/json' \
+  --header 'Content-Type: application/json' \
+  --header 'x-api-key: {your_api_key}' \
+  --header 'idempotency-key: {unique_uuid_per_payment}' \
  --data '{
  "method": "DEBIT_CARD",
  "amount": 125.90,
  "currency": "BRL",
  "reference": "pedido-123",
  "payer": { "name": "Ana Souza", "email": "ana@exemplo.com", "taxId": "12345678909" },
  "returnUrl": "https://loja.exemplo.com/pagamento/retorno"
}'

Sequência completa da integração

O pagamento com cartão possui duas etapas conectadas. Primeiro, o seu backend cria o pagamento na API da LeonaPay. Depois que a resposta trouxer nextAction.type: CONFIRM_CARD, o seu frontend usa o clientSecret temporário para montar três campos seguros e separados. Assim, você controla completamente o layout sem que PAN, validade ou CVV passem pelo seu servidor. Para iniciar esse fluxo, não envie paymentMethodToken: ele representa um cartão já tokenizado e não é a credencial temporária dos campos.
A chave LeonaPay existente continua somente no servidor. O navegador recebe apenas o clientSecret temporário da sessão. Não é necessário criar ou distribuir uma segunda chave pública da LeonaPay.
OrdemResponsávelO que acontece
1Seu frontendO comprador escolhe crédito ou débito e confirma o pedido
2Seu backendCria o pagamento com DEBIT_CARD, usando a chave LeonaPay existente somente no servidor
3API LeonaPayQuando a sessão estiver pronta, retorna ACTION_REQUIRED com data.nextAction; enquanto prepara, pode retornar PENDING
4Seu frontendChama OnpayCard.elements(...) e monta cardNumber, cardExpiry e cardCvc
5LeonaPayTokeniza cada campo seguro sem expor os dados do cartão à sua aplicação
6CompradorPreenche o cartão e conclui o desafio 3DS, quando solicitado
7Seu backendRecebe o webhook assinado e libera o pedido somente após payment.paid

Preparação assíncrona e consulta da sessão

A criação aguarda a preparação por um período limitado. HTTP 201 comPENDING e sem data.nextAction significa que o pagamento foi criado, mas os campos ainda não estão prontos. Consulte GET /api/v2/transactions/{transactionId}no seu backend, usando a mesma empresa e ambiente e a permissãotransactions:read. A cada consulta, atualize tambémdata.nextAction, não somente o status. Não crie outra cobrança nem troque a chave de idempotência para obter os campos.
Uma repetição idempotente da criação pode reproduzir a resposta inicial. Use a consulta para obter o estado atual. Respeite expiresAt: uma sessão expirada não deve ser reutilizada. checkoutUrl não significa HOSTED quando integrationMode é SECURE_FIELDS. returnUrl deve usar a mesma origem HTTPS do checkout que montará os campos.
RespostaTratamento
PENDING sem nextActionMostrar preparação e consultar a mesma transação com intervalo e limite de tentativas
ACTION_REQUIRED + SECURE_FIELDS + clientSecretMontar os campos integrados com data.nextAction.clientSecret
ACTION_REQUIRED + HOSTEDUsar checkoutUrl; essa sessão não oferece campos integrados
ACTION_REQUIRED sem nextActionNão montar campos nem considerar pago; pode haver análise de segurança. Consultar e acompanhar
AUTHORIZEDAutorizado, mas ainda não liquidado; não liberar o pedido
PAIDConfirmar no backend antes de liberar o pedido uma única vez
FAILED ou EXPIREDInterromper a espera e apresentar o resultado

Cartões de teste no Sandbox

Use estes números somente nos campos seguros do Sandbox. Informe qualquer nome, validade futura e CVV de três dígitos, exceto quando o cenário indicar outro valor. O resultado é determinístico e nenhum dado é enviado a uma adquirente real.
Números diferentes dos documentados são recusados no Sandbox. Nunca use cartões reais em testes.
NúmeroCenário
4242424242424242Pagamento aprovado
4000000000003220Pagamento aprovado com desafio 3DS
4000000000000002Cartão recusado
4000000000009995Saldo insuficiente
4000000000000069Cartão expirado
4000000000000127CVV inválido
4000000000003063Desafio 3DS recusado
4000000000000119Timeout: pagamento permanece pendente

clientSecret, sessionToken e paymentMethodToken

paymentMethodToken é uma entrada opcional para integrações previamente habilitadas que já possuem um cartão tokenizado no mesmo ambiente. Ele nunca é retornado pela criação do pagamento. No fluxo recomendado, omita esse campo. A LeonaPay cria a sessão e responde comclientSecret; use-o somente no navegador para construir os campos seguros. sessionToken e checkoutUrl são aliases legados mantidos durante a migração do checkout hospedado.
Nunca envie número do cartão, validade ou CVV para POST /api/v2/payments, para seu backend ou para logs. Esses dados existem somente dentro dos campos seguros LeonaPay.
CampoDireçãoFinalidade
paymentMethodTokenEntrada opcionalConfirma um cartão já tokenizado; não cria nem abre o checkout
clientSecretSaída recomendadaAutoriza temporariamente os campos seguros daquela transação
sessionTokenAlias legadoMantém compatibilidade com integrações de checkout hospedado
checkoutUrlSaída legadaEndereço do checkout hospedado durante o período de migração

Campos seguros LeonaPay

Monte número, validade e CVV separadamente na sua própria interface. Crie uma única coleção com OnpayCard.elements, monte cada elemento no contêiner escolhido e finalize comconfirmCardPayment. Todos os elementos precisam usar o mesmo clientSecret.
O clientSecret é temporário e autoriza somente aquela confirmação. Não o registre em logs ou analytics. O retorno do navegador não confirma o pagamento: use GET /api/v2/payments/{paymentId} ou aguarde payment.paid no webhook assinado.
<!-- checkout.html: você controla a estrutura e o visual externo -->
<label>Número do cartão <div id="card-number"></div></label>
<div class="card-row">
  <label>Validade <div id="card-expiry"></div></label>
  <label>CVV <div id="card-cvc"></div></label>
</div>
<button id="pay-card" type="button">Pagar</button>
<p id="card-error" role="alert"></p>
<script src="https://client.leonapay.com.br/sdk/onpay-card.js"></script>
<script src="/checkout.js"></script>

// checkout.js
async function prepararPagamentoDoCartao(dadosDoPedido) {
  // Esta rota pertence ao seu backend. É ele que chama POST /api/v2/payments.
  const response = await fetch("/api/pagamentos", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(dadosDoPedido)
  });
  let payment = await response.json();

  if (!response.ok) throw new Error(payment.onpayDescription || "Falha ao criar pagamento");

  const transactionId = payment?.data?.id;
  if (!transactionId) throw new Error("Resposta sem identificador de pagamento");

  // A rota GET abaixo também pertence ao seu backend: ela consulta
  // GET /api/v2/transactions/{transactionId}, com a chave somente no servidor.
  for (let attempt = 0; attempt < 30; attempt++) {
    if (payment?.data?.nextAction || !["PENDING", "ACTION_REQUIRED"].includes(payment?.data?.status)) break;
    await new Promise(resolve => setTimeout(resolve, 1000));
    const lookup = await fetch("/api/pagamentos/" + encodeURIComponent(transactionId), { cache: "no-store" });
    const latest = await lookup.json();
    if (!lookup.ok || latest.success === false) throw new Error(latest.onpayDescription || "Falha ao consultar pagamento");
    payment = latest; // Atualize a resposta inteira, incluindo data.nextAction.
  }

  const action = payment?.data?.nextAction;
  if (action?.type !== "CONFIRM_CARD") {
    // Mostre o estado atual. Preserve o ID para retomar a consulta, sem novo POST.
    // ACTION_REQUIRED isolado pode ser análise de segurança, não campos prontos.
    return { transactionId, status: payment?.data?.status };
  }
  if (action.integrationMode !== "SECURE_FIELDS" || !action.clientSecret) {
    throw new Error("Esta sessão não oferece campos integrados; verifique o modo de integração");
  }
  if (!Number.isFinite(Date.parse(action.expiresAt)) || Date.parse(action.expiresAt) <= Date.now()) {
    throw new Error("A sessão de cartão expirou");
  }

  const elements = OnpayCard.elements({
    clientSecret: action.clientSecret,
    appearance: {
      color: "#111827",
      fontFamily: "Inter, sans-serif",
      fontSize: "16px"
    }
  });

  elements.create("cardNumber").mount("#card-number");
  elements.create("cardExpiry").mount("#card-expiry");
  elements.create("cardCvc").mount("#card-cvc");

  document.querySelector("#pay-card").addEventListener("click", async () => {
    const button = document.querySelector("#pay-card");
    const errorNode = document.querySelector("#card-error");
    button.disabled = true;
    errorNode.textContent = "";

    const result = await OnpayCard.confirmCardPayment({
      clientSecret: action.clientSecret,
      elements
    });

    if (result.error) {
      errorNode.textContent = result.error.message;
      button.disabled = false;
      return;
    }

    // Mostre "processando". Seu backend confirma por webhook ou consulta.
    console.log("Confirmação enviada", result.payment.id);
  });
}

prepararPagamentoDoCartao({
  method: "DEBIT_CARD",
  amount: 125.90,
  currency: "BRL",
  reference: "pedido-123",
  payer: {
    name: "Ana Souza",
    email: "ana@exemplo.com",
    taxId: "12345678909"
  },
  returnUrl: "https://loja.exemplo.com/pagamento/retorno"
});

Migração do checkout hospedado

Integrações novas devem usar OnpayCard.elements. O métodoOnpayCard.create continua disponível temporariamente para quem já abre o checkout hospedado, sem interromper pagamentos durante a migração. Você pode atualizar a apresentação sem trocar a chave secreta LeonaPay usada pelo seu backend.
IntegraçãoEstadoAção recomendada
OnpayCard.elementsRecomendadaAdote campos separados em integrações novas
OnpayCard.createCompatibilidadeMigre gradualmente para os campos separados
Chave LeonaPay do backendSem alteraçãoContinue autenticando POST /api/v2/payments no servidor

Política de segurança de conteúdo (CSP)

Se sua página usa uma CSP restritiva, autorize o SDK da LeonaPay e os domínios técnicos abaixo. Eles são usados exclusivamente para tokenização e autenticação do cartão; nenhuma chave nativa é exposta.
script-src 'self' https://client.leonapay.com.br https://js.stripe.com;
connect-src 'self' https://client.leonapay.com.br https://api.stripe.com https://r.stripe.com https://m.stripe.network;
frame-src 'self' https://client.leonapay.com.br https://js.stripe.com https://hooks.stripe.com;

Crédito e débito: o que muda?

O momento de exibição do formulário, o SDK e o tratamento do 3DS são iguais nos dois fluxos. Envie o método escolhido pelo comprador. Antes da captura, a LeonaPay pode corrigir method conforme o tipo real do cartão: credit corresponde a CREDIT_CARD; debit ou prepaid, a DEBIT_CARD. A correção exige empresa habilitada, método permitido na mesma integração e moeda, limites válidos e valor suficiente para as taxas e os splits existentes. Tipo desconhecido ou condições não permitidas não autorizam a captura. O valor bruto, a moeda, a referência e o PaymentIntent original são preservados.
Método processadoTipo real do cartãoConfirmação
CREDIT_CARDCrédito (funding credit)Checkout seguro e 3DS quando solicitado
DEBIT_CARDDébito ou pré-pago (funding debit ou prepaid)Checkout seguro e 3DS quando solicitado

Aviso de correção do método

Quando a correção é registrada, data.method indica o método processado e data.warnings inclui ONP_CARD_METHOD_CORRECTED na consulta GET /api/v2/payments/{paymentId} e nos webhooks assinados de pagamento. A resposta inicial de POST /api/v2/payments pode ainda não conter o aviso, pois a confirmação é assíncrona. O exemplo abaixo mostra um trecho de data de um pagamento corrigido e já confirmado.
O aviso não é uma recusa nem uma confirmação de pagamento. Não reenvie a cobrança corrigida. Use processedMethod apenas em solicitações futuras com esse tipo de cartão e acompanhe o pagamento atual até o status final. AUTHORIZED ainda aguarda captura; libere o pedido somente após PAID.
{
  "id": "pay_123",
  "reference": "pedido_123",
  "status": "PAID",
  "method": "DEBIT_CARD",
  "warnings": [
    {
      "code": "ONP_CARD_METHOD_CORRECTED",
      "field": "method",
      "requestedMethod": "CREDIT_CARD",
      "processedMethod": "DEBIT_CARD",
      "message": "O método foi ajustado ao tipo do cartão informado. Use DEBIT_CARD nas próximas solicitações com este tipo de cartão. Não reenvie esta cobrança."
    }
  ]
}

Fluxo 3DS e confirmação final

O emissor decide se o fluxo será frictionless ou challenge. Durante o desafio, mantenha o pedido aguardando. A fonte definitiva de verdade é o webhook assinado da LeonaPay, não o retorno do navegador.
A LeonaPay valida valor, moeda, empresa, PaymentIntent e tipo real do cartão antes da captura. CREDIT_CARD aceita funding credit; DEBIT_CARD aceita debit ou prepaid.
Status LeonaPayEventoAção
ACTION_REQUIREDpayment.action_requiredExibir ou continuar a autenticação 3DS
AUTHORIZEDpayment.authorizedAguardar captura/conclusão
PAIDpayment.paidLiberar o pedido
FAILEDpayment.failedPermitir nova tentativa com nova idempotência
REFUNDEDpayment.refundedRegistrar a devolução

Liquidação e antecipação

Crédito e débito possuem configurações comerciais independentes. A LeonaPay resolve primeiro a regra específica da empresa e, quando ela não existe, usa a configuração global do método. A taxa e o prazo vigentes são gravados como snapshot na criação do pagamento. Somente uma correção segura do método antes da captura recalcula a taxa e registra as condições de liquidação e antecipação do método processado. Após essa decisão, o snapshot permanece imutável; mudanças posteriores nas configurações não alteram o pagamento. settlementDays usa dias corridos; após a confirmação, releaseAt informa quando o valor sai de RELEASING e fica disponível.
Consulte releaseAt em vez de calcular a data no seu sistema. Feriados e finais de semana não suspendem a contagem configurada.
MétodoConfiguraçãoLiquidação
CREDIT_CARDTaxa, prazo e antecipação própriosDias corridos
DEBIT_CARDTaxa, prazo e antecipação própriosDias corridos

Ciclo da antecipação

Um recebível elegível nasce como SCHEDULED. Ao solicitar antecipação, ele fica RESERVED_FOR_ADVANCE e a solicitação permanece UNDER_REVIEW. Se aprovada, o líquido da antecipação é creditado e o recebível muda para ADVANCED; se recusada, volta ao agendamento normal. Repetições devem preservar a mesma chave de idempotency para não criar solicitações duplicadas.
EstadoSignificado
SCHEDULEDAguardando a data normal de liberação
RESERVED_FOR_ADVANCEReservado enquanto a solicitação é analisada
ADVANCEDAntecipação aprovada e liquidada
RELEASEDLiberado pela agenda normal

Erros e retentativas

Erros de validação retornam 4xx. Indisponibilidade transitória retorna ONP503_CREDIT_CARD_TEMPORARILY_UNAVAILABLE ou ONP503_DEBIT_CARD_TEMPORARILY_UNAVAILABLE. Recusa, falha de 3DS, tipo de cartão desconhecido ou correção não permitida podem resultar em payment.failed. Uma correção aceita aparece em warnings e não exige reenvio. Consulte o pagamento antes de iniciar outra cobrança. Retente falhas transitórias somente quando retryable=true, com a mesma idempotência; após confirmar FAILED, uma nova tentativa do cliente deve usar outro UUID. Em Production, use pelo menos USD 1,00 no teste controlado, pois valores inferiores podem não alcançar o mínimo do adquirente após a conversão para a moeda de liquidação.
CódigoSignificado
ONP202_3DSConfirmação do titular necessária
ONP422_*_PAYMENT_METHOD_TOKEN_INVALIDToken inválido ou criado em outro ambiente; gere um novo token e use uma nova idempotência
ONP422_*_AMOUNT_TOO_SMALLValor abaixo do mínimo do adquirente; aumente o valor e use uma nova idempotência
ONP400_RETURN_URLURL de retorno/3DS ausente
ONP409_IDEMPOTENCY_CONFLICTChave reutilizada com conteúdo diferente
ONP503_*_TEMPORARILY_UNAVAILABLEMétodo temporariamente indisponível
payment.failedEmissor, 3DS ou funding recusou a operação

Erros e retentativas de Cartão de débito

Toda falha inclui error.code, error.retryable, error.requestId e um link direto para a referência. Corrija erros ONP400 antes de criar uma nova tentativa. Para ONP502, ONP503 ou ONP429, repita exatamente a mesma requisição com a mesma chave de idempotência após retryAfter.
No painel, Integrações → Logs da API · 24h mostra a resposta pública e a tratativa pelo requestId, sem expor a liquidante usada.
CódigoInterpretaçãoAção
ONP400_*Campo, moeda ou regra do método inválidaCorrigir o campo indicado em error.field
ONP422_DEBIT_CARD_REJECTEDOperação recusada de forma definitivaRevisar os dados e criar nova tentativa
ONP502_DEBIT_CARD_INVALID_RESPONSEInstruções de pagamento incompletasRetentar com a mesma idempotência
ONP503_DEBIT_CARD_TEMPORARILY_UNAVAILABLEMétodo temporariamente indisponívelBackoff com jitter e mesma idempotência

Resposta

Uma criação aceita retorna HTTP 202 quando exige confirmação. O status inicial pode ser ACTION_REQUIRED, PENDING, AUTHORIZED ou PAID.
{
  "success": true,
  "onpayCode": "ONP202_3DS",
  "onpayDescription": "Customer confirmation required",
  "data": {
    "id": "pay_01J5VX8M8W8W6Q",
    "reference": "pedido-123",
    "status": "ACTION_REQUIRED",
    "method": "DEBIT_CARD",
    "currency": "BRL",
    "amount": 125.90,
    "fee": 1.75,
    "settlementDays": 7,
    "releaseAt": null,
    "customer": {
      "id": "cus_01J5VY2Q8G4K9D",
      "name": "Ana Souza",
      "email": null,
      "phone": null,
      "taxId": "12345678909"
    },
    "nextAction": {
      "type": "CONFIRM_CARD",
      "integrationMode": "SECURE_FIELDS",
      "clientSecret": "ocs_...",
      "sessionToken": "ocs_...",
      "checkoutUrl": "https://client.leonapay.com.br/card-session/ocs_...",
      "expiresAt": "2026-08-28T18:30:00Z"
    },
    "paidAt": null,
    "createdAt": "2026-08-25T01:04:23.295Z",
    "updatedAt": "2026-08-25T01:04:24.738Z"
  }
}

Ciclo da transação

Não conclua o pedido apenas pelo retorno do navegador. Consulte a API ou espere o evento assinado payment.paid.
StatusDescrição
ACTION_REQUIREDAguardando confirmação ou autenticação 3DS
PENDINGAguardando ação ou confirmação
AUTHORIZEDAutorizado, aguardando conclusão
PAIDPagamento confirmado
FAILEDFalha no processamento
EXPIREDPrazo encerrado
REFUNDEDValor estornado
LeonaPay Developers · API v2