A criação de cobranças Pix é uma funcionalidade essencial para receber pagamentos instantâneos através da API da Nuvende. Este processo envolve a geração de QR Codes dinâmicos que permitem aos pagadores efetuar pagamentos de forma rápida e segura.
Cobranças Pix são solicitações de pagamento que geram QR Codes dinâmicos únicos para cada transação. Diferentemente de QR Codes estáticos, os dinâmicos contêm informações específicas como valor, descrição e identificador único da transação.
QR Code Dinâmico:
QR Code Estático:
POST /api/v2/auth/login com scopes cob.write e webhooks.write (referência)PUT /api/v2/cobranca/cob/{txid} para criar a cobrança (referência)GET /api/v2/cobranca/cob/{txid} para consultar status (referência)PUT /api/v2/cobranca/webhooks/{chave} com evento recebimento (referência)Cria cobrança dinâmica com o txid na URL (PUT criação, não atualização — atualizar usa PATCH quando disponível):
PUT /api/v2/cobranca/cob/{txid}
Scope: cob.write. Alternativa sem txid na URL: Criar sem txid (POST).
Authorization: Bearer {access_token}
Content-Type: application/json
Accept: application/json
Não envie header de conta — a conta é a associada ao token.
Validação HTTP (422): apenas chave é obrigatória.
Para obter 200 na prática: também envie valor.original (> 0). Omitir valor.original tende a 500, não 422. O {txid} no path deve ter 26–35 caracteres.
{
"chave": "[email protected]",
"valor": { "original": 150.00 },
"calendario": { "expiracao": 3600 }
}
| Campo | Obrigatório | Observação |
|---|---|---|
chave | Sim (422 se ausente) | Deve pertencer à conta autenticada |
valor.original | Sim para 200 | > 0; omitir → erro 500 |
calendario.expiracao | Recomendado | Opcional no HTTP; não há default 3600 automático na borda se omitido |
nomeRecebedor | Não | Preenchido com o titular da chave se omitido |
devedor.* | Não | Opcional; sem regra “CPF ou CNPJ exclusivo” |
valor.modalidadeAlteracao | Não | Default 0 |
solicitacaoPagador | Não | Opcional |
Referência: PUT Cob {txid}.
e3fe4462-4c8e-42c1-8360-b41b404ae5571 = pagador pode alterar3600). Omitir não aplica default 3600 na bordaOs exemplos de cliente abaixo podem aplicar validações mais rígidas que a API (ex.: exigir
nomeRecebedor/ XOR de documento). Use a tabela de body acima como contrato.
const axios = require('axios');
const { v4: uuidv4 } = require('uuid');
class CobrancaPixService {
constructor(baseUrl, accessToken) {
this.baseUrl = baseUrl;
this.accessToken = accessToken;
}
/**
* Cria uma nova cobrança Pix com QR Code dinâmico
* @param {Object} dadosCobranca - Dados da cobrança
* @param {string} dadosCobranca.chave - Chave Pix do recebedor
* @param {number} dadosCobranca.valor - Valor da cobrança
* @param {string} dadosCobranca.nomeRecebedor - Nome do recebedor
* @param {string} dadosCobranca.solicitacaoPagador - Mensagem para o pagador
* @param {number} dadosCobranca.expiracao - Tempo de expiração em segundos
* @param {Object} dadosCobranca.devedor - Dados do devedor
* @param {string} dadosCobranca.devedor.nome - Nome do devedor
* @param {string} dadosCobranca.devedor.cpf - CPF do devedor (pessoa física)
* @param {string} dadosCobranca.devedor.cnpj - CNPJ do devedor (pessoa jurídica)
* @param {number} dadosCobranca.modalidadeAlteracao - Modalidade de alteração do valor (0 ou 1)
* @param {string} txid - TXID personalizado (opcional)
* @returns {Promise<Object>} Dados da cobrança criada
*/
async criarCobranca(dadosCobranca, txid = null) {
try {
// Gerar TXID se não fornecido
const txidFinal = txid || uuidv4();
console.log(`Criando cobrança Pix com TXID: ${txidFinal}`);
const payload = {
chave: dadosCobranca.chave,
solicitacaoPagador: dadosCobranca.solicitacaoPagador || null,
nomeRecebedor: dadosCobranca.nomeRecebedor,
calendario: {
expiracao: dadosCobranca.expiracao || 3600
},
valor: {
original: dadosCobranca.valor.toFixed(2),
modalidadeAlteracao: dadosCobranca.modalidadeAlteracao || 0
},
devedor: {
nome: dadosCobranca.devedor.nome,
cpf: dadosCobranca.devedor.cpf || null,
cnpj: dadosCobranca.devedor.cnpj || null
}
};
const response = await axios.put(
`${this.baseUrl}/api/v2/cobranca/cob/${txidFinal}`,
payload,
{
headers: {
'Authorization': `Bearer ${this.accessToken}`,
'Content-Type': 'application/json',
'Accept': 'application/json',
}
}
);
console.log('Cobrança Pix criada com sucesso');
return {
txid: txidFinal,
status: response.data.status,
qrCode: response.data.pixCopiaECola,
qrCodeImage: response.data.qrCode,
valor: response.data.valor.original,
expiracao: response.data.calendario.expiracao,
criacao: response.data.calendario.criacao
};
} catch (error) {
console.error('Erro ao criar cobrança Pix:', error.response?.data || error.message);
throw this.tratarErroCobranca(error);
}
}
/**
* Valida os dados da cobrança antes do envio
* @param {Object} dados - Dados a serem validados
* @returns {Object} Resultado da validação
*/
validarDadosCobranca(dados) {
const erros = [];
// Validar chave Pix
if (!dados.chave || !this.validarUUID(dados.chave)) {
erros.push('Chave Pix deve ser um UUID válido');
}
// Validar nome do recebedor
if (!dados.nomeRecebedor || dados.nomeRecebedor.trim().length === 0) {
erros.push('Nome do recebedor é obrigatório');
}
if (dados.nomeRecebedor && dados.nomeRecebedor.length > 200) {
erros.push('Nome do recebedor não pode exceder 200 caracteres');
}
// Validar valor
if (!dados.valor || dados.valor <= 0) {
erros.push('Valor deve ser maior que zero');
}
if (dados.valor > 1000000) {
erros.push('Valor não pode exceder R$ 1.000.000,00');
}
// Validar modalidade de alteração
if (dados.modalidadeAlteracao !== undefined && ![0, 1].includes(dados.modalidadeAlteracao)) {
erros.push('Modalidade de alteração deve ser 0 ou 1');
}
// Validar dados do devedor
if (!dados.devedor || !dados.devedor.nome || dados.devedor.nome.trim().length === 0) {
erros.push('Nome do devedor é obrigatório');
}
if (dados.devedor && dados.devedor.nome && dados.devedor.nome.length > 200) {
erros.push('Nome do devedor não pode exceder 200 caracteres');
}
// Validar CPF ou CNPJ (deve ter pelo menos um)
if (dados.devedor) {
const temCpf = dados.devedor.cpf && dados.devedor.cpf.trim().length > 0;
const temCnpj = dados.devedor.cnpj && dados.devedor.cnpj.trim().length > 0;
if (!temCpf && !temCnpj) {
erros.push('Deve ser informado CPF ou CNPJ do devedor');
}
if (temCpf && temCnpj) {
erros.push('Informe apenas CPF ou CNPJ, nunca ambos');
}
// Validar formato do CPF
if (temCpf && !this.validarCPF(dados.devedor.cpf)) {
erros.push('CPF deve conter apenas números e ter 11 dígitos');
}
// Validar formato do CNPJ
if (temCnpj && !this.validarCNPJ(dados.devedor.cnpj)) {
erros.push('CNPJ deve conter apenas números e ter 14 dígitos');
}
}
// Validar expiração
if (dados.expiracao && (dados.expiracao < 1 || dados.expiracao > 86400)) {
erros.push('Expiração deve estar entre 1 e 86400 segundos');
}
// Validar solicitação do pagador
if (dados.solicitacaoPagador && dados.solicitacaoPagador.length > 140) {
erros.push('Solicitação do pagador não pode exceder 140 caracteres');
}
return {
valido: erros.length === 0,
erros: erros
};
}
/**
* Valida se uma string é um UUID válido
* @param {string} uuid - String a ser validada
* @returns {boolean} True se for UUID válido
*/
validarUUID(uuid) {
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
return uuidRegex.test(uuid);
}
/**
* Valida formato do CPF
* @param {string} cpf - CPF a ser validado
* @returns {boolean} True se formato for válido
*/
validarCPF(cpf) {
const cpfLimpo = cpf.replace(/\D/g, '');
return cpfLimpo.length === 11 && /^\d{11}$/.test(cpfLimpo);
}
/**
* Valida formato do CNPJ
* @param {string} cnpj - CNPJ a ser validado
* @returns {boolean} True se formato for válido
*/
validarCNPJ(cnpj) {
const cnpjLimpo = cnpj.replace(/\D/g, '');
return cnpjLimpo.length === 14 && /^\d{14}$/.test(cnpjLimpo);
}
/**
* Trata erros específicos da API de cobranças
* @param {Error} error - Erro capturado
* @returns {Error} Erro tratado
*/
tratarErroCobranca(error) {
const status = error.response?.status;
const data = error.response?.data;
switch (status) {
case 400:
return new Error(`Dados inválidos: ${data?.detail || 'Verifique os parâmetros enviados'}`);
case 401:
return new Error('Token de acesso inválido ou expirado');
case 403:
return new Error('Permissões insuficientes para criar cobranças');
case 404:
return new Error('Chave Pix não encontrada');
case 422:
return new Error(`Dados não processáveis: ${data?.detail || 'Verifique o formato dos dados'}`);
case 500:
return new Error('Erro interno do servidor. Tente novamente mais tarde');
default:
return new Error(`Erro inesperado: ${error.message}`);
}
}
}
// Exemplo de uso prático
async function exemploCriacaoCobranca() {
const cobrancaService = new CobrancaPixService(
'https://api-h.nuvende.com.br',
'seu_access_token');
try {
// Dados da cobrança
const dadosCobranca = {
chave: 'e3fe4462-4c8e-42c1-8360-b41b404ae557',
nomeRecebedor: 'João da Silva',
valor: 100.00,
modalidadeAlteracao: 0, // Valor não pode ser alterado
solicitacaoPagador: 'Pagamento de teste via Pix',
expiracao: 3600,
devedor: {
nome: 'Maria Santos',
cpf: '39053344705'
// cnpj: '11222333000181' // Use apenas CPF ou CNPJ
}
};
// Validar dados antes de enviar
const validacao = cobrancaService.validarDadosCobranca(dadosCobranca);
if (!validacao.valido) {
console.error('Dados inválidos:', validacao.erros);
return;
}
// Criar cobrança
const cobranca = await cobrancaService.criarCobranca(dadosCobranca);
console.log('Cobrança criada:', {
txid: cobranca.txid,
valor: cobranca.valor,
qrCode: cobranca.qrCode,
expiracao: cobranca.expiracao
});
// Exibir QR Code para o usuário
console.log('QR Code para pagamento:', cobranca.qrCodeImage);
} catch (error) {
console.error('Erro na criação da cobrança:', error.message);
}
}
import requests
import uuid
import json
from typing import Dict, Optional, List, Union
class CobrancaPixService:
def __init__(self, base_url: str, access_token: str):
self.base_url = base_url
self.access_token = access_token
self.session = requests.Session()
# Configurar headers padrão
self.session.headers.update({
'Authorization': f'Bearer {self.access_token}',
'Content-Type': 'application/json',
'Accept': 'application/json',
})
def criar_cobranca(self, dados_cobranca: Dict, txid: Optional[str] = None) -> Dict:
"""
Cria uma nova cobrança Pix com QR Code dinâmico
Args:
dados_cobranca: Dicionário com os dados da cobrança
txid: TXID personalizado (opcional)
Returns:
Dicionário com os dados da cobrança criada
"""
try:
# Gerar TXID se não fornecido
txid_final = txid or str(uuid.uuid4())
print(f"Criando cobrança Pix com TXID: {txid_final}")
# Montar payload
payload = {
"chave": dados_cobranca["chave"],
"solicitacaoPagador": dados_cobranca.get("solicitacaoPagador"),
"nomeRecebedor": dados_cobranca["nomeRecebedor"],
"calendario": {
"expiracao": dados_cobranca.get("expiracao", 3600)
},
"valor": {
"original": f"{dados_cobranca['valor']:.2f}",
"modalidadeAlteracao": dados_cobranca.get("modalidadeAlteracao", 0)
},
"devedor": {
"nome": dados_cobranca["devedor"]["nome"],
"cpf": dados_cobranca["devedor"].get("cpf"),
"cnpj": dados_cobranca["devedor"].get("cnpj")
}
}
# Fazer requisição
url = f"{self.base_url}/api/v2/cobranca/cob/{txid_final}"
response = self.session.put(url, json=payload)
response.raise_for_status()
print("Cobrança Pix criada com sucesso")
return {
"txid": txid_final,
"status": response.json()["status"],
"qrCode": response.json()["pixCopiaECola"],
"qrCodeImage": response.json()["qrCode"],
"valor": response.json()["valor"]["original"],
"expiracao": response.json()["calendario"]["expiracao"],
"criacao": response.json()["calendario"]["criacao"]
}
except requests.exceptions.RequestException as error:
print(f"Erro ao criar cobrança Pix: {error}")
raise self._tratar_erro_cobranca(error)
def validar_dados_cobranca(self, dados: Dict) -> Dict[str, Union[bool, List[str]]]:
"""
Valida os dados da cobrança antes do envio
Args:
dados: Dados a serem validados
Returns:
Dicionário com resultado da validação
"""
erros = []
# Validar chave Pix
if not dados.get("chave") or not self._validar_uuid(dados["chave"]):
erros.append("Chave Pix deve ser um UUID válido")
# Validar nome do recebedor
if not dados.get("nomeRecebedor") or not dados["nomeRecebedor"].strip():
erros.append("Nome do recebedor é obrigatório")
if dados.get("nomeRecebedor") and len(dados["nomeRecebedor"]) > 200:
erros.append("Nome do recebedor não pode exceder 200 caracteres")
# Validar valor
if not dados.get("valor") or dados["valor"] <= 0:
erros.append("Valor deve ser maior que zero")
if dados.get("valor") and dados["valor"] > 1000000:
erros.append("Valor não pode exceder R$ 1.000.000,00")
# Validar modalidade de alteração
modalidade = dados.get("modalidadeAlteracao")
if modalidade is not None and modalidade not in [0, 1]:
erros.append("Modalidade de alteração deve ser 0 ou 1")
# Validar dados do devedor
devedor = dados.get("devedor", {})
if not devedor.get("nome") or not devedor["nome"].strip():
erros.append("Nome do devedor é obrigatório")
if devedor.get("nome") and len(devedor["nome"]) > 200:
erros.append("Nome do devedor não pode exceder 200 caracteres")
# Validar CPF ou CNPJ
tem_cpf = devedor.get("cpf") and devedor["cpf"].strip()
tem_cnpj = devedor.get("cnpj") and devedor["cnpj"].strip()
if not tem_cpf and not tem_cnpj:
erros.append("Deve ser informado CPF ou CNPJ do devedor")
if tem_cpf and tem_cnpj:
erros.append("Informe apenas CPF ou CNPJ, nunca ambos")
# Validar formato do CPF
if tem_cpf and not self._validar_cpf(devedor["cpf"]):
erros.append("CPF deve conter apenas números e ter 11 dígitos")
# Validar formato do CNPJ
if tem_cnpj and not self._validar_cnpj(devedor["cnpj"]):
erros.append("CNPJ deve conter apenas números e ter 14 dígitos")
# Validar expiração
expiracao = dados.get("expiracao")
if expiracao and (expiracao < 1 or expiracao > 86400):
erros.append("Expiração deve estar entre 1 e 86400 segundos")
# Validar solicitação do pagador
solicitacao = dados.get("solicitacaoPagador")
if solicitacao and len(solicitacao) > 140:
erros.append("Solicitação do pagador não pode exceder 140 caracteres")
return {
"valido": len(erros) == 0,
"erros": erros
}
def _validar_uuid(self, uuid_str: str) -> bool:
"""Valida se uma string é um UUID válido"""
try:
uuid.UUID(uuid_str)
return True
except ValueError:
return False
def _validar_cpf(self, cpf: str) -> bool:
"""Valida formato do CPF"""
cpf_limpo = ''.join(filter(str.isdigit, cpf))
return len(cpf_limpo) == 11 and cpf_limpo.isdigit()
def _validar_cnpj(self, cnpj: str) -> bool:
"""Valida formato do CNPJ"""
cnpj_limpo = ''.join(filter(str.isdigit, cnpj))
return len(cnpj_limpo) == 14 and cnpj_limpo.isdigit()
def _tratar_erro_cobranca(self, error: requests.exceptions.RequestException) -> Exception:
"""Trata erros específicos da API de cobranças"""
if hasattr(error, 'response') and error.response is not None:
status_code = error.response.status_code
try:
data = error.response.json()
detail = data.get('detail', 'Erro desconhecido')
except:
detail = 'Erro desconhecido'
if status_code == 400:
return Exception(f"Dados inválidos: {detail}")
elif status_code == 401:
return Exception("Token de acesso inválido ou expirado")
elif status_code == 403:
return Exception("Permissões insuficientes para criar cobranças")
elif status_code == 404:
return Exception("Chave Pix não encontrada")
elif status_code == 422:
return Exception(f"Dados não processáveis: {detail}")
elif status_code == 500:
return Exception("Erro interno do servidor. Tente novamente mais tarde")
return Exception(f"Erro inesperado: {str(error)}")
# Exemplo de uso em Python
async def exemplo_criacao_cobranca():
cobranca_service = CobrancaPixService(
base_url='https://api-h.nuvende.com.br',
access_token='seu_access_token')
try:
# Dados da cobrança
dados_cobranca = {
"chave": "e3fe4462-4c8e-42c1-8360-b41b404ae557",
"nomeRecebedor": "João da Silva",
"valor": 100.00,
"modalidadeAlteracao": 0, # Valor não pode ser alterado
"solicitacaoPagador": "Pagamento de teste via Pix",
"expiracao": 3600,
"devedor": {
"nome": "Maria Santos",
"cpf": "39053344705"
# "cnpj": "11222333000181" # Use apenas CPF ou CNPJ
}
}
# Validar dados antes de enviar
validacao = cobranca_service.validar_dados_cobranca(dados_cobranca)
if not validacao["valido"]:
print("Dados inválidos:", validacao["erros"])
return
# Criar cobrança
cobranca = cobranca_service.criar_cobranca(dados_cobranca)
print("Cobrança criada:", {
"txid": cobranca["txid"],
"valor": cobranca["valor"],
"qrCode": cobranca["qrCode"],
"expiracao": cobranca["expiracao"]
})
# Exibir QR Code para o usuário
print("QR Code para pagamento:", cobranca["qrCodeImage"])
except Exception as error:
print("Erro na criação da cobrança:", str(error))
# Executar exemplo
if __name__ == "__main__":
exemplo_criacao_cobranca()
#!/bin/bash
# Configurações da API
BASE_URL="https://api-h.nuvende.com.br"
ACCESS_TOKEN="seu_access_token"
TXID="$(uuidgen)" # Gerar TXID único
# Dados da cobrança para pessoa jurídica
CHAVE_PIX="e3fe4462-4c8e-42c1-8360-b41b404ae557"
NOME_RECEBEDOR="Empresa ABC Ltda"
VALOR="500.00"
MODALIDADE_ALTERACAO="1" # Valor pode ser alterado
SOLICITACAO_PAGADOR="Pagamento de serviços - Empresa XYZ"
EXPIRACAO="7200" # 2 horas
NOME_DEVEDOR="Empresa Cliente Ltda"
CNPJ_DEVEDOR="12345678000195"
echo "Criando cobrança Pix para pessoa jurídica com TXID: $TXID"
# Fazer requisição PUT para criar cobrança
curl -X PUT \
"${BASE_URL}/api/v2/cobranca/cob/${TXID}" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d "{
\"chave\": \"${CHAVE_PIX}\",
\"solicitacaoPagador\": \"${SOLICITACAO_PAGADOR}\",
\"nomeRecebedor\": \"${NOME_RECEBEDOR}\",
\"calendario\": {
\"expiracao\": ${EXPIRACAO}
},
\"valor\": {
\"original\": \"${VALOR}\",
\"modalidadeAlteracao\": ${MODALIDADE_ALTERACAO}
},
\"devedor\": {
\"nome\": \"${NOME_DEVEDOR}\",
\"cnpj\": \"${CNPJ_DEVEDOR}\"
}
}" \
--silent \
--show-error \
--fail
echo ""
echo "Cobrança Pix para pessoa jurídica criada com sucesso!"