Integre pagamentos PIX sem carregar a complexidade financeira.
Seu backend cria a cobrança, mostra o PIX no seu próprio checkout e recebe a confirmação assinada. A integração pública usa uma API única e dois modelos de produto.
O que você precisa
Para produção, sua integração precisa de apenas três credenciais/configurações.
gpk_live_...Dois modelos oficiais de integração
Vendas / checkout
Loja, produto, serviço ou cobrança. O comprador paga; sua aplicação não mantém um saldo Graffit individual para ele.
Abrir guia de vendasClientes + saldo separado
Plataformas em que cada usuário precisa ter um customer_id persistente e uma visão de lançamentos/saldo separada.
Credenciais e base da API
Base URL: https://graffitpay.com/v1 Authorization: Bearer gpk_live_... Content-Type: application/json Idempotency-Key: pedido-1001
Para consultar o contrato dinâmico liberado para sua própria conta, use GET /v1/documentation.
A API Key identifica sua conta automaticamente. Não envie merchant_id. Use gpk_test_... apenas no ambiente TEST e gpk_live_... apenas em produção.
payer.name + payer.cpf. Opcionais: payer.email + payer.phone.01 · DIRECT_SALES — loja, produto e serviço
No DIRECT_SALES, o comprador não precisa passar KYC por padrão para uma compra PIX comum.
Use este modelo quando sua aplicação só precisa cobrar e saber quando o pagamento terminou. Para uma compra PIX comum, o comprador não cria Customer e não passa pelo fluxo de KYC da Graffit. Em DIRECT_SALES, customer_id é opcional.
POST https://graffitpay.com/v1/payment_intents
Authorization: Bearer gpk_live_...
Idempotency-Key: pedido-1001
Content-Type: application/json
{
"payment_method": "PIX",
"amount_brl": 10.00,
"reference": "pedido-1001",
"payer": {
"name": "Maria da Silva",
"cpf": "52998224725",
"email": "maria@exemplo.com",
"phone": "+5567999999999"
},
"metadata": {
"order_id": "1001"
}
}email, phone e metadata são opcionais. O metadata deve ser um objeto JSON e tem limite de 8 KB.
Use payment.pix.br_code para o copia-e-cola e payment.pix.qr_url para a imagem do QR. Você também pode gerar seu próprio QR a partir de br_code.
payment.checkout_url é opcional. É apenas um checkout hospedado/fallback. Uma loja com checkout próprio não precisa mostrar botão “Abrir checkout Graffit”.GET https://graffitpay.com/v1/payment_intents/pi_...
Polling de 3–5 segundos pode ser usado como apoio. O webhook é recomendado para atualização instantânea. Pare o polling em SUCCEEDED, FAILED ou EXPIRED.
PROCESSING/pix.paid significa que o PIX foi identificado, mas a operação ainda pode estar finalizando. Libere o produto/serviço somente quando o Intent estiver SUCCEEDED ou quando receber payment.completed com intent_status: "SUCCEEDED".
02 · CUSTOMER_BALANCES — cliente persistente + saldo separado
CUSTOMER_BALANCES usa KYC individual aplicável quando o fluxo daquele Customer exigir verificação.
Use quando cada usuário da sua plataforma precisa ficar vinculado a um Customer Graffit fixo. Crie o Customer uma vez e reutilize sempre o mesmo ID.
POST https://graffitpay.com/v1/customers
Authorization: Bearer gpk_live_...
Content-Type: application/json
{
"display_name": "Maria da Silva",
"email": "maria@exemplo.com",
"account_type": "INDIVIDUAL",
"customer_code": "usuario-123"
}Guarde o id retornado (cus_...) no cadastro do seu usuário. customer_code é opcional.
POST /v1/customers/:id/live/onboarding GET /v1/customers/:id/live/status?sync=true
O primeiro endpoint retorna o estado do onboarding e, quando necessário, um portal seguro para o usuário concluir a verificação. Sua aplicação não recebe documento ou biometria.
customer_idPOST /v1/payment_intents
Idempotency-Key: recarga-9001
{
"customer_id": "cus_...",
"payment_method": "PIX",
"amount_brl": 100.00,
"reference": "recarga-9001",
"payer": {
"name": "Maria da Silva",
"cpf": "52998224725"
}
}GET /v1/customers/:id/balances
Esse endpoint representa os lançamentos processados pela Graffit para aquele Customer. Créditos, bônus, pontos ou débitos internos que existam apenas no seu produto continuam sendo responsabilidade do seu sistema.
Resposta do Payment Intent e PIX
Ao criar ou consultar um Intent, use o status do próprio Payment Intent como estado principal da sua aplicação.
{
"id": "pi_...",
"object": "payment_intent",
"mode": "live",
"payment_method": "PIX",
"amount_brl": 10,
"status": "REQUIRES_PAYMENT",
"reference": "pedido-1001",
"payment": {
"id": "pay_...",
"status": "PIX_PENDING",
"checkout_url": "https://graffitpay.com/checkout.html?t=...",
"pix": {
"mode": "live",
"txid": "...",
"br_code": "000201...",
"status": "pending",
"qr_url": "https://graffitpay.com/api/public/checkout/.../qr.png",
"expires_at": "..."
}
}
}| Campo | Use para |
|---|---|
id | Salvar o Payment Intent no seu pedido |
status | Estado canônico: REQUIRES_PAYMENT, PROCESSING, SUCCEEDED... |
payment.pix.br_code | PIX copia-e-cola |
payment.pix.qr_url | Imagem do QR Code |
payment.checkout_url | Fallback hospedado opcional |
fees.graffit_brl | Taxa Graffit congelada na cobrança |
fees.net_brl | Líquido contratual da sua conta |
Status que sua aplicação deve tratar
| Payment Intent | Significado | Ação |
|---|---|---|
CREATING | A cobrança está sendo criada. | Aguarde ou repita com a mesma Idempotency-Key. |
REQUIRES_PAYMENT | PIX disponível e aguardando pagamento. | Mostre QR/copia-e-cola. |
PROCESSING | PIX identificado e/ou finalização financeira em andamento. | Mostre “pagamento identificado/processando”; não libere como final. |
SUCCEEDED | Operação concluída pela Graffit. | Finalize o pedido/entrega. |
FAILED | Falha final. | Não entregue; exiba erro e crie nova tentativa quando apropriado. |
EXPIRED | A cobrança expirou. | Gere um novo Payment Intent com nova Idempotency-Key. |
payment.status como substituto do Intent. O pagamento interno pode estar PIX_PAID enquanto o Intent corretamente permanece PROCESSING. Para sua lógica de pedido, priorize o status do Payment Intent.Webhook + HMAC
Configure a URL HTTPS e crie o Webhook Secret HMAC na aba API do Portal Parceiro. A Graffit envia os eventos automaticamente.
| Evento | Uso |
|---|---|
payment.created | Cobrança criada. |
pix.paid | PIX identificado. Trate como processamento, não como conclusão final. |
payment.completed | Finalização concluída. Confirme intent_status === "SUCCEEDED" e finalize seu pedido. |
Headers enviados
X-Graffit-Event: payment.completed X-Graffit-Event-Id: evt_... X-Graffit-Timestamp: 178... X-Graffit-Signature: <hex HMAC SHA-256> X-Graffit-Webhook-Version: 1
Como validar a assinatura
signed_payload = timestamp + "." + RAW_REQUEST_BODY expected = HMAC_SHA256(WEBHOOK_SECRET, signed_payload) compare expected com X-Graffit-Signature usando comparação timing-safe
X-Graffit-Event-Id para não processar o mesmo evento duas vezes.const crypto = require('crypto');
function validGraffitWebhook({ rawBody, timestamp, signature, secret }) {
const age = Math.abs(Math.floor(Date.now()/1000) - Number(timestamp));
if (!Number.isFinite(age) || age > 300) return false;
const expected = crypto
.createHmac('sha256', secret)
.update(`${timestamp}.${rawBody}`)
.digest('hex');
const a = Buffer.from(String(signature || ''), 'hex');
const b = Buffer.from(expected, 'hex');
return a.length === b.length && crypto.timingSafeEqual(a, b);
}Payload final real
{
"id": "evt_...",
"type": "payment.completed",
"created_at": "...",
"data": {
"payment": {
"id": "pay_...",
"payment_intent_id": "pi_...",
"reference": "pedido-1001",
"amount_brl": 10,
"status": "COMPLETED",
"intent_status": "SUCCEEDED",
"payment_method": "PIX",
"payer": {
"name": "Maria da Silva",
"cpf": "52998224725",
"email": "maria@exemplo.com",
"phone": "+5567999999999"
}
}
}
}Responda HTTP 2xx rapidamente depois de validar/persistir o evento. Faça tarefas demoradas em fila/background. Se seu endpoint falhar, a Graffit tenta entregar novamente.
Idempotência — evite cobrança duplicada
Use uma chave única e estável para cada tentativa lógica de pagamento. Se sua aplicação sofrer timeout ou perder a resposta do POST, repita o mesmo request com a mesma chave.
Idempotency-Key: pedido-1001
Não gere uma nova chave só porque houve timeout. A mesma chave com payload diferente retorna conflito.
Erros que sua integração deve tratar
| HTTP / código | O que fazer |
|---|---|
400 PAYER_NAME_REQUIRED | Envie payer.name. |
400 PAYER_CPF_REQUIRED | Envie payer.cpf. |
400 PAYER_CPF_INVALID | Corrija o CPF. |
400 PAYER_EMAIL_INVALID | Corrija/remova o e-mail opcional. |
400 PAYER_PHONE_INVALID | Corrija/remova o celular opcional. |
400 INVALID_AMOUNT | Envie valor BRL maior que zero e dentro dos limites da conta. |
400 IDEMPOTENCY_KEY_REQUIRED | Envie Idempotency-Key. |
409 IDEMPOTENCY_CONFLICT | A chave já foi usada com outro payload. Não reutilize para outra compra. |
404 PAYMENT_INTENT_NOT_FOUND | Confira o ID e se ele pertence à mesma API Key. |
404 CUSTOMER_NOT_FOUND | Confira o customer_id no modelo CUSTOMER_BALANCES. |
409 CUSTOMER_LIVE_KYC_REQUIRED | Conclua o KYC do Customer antes da operação que o exigir. |
403 MERCHANT_LIVE_DISABLED | Sua conta ainda não está liberada para operar LIVE. |
429 CORE_LIVE_RATE_LIMITED | Respeite retry_after_seconds e não replique a cobrança. |
Erros usam JSON com error e code. Em erros transitórios, consulte o Payment Intent antes de criar uma nova cobrança.
TEST — valide sem dinheiro real
Na aba API do Portal Parceiro você pode criar uma TEST API Key, simular pagamento e enviar eventos de webhook assinados. Use TEST para validar sua integração antes de trocar para a chave LIVE.