EasyPay

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 — nunca as de outro.

Ainda não tem chave? Crie sua conta. Cada cadastro é aprovado manualmente e, na aprovação, você recebe por e-mail o acesso ao painel e a primeira chave.

A chave é mostrada uma vez e guardada apenas como hash — nem nós conseguimos recuperá-la depois. Perdeu? Emitimos outra pelo painel e a anterior é revogada.

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 (padrão) ou link — ver abaixo.
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.authorizedcartão aprovado. Libere o serviço; o dinheiro cai depois
charge.declinedcartão recusado. Não libere; o pagador pode tentar outro meio
charge.chargebacko portador contestou e o valor foi retirado. Suspenda
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"

Painel

Além da API, sua conta tem painel em easypay.linkjohn.com/admin — mesmo usuário e senha do cadastro.

Nele você acompanha o que entrou no dia e no mês, busca cobrança por fatura ou por cliente, vê a fila de eventos exatamente como o seu sistema a lê, e cancela cobrança quando precisa. Você enxerga apenas o seu projeto.

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.

Mandando "method": "link", a resposta traz checkout_url em vez do código PIX. É uma página hospedada onde o pagador conclui o pagamento.

Cartão de crédito está pausado. Hoje o link sai como boleto. Para receber na hora, use "method": "pix", que é instantâneo e continua sendo o caminho principal.

{
  "id": 186,
  "external_id": "PEDIDO-8422",
  "status": "pending",
  "amount_cents": 5990,
  "method": "link",
  "checkout_url": "https://pagamento.sejaefi.com.br/bb6c62c7-...",
  "payment_url": "https://easypay.linkjohn.com/p/OE7KsbN3x5Ju"
}

Os dois endereços servem: o payment_url é o nosso e redireciona para o outro. Mande o que preferir.

Cartão não é dinheiro na hora. Quando o pagamento é por cartão, a cobrança passa por authorized antes de paid:

Se o seu sistema só reage a paid, o cliente que pagou com cartão espera o serviço até a liquidação. Trate charge.authorized.

Taxa e parcelamento seguem a configuração da conta e aparecem para o pagador na própria tela. O valor que você manda em amount_cents é o valor do produto.

Saques

Manda PIX para a chave de alguém. Só funciona em projeto habilitado para isso e com uma chave que tenha o escopo payouts:create — ele não vem por padrão.

POST /api/v1/payouts
Authorization: Bearer pk_live_xxx
Idempotency-Key: SAQUE-123
Content-Type: application/json

{
  "external_id": "SAQUE-123",
  "amount_cents": 5000,
  "pix_key": "chave-de-quem-recebe",
  "pix_key_type": "cpf | cnpj | email | phone | evp",
  "receiver": { "name": "...", "document": "..." }
}

202:

{
  "id": "9f3c...",
  "external_id": "SAQUE-123",
  "status": "processing",
  "amount_cents": 5000,
  "pix_key_type": "cpf",
  "pix_key": "1114···7735"
}

Responde 202, nunca 201. O saque foi aceito, não concluído. PIX enviado leva alguns segundos para confirmar — e pode falhar. Só trate como pago quando chegar payout.paid.

Estados

pending_approval ──► approved ──► processing ──┬──► paid
                          │                     └──► failed
                          └──► rejected
estadoo que significa
pending_approvalpassou do valor automático. Um humano precisa aprovar no painel. Não saiu nada
approvedliberado, prestes a sair
processingenviado, aguardando confirmação. Não reenvie
paidcaiu na conta de quem recebe. Vem com e2e_id
failednão saiu. O motivo vem em failure_reason
rejectedum humano recusou no painel

Eventos

Chegam pela mesma fila de GET /events, com o external_id do saque:

eventoo que fazer
payout.pending_approvalavise o usuário que está em análise
payout.approvedsaiu do limbo, vai sair
payout.rejecteddevolva o saldo ao usuário. Motivo em reason
payout.paidé aqui que acabou. Traz e2e_id
payout.faileddevolva o saldo. Motivo em reason

Se um saque ficar parado em processing, não reenvie e não desista. A conciliação pergunta à EFI a cada poucos minutos e resolve para paid ou failed sozinha. Reenviar um PIX que já saiu é pagar duas vezes.

Limites

GET /api/v1/payouts devolve, junto da lista, quanto ainda dá para sacar hoje:

"limits": {
  "auto_approval_cents": 10000,
  "daily_cap_cents": 30000,
  "spent_today_cents": 15000,
  "remaining_today_cents": 15000
}

Acima de auto_approval_cents o saque espera aprovação. Acima do teto diário, a criação é recusada com daily_cap_exceeded.

GET /api/v1/payouts/{id} aceita o id ou o external_id — útil para reconciliar depois de uma queda.

Estados

pending ──┬──► paid ──┬──► refunded
          │           └──► chargeback
          ├──► authorized ──► paid     (cartão: aprovado, depois liquidado)
          ├──► expired ──► paid        (PIX pago no limite acontece)
          ├──► declined                (cartão recusado; pode tentar de novo)
          └──► cancelled