Criar uma cobrança
Uma chamada, um header de idempotência, e a resposta já traz o QR Code.
#!/usr/bin/env bash
# Cria uma cobrança PIX. Precisa de LM_API_URL e LM_API_KEY no ambiente.
# export LM_API_URL=https://api.leztypay.com
# export LM_API_KEY=sk_test_...
set -euo pipefail
curl --fail-with-body -sS "$LM_API_URL/v1/transactions" \
-H "Authorization: Bearer $LM_API_KEY" \
-H "Idempotency-Key: pedido-A123-tentativa-1" \
-H "Content-Type: application/json" \
-d '{
"amount": 10000,
"payment_method": "pix",
"customer": {
"name": "Maria Silva",
"email": "[email protected]",
"document": "123.456.789-09"
},
"items": [{ "name": "Curso X", "quantity": 1, "unit_amount": 10000 }],
"pix": { "expires_in_seconds": 1800 },
"postback_url": "https://loja.exemplo.com/webhooks/leztypay",
"metadata": { "order_id": "A123" }
}'
O mesmo em Node, com tratamento de erro:
// Cria uma cobrança PIX com o fetch global do Node 22. Sem dependência.
// LM_API_URL=https://api.leztypay.com LM_API_KEY=sk_test_... node create-transaction.mjs
const apiUrl = process.env.LM_API_URL;
const apiKey = process.env.LM_API_KEY;
// A chave de idempotência é do SEU pedido, não da requisição. Repetir a mesma chave com o
// mesmo corpo devolve a cobrança já criada em vez de criar uma segunda.
const idempotencyKey = 'pedido-A123-tentativa-1';
const res = await fetch(`${apiUrl}/v1/transactions`, {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Idempotency-Key': idempotencyKey,
'Content-Type': 'application/json',
},
body: JSON.stringify({
amount: 10000,
payment_method: 'pix',
customer: {
name: 'Maria Silva',
email: '[email protected]',
document: '123.456.789-09',
},
items: [{ name: 'Curso X', quantity: 1, unit_amount: 10000 }],
pix: { expires_in_seconds: 1800 },
postback_url: 'https://loja.exemplo.com/webhooks/leztypay',
metadata: { order_id: 'A123' },
}),
});
const body = await res.json();
if (!res.ok) {
// Todo erro tem o mesmo envelope. `request_id` é o que o suporte pede.
console.error(res.status, body.error.code, body.error.message, body.error.request_id);
process.exit(1);
}
if (res.headers.get('Idempotent-Replayed') === 'true') {
console.log('resposta salva de uma tentativa anterior com a mesma chave');
}
console.log('id:', body.id);
console.log('status:', body.status);
console.log('expira em:', body.pix.expires_at);
console.log('copia e cola:', body.pix.payload);
A lista completa do que entra e do que volta está na referência da operação. Aqui ficam só as regras que a referência não conta.
O mínimo
Só amount e customer são obrigatórios. Todo o resto tem padrão. Em cobrança PIX, amount tem
mínimo de R$ 3,00 (300); abaixo disso é 400 amount_too_small.
O cliente precisa de nome, e-mail e documento. Em cobrança PIX, o documento é CPF ou CNPJ e é
validado de verdade, com dígito verificador; pode vir formatado ou só com dígitos, tanto faz.
Documento inválido é 400 invalid_document.
payment_method aceita pix, que é o padrão, voucher, a cobrança em moeda local descrita mais
abaixo, e crypto, a cobrança em USDT ou USDC descrita em Cobrança em cripto.
Qualquer outro valor é 400 unsupported_payment_method.
A taxa é congelada na criação
A resposta traz fee e net já calculados (reserve vem 0 no PIX) com as condições vigentes naquele instante, e
eles não mudam mais. Uma cobrança criada hoje liquida com a taxa de hoje, mesmo que seja paga daqui
a uma semana com outra tabela em vigor.
Se a taxa consumisse a cobrança inteira, a criação é recusada com 400 amount_too_small. É por isso
que um valor acima do mínimo ainda pode ser recusado por ser pequeno demais.
Os campos que existem para você, não para nós
metadataé o seu campo livre: um objeto plano de strings que volta igual em toda consulta e em todo evento. É onde mora o id do pedido na sua loja. Nós não lemos, não indexamos e não interpretamos nada dele. Tetos em Limites.itemsé informativo. A soma dos itens não precisa bater comamount, e não validamos isso. Serve para o extrato e para o painel ficarem legíveis.descriptionviaja junto para o processador de pagamentos. Se você não mandar, geramos uma.trackingcarrega parâmetros de origem da venda. Só as sete chaves previstas são aceitas; chave desconhecida é400 validation_failed.
postback_url: webhook só desta cobrança
postback_url aponta o destino do webhook daquela cobrança, sem passar pelo endpoint da conta.
É o caminho rápido para testar e para quem precisa separar destinos por fluxo.
Regras: precisa ser https e precisa apontar para um destino público. Endereço interno, IP privado
ou localhost são recusados com 400 invalid_url, e não é configurável: é o que impede a API de
ser usada para varrer a rede de quem a hospeda.
Em produção, domínios reservados para exemplo (example.com, example.net, example.org,
localhost e os finais .example, .test, .invalid, .localhost) são recusados com
400 validation_failed e param: postback_url. No ambiente de teste não há essa checagem.
Em produção, prefira o endpoint da conta, cadastrado no painel: ele tem segredo próprio, recebe todos os eventos e aparece no histórico de entregas.
Cobrança em moeda local: voucher
Com payment_method: "voucher" a cobrança sai na moeda do comprador, em vez de PIX. Hoje as moedas
aceitas são MXN (México), CLP (Chile), COP (Colômbia) e PEN (Peru); qualquer outra é
400 unsupported_currency.
{
"payment_method": "voucher",
"currency": "MXN",
"amount": 10000,
"customer": {
"name": "Juan Perez",
"email": "[email protected]",
"document": "XAXX010101000",
"document_type": "RFC"
}
}
O que muda em relação ao PIX:
currencyé obrigatório.amountvai na moeda local, com duas casas implícitas em toda moeda:10000são 100,00 pesos mexicanos e500000são 5.000 pesos chilenos.- Valor mínimo por moeda:
MXN70,00 (7000),CLP3.800 (380000),COP13.000 (1300000) ePEN14,00 (1400), perto de R$ 20 em cada. Abaixo disso é400 amount_too_small, comparam: "amount". customer.document_typeé obrigatório e depende da moeda:RFCouCURPemMXN,RUTouRUNemCLP,CCouNITemCOP,DNIouRUCemPEN. Documento que não bate com o tipo é400 invalid_document_for_country. O mesmo erro (comparam: "customer.document") volta quando o processador recusa o documento na criação: a cobrança ficafailed, é erro do comprador e não adianta repetir com o mesmo documento.customer.addressé opcional. Se vier,countryprecisa ser o país da moeda (MX,CL,COouPE).splitsé aceito só comkind: percent, e aceitabase(grossounet).fixedé400 split_rejected_for_voucher. Veja Split em voucher. O objetopixnão é aceito (400 validation_failed).
Na resposta, pix vem null e os dados de pagamento vêm em voucher: barcode,
digitable_line, url, expiration_date, instructions e receipt_url. Qualquer um deles pode
ser null, conforme o meio de pagamento do país: mostre ao comprador os que vierem preenchidos.
No Peru, por exemplo, vem só digitable_line (o código de pagamento), sem url.
currency e local_amount trazem a moeda e o valor cobrados. amount, fee e net continuam em
reais: são a conversão do dia da criação e valem como estimativa até a cobrança ficar paid.
Os eventos são os mesmos transaction.*, com payment_method: "voucher". Cobrança voucher não
pode ser estornada: o pedido de estorno responde 409 refund_unsupported_for_payment_method.
Quando a criação falha
400de validação: nada foi criado. Corrija e repita com a mesmaIdempotency-Key.403 seller_not_allowed: a conta não pode cobrar neste ambiente agora. Ver Autenticação.409 idempotency_conflictou409 idempotency_in_progress: ver Idempotência.503 acquirer_timeout: o processador não respondeu a tempo e a cobrança pode ter sido criada lá. Ela ficapending, sem QR. Espere 60 s e repita com a mesmaIdempotency-Key: a Lezty Pay pergunta de novo ao processador pela mesma cobrança, sem criar outra, e devolve o201com o QR. Antes dos 60 s a mesma chave responde409 idempotency_in_progress. Se o503voltar comIdempotent-Replayed: true, a falha ficou gravada (processador que não garante a mesma cobrança): aí sim use chave nova. Trocar de chave antes disso pode gerar duas cobranças.503 acquirer_rejectedouacquirer_unavailable: o processador recusou ou estava fora. A cobrança ficafailed, sem QR. Repita com uma chave de idempotência nova, porque a mesma chave devolveria esse mesmo503de novo.
Cobrança failed responde com pix.payload e pix.qr_base64 nulos. Nunca mostre a tela de
pagamento antes de conferir que eles vieram preenchidos.
Próximo passo
QR Code: o que fazer com o pix.payload e com o pix.qr_base64 que acabaram
de chegar.
Referência: Cria uma cobrança (PIX, voucher, cripto, cartão ou carteira).