← Voltar aos tutoriais

Upsell custom em HTML puro

Dois arquivos, zero build step. Ideal para landing pages estáticas, Framer HTML embed, Wix Custom HTML, Webflow Embed ou qualquer site onde você consegue inserir <script>.

Tutoriais › HTML puro

Passo a passo

  1. Inclua o SDK

    Adicione a tag <script> no <head> ou antes do </body>:

    <script src="https://pay.celetus.com/assets/js/celetus.upsell.sdk.js"></script>
  2. Monte o HTML do seu jeito

    O SDK não injeta DOM — você controla 100% do markup. Dê id nos elementos que vai preencher (título, preço, botões, QR code).

    <div class="upsell">
      <h1 id="titulo">Carregando…</h1>
      <p id="preco"></p>
      <img id="produto-img" alt="" />
    
      <div id="passo-oferta">
        <button id="sim">Sim, quero!</button>
        <button id="nao">Não, obrigado</button>
      </div>
    
      <div id="passo-pix" hidden>
        <img id="qr" alt="QR Code PIX" />
        <button id="copiar">Copiar chave PIX</button>
      </div>
    
      <div id="passo-pago" hidden>✅ Pagamento confirmado!</div>
    </div>
  3. Pegue a key no painel

    No painel Celetus, em Funil de vendas, clique no ícone </> da etapa que você quer integrar e copie o valor da key. Ela fica hardcoded no seu código — mesma mecânica que o widget iframe atual já usa hoje com Upsell.Widget.show({ key }).

  4. Inicialize e carregue os dados

    Passe a key no init() e use loadStep() para buscar os dados do passo (produto, preço, labels). O sfid o SDK lê sozinho da URL (plantado pelo redirect do checkout).

    const upsell = CeletusUpsell.init({
      key: "SUA_KEY_AQUI" // ← copiada do painel Celetus
    });
    
    upsell.loadStep().then(step => {
      document.getElementById("titulo").textContent    = step.productName;
      document.getElementById("preco").textContent     = step.valueFormatted;
      document.getElementById("produto-img").src       = step.productImage;
      document.getElementById("sim").textContent       = step.positiveButtonLabel;
      document.getElementById("nao").textContent       = step.negativeButtonLabel;
    });
  5. Amarre os botões

    document.getElementById("sim").onclick = () => upsell.accept();
    document.getElementById("nao").onclick = () => upsell.decline();
  6. Reaja aos eventos

    Se for PIX, o código chega no evento qrReady. Se for cartão one-click, o evento paid dispara direto.

    upsell.on("qrReady", ({ pixQrUrl, pixQrCode }) => {
      document.getElementById("passo-oferta").hidden = true;
      document.getElementById("passo-pix").hidden    = false;
    
      const qr = document.getElementById("qr");
      // pixQrUrl pode ser uma URL http OU um código copia-e-cola.
      if (pixQrUrl && pixQrUrl.startsWith("http")) {
        qr.src = pixQrUrl;
      } else {
        // Código copia-e-cola — use qrcode.js se quiser gerar imagem local.
        qr.alt = pixQrCode;
      }
    
      document.getElementById("copiar").onclick = () => {
        navigator.clipboard.writeText(pixQrCode);
      };
    });
    
    upsell.on("paid", () => {
      document.getElementById("passo-pix").hidden    = true;
      document.getElementById("passo-oferta").hidden = true;
      document.getElementById("passo-pago").hidden   = false;
      // Avança para a próxima etapa do funil (configurada no painel Celetus)
      setTimeout(() => upsell.next(), 1500);
    });
    
    upsell.on("failed", ({ reason }) => alert("Pagamento: " + reason));
    upsell.on("error",  (err)        => console.error(err));
  7. Encadeamento automático entre etapas

    Um funil de upsell pode ter várias etapas em sequência. As URLs da próxima etapa são configuradas no painel Celetus (positiva e negativa) — você não precisa hardcodar caminhos no código. Use:

    • upsell.next() — auto-detecta direção pelo estado atual (positivo se paid, negativo caso contrário)
    • upsell.decline() — atalho para o caminho negativo (quando o cliente clica "Não, obrigado")

    O SDK preserva a query string da página no redirect (exceto ?key=, substituída pela key da próxima etapa), garantindo que sfid, UTMs e outros params cheguem intactos.

    Se preferir, use step.positivePageUrl diretamente em um <a href>, ou chame upsell.next({ noRedirect: true }) pra obter a URL de destino sem navegar.

  8. (Opcional) Gerar QR a partir do copia-e-cola

    Quando o evento traz só o código copia-e-cola em string, inclua a lib qrcode via CDN para gerar a imagem localmente:

    <script src="https://cdn.jsdelivr.net/npm/qrcode@1.5.3/build/qrcode.min.js"></script>
    
    <script>
    upsell.on("qrReady", ({ pixQrUrl, pixQrCode }) => {
      const qr = document.getElementById("qr");
      if (pixQrUrl && pixQrUrl.startsWith("http")) {
        qr.src = pixQrUrl;
      } else if (pixQrCode) {
        QRCode.toDataURL(pixQrCode, { width: 240 }, (err, url) => {
          if (!err) qr.src = url;
        });
      }
    });
    </script>

⚠️ Link de uso único (importante para testes)

Por segurança, cada compra gera um link que pode ser carregado uma única vez — isso impede que o cliente compartilhe com outras pessoas. O SDK guarda os dados em localStorage por 1 minuto para sobreviver a reloads dentro dessa janela.

Exemplo executável

Veja um arquivo completo pronto pra abrir no navegador: abrir exemplo.html.

Deploy

Hospede o HTML em qualquer lugar (S3, Netlify, Vercel, GitHub Pages, seu WordPress, Framer, Wix). Depois configure no painel Celetus, no passo do funil, a URL da sua página customizada — o checkout principal passa a redirecionar para lá após a compra.

CORS: se sua página roda num domínio diferente do checkout Celetus, o backend precisa liberar o CORS do seu domínio. Abra chamado com o suporte informando o domínio exato.