easypay

Referência da API de cobrança · v1

API de cobrança

Uma API REST para criar cobranças PIX e acompanhar pagamentos. Autenticação por chave; respostas em JSON.

Base e autenticação

https://easypay.linkjohn.com/api/v1
Authorization: Bearer pk_live_xxxxx

Cada sistema integrado usa uma chave própria, revogável a qualquer momento. Uma chave só enxerga as cobranças criadas por ela.

Criar cobrança

POST/charges

curl -X POST https://easypay.linkjohn.com/api/v1/charges \
  -H "Authorization: Bearer pk_live_xxxxx" \
  -H "Idempotency-Key: pedido-8421" \
  -H "Content-Type: application/json" \
  -d '{
    "external_id": "PEDIDO-8421",
    "customer": { "name": "Maria Souza", "document": "12345678909" },
    "amount_cents": 5990,
    "description": "Plano VPS",
    "method": "pix",
    "expires_in": 3600
  }'
campotipo
external_idstringO id da cobrança no seu sistema. Único por integração.
customer.namestringNome do pagador.
customer.documentstringCPF (11) ou CNPJ (14 dígitos).
customer.emailstringOpcional.
amount_centsinteiroValor em centavos. 5990 = R$ 59,90.
descriptionstringOpcional. Aparece para o pagador.
methodstringpix.
expires_ininteiroSegundos até o código deixar de ser pagável. Padrão 3600.

201:

{
  "id": 185,
  "external_id": "PEDIDO-8421",
  "status": "pending",
  "amount_cents": 5990,
  "amount_paid_cents": 0,
  "payment_url": "https://easypay.linkjohn.com/p/m2znt-N-aTWg",
  "pix": {
    "copy_paste": "00020101021226830014BR.GOV.BCB.PIX...",
    "qr_code": null,
    "txid": "2b4351fd1a55b2e5e055e3d65440e98f"
  },
  "expires_at": "2026-08-05T20:27:04Z"
}

Você tem dois caminhos:

Idempotency-Key é obrigatório

Sem o header, a requisição é recusada com 400.

Use um valor estável e único por cobrança — o id do pedido no seu banco serve bem. Não use timestamp nem um UUID novo a cada tentativa: assim ele não protege de nada.

Toda chamada HTTP pode dar timeout sem você saber se chegou. Repetindo sem chave, o cliente ganha duas cobranças. Com a mesma chave, você recebe de volta a cobrança que já existia — com 200 em vez de 201, e nunca uma segunda.

A mesma chave com um corpo diferente devolve 422: é sinal de que a chave foi reaproveitada para outra cobrança.

Dinheiro é sempre inteiro, em centavos

"amount_cents": 5990     ✅  R$ 59,90
"amount": 59.90          ❌  400

Página de pagamento pronta

Toda cobrança nasce com um payment_url. É um link curto, feito para caber numa mensagem de WhatsApp:

https://easypay.linkjohn.com/p/m2znt-N-aTWg

A página tem QR Code, botão de copiar o código PIX, valor, descrição e prazo. É responsiva — a maioria dos pagadores abre no celular.

Ela vira "Pagamento confirmado" sozinha. A página verifica o estado a cada poucos segundos e muda assim que o pagamento cai, sem o pagador precisar recarregar nem voltar ao seu site.

Também trata os outros estados: cobrança expirada, cancelada ou estornada mostram a mensagem certa em vez de oferecer um código que não funciona mais.

O que a página mostra e o que não mostra

No topo aparece o nome da sua operação, para o pagador reconhecer de quem é a cobrança. Peça para configurá-lo junto com a sua chave.

Nome e documento do pagador não aparecem. Link é encaminhado; quem receber não pode ver o CPF de outra pessoa. Se você precisa que o pagador confira esses dados, mostre na sua própria tela antes de mandar o link.

Quem tem o link, vê a cobrança

O token é a credencial da página — não há senha. Trate o payment_url como um dado do pedido: mande ao pagador, não publique em página aberta nem em log compartilhado.

Acompanhar pagamentos

GET/events?since={id}&limit={n}

Não há webhook para o seu sistema. Você lê a fila. Guarde o último id processado e peça dali para frente.

curl "https://easypay.linkjohn.com/api/v1/events?since=1841&limit=100" \
  -H "Authorization: Bearer pk_live_xxxxx"
{
  "events": [
    {
      "id": 1842,
      "type": "payment.confirmed",
      "external_id": "PEDIDO-8421",
      "amount_cents": 5990,
      "amount_paid_cents": 5990,
      "paid_at": "2026-08-05T13:20:11Z",
      "status": "paid"
    }
  ],
  "last_id": 1842,
  "has_more": false
}

Ficou horas fora do ar? Ao voltar, peça a partir do último id que você processou e receba tudo o que passou no intervalo. A fila não expira, então não existe evento perdido — e você não precisa expor endpoint público nenhum.

O laço

a cada 10–30 segundos:
    eventos = GET /events?since={ultimo_id_salvo}
    para cada evento:
        processa                    # libera o serviço, marca como pago
    salva eventos.last_id           # SÓ DEPOIS de processar

Salve o ponteiro depois de processar, nunca antes. Se o processo morrer no meio, o pior que acontece é reprocessar um evento na volta seguinte — inofensivo, desde que a sua liberação de serviço seja idempotente. Salvando antes, um crash perde o evento para sempre.

Sem eventos novos, last_id volta igual ao que você enviou. Não zere seu ponteiro por causa disso.

Tipos de evento

tipoo que fazer
payment.confirmedlibere o serviço
payment.partialpagou menos que o total; a cobrança segue pending. Não libere
payment.overpaidpagou mais; a cobrança fechou. Libere; o excesso vem no evento
charge.expiredo prazo venceu. Ainda pode ser paga depois
charge.cancelledcancelada
charge.createdinformativo — você já soube pela resposta do POST

Consultar e cancelar

GET/charges/{id}

DELETE/charges/{id}

curl https://easypay.linkjohn.com/api/v1/charges/185 \
  -H "Authorization: Bearer pk_live_xxxxx"

curl -X DELETE https://easypay.linkjohn.com/api/v1/charges/185 \
  -H "Authorization: Bearer pk_live_xxxxx"

Erros

Trate pelo code, nunca pelo texto — a mensagem pode mudar, o code não.

{ "error": { "code": "idempotency_key_reused", "message": "..." } }
HTTPcodeo que fazer
400bad_requestfalta o Idempotency-Key, ou o corpo está errado
400validation_errorveja details
401unauthorizedchave ausente, inválida ou revogada
403forbiddena chave não tem o escopo necessário
404not_foundnão existe, ou não pertence à sua chave
409external_id_existsjá existe cobrança com esse external_id
409idempotency_in_progressa primeira requisição ainda está rodando; repita em 1–2s
422idempotency_key_reusedmesma chave com corpo diferente
502gateway_erroro provedor recusou ou está indisponível
504gateway_timeouto provedor não respondeu a tempo

Em 502, 504 e idempotency_in_progress, repita com a mesma Idempotency-Key. É seguro: ou você recebe a cobrança que já existia, ou ela é criada agora. Nunca duas.

Estados

pending ──┬──► paid ──► refunded
          ├──► expired ──► paid      (PIX pago no limite acontece)
          └──► cancelled