A API da Nuvende permite a criação e impressão de documentos através dos terminais POS, fornecendo uma funcionalidade essencial para o fluxo de pagamentos. Esta funcionalidade garante que tanto o comerciante quanto o cliente tenham evidência física da transação realizada.
Documentos são elementos que podem ser impressos ou processados por um terminal POS, contendo linhas de texto, QR codes ou imagens. Eles servem como evidência legal da transação e são fundamentais para:
A impressão de documentos é crucial no ambiente de pagamentos porque:
Baseado na especificação OpenAPI da Nuvende:
POST /api/v2/pagamentos/documents/ - Criar documentoPUT /api/v2/pagamentos/documents/{id} - Atualizar documentoAuthorization: Bearer {access_token}
Content-Type: application/json
Accept: application/json
{
"status": "string",
"merchant_id": "string (UUID)",
"url": "string (opcional)",
"terminal_id": "string (UUID)",
"token": "Bearer",
"token_type": "Bearer",
"lines": [
{
"content": "string",
"type": "string",
"order": "number"
}
]
}
{
"content": "string",
"type": "text|qrcode|image",
"order": "number"
}
const axios = require('axios');
class DocumentoService {
constructor(baseUrl, accessToken) {
this.baseUrl = baseUrl;
this.accessToken = accessToken;
}
/**
* Cria um novo documento para impressão
* @param {Object} dadosDocumento - Dados do documento a ser criado
* @returns {Promise<Object>} Documento criado com sucesso
*/
async criarDocumento(dadosDocumento) {
try {
console.log('Iniciando criação de documento...');
console.log('Merchant ID:', dadosDocumento.merchant_id);
console.log('Terminal ID:', dadosDocumento.terminal_id);
console.log('Número de linhas:', dadosDocumento.lines.length);
// Validar dados antes de enviar
const validacao = this.validarDadosDocumento(dadosDocumento);
if (!validacao.valido) {
throw new Error(`Dados inválidos: ${validacao.erros.join(', ')}`);
}
const response = await axios.post(`${this.baseUrl}/api/v2/pagamentos/documents/`, dadosDocumento, {
headers: {
'Authorization': `Bearer ${this.accessToken}`,
'Content-Type': 'application/json',
'Accept': 'application/json'
}
});
console.log('✅ Documento criado com sucesso');
console.log('Status:', response.data.status);
return response.data;
} catch (error) {
console.error('❌ Erro ao criar documento:', error.response?.data || error.message);
throw this.tratarErro(error);
}
}
/**
* Atualiza um documento existente
* @param {string} documentoId - ID do documento a ser atualizado
* @param {Object} dadosAtualizacao - Novos dados para o documento
* @returns {Promise<Object>} Documento atualizado
*/
async atualizarDocumento(documentoId, dadosAtualizacao) {
try {
console.log(`Atualizando documento: ${documentoId}`);
console.log('Dados de atualização:', dadosAtualizacao);
const response = await axios.put(`${this.baseUrl}/api/v2/pagamentos/documents/${documentoId}`, dadosAtualizacao, {
headers: {
'Authorization': `Bearer ${this.accessToken}`,
'Content-Type': 'application/json',
'Accept': 'application/json'
}
});
console.log('✅ Documento atualizado com sucesso');
console.log('Novo status:', response.data.status);
return response.data;
} catch (error) {
console.error('❌ Erro ao atualizar documento:', error.response?.data || error.message);
throw this.tratarErro(error);
}
}
/**
* Cria um comprovante de venda simples
* @param {string} merchantId - ID do comerciante
* @param {string} terminalId - ID do terminal
* @param {Object} dadosVenda - Dados da venda
* @returns {Promise<Object>} Documento criado
*/
async criarComprovanteVenda(merchantId, terminalId, dadosVenda) {
try {
console.log('Criando comprovante de venda...');
const linhas = [
{
content: dadosVenda.nomeEstabelecimento || 'Nuvende',
type: 'text',
order: 1
},
{
content: '==============================',
type: 'text',
order: 2
},
{
content: `Data: ${dadosVenda.data || new Date().toLocaleDateString()}`,
type: 'text',
order: 3
},
{
content: `Hora: ${dadosVenda.hora || new Date().toLocaleTimeString()}`,
type: 'text',
order: 4
},
{
content: `Valor: R$ ${dadosVenda.valor || '0,00'}`,
type: 'text',
order: 5
},
{
content: `Método: ${dadosVenda.metodoPagamento || 'Cartão'}`,
type: 'text',
order: 6
},
{
content: '==============================',
type: 'text',
order: 7
},
{
content: 'Obrigado pela preferência!',
type: 'text',
order: 8
}
];
// Adicionar QR code se fornecido
if (dadosVenda.qrCodeUrl) {
linhas.push({
content: dadosVenda.qrCodeUrl,
type: 'qrcode',
order: 9
});
}
const dadosDocumento = {
status: 'pending',
merchant_id: merchantId,
terminal_id: terminalId,
token: 'Bearer',
token_type: 'Bearer',
lines: linhas
};
return await this.criarDocumento(dadosDocumento);
} catch (error) {
console.error('❌ Erro ao criar comprovante de venda:', error.message);
throw error;
}
}
/**
* Cria um documento com QR code
* @param {string} merchantId - ID do comerciante
* @param {string} terminalId - ID do terminal
* @param {string} qrCodeUrl - URL para gerar QR code
* @param {string} titulo - Título do documento
* @returns {Promise<Object>} Documento criado
*/
async criarDocumentoComQRCode(merchantId, terminalId, qrCodeUrl, titulo = 'QR Code') {
try {
console.log('Criando documento com QR code...');
const linhas = [
{
content: titulo,
type: 'text',
order: 1
},
{
content: '==============================',
type: 'text',
order: 2
},
{
content: qrCodeUrl,
type: 'qrcode',
order: 3
},
{
content: '==============================',
type: 'text',
order: 4
}
];
const dadosDocumento = {
status: 'pending',
merchant_id: merchantId,
terminal_id: terminalId,
token: 'Bearer',
token_type: 'Bearer',
lines: linhas
};
return await this.criarDocumento(dadosDocumento);
} catch (error) {
console.error('❌ Erro ao criar documento com QR code:', error.message);
throw error;
}
}
/**
* Cria um documento com imagem
* @param {string} merchantId - ID do comerciante
* @param {string} terminalId - ID do terminal
* @param {string} imagemBase64 - Imagem em base64
* @param {string} titulo - Título do documento
* @returns {Promise<Object>} Documento criado
*/
async criarDocumentoComImagem(merchantId, terminalId, imagemBase64, titulo = 'Imagem') {
try {
console.log('Criando documento com imagem...');
const linhas = [
{
content: titulo,
type: 'text',
order: 1
},
{
content: '==============================',
type: 'text',
order: 2
},
{
content: imagemBase64,
type: 'image',
order: 3
},
{
content: '==============================',
type: 'text',
order: 4
}
];
const dadosDocumento = {
status: 'pending',
merchant_id: merchantId,
terminal_id: terminalId,
token: 'Bearer',
token_type: 'Bearer',
lines: linhas
};
return await this.criarDocumento(dadosDocumento);
} catch (error) {
console.error('❌ Erro ao criar documento com imagem:', error.message);
throw error;
}
}
/**
* Valida dados de documento de forma completa
* @param {Object} dados - Dados a serem validados
* @returns {Object} Resultado detalhado da validação
*/
validarDadosDocumento(dados) {
const erros = [];
console.log('Iniciando validação de dados do documento...');
// Validação do status
if (!dados.status || dados.status.trim().length === 0) {
erros.push('Status é obrigatório');
}
// Validação do merchant_id
if (!dados.merchant_id || dados.merchant_id.trim().length === 0) {
erros.push('ID do comerciante é obrigatório');
}
// Validação do terminal_id
if (!dados.terminal_id || dados.terminal_id.trim().length === 0) {
erros.push('ID do terminal é obrigatório');
}
// Validação do token
if (!dados.token || dados.token !== 'Bearer') {
erros.push('Token deve ser "Bearer"');
}
// Validação do token_type
if (!dados.token_type || dados.token_type !== 'Bearer') {
erros.push('Token type deve ser "Bearer"');
}
// Validação das linhas
if (!dados.lines || !Array.isArray(dados.lines) || dados.lines.length === 0) {
erros.push('Linhas são obrigatórias e devem ser um array não vazio');
} else {
dados.lines.forEach((linha, index) => {
if (!linha.content || linha.content.trim().length === 0) {
erros.push(`Conteúdo da linha ${index + 1} é obrigatório`);
}
if (!linha.type || !['text', 'qrcode', 'image'].includes(linha.type)) {
erros.push(`Tipo da linha ${index + 1} deve ser "text", "qrcode" ou "image"`);
}
if (linha.order === undefined || linha.order === null || typeof linha.order !== 'number') {
erros.push(`Ordem da linha ${index + 1} deve ser um número`);
}
});
}
const resultado = {
valido: erros.length === 0,
erros: erros
};
console.log(`Validação concluída: ${erros.length} erros`);
return resultado;
}
/**
* Trata erros da API de forma didática e informativa
* @param {Error} error - Erro capturado
* @returns {Error} Erro tratado com informações claras
*/
tratarErro(error) {
const status = error.response?.status;
const data = error.response?.data;
console.log('Tratando erro da API...');
console.log('Status HTTP:', status);
console.log('Dados do erro:', data);
switch (status) {
case 400:
return new Error(`Dados inválidos enviados: ${data?.message || 'Verifique os parâmetros enviados e tente novamente'}`);
case 401:
return new Error('Token de acesso inválido ou expirado - faça login novamente');
case 403:
return new Error('Permissões insuficientes para criar documentos - verifique suas credenciais');
case 404:
return new Error('Documento não encontrado - verifique o ID fornecido');
case 422:
return new Error(`Dados não processáveis: ${data?.message || 'Verifique o formato dos dados enviados'}`);
case 429:
return new Error('Muitas requisições - aguarde um momento antes de tentar novamente');
case 500:
return new Error('Erro interno do servidor - tente novamente mais tarde ou entre em contato com o suporte');
default:
return new Error(`Erro inesperado: ${error.message} - entre em contato com o suporte técnico`);
}
}
}
// Exemplo de uso prático
async function exemploCompletoImpressaoDocumentos() {
console.log('🚀 Iniciando exemplo completo de impressão de documentos');
const documentoService = new DocumentoService(
'https://api-h.nuvende.com.br',
'seu_access_token_aqui'
);
try {
const merchantId = '9dd1b184-a167-4131-b68a-27be0c20c2b9';
const terminalId = '32a080cb-eb8a-4a51-9e6b-d1e2d22cbdb9';
// 1. Criar comprovante de venda
console.log('\n📄 Passo 1: Criando comprovante de venda...');
const comprovanteVenda = await documentoService.criarComprovanteVenda(merchantId, terminalId, {
nomeEstabelecimento: 'Loja Exemplo',
valor: '150,00',
metodoPagamento: 'Cartão de Crédito',
qrCodeUrl: 'https://nuvende.com.br'
});
console.log('✅ Comprovante de venda criado');
// 2. Criar documento com QR code
console.log('\n📄 Passo 2: Criando documento com QR code...');
const documentoQR = await documentoService.criarDocumentoComQRCode(
merchantId,
terminalId,
'https://nuvende.com.br',
'Acesse nosso site'
);
console.log('✅ Documento com QR code criado');
// 3. Criar documento personalizado
console.log('\n📄 Passo 3: Criando documento personalizado...');
const documentoPersonalizado = await documentoService.criarDocumento({
status: 'pending',
merchant_id: merchantId,
terminal_id: terminalId,
token: 'Bearer',
token_type: 'Bearer',
lines: [
{
content: 'Nuvende',
type: 'text',
order: 1
},
{
content: 'https://nuvende.com.br',
type: 'qrcode',
order: 2
},
{
content: 'Obrigado pela preferência!',
type: 'text',
order: 3
}
]
});
console.log('✅ Documento personalizado criado');
console.log('\n🎉 Exemplo completo executado com sucesso!');
} catch (error) {
console.error('\n❌ Erro durante execução do exemplo:', error.message);
}
}
exemploCompletoImpressaoDocumentos();