Uma API REST para criar cobranças PIX e acompanhar pagamentos. Autenticação por chave; respostas em JSON.
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.
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
}'
| campo | tipo | |
|---|---|---|
external_id | string | O id da cobrança no seu sistema. Único por integração. |
customer.name | string | Nome do pagador. |
customer.document | string | CPF (11) ou CNPJ (14 dígitos). |
customer.email | string | Opcional. |
amount_cents | inteiro | Valor em centavos. 5990 = R$ 59,90. |
description | string | Opcional. Aparece para o pagador. |
method | string | pix (padrão) ou link — ver abaixo. |
expires_in | inteiro | Segundos 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:
payment_url ao pagador
e acabou. É uma página pronta — veja abaixo.pix.copy_paste. O
campo qr_code costuma vir null; gere a imagem
localmente a partir do copy_paste, é a mesma string.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.
"amount_cents": 5990 ✅ R$ 59,90
"amount": 59.90 ❌ 400
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.
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.
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.
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.
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.
| tipo | o que fazer |
|---|---|
payment.confirmed | libere o serviço |
payment.partial | pagou menos que o total; a cobrança segue pending. Não libere |
payment.overpaid | pagou mais; a cobrança fechou. Libere; o excesso vem no evento |
charge.expired | o prazo venceu. Ainda pode ser paga depois |
charge.cancelled | cancelada |
charge.authorized | cartão aprovado. Libere o serviço; o dinheiro cai depois |
charge.declined | cartão recusado. Não libere; o pagador pode tentar outro meio |
charge.chargeback | o portador contestou e o valor foi retirado. Suspenda |
charge.created | informativo — você já soube pela resposta do POST |
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"
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.
Trate pelo code, nunca pelo texto — a mensagem pode mudar, o
code não.
{ "error": { "code": "idempotency_key_reused", "message": "..." } }
| HTTP | code | o que fazer |
|---|---|---|
| 400 | bad_request | falta o Idempotency-Key, ou o corpo está errado |
| 400 | validation_error | veja details |
| 401 | unauthorized | chave ausente, inválida ou revogada |
| 403 | forbidden | a chave não tem o escopo necessário |
| 404 | not_found | não existe, ou não pertence à sua chave |
| 409 | external_id_exists | já existe cobrança com esse external_id |
| 409 | idempotency_in_progress | a primeira requisição ainda está rodando; repita em 1–2s |
| 422 | idempotency_key_reused | mesma chave com corpo diferente |
| 502 | gateway_error | o provedor recusou ou está indisponível |
| 504 | gateway_timeout | o 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:
authorized — a venda foi aprovada pelo emissor. Libere o
serviço, mas saiba que o dinheiro ainda não caiu.paid — o valor foi liquidado na conta. No PIX os dois momentos
são o mesmo; no cartão, separa por semanas.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.
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.
pending_approval ──► approved ──► processing ──┬──► paid
│ └──► failed
└──► rejected
| estado | o que significa |
|---|---|
pending_approval | passou do valor automático. Um humano precisa aprovar no painel. Não saiu nada |
approved | liberado, prestes a sair |
processing | enviado, aguardando confirmação. Não reenvie |
paid | caiu na conta de quem recebe. Vem com e2e_id |
failed | não saiu. O motivo vem em failure_reason |
rejected | um humano recusou no painel |
Chegam pela mesma fila de GET /events, com o
external_id do saque:
| evento | o que fazer |
|---|---|
payout.pending_approval | avise o usuário que está em análise |
payout.approved | saiu do limbo, vai sair |
payout.rejected | devolva o saldo ao usuário. Motivo em reason |
payout.paid | é aqui que acabou. Traz e2e_id |
payout.failed | devolva 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.
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.
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
paid nunca volta para pendingexpired que recebe pagamento vira paidpending com amount_paid_cents menorpaid significa dinheiro na conta, sempre. Cartão
aprovado é authorizedchargeback é o portador contestando: o valor foi retirado. Pode
voltar para paid se a disputa for ganha