Fluxo Completo de PDV
Este guia descreve a integração de ponta a ponta de um PDV real com o EloFiel, cobrindo desde a chegada do cliente até a confirmação do resgate.
Modelos de resgate disponíveis
O EloFiel suporta três modelos para aplicar cashback como desconto. Consulte GET /api/v1/configuracao na inicialização do PDV para descobrir quais estão habilitados.
| Modelo 1 — Resgate Separado | Modelo 2 — Checkout Integrado | Modelo 3 — Desconto Imediato | |
|---|---|---|---|
| Chamadas | 3 (/saldo + /resgate + /venda) | 1 (/venda com usarCashbackDisponivel: true) | 2 (/venda/preview + /venda com usarCashbackGerado: true) |
| Saldo utilizado | Acumulado em compras anteriores | Acumulado em compras anteriores | Cashback que esta venda geraria |
| Quando usar | permiteCheckoutCashbackNaVenda: false | permiteCheckoutCashbackNaVenda: true | permiteDescontoSobreGerado: true |
| Controle do desconto | PDV define o valor do resgate | API aplica FIFO automático | API calcula cashback gerado e aplica como desconto |
| Atomicidade | Resgate e venda são chamadas separadas | Tudo numa única transação | Preview + venda (preview é read-only) |
| Funciona na 1ª compra? | Não (sem saldo acumulado) | Não (sem saldo acumulado) | Sim — desconta o cashback que seria gerado |
Modelo 1 — Resgate Separado
O cliente usa o saldo acumulado em compras anteriores como desconto. O PDV debita o saldo antes de registrar a venda.
CLIENTE OPERADOR PDV API ELOFIEL
| | |
| [Inicialização do PDV] |
| |-- GET /configuracao --->|
| |<-- tipo campanha -------|
| | |
|-- Informa CPF ---------->| |
| |-- GET /resgate/saldo -->|
| |<-- saldo disponível ----|
|<-- Operador exibe saldo--| |
| | |
|-- Decide usar saldo? --->| |
| (sim / não) | |
| | |
|-- Itens finalizados ---->| |
| | |
| [Se vai usar saldo] | |
| |-- POST /resgate ------->|
| |<-- resgate confirmado --|
| | |
|<-- Desconto aplicado ----| |
| | |
|-- Pagamento confirmado ->| |
| |-- POST /venda --------->|
| |<-- cashback creditado --|
|<-- Comprovante + saldo --| |
Modelo 2 — Checkout Integrado
O saldo disponível é aplicado como desconto na mesma chamada de registro da venda. Menos chamadas, menos pontos de falha.
CLIENTE OPERADOR PDV API ELOFIEL
| | |
| [Inicialização do PDV] |
| |-- GET /configuracao --->|
| |<-- permiteCheckout: true|
| | |
|-- Informa CPF ---------->| |
| |-- GET /resgate/saldo -->|
| |<-- saldo disponível ----|
|<-- "Usar R$ X desconto?"-| |
| | |
|-- Decide (sim / não) --->| |
|-- Itens finalizados ---->| |
|-- Pagamento confirmado ->| |
| |-- POST /venda --------->|
| | usarCashbackDisponivel: true
| |<-- cashbackUsado: X ----|
| |<-- valorFinal: Y -------|
| |<-- cashbackCreditado: Z-|
|<-- Comprovante + saldo --| |
Modelo 3 — Desconto Imediato sobre Cashback Gerado
O PDV oferece ao cliente um desconto igual ao cashback que esta venda geraria — sem consumir saldo acumulado. Ideal para a primeira compra.
CLIENTE OPERADOR PDV API ELOFIEL
| | |
| [Inicialização do PDV] |
| |-- GET /configuracao --->|
| |<-- permiteDescontoSobreGerado: true
| | |
|-- Informa CPF ---------->| |
|-- Itens finalizados ---->| |
| | |
| [PDV simula desconto] | |
| |-- POST /venda/preview ->|
| |<-- cashbackGeradoPrevisto: 10.00
| | valorComDesconto: 90.00
| | |
|<-- "Desconto de R$ 10?" -| |
| | |
|-- Decide (sim / não) --->| |
|-- Pagamento confirmado ->| |
| |-- POST /venda --------->|
| | usarCashbackGerado: true
| |<-- descontoGerado: 10.00|
| |<-- valorFinal: 90.00 ---|
| |<-- cashbackCreditado: 0 |
|<-- Comprovante ----------| |
Antes do primeiro atendimento, chame GET /api/v1/configuracao para saber o tipo de campanha ativa e quais modelos de resgate estão habilitados. Veja o guia Configuração do PDV.
Código completo de exemplo
- Modelo 3 — Desconto Imediato
- Modelo 2 — Checkout Integrado
- Modelo 1 — Resgate Separado
- JavaScript / Node.js
- PHP
const BASE_URL = 'https://api.elofiel.com.br';
const API_KEY = process.env.ELOFIEL_API_KEY;
const CNPJ_LOJA = '12.345.678/0001-90';
async function eloFielRequest(method, path, body = null) {
const options = {
method,
headers: { 'Content-Type': 'application/json', 'X-Api-Key': API_KEY },
};
if (body) options.body = JSON.stringify(body);
const response = await fetch(`${BASE_URL}${path}`, options);
const resultado = await response.json();
if (!resultado.sucesso) {
throw new Error(resultado.erros?.[0] ?? resultado.mensagem ?? 'Erro desconhecido');
}
return resultado.dados;
}
/**
* Fluxo Modelo 3 — desconto sobre o cashback que esta venda geraria.
* Usar quando permiteDescontoSobreGerado === true.
*/
async function fluxoDescontoImediato(cpfCliente, valorVenda, numeroCupom) {
// 1. Simular desconto antes de exibir ao cliente
let preview;
try {
preview = await eloFielRequest('POST', '/api/v1/venda/preview', {
cnpjLoja: CNPJ_LOJA,
cpfCliente,
valorVenda,
});
} catch {
preview = { permiteAplicarDesconto: false };
}
let clienteQuerDesconto = false;
if (preview.permiteAplicarDesconto) {
// Exibir diálogo: "Desconto de R$ X — Total: R$ Y. Confirmar?"
console.log(`Desconto disponível: R$ ${preview.cashbackGeradoPrevisto}`);
console.log(`Total com desconto: R$ ${preview.valorComDesconto}`);
clienteQuerDesconto = true; // [PDV aguarda confirmação do cliente]
} else {
console.log(`Desconto indisponível: ${preview.motivoBloqueio ?? 'campanha não permite'}`);
}
// 2. Registrar a venda
const venda = await eloFielRequest('POST', '/api/v1/venda', {
cnpjLoja: CNPJ_LOJA,
cpfCliente,
valorVenda,
documentoExterno: numeroCupom,
usarCashbackGerado: clienteQuerDesconto,
});
if (clienteQuerDesconto) {
console.log(`Desconto aplicado: R$ ${venda.descontoGerado}`);
}
console.log(`Total pago: R$ ${venda.valorFinal}`);
console.log(`Cashback creditado: R$ ${venda.cashbackCreditado}`);
return venda;
}
const resultado = await fluxoDescontoImediato('123.456.789-09', 100.00, `PDV-${Date.now()}`);
<?php
define('ELOFIEL_BASE_URL', 'https://api.elofiel.com.br');
define('ELOFIEL_API_KEY', getenv('ELOFIEL_API_KEY'));
define('CNPJ_LOJA', '12.345.678/0001-90');
function eloFielRequest(string $method, string $path, ?array $body = null): array {
$ch = curl_init(ELOFIEL_BASE_URL . $path);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-Api-Key: ' . ELOFIEL_API_KEY,
],
CURLOPT_POSTFIELDS => $body ? json_encode($body) : null,
]);
$resposta = json_decode(curl_exec($ch), true);
curl_close($ch);
if (!$resposta['sucesso']) {
throw new RuntimeException($resposta['erros'][0] ?? $resposta['mensagem'] ?? 'Erro');
}
return $resposta['dados'];
}
// 1. Simular desconto
$preview = ['permiteAplicarDesconto' => false];
try {
$preview = eloFielRequest('POST', '/api/v1/venda/preview', [
'cnpjLoja' => CNPJ_LOJA,
'cpfCliente' => '123.456.789-09',
'valorVenda' => 100.00,
]);
} catch (RuntimeException $e) {
echo "Preview indisponível: {$e->getMessage()}\n";
}
$clienteQuerDesconto = false;
if ($preview['permiteAplicarDesconto']) {
echo "Desconto disponível: R$ {$preview['cashbackGeradoPrevisto']}\n";
echo "Total com desconto: R$ {$preview['valorComDesconto']}\n";
$clienteQuerDesconto = true; // [PDV aguarda confirmação]
}
// 2. Registrar a venda
try {
$venda = eloFielRequest('POST', '/api/v1/venda', [
'cnpjLoja' => CNPJ_LOJA,
'cpfCliente' => '123.456.789-09',
'valorVenda' => 100.00,
'documentoExterno' => 'PDV-' . time(),
'usarCashbackGerado' => $clienteQuerDesconto,
]);
echo "Total pago: R$ {$venda['valorFinal']}\n";
echo "Cashback creditado: R$ {$venda['cashbackCreditado']}\n";
} catch (RuntimeException $e) {
error_log("Venda não registrada no EloFiel: {$e->getMessage()}");
// Fila de reprocessamento
}
- JavaScript / Node.js
- PHP
const BASE_URL = 'https://api.elofiel.com.br';
const API_KEY = process.env.ELOFIEL_API_KEY;
const CNPJ_LOJA = '12.345.678/0001-90';
async function eloFielRequest(method, path, body = null) {
const options = {
method,
headers: { 'Content-Type': 'application/json', 'X-Api-Key': API_KEY },
};
if (body) options.body = JSON.stringify(body);
const response = await fetch(`${BASE_URL}${path}`, options);
const resultado = await response.json();
if (!resultado.sucesso) {
throw new Error(resultado.erros?.[0] ?? resultado.mensagem ?? 'Erro desconhecido');
}
return resultado.dados;
}
async function consultarSaldo(cpfCliente) {
const params = new URLSearchParams({ cnpjLoja: CNPJ_LOJA, cpfCliente });
return eloFielRequest('GET', `/api/v1/resgate/saldo?${params}`);
}
/**
* Fluxo Modelo 2 — tudo numa única chamada POST /venda.
* Usar quando permiteCheckoutCashbackNaVenda === true.
*/
async function fluxoCheckoutIntegrado(cpfCliente, valorVenda, numeroCupom) {
// 1. Consultar saldo para exibir ao cliente antes do pagamento
let saldo;
try {
saldo = await consultarSaldo(cpfCliente);
console.log(`Saldo disponível: R$ ${saldo.saldoCashback}`);
} catch {
saldo = null;
}
const clienteQuerUsarSaldo = saldo?.saldoCashback > 0;
// [PDV exibe opção e aguarda confirmação do cliente]
// 2. Registrar venda com desconto integrado
const venda = await eloFielRequest('POST', '/api/v1/venda', {
cnpjLoja: CNPJ_LOJA,
cpfCliente,
valorVenda,
documentoExterno: numeroCupom,
usarCashbackDisponivel: clienteQuerUsarSaldo,
});
console.log(`Desconto aplicado: R$ ${venda.cashbackUsado}`);
console.log(`Total pago: R$ ${venda.valorFinal}`);
console.log(`Novo cashback: R$ ${venda.cashbackCreditado}`);
console.log(`Saldo atual: R$ ${venda.saldoAtual.saldoCashback}`);
return venda;
}
const resultado = await fluxoCheckoutIntegrado('123.456.789-09', 200.00, `PDV-${Date.now()}`);
<?php
define('ELOFIEL_BASE_URL', 'https://api.elofiel.com.br');
define('ELOFIEL_API_KEY', getenv('ELOFIEL_API_KEY'));
define('CNPJ_LOJA', '12.345.678/0001-90');
function eloFielRequest(string $method, string $path, ?array $body = null): array {
$ch = curl_init(ELOFIEL_BASE_URL . $path);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-Api-Key: ' . ELOFIEL_API_KEY,
],
CURLOPT_POSTFIELDS => $body ? json_encode($body) : null,
]);
$resposta = json_decode(curl_exec($ch), true);
curl_close($ch);
if (!$resposta['sucesso']) {
throw new RuntimeException($resposta['erros'][0] ?? $resposta['mensagem'] ?? 'Erro');
}
return $resposta['dados'];
}
// 1. Consultar saldo para exibir ao cliente
$saldo = null;
try {
$params = http_build_query(['cnpjLoja' => CNPJ_LOJA, 'cpfCliente' => '123.456.789-09']);
$saldo = eloFielRequest('GET', '/api/v1/resgate/saldo?' . $params);
echo "Saldo disponível: R$ {$saldo['saldoCashback']}\n";
} catch (RuntimeException $e) {
echo "Saldo não consultado: {$e->getMessage()}\n";
}
$clienteQuerUsarSaldo = ($saldo['saldoCashback'] ?? 0) > 0;
// [PDV exibe opção e aguarda confirmação do cliente]
// 2. Registrar venda com desconto integrado
try {
$venda = eloFielRequest('POST', '/api/v1/venda', [
'cnpjLoja' => CNPJ_LOJA,
'cpfCliente' => '123.456.789-09',
'valorVenda' => 200.00,
'documentoExterno' => 'PDV-' . time(),
'usarCashbackDisponivel' => $clienteQuerUsarSaldo,
]);
echo "Desconto aplicado: R$ {$venda['cashbackUsado']}\n";
echo "Total pago: R$ {$venda['valorFinal']}\n";
echo "Novo cashback: R$ {$venda['cashbackCreditado']}\n";
} catch (RuntimeException $e) {
error_log("Venda não registrada no EloFiel: {$e->getMessage()}");
// Fila de reprocessamento
}
- JavaScript / Node.js
- PHP
const BASE_URL = 'https://api.elofiel.com.br';
const API_KEY = process.env.ELOFIEL_API_KEY;
const CNPJ_LOJA = '12.345.678/0001-90';
async function eloFielRequest(method, path, body = null) {
const options = {
method,
headers: {
'Content-Type': 'application/json',
'X-Api-Key': API_KEY,
},
};
if (body) {
options.body = JSON.stringify(body);
}
const response = await fetch(`${BASE_URL}${path}`, options);
const resultado = await response.json();
if (!resultado.sucesso) {
throw new Error(resultado.erros?.[0] ?? resultado.mensagem ?? 'Erro desconhecido');
}
return resultado.dados;
}
/**
* Etapa 1: Consultar saldo do cliente ao início do atendimento.
*/
async function consultarSaldo(cpfCliente) {
const params = new URLSearchParams({
cnpjLoja: CNPJ_LOJA,
cpfCliente,
});
return eloFielRequest('GET', `/api/v1/resgate/saldo?${params}`);
}
/**
* Etapa 2 (opcional): Aplicar resgate antes do pagamento.
*/
async function aplicarResgate(cpfCliente, tipoBeneficio, valorResgate = null) {
return eloFielRequest('POST', '/api/v1/resgate', {
cnpjLoja: CNPJ_LOJA,
cpfCliente,
tipoBeneficio,
valorResgate,
pontosResgate: null,
documentoExterno: `RES-${Date.now()}`,
observacao: null,
});
}
/**
* Etapa 3: Registrar a venda após o pagamento confirmado.
*/
async function registrarVenda(cpfCliente, valorVenda, numeroCupom) {
return eloFielRequest('POST', '/api/v1/venda', {
cnpjLoja: CNPJ_LOJA,
cpfCliente,
valorVenda,
documentoExterno: numeroCupom,
observacao: null,
});
}
/**
* Fluxo completo de atendimento no PDV.
*/
async function fluxoAtendimentoPDV(cpfCliente, valorVenda, usarCashback = false) {
// 1. Verificar saldo disponível
let saldoInicial;
try {
saldoInicial = await consultarSaldo(cpfCliente);
console.log(`Saldo disponível: R$ ${saldoInicial.saldoCashback}`);
} catch (erro) {
// Saldo não disponível — continuar sem benefício
console.warn('Saldo não consultado:', erro.message);
saldoInicial = null;
}
let descontoAplicado = 0;
// 2. Aplicar resgate se o cliente desejar e tiver saldo
if (usarCashback && saldoInicial?.saldoCashback > 0) {
try {
const resgate = await aplicarResgate(
cpfCliente,
'Cashback',
saldoInicial.saldoCashback // resgatar tudo; ajuste conforme necessário
);
descontoAplicado = resgate.cashbackResgatado;
console.log(`Desconto aplicado: R$ ${descontoAplicado}`);
} catch (erro) {
console.error('Resgate falhou:', erro.message);
// Continuar sem desconto
}
}
// 3. Calcular total com desconto
const valorFinal = Math.max(0, valorVenda - descontoAplicado);
console.log(`Total a pagar: R$ ${valorFinal}`);
// [Aqui o PDV processa o pagamento...]
// 4. Registrar a venda
const numeroCupom = `PDV-${Date.now()}`;
let venda;
try {
venda = await registrarVenda(cpfCliente, valorVenda, numeroCupom);
// Issue #3 — itera sobre beneficiosAplicados (1 ou 2 entries).
for (const beneficio of venda.beneficiosAplicados) {
console.log(`${beneficio.campanhaDescricao}: ` +
(beneficio.tipoBeneficio === 'Cashback'
? `R$ ${beneficio.cashbackCreditado} cashback`
: `${beneficio.pontosCreditados} pontos`));
}
} catch (erro) {
// Falha na API não deve impedir a finalização da venda no caixa
console.error('Venda não registrada no EloFiel:', erro.message);
// Adicionar à fila de reprocessamento
}
return {
descontoAplicado,
valorFinal,
cashbackCreditado: venda?.cashbackCreditado ?? 0,
saldoAtual: venda?.saldoAtual ?? null,
};
}
// Exemplo de uso:
// Cliente retira R$ 20,00 de cashback e faz compra de R$ 200,00
const resultado = await fluxoAtendimentoPDV(
'123.456.789-09',
200.00,
true // usar cashback disponível
);
console.log('Atendimento finalizado:', resultado);
<?php
define('ELOFIEL_BASE_URL', 'https://api.elofiel.com.br');
define('ELOFIEL_API_KEY', getenv('ELOFIEL_API_KEY'));
define('CNPJ_LOJA', '12.345.678/0001-90');
function eloFielRequest(string $method, string $path, ?array $body = null): array {
$url = ELOFIEL_BASE_URL . $path;
$headers = [
'Content-Type: application/json',
'X-Api-Key: ' . ELOFIEL_API_KEY,
];
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_POSTFIELDS => $body ? json_encode($body) : null,
]);
$resposta = json_decode(curl_exec($ch), true);
curl_close($ch);
if (!$resposta['sucesso']) {
$erro = $resposta['erros'][0] ?? $resposta['mensagem'] ?? 'Erro desconhecido';
throw new RuntimeException($erro);
}
return $resposta['dados'];
}
function consultarSaldo(string $cpfCliente): array {
$params = http_build_query(['cnpjLoja' => CNPJ_LOJA, 'cpfCliente' => $cpfCliente]);
return eloFielRequest('GET', '/api/v1/resgate/saldo?' . $params);
}
function aplicarResgate(string $cpfCliente, string $tipo, ?float $valor = null): array {
return eloFielRequest('POST', '/api/v1/resgate', [
'cnpjLoja' => CNPJ_LOJA,
'cpfCliente' => $cpfCliente,
'tipoBeneficio' => $tipo,
'valorResgate' => $valor,
'pontosResgate' => null,
'documentoExterno' => 'RES-' . time(),
'observacao' => null,
]);
}
function registrarVenda(string $cpfCliente, float $valorVenda, string $cupom): array {
return eloFielRequest('POST', '/api/v1/venda', [
'cnpjLoja' => CNPJ_LOJA,
'cpfCliente' => $cpfCliente,
'valorVenda' => $valorVenda,
'documentoExterno' => $cupom,
'observacao' => null,
]);
}
// Fluxo completo
$cpf = '123.456.789-09';
$valorVenda = 200.00;
$desconto = 0.0;
try {
$saldo = consultarSaldo($cpf);
echo "Saldo disponível: R$ {$saldo['saldoCashback']}\n";
if ($saldo['saldoCashback'] > 0) {
$resgate = aplicarResgate($cpf, 'Cashback', $saldo['saldoCashback']);
$desconto = $resgate['cashbackResgatado'];
echo "Desconto aplicado: R$ {$desconto}\n";
}
} catch (RuntimeException $e) {
echo "Aviso: {$e->getMessage()}\n";
}
$valorFinal = max(0, $valorVenda - $desconto);
echo "Total a pagar: R$ {$valorFinal}\n";
// [Processar pagamento no PDV...]
try {
$venda = registrarVenda($cpf, $valorVenda, 'PDV-' . time());
echo "Cashback creditado: R$ {$venda['cashbackCreditado']}\n";
} catch (RuntimeException $e) {
error_log("Venda não registrada no EloFiel: {$e->getMessage()}");
// Fila de reprocessamento
}
Pontos de atenção
Modelo 1: o resgate ocorre ANTES da venda
O resgate (debitar o saldo do cliente) ocorre antes de registrar a nova venda. O cliente usa saldo de compras anteriores como desconto.
Modelo 2: atomicidade garantida pela API
No checkout integrado, a API consome os lotes de cashback e registra a venda numa única transação. Não há risco de débito sem crédito.
Modelo 3: preview é read-only, venda é atômica
POST /api/v1/venda/preview não grava nada — é apenas uma simulação. A operação real ocorre na POST /api/v1/venda. Se o cliente recusar o desconto após o preview, basta enviar a venda normalmente sem usarCashbackGerado: true.
Modelos 2 e 3 não podem ser combinados na mesma venda. Se usarCashbackDisponivel: true e usarCashbackGerado: true forem enviados simultaneamente, a API retorna 422.
Tolerância a falhas
A integração com o EloFiel não deve bloquear o caixa. Implemente:
- Timeout curto (5-10s) nas chamadas de saldo/resgate
- Fila de reprocessamento para vendas não registradas por falha de rede
- Log local de todas as operações para auditoria
Idempotência com documentoExterno
Use o mesmo documentoExterno (número do cupom fiscal) se precisar retransmitir uma venda. A API pode identificar duplicatas e evitar duplo crédito.