Regenera G10 — Página Pública do QR Code

Esta integração permite que o domínio público regenerag10.com.br exiba informações públicas de uma área/talhão do G10 a partir de um QR Code. A consulta ao ERP deve continuar acontecendo em modo server-to-server.

ERP: agroecologia.grupoekos.com.br Método: POST Formato: JSON action: apiG10 f: clientePublicoPorToken Fotos: tipo_foto

1) URL pública do QR Code

1.1

O QR Code impresso na placa deve apontar para uma URL neste formato:

https://regenerag10.com.br/qrcode/TOKEN

Exemplo:

https://regenerag10.com.br/qrcode/cli_bce3ebfe0cb2aeaca4aea4ee49
1.2
O navegador do visitante acessa somente o domínio público. O servidor da página pública extrai o TOKEN da URL e consulta o ERP pelo backend. A api_key nunca deve ficar no JavaScript do navegador.

2) Endpoint do ERP

2.1

URL oficial do ERP

POST https://agroecologia.grupoekos.com.br/router/action.php
2.2

Parâmetros obrigatórios

Campo Obrigatório Exemplo Descrição
action Sim apiG10 Controller do ERP.
f Sim clientePublicoPorToken Função pública para carregar os dados da página.
token Sim cli_bce3ebfe0cb2aeaca4aea4ee49 Token extraído da URL pública.
api_key Sim SUA_API_KEY Chave da página pública para consultar o ERP. Não expor no navegador.
2.3

Exemplo cURL

curl -X POST "https://agroecologia.grupoekos.com.br/router/action.php" \
  -H "Content-Type: application/x-www-form-urlencoded; charset=UTF-8" \
  --data "action=apiG10&f=clientePublicoPorToken&token=cli_bce3ebfe0cb2aeaca4aea4ee49&api_key=SUA_API_KEY"

3) Dados retornados pela API

A chamada continua a mesma. O que muda é o conteúdo retornado no campo data. A resposta deve trazer informações do cliente, fotos públicas e geocerca.

Exemplo de resposta esperada

{
  "success": true,
  "msg": "ok",
  "data": {
    "api": "Regenera G10",
    "publico": true,

    "g10": {
      "id": 2,
      "unidade_referencia": "G10 - 02",
      "numero_aplicacoes": 4
    },

    "cliente": {
      "idade_pomar": "8",
      "variedade_planta": "Pera Rio",
      "porta_enxerto": "Swingle"
    },

    "resultado": {
      "percentual_evolucao": 38.5,
      "data_ultima_atualizacao": "2026-06-30",
      "data_proxima_atualizacao": "2026-09-30"
    },

    "fotos": [
      {
        "id": 12,
        "url": "https://agroecologia.grupoekos.com.br/router/action.php?action=apiG10&f=foto_publica&id_foto=12&token=cli_bce3ebfe0cb2aeaca4aea4ee49",
        "data_foto": "2026-05-05",
        "hora_foto": "10:35:20",
        "latitude": "-21.86454000",
        "longitude": "-47.49973000",
        "observacao": "Foto da área central do talhão",
        "tipo_foto": "pomar"
      }
    ],

    "geocerca": {
      "id": 1,
      "descricao": "Talhão 9",
      "poligonos": [
        {
          "latitude": "-21.86456790",
          "longitude": "-47.49966836",
          "ordem": 1
        },
        {
          "latitude": "-21.86449821",
          "longitude": "-47.49988561",
          "ordem": 2
        },
        {
          "latitude": "-21.86450567",
          "longitude": "-47.50004118",
          "ordem": 3
        },
        {
          "latitude": "-21.86466748",
          "longitude": "-47.50011628",
          "ordem": 4
        },
        {
          "latitude": "-21.86478198",
          "longitude": "-47.49977296",
          "ordem": 5
        }
      ]
    },

    "token": {
      "usos_agora": 1,
      "uso_maximo": null,
      "expira_em": null
    }
  }
}
Importante: as fotos não são retornadas em base64 e a pasta real das imagens não é pública. O campo url aponta para um endpoint controlado do ERP: foto_publica. Esse endpoint valida o token, valida se a foto pertence ao mesmo cliente/G10, verifica se está aprovada e se está marcada com exibir_pagina_publica = 1. Somente depois disso a imagem é entregue.
Tipo da foto: o campo tipo_foto identifica a categoria da imagem pública. Valores possíveis: folha, fruto, chao ou pomar. A página pública deve usar esse campo para filtros e para evitar comparação entre fotos de tipos diferentes.

4) Como as fotos são exibidas

As fotos ficam armazenadas em uma pasta protegida no ERP. Por isso, a API pública não retorna o arquivo diretamente e também não retorna base64. Ela retorna apenas uma URL controlada.

Formato do link da foto

https://agroecologia.grupoekos.com.br/router/action.php?action=apiG10&f=foto_publica&id_foto=ID_DA_FOTO&token=TOKEN

Exemplo

https://agroecologia.grupoekos.com.br/router/action.php?action=apiG10&f=foto_publica&id_foto=12&token=cli_bce3ebfe0cb2aeaca4aea4ee49

Na página pública, basta usar esse link no atributo src da imagem.

<img
  src="https://agroecologia.grupoekos.com.br/router/action.php?action=apiG10&amp;f=foto_publica&amp;id_foto=12&amp;token=cli_bce3ebfe0cb2aeaca4aea4ee49"
  alt="Foto do talhão"
>
O navegador acessa o endpoint foto_publica, não a pasta real da imagem. Assim a pasta continua protegida, e o ERP decide se a foto pode ou não ser exibida.

4) Origem dos dados no ERP

4.1

Dados do cliente

Buscar na tabela clientes:

idade_pomar
variedade_planta
porta_enxerto
4.2

Fotos públicas

Buscar na tabela g10_fotos somente fotos liberadas para exibição pública:

status = 'aprovada'
exibir_pagina_publica = 1

Campos úteis:

id
arquivo
data_foto
hora_foto
latitude
longitude
observacao
tipo_foto
4.3

Geocerca

Buscar em:

g10_geocerca
g10_geocerca_poligonos

A geocerca deve ser retornada com os pontos do polígono ordenados pelo campo ordem.

4.4

Dados do G10

Buscar na tabela g10_participacoes:

id

A página deve montar a unidade de referência usando:

G10 - ID

Exemplo:

G10 - 02
4.5

Número de aplicações

Buscar na tabela g10_aplicacoes:

WHERE id_g10_participacao = ID_G10
AND status = 'finalizada'
4.6

Quantidade de plantas da área tratada

Buscar na tabela g10_geocerca:

qtde_plantas_area_tratada
4.7

Resultado G10

Buscar na tabela g10_resultados_tecnicos somente o resultado ativo mais recente:

percentual_evolucao
data_ultima_atualizacao
data_proxima_atualizacao

WHERE status = 1
ORDER BY data_ultima_atualizacao DESC, id DESC
LIMIT 1

5) Como usar os dados na página pública

A página pública deve exibir os dados de forma simples:

Se não houver fotos públicas, a página pode ocultar a galeria ou mostrar uma mensagem simples: Nenhuma foto pública disponível no momento.

6) Exemplo simples usando Google Maps

O array geocerca.poligonos deve ser convertido para o formato esperado pelo Google Maps: { lat: ..., lng: ... }.

HTML

<div id="mapa-geocerca" style="width:100%; height:420px;"></div>

<script src="https://maps.googleapis.com/maps/api/js?key=SUA_GOOGLE_MAPS_KEY&callback=initMap" async defer></script>

JavaScript

let dadosPublicos = null;

async function carregarPaginaPublica() {
  const response = await fetch("/api-publica/qrcode/cli_bce3ebfe0cb2aeaca4aea4ee49");
  const json = await response.json();

  if (!json.success) {
    console.error(json.msg || "Erro ao carregar página pública.");
    return;
  }

  dadosPublicos = json.data;

  renderizarDadosCliente(dadosPublicos.cliente);
  renderizarFotos(dadosPublicos.fotos);
  initMap();
}

function renderizarDadosCliente(cliente) {
  document.getElementById("idade_pomar").textContent = cliente.idade_pomar || "-";
  document.getElementById("variedade_planta").textContent = cliente.variedade_planta || "-";
  document.getElementById("porta_enxerto").textContent = cliente.porta_enxerto || "-";
}

function renderizarFotos(fotos) {
  const box = document.getElementById("galeria");
  box.innerHTML = "";

  if (!fotos || fotos.length === 0) {
    box.innerHTML = "<p>Nenhuma foto pública disponível no momento.</p>";
    return;
  }

  fotos.forEach(function(foto) {
    const card = document.createElement("div");
    card.style.display = "inline-block";
    card.style.margin = "6px";
    card.style.verticalAlign = "top";

    const img = document.createElement("img");
    img.src = foto.url;
    img.alt = "Foto do talhão - " + (foto.tipo_foto || "sem classificação");
    img.style.maxWidth = "220px";
    img.style.borderRadius = "10px";

    const label = document.createElement("div");
    label.style.fontSize = "12px";
    label.style.marginTop = "4px";
    label.textContent = "Tipo: " + (foto.tipo_foto || "sem classificação");

    card.appendChild(img);
    card.appendChild(label);

    box.appendChild(card);
  });
}

function initMap() {
  if (!dadosPublicos || !dadosPublicos.geocerca) {
    return;
  }

  const poligonos = dadosPublicos.geocerca.poligonos || [];

  if (poligonos.length === 0) {
    return;
  }

  const path = poligonos.map(function(p) {
    return {
      lat: parseFloat(p.latitude),
      lng: parseFloat(p.longitude)
    };
  });

  const mapa = new google.maps.Map(document.getElementById("mapa-geocerca"), {
    zoom: 18,
    center: path[0],
    mapTypeId: "satellite"
  });

  const bounds = new google.maps.LatLngBounds();

  path.forEach(function(p) {
    bounds.extend(p);
  });

  const polygon = new google.maps.Polygon({
    paths: path,
    strokeColor: "#FF0000",
    strokeOpacity: 1,
    strokeWeight: 3,
    fillColor: "#FACC15",
    fillOpacity: 0.35,
    map: mapa
  });

  mapa.fitBounds(bounds);
}

carregarPaginaPublica();
Nesse exemplo, a geocerca aparece com borda vermelha e preenchimento amarelo claro, como no modelo visual esperado.

7) Exemplo visual da página pública

A página pode ter uma estrutura simples:

<section>
  <h2>Informações do Talhão</h2>

  <p><b>Idade do pomar:</b> <span id="idade_pomar">-</span></p>
  <p><b>Variedade:</b> <span id="variedade_planta">-</span></p>
  <p><b>Porta-enxerto:</b> <span id="porta_enxerto">-</span></p>
</section>

<section>
  <h2>Geocerca</h2>
  <div id="mapa-geocerca" style="width:100%; height:420px;"></div>
</section>

<section>
  <h2>Fotos</h2>

  <div id="filtros_fotos">
    <button type="button" data-tipo="todos">Todas</button>
    <button type="button" data-tipo="folha">Folha</button>
    <button type="button" data-tipo="fruto">Fruto</button>
    <button type="button" data-tipo="chao">Chão</button>
    <button type="button" data-tipo="pomar">Pomar</button>
  </div>

  <div id="galeria"></div>
</section>

8) Respostas de erro

O padrão de erro continua igual:

{
  "success": false,
  "error": "token_not_found",
  "msg": "Token não encontrado.",
  "data": {}
}

Possíveis erros:

Erro Quando ocorre
missing_token Token não enviado.
token_not_found Token não localizado no ERP.
token_inactive Token inativo.
token_expired Token expirado.
client_not_found Cliente vinculado ao token não localizado.
db_error Erro interno ao consultar o banco.

9) Recomendações importantes

Atualizações

Atualização 15/07/2026 — Tipo da foto na Página Pública

O endpoint clientePublicoPorToken passa a retornar o campo tipo_foto dentro de cada item do array fotos.

Esse campo deve ser usado pela página pública para filtros da galeria e para evitar comparação visual entre fotos de tipos diferentes.

  • Adicionado campo tipo_foto na resposta das fotos públicas.
  • Valores possíveis: folha, fruto, chao, pomar.
  • A página pública pode exibir os filtros como: Folha, Fruto, Chão e Pomar.
  • Fotos antigas sem tipo_foto podem ser tratadas como sem classificação.
  • Fotos de tipos diferentes não devem ser comparadas entre si.
{
  "id": 12,
  "tipo_foto": "pomar"
}