Criando um QRCode com vencimento
Criando um QRCode com vencimento
A criação de um QRCode com vencimento na API do Pagou permite gerar um código Pix dinâmico com data de vencimento, ideal para pagamentos que requerem multas, juros ou descontos, similar a um boleto.
Visão Geral Técnica
O endpoint POST /v1/pix/due cria um QRCode Pix com data de vencimento, com base nas informações fornecidas no payload JSON. O QRCode é identificado por um UUID único, retornado na resposta, e inclui um payload Pix e uma imagem em base64 para exibição. A operação é síncrona, retornando 201 Created com os detalhes do QRCode, incluindo o código Pix (payload.data) e a imagem (payload.image). O pagamento pode ser monitorado via webhook (ex.: evento QRCodeCompleted e QRCodeRefunded).
Especificações:
Método:
POSTURL:
Produção:
https://api.pagou.com.br/v1/pix/dueSandbox:
https://sandbox.api.pagou.com.br/v1/pix/due
Autenticação: Cabeçalho
X-API-KEYcom chave do painel.Content-Type:
application/jsonResposta: Status
201 Createdcom corpo JSON contendo os detalhes do QRCode.Erros:
400 Bad Request,401 Unauthorized,500 Internal Server Error.
Cabeçalhos
X-API-KEY
sua_chave_api
Chave de autenticação.
Content-Type
application/json
Formato JSON para o corpo da requisição.
User-Agent
NomeDaSuaAplicacao/1.0
Identificador da aplicação (ex.: MinhaLoja/1.0).
Corpo da Requisição
O payload deve conter informações do pagamento Pix, incluindo valor, descrição, data de vencimento, pagador, multas, juros, descontos e configurações de notificação.
{
"amount": 100.00,
"description": "Payment with due date",
"expiration": 30,
"due_date": "2024-12-31",
"metadata": [
{
"key": "order_id",
"value": "ORD-2024-001"
},
{
"key": "invoice_number",
"value": "INV-12345"
}
],
"payer": {
"name": "João Silva",
"document": "12345678901"
},
"discount": {
"type": "fixed",
"amount": 10.00,
"limit_date": "2024-12-25"
},
"fine": {
"type": "percentage",
"amount": 2.5
},
"interest": {
"type": "percentage_calendar_days",
"amount": 1.0
},
"notification_url": "https://your-webhook.com/notifications"
}Campos do Payload:
amount
number
Sim
Valor do pagamento em reais (ex.: 100.00 para R$ 100,00).
description
string
Sim
Descrição do pagamento (máx. 255 caracteres).
expiration
number
Sim
Tempo de expiração do QRCode em dias (mínimo 1).
due_date
string
Sim
Data de vencimento (formato YYYY-MM-DD).
metadata
array
Não
Pares chave-valor para dados adicionais (ex.: ID do pedido).
metadata[].key
string
Sim (se usado)
Chave do metadado (ex.: order_id).
metadata[].value
string
Sim (se usado)
Valor do metadado (ex.: ORD-2024-001).
payer
object
Sim
Dados do pagador.
payer.name
string
Sim
Nome completo do pagador (máx. 100 caracteres).
payer.document
string
Sim
CPF (11 dígitos) ou CNPJ (14 dígitos).
discount
object
Não
Desconto para pagamento antecipado.
discount.type
string
Sim (se usado)
Tipo de desconto (fixed, percentage, fixed_calendar_days, etc.).
discount.amount
number
Sim (se usado)
Valor do desconto (em reais para fixed, ou percentual para percentage).
discount.limit_date
string
Sim (se usado)
Data limite para o desconto (formato YYYY-MM-DD).
fine
object
Não
Multa por atraso.
fine.type
string
Sim (se usado)
Tipo de multa (percentage, fixed, etc.).
fine.amount
number
Sim (se usado)
Valor da multa (em percentual ou reais).
interest
object
Não
Juros por atraso.
interest.type
string
Sim (se usado)
Tipo de juros (percentage_calendar_days, fixed, etc.).
interest.amount
number
Sim (se usado)
Valor dos juros (em percentual ou reais).
notification_url
string
Não
URL HTTPS para webhooks (ex.: https://your-webhook.com/notifications).
Validações:
amount: Valor mínimo de 5 (R$ 5,00).description: Máximo de 255 caracteres.expiration: Valor inteiro maior ou igual a 60 (em segundos).due_date: Deve ser uma data futura (mín. 1 dia após a criação, formatoYYYY-MM-DD).payer.document: Deve ser um CPF (11 dígitos) ou CNPJ (14 dígitos) válido.discount.limit_date: Deve ser anterior adue_date.discount.amount: Deve ser menor queamount.fine.amounteinterest.amount: Devem ser menores queamount.notification_url: Deve ser uma URL HTTPS válida.
Notas Técnicas:
O status inicial após a criação (
201 Created) é tipicamenteStatusEmpty, transitando rapidamente paraStatusActiveapós validação.Use o endpoint
GET/v1/pix/{id}para consultar o status atual.Configure o
notification_urlno payload para receber o eventoQRCodeCompletedquando o status mudar paraStatusCompletedouQRCodeRefundedquando o status mudar paraStatusRefundedO campo
expirationdefine o período em dias após o qual o QRCode expira automaticamente, transitando paraStatusCanceledse não pago atédue_date.
Resposta
Status:
201 CreatedCorpo: JSON com os detalhes do QRCode, incluindo o UUID, payload Pix e imagem em base64.
Exemplo de Resposta:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"amount": 100.00,
"description": "Payment with due date",
"expiration": 30,
"due_date": "2024-12-31",
"metadata": [
{
"key": "order_id",
"value": "ORD-2024-001"
},
{
"key": "invoice_number",
"value": "INV-12345"
}
],
"payer": {
"name": "João Silva",
"document": "12345678901"
},
"discount": {
"type": "fixed",
"amount": 10.00,
"limit_date": "2024-12-25"
},
"fine": {
"type": "percentage",
"amount": 2.5
},
"interest": {
"type": "percentage_calendar_days",
"amount": 1.0
},
"notification_url": "https://your-webhook.com/notifications",
"payload": {
"payload_id": 12345,
"data": "00020126580014br.gov.bcb.pix0136123e4567-e12b-12d1-a456-426614174000520400005303986540100.005802BR5913JOAO SILVA6008SAO PAULO62070503***63041234",
"image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAdgAAAHYBTnsmCAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAANCSURBVHic7doxAQAAAMKg9U9tCj+gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4GvAAAEAAQ=="
}
}Campos da Resposta:
id
string
UUID único do QRCode (ex.: 550e8400-e29b-41d4-a716-446655440000).
amount
number
Valor do pagamento em reais (ex.: 100.00).
description
string
Descrição do pagamento (máx. 255 caracteres).
expiration
number
Tempo de expiração do QRCode em dias (ex.: 30).
due_date
string
Data de vencimento (formato YYYY-MM-DD).
metadata
array
Pares chave-valor para dados adicionais.
metadata[].key
string
Chave do metadado (ex.: order_id).
metadata[].value
string
Valor do metadado (ex.: ORD-2024-001).
payer
object
Dados do pagador.
payer.name
string
Nome completo do pagador (máx. 100 caracteres).
payer.document
string
CPF (11 dígitos) ou CNPJ (14 dígitos).
discount
object
Desconto para pagamento antecipado.
discount.type
string
Tipo de desconto (fixed, percentage, fixed_calendar_days).
discount.amount
number
Valor do desconto (em reais ou percentual).
discount.limit_date
string
Data limite para o desconto (formato YYYY-MM-DD).
fine
object
Multa por atraso.
fine.type
string
Tipo de multa (percentage ou fixed.).
fine.amount
number
Valor da multa (em percentual ou reais).
interest
object
Juros por atraso.
interest.type
string
Tipo de juros (percentage_calendar_days, fixed, fixed ou percentage).
interest.amount
number
Valor dos juros (em percentual ou reais).
notification_url
string
URL HTTPS para webhooks.
payload
object
Dados do QRCode Pix.
payload.payload_id
number
ID interno do payload Pix.
payload.data
string
Código Pix para pagamento (copia-e-cola).
payload.image
string
Imagem do QRCode em base64 (formato data:image/png;base64,...).
Exemplos de Código
curl -X POST https://sandbox.api.pagou.com.br/v1/pix/due \
-H "X-API-KEY: sua_chave_api" \
-H "Content-Type: application/json" \
-H "User-Agent: MinhaLoja/1.0" \
-d '{
"amount": 100.00,
"description": "Payment with due date",
"expiration": 30,
"due_date": "2024-12-31",
"metadata": [
{
"key": "order_id",
"value": "ORD-2024-001"
},
{
"key": "invoice_number",
"value": "INV-12345"
}
],
"payer": {
"name": "João Silva",
"document": "12345678901"
},
"discount": {
"type": "fixed",
"amount": 10.00,
"limit_date": "2024-12-25"
},
"fine": {
"type": "percentage",
"amount": 2.5
},
"interest": {
"type": "percentage_calendar_days",
"amount": 1.0
},
"notification_url": "https://your-webhook.com/notifications"
}'const fetch = require('node-fetch');
const { validate } = require('jsonschema');
// Esquema de validação do payload
const schema = {
type: 'object',
required: ['amount', 'description', 'expiration', 'due_date', 'payer'],
properties: {
amount: { type: 'number', minimum: 0.01 },
description: { type: 'string', maxLength: 255 },
expiration: { type: 'integer', minimum: 1 },
due_date: { type: 'string', pattern: '^\\d{4}-\\d{2}-\\d{2}$' },
metadata: {
type: 'array',
items: {
type: 'object',
required: ['key', 'value'],
properties: {
key: { type: 'string' },
value: { type: 'string' }
}
}
},
payer: {
type: 'object',
required: ['name', 'document'],
properties: {
name: { type: 'string', maxLength: 100 },
document: { type: 'string', pattern: '^\\d{11}|\\d{14}$' }
}
},
discount: {
type: 'object',
required: ['type', 'amount', 'limit_date'],
properties: {
type: { type: 'string', enum: ['fixed', 'percentage', 'fixed_calendar_days'] },
amount: { type: 'number', minimum: 0 },
limit_date: { type: 'string', pattern: '^\\d{4}-\\d{2}-\\d{2}$' }
}
},
fine: {
type: 'object',
required: ['type', 'amount'],
properties: {
type: { type: 'string', enum: ['percentage', 'fixed'] },
amount: { type: 'number', minimum: 0 }
}
},
interest: {
type: 'object',
required: ['type', 'amount'],
properties: {
type: { type: 'string', enum: ['percentage_calendar_days', 'fixed'] },
amount: { type: 'number', minimum: 0 }
}
},
notification_url: { type: 'string', format: 'uri' }
}
};
// Payload
const payload = {
amount: 100.00,
description: 'Payment with due date',
expiration: 30,
due_date: '2024-12-31',
metadata: [
{ key: 'order_id', value: 'ORD-2024-001' },
{ key: 'invoice_number', value: 'INV-12345' }
],
payer: {
name: 'João Silva',
document: '12345678901'
},
discount: {
type: 'fixed',
amount: 10.00,
limit_date: '2024-12-25'
},
fine: {
type: 'percentage',
amount: 2.5
},
interest: {
type: 'percentage_calendar_days',
amount: 1.0
},
notification_url: 'https://your-webhook.com/notifications'
};
// Validar payload
const validation = validate(payload, schema);
if (!validation.valid) {
console.error('Erro de validação:', validation.errors);
process.exit(1);
}
// Validar due_date como data futura
const dueDate = new Date(payload.due_date);
if (dueDate <= new Date()) {
console.error('Erro: due_date must be a future date');
process.exit(1);
}
// Validar discount.limit_date <= due_date
if (payload.discount && payload.discount.limit_date) {
const limitDate = new Date(payload.discount.limit_date);
if (limitDate > dueDate) {
console.error('Erro: discount.limit_date must be before or equal to due_date');
process.exit(1);
}
}
// Validar discount.amount, fine.amount e interest.amount < amount
if (payload.discount && payload.discount.amount >= payload.amount) {
console.error('Erro: discount.amount must be less than amount');
process.exit(1);
}
if (payload.fine && payload.fine.amount >= payload.amount) {
console.error('Erro: fine.amount must be less than amount');
process.exit(1);
}
if (payload.interest && payload.interest.amount >= payload.amount) {
console.error('Erro: interest.amount must be less than amount');
process.exit(1);
}
// Enviar requisição
fetch('https://sandbox.api.pagou.com.br/v1/pix/due', {
method: 'POST',
headers: {
'X-API-KEY': 'sua_chave_api',
'Content-Type': 'application/json',
'User-Agent': 'MinhaLoja/1.0'
},
body: JSON.stringify(payload)
})
.then(response => {
if (!response.ok) {
return response.json().then(err => { throw new Error(`Erro ${response.status}: ${err.error.message}`); });
}
return response.json();
})
.then(data => {
console.log(`QRCode ID: ${data.id}`);
console.log(`Status: ${data.status}`);
console.log(`Código Pix: ${data.payload.data}`);
console.log(`Imagem Base64: ${data.payload.image}`);
})
.catch(error => console.error('Erro na requisição:', error));import requests
from jsonschema import validate, ValidationError
import re
from datetime import datetime
# Esquema de validação do payload
schema = {
"type": "object",
"required": ["amount", "description", "expiration", "due_date", "payer"],
"properties": {
"amount": {"type": "number", "minimum": 0.01},
"description": {"type": "string", "maxLength": 255},
"expiration": {"type": "integer", "minimum": 1},
"due_date": {"type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$"},
"metadata": {
"type": "array",
"items": {
"type": "object",
"required": ["key", "value"],
"properties": {
"key": {"type": "string"},
"value": {"type": "string"}
}
}
},
"payer": {
"type": "object",
"required": ["name", "document"],
"properties": {
"name": {"type": "string", "maxLength": 100},
"document": {"type": "string", "pattern": "^\\d{11}|\\d{14}$"}
}
},
"discount": {
"type": "object",
"required": ["type", "amount", "limit_date"],
"properties": {
"type": {"type": "string", "enum": ["fixed", "percentage", "fixed_calendar_days"]},
"amount": {"type": "number", "minimum": 0},
"limit_date": {"type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$"}
}
},
"fine": {
"type": "object",
"required": ["type", "amount"],
"properties": {
"type": {"type": "string", "enum": ["percentage", "fixed"]},
"amount": {"type": "number", "minimum": 0}
}
},
"interest": {
"type": "object",
"required": ["type", "amount"],
"properties": {
"type": {"type": "string", "enum": ["percentage_calendar_days", "fixed"]},
"amount": {"type": "number", "minimum": 0}
}
},
"notification_url": {"type": "string", "format": "uri"}
}
}
# Payload
payload = {
"amount": 100.00,
"description": "Payment with due date",
"expiration": 30,
"due_date": "2024-12-31",
"metadata": [
{"key": "order_id", "value": "ORD-2024-001"},
{"key": "invoice_number", "value": "INV-12345"}
],
"payer": {
"name": "João Silva",
"document": "12345678901"
},
"discount": {
"type": "fixed",
"amount": 10.00,
"limit_date": "2024-12-25"
},
"fine": {
"type": "percentage",
"amount": 2.5
},
"interest": {
"type": "percentage_calendar_days",
"amount": 1.0
},
"notification_url": "https://your-webhook.com/notifications"
}
# Validar payload
try:
validate(instance=payload, schema=schema)
# Validar due_date como data futura
due_date = datetime.strptime(payload["due_date"], "%Y-%m-%d")
if due_date <= datetime.now():
raise ValueError("due_date must be a future date")
# Validar discount.limit_date <= due_date
if "discount" in payload and payload["discount"].get("limit_date"):
limit_date = datetime.strptime(payload["discount"]["limit_date"], "%Y-%m-%d")
if limit_date > due_date:
raise ValueError("discount.limit_date must be before or equal to due_date")
# Validar discount.amount, fine.amount e interest.amount < amount
if "discount" in payload and payload["discount"].get("amount", 0) >= payload["amount"]:
raise ValueError("discount.amount must be less than amount")
if "fine" in payload and payload["fine"].get("amount", 0) >= payload["amount"]:
raise ValueError("fine.amount must be less than amount")
if "interest" in payload and payload["interest"].get("amount", 0) >= payload["amount"]:
raise ValueError("interest.amount must be less than amount")
except (ValidationError, ValueError) as e:
print(f"Erro de validação: {e}")
exit(1)
# Enviar requisição
url = "https://sandbox.api.pagou.com.br/v1/pix/due"
headers = {
"X-API-KEY": "sua_chave_api",
"Content-Type": "application/json",
"User-Agent": "MinhaLoja/1.0"
}
try:
response = requests.post(url, json=payload, headers=headers)
response.raise_for_status()
data = response.json()
print(f"QRCode ID: {data['id']}")
print(f"Status: {data.get('status', 'StatusEmpty')}")
print(f"Código Pix: {data['payload']['data']}")
print(f"Imagem Base64: {data['payload']['image']}")
except requests.RequestException as e:
print(f"Erro na requisição: {e}")
if e.response:
print(f"Detalhes: {e.response.json()}")Tratamento de Erros
400
Bad Request
Payload inválido (ex.: CPF inválido, amount < 5, due_date no passado, discount.limit_date após due_date, discount.amount ≥ amount).
Validar dados antes de enviar.
401
Unauthorized
X-API-KEY inválido ou ausente.
Verificar chave.
Exemplo de Resposta de Erro:
{
"error": "discount.limit_date must be before or equal to due_date"
}Mapa de Status do Pix
Cada QRCode Pix com vencimento possui um status que reflete seu estado no ciclo de vida. Os status possíveis, conforme definido na API do Pagou, são:
StatusEmpty
1
Estado inicial transitório, indicando que o QRCode foi criado, mas ainda não está ativo (ex.: aguardando validação interna).
StatusActive
2
QRCode ativo, pronto para pagamento, com código Pix e imagem disponíveis.
StatusCanceled
3
QRCode cancelado, não mais válido para pagamento (ex.: por solicitação ou expiração).
StatusCompleted
4
QRCode pago pelo cliente, com liquidação confirmada.
StatusRefunded
5
QRCode pago, mas reembolsado ao cliente (ex.: após devolução do produto).
Transições de Status:
StatusEmpty→StatusActive: Após validação interna bem-sucedida (geralmente imediata).StatusActive→StatusCompleted: Quando o pagamento é confirmado (notificado via webhookQRCodeCompleted).StatusActive→StatusCanceled: Após solicitação de cancelamento (ex.:DELETE/v1/pix/{id}) ou expiração do QRCode (definida pordue_dateeexpiration).StatusCompleted→StatusRefunded: Após processamento de reembolso (se aplicável).StatusCanceled,StatusRefunded: Estados finais, sem transições adicionais.
Boas Práticas Técnicas
Validação de Dados: Use esquemas JSON (como nos exemplos) para validar o payload antes de enviar, incluindo verificações específicas como
due_datefutura,discount.limit_date≤due_dateediscount.amount,fine.amount,interest.amount<amount.Segurança: Armazene
X-API-KEYem variáveis de ambiente e use HTTPS para todas as requisições.Status do Pix: Após a criação, verifique o status com
GET/v1/pix/{id}(a ser documentado) para confirmar a transição deStatusEmptyparaStatusActive.Webhooks: Configure o
notification_urlpara receber o eventoQRCodeCompletedquando o status mudar paraStatusCompletedouQRCodeRefundedquando o status mudar paraStatusRefunded.Testes no Sandbox: Use
https://sandbox.api.pagou.com.br/v1/pix/duepara simular criação de QRCodes sem custos reais.Monitoramento: Registre todas as requisições e respostas (incluindo
id,status,payload.dataepayload.image) em logs para auditoria e depuração.
Last updated