VoltarDocumentação

Integre o Café SaaS em qualquer site.

Uma tag de script (ou um componente React) que adiciona um rodapé global, um splash de doação e checkout via Mercado Pago.

Prioridade: Componente React

Se seu app é React, Next.js, Remix, TanStack Start ou qualquer Vite + React — use sempre o Componente React. A Script Tag é o último caso, apenas para hosts sem React (HTML estático, WordPress, Webflow, Shopify Liquid, Astro sem React, etc.).

Início rápido

  1. 1. Entre no painel e cadastre seu site — você recebe um SEU_SLUG.
  2. 2. O Mercado Pago já está configurado neste servidor — você não precisa de conta, chave nem nada. O checkout roda no domínio do Café SaaS.
  3. 3. Em apps React, instale o Componente React. Em sites não-React, use a Script Tag.

Componente React (recomendado)

Esta é a forma padrão de integrar em qualquer app React. O pacote @joaosmfilho/cafe-embed-react não está publicado no npm — o tarball pronto é hospedado pelo próprio servidor. Não precisa clonar nada, não precisa buildar nada.

Passo 1 — Download direto

Tarball oficial, versionado, servido como asset estático:

Passo 2 — Instalar

npm, bun, pnpm e yarn aceitam instalar direto de uma URL HTTPS de tarball. Rode no projeto onde quer usar o componente:

Terminal
# Instale direto da URL hospedada — não precisa clonar nada.
npm install https://cafe.joaosmfilho.org/downloads/cafe-embed-react-0.1.0.tgz

# Equivalentes:
bun  add  https://cafe.joaosmfilho.org/downloads/cafe-embed-react-0.1.0.tgz
pnpm add  https://cafe.joaosmfilho.org/downloads/cafe-embed-react-0.1.0.tgz
yarn add  https://cafe.joaosmfilho.org/downloads/cafe-embed-react-0.1.0.tgz

Passo 3 — Renderizar

Coloque <CafeEmbed> uma única vez no layout raiz da sua aplicação.

App.tsx
import { CafeEmbed } from "@joaosmfilho/cafe-embed-react";

export default function App() {
  return (
    <>
      {/* ...sua app */}
      <CafeEmbed siteSlug="SEU_SLUG" baseUrl="https://cafe.joaosmfilho.org" />
    </>
  );
}

SSR e como funciona

O componente injeta a tag <script src="https://cafe.joaosmfilho.org/api/public/embed.js?site=SEU_SLUG"> no host via useEffect. SSR-safe: o efeito só roda no client e o componente retorna null no servidor — funciona em Next.js, Remix e TanStack Start sem next/dynamic.

Props

CafeEmbedProps
export type CafeEmbedProps = {
  siteSlug: string; // slug do site cadastrado no painel
  baseUrl: string;  // URL do servidor Café SaaS
};

Essas são as únicas props expostas. Não há eventos ou refs.

Script Tag (último caso)

Use somente se o host não for React. Para qualquer app React/Next/Remix/TanStack Start, prefira o Componente React acima — ele evita duplicação e respeita o ciclo de vida do framework.

Em sites HTML estáticos, WordPress, Webflow, Shopify Liquid, Astro sem React etc., cole esta tag antes do fechamento de </body>:

HTML
<script src="https://cafe.joaosmfilho.org/api/public/embed.js?site=SEU_SLUG" defer></script>

Substitua SEU_SLUG pelo slug do seu site. O script aceita apenas o parâmetro ?site=. CORS aberto. Servido com Cache-Control: public, max-age=0, must-revalidate + ETag — o browser revalida a cada page-load do hospedeiro, então mudanças no painel aparecem no próximo carregamento (servidor responde 304 quando nada mudou).

Alternativa: rota dedicada /cafezinho

Em vez do modal (Métodos 1 e 2), o hospedeiro pode criar uma rota real /cafezinho que renderiza um <iframe> apontando direto para o checkout servido por cafe.joaosmfilho.org. A página de checkout (/checkout/{slug}) já é servida com frame-ancestors *, então pode ser embutida em qualquer origem.

Quando usar: quer URL real (compartilhável, indexável, bookmarkable), CSP do hospedeiro mínima e zero JS de terceiros executando fora do iframe. Quando NÃO usar: precisa do footer global e do splash automático em todas as páginas — para isso, use Método 1 ou 2 (que continuam padrão).

TanStack Start / React

src/routes/cafezinho.tsx
import { createFileRoute } from "@tanstack/react-router";

export const Route = createFileRoute("/cafezinho")({
  component: Cafezinho,
});

function Cafezinho() {
  return (
    <iframe
      src="https://cafe.joaosmfilho.org/checkout/SEU_SLUG"
      title="Me pague um café"
      allow="payment"
      style={{ width: "100%", height: "100vh", border: 0, display: "block" }}
    />
  );
}

HTML puro

cafezinho.html
<!doctype html>
<html><head><meta charset="utf-8"><title>Me pague um café</title>
<style>html,body,iframe{margin:0;padding:0;height:100%;width:100%;border:0}</style>
</head><body>
<iframe src="https://cafe.joaosmfilho.org/checkout/SEU_SLUG" allow="payment" title="Me pague um café"></iframe>
</body></html>

Substitua SEU_SLUG. O atributo allow="payment" habilita a Payment Request API dentro do iframe (necessário para alguns fluxos do Mercado Pago).

CSP mínima do hospedeiro

Como nenhum script de terceiros roda no DOM do hospedeiro, a única diretiva extra necessária é frame-src:

frame-src https://cafe.joaosmfilho.org

Eventos opcionais

O iframe envia window.postMessage ao pai com { source: "cafe-checkout", type, payload }. Tipos atuais: ready, success, error. Escute se quiser reagir (analytics, redirect pós-pagamento, etc.):

Listener (opcional)
useEffect(() => {
  function onMsg(e: MessageEvent) {
    if (e.origin !== "https://cafe.joaosmfilho.org") return;
    if (e.data?.source !== "cafe-checkout") return;
    if (e.data.type === "success") {
      // pagamento aprovado — e.data.payload tem { id, status, ... }
    }
  }
  window.addEventListener("message", onMsg);
  return () => window.removeEventListener("message", onMsg);
}, []);

O que o embed faz

  • Busca a config do site em GET /api/public/config/{slug} (cache de 5 min).
  • Renderiza um rodapé #cafe-embed-footer no fim do <body> com o texto configurado, o link e uma pílula âmbar "Me pague um café" apontando para /cafezinho no domínio do site hospedeiro (o clique é interceptado e abre o modal sem navegar). Abrir /cafezinho diretamente também abre o modal automaticamente.
  • Abre o splash de doação assim que a config carrega (sem delay, sem countdown). Frequência (session / once / always) vem da config.
  • Renderiza o Checkout Bricks do Mercado Pago dentro do próprio modal. Carrega https://sdk.mercadopago.com/js/v2, chama POST /api/public/checkout/{slug} para obter public_key e amount, e monta o Payment Brick (cartão de crédito/débito, Pix, boleto e Conta Mercado Pago) no container #cafe-payment-brick. O pagamento é finalizado sem sair do site hospedeiro.
  • No envio do formulário, o Brick chama POST /api/public/process-payment/{slug} com formData + selectedPaymentMethod; o servidor cria o pagamento na API do Mercado Pago (POST /v1/payments) com X-Idempotency-Key. O modal mostra QR code (Pix), link do boleto, ou confirmação de aprovação direto na tela.

Configuração do site

Tudo é editado no painel. A config pública exposta para o embed inclui o texto/links do rodapé, título/mensagem/CTA do splash, se o splash está ativado, a frequência e o valor padrão da doação (em centavos).

Endpoint de checkout

POST /api/public/checkout/{slug} — corpo opcional { "amount": 1500 } (centavos, entre 100 e 1.000.000). Sem corpo, usa o valor padrão da config. Responde { "amount": 500, "public_key": "...", "site": { "slug": "...", "name": "..." } }. Não cria preference no Mercado Pago (evita bloqueio do PolicyAgent). O embed monta o Payment Brick só com amount + public_key; o charge real acontece em /api/public/process-payment/{slug} via POST /v1/payments. CORS aberto.

POST /api/public/process-payment/{slug} — corpo { formData, selectedPaymentMethod } exatamente como entregue pelo callback onSubmit do Payment Brick. O servidor valida o valor (server-side, limites de /checkout), chama POST https://api.mercadopago.com/v1/payments com X-Idempotency-Key e registra o resultado em payments. Responde com { id, status, status_detail, payment_method_id, point_of_interaction, transaction_details } — o embed usa point_of_interaction.transaction_data.qr_code_base64 para Pix e transaction_details.external_resource_url para boleto.

POST /api/public/pix/{slug} — atalho Pix direto, sem Payment Brick. Corpo opcional { "amount": 1500, "email": "voce@exemplo.com" }. Sem corpo, usa o valor padrão do site; sem email, usa fallback interno. O servidor cria o pagamento em POST /v1/payments com payment_method_id: "pix", registra em payments e responde com { id, status, amount, qr_code, qr_code_base64, ticket_url, expires_at }. É o que o embed usa para o botão "Pix copia e cola" da tela de valor e o checkout usa na aba Pix.

CSP do hospedeiro — leia antes de integrar

Desde a v2, o checkout do Café SaaS roda dentro de um iframe servido por cafe.joaosmfilho.org. A CSP que vale para os scripts do Mercado Pago é a do iframe, configurada pelo Café SaaS — não a sua. Sua CSP só precisa permitir o domínio do Café SaaS:

script-src   https://cafe.joaosmfilho.org
connect-src  https://cafe.joaosmfilho.org
frame-src    https://cafe.joaosmfilho.org

Snippet copy-paste para index.html:

<meta http-equiv="Content-Security-Policy" content="
  default-src 'self';
  script-src  'self' 'unsafe-inline' https://cafe.joaosmfilho.org;
  style-src   'self' 'unsafe-inline' https://cafe.joaosmfilho.org;
  connect-src 'self' https://cafe.joaosmfilho.org;
  frame-src   https://cafe.joaosmfilho.org;
  img-src     'self' data: https:;" />

Modo legado (sem iframe)

Se você está usando uma versão antiga do embed que monta o Brick direto no DOM do hospedeiro, a CSP precisa liberar TODOS estes domínios:

script-src   https://sdk.mercadopago.com https://http2.mlstatic.com
connect-src  https://api.mercadopago.com https://*.mercadopago.com https://http2.mlstatic.com https://api.mercadolibre.com https://www.mercadolibre.com
frame-src    https://*.mercadopago.com https://www.mercadolibre.com
img-src      https://http2.mlstatic.com data:

Webhook do Mercado Pago

Configure esta URL na sua aplicação Mercado Pago (Webhooks → Notificações), tópico payment:

https://cafe.joaosmfilho.org/api/public/mp-webhook

Se a variável MP_WEBHOOK_SECRET estiver definida no servidor, o endpoint valida a assinatura via header x-signature e rejeita inválidas com 401. Sem a secret, o endpoint aceita o webhook sem validar (apropriado só para testes).

Todo evento recebido é registrado em webhook_events. Quando o tipo contém payment, o endpoint busca o pagamento na API do MP e grava o resultado vinculado ao site (via external_reference).

Prompt Lovable / Claude Code

Cole este prompt no Lovable, Claude Code, Cursor ou qualquer agente de código para integrar o Café SaaS automaticamente. Ele descreve só o que existe de verdade — script tag, componente React e o comportamento do embed — sem inventar APIs.

Prompt
Quero integrar o Café SaaS no meu site.

Servidor: https://cafe.joaosmfilho.org
Meu slug: SEU_SLUG

PRIORIDADE ABSOLUTA: use o COMPONENTE REACT. Só caia para script tag se o host
NÃO for React (HTML puro, WordPress, Webflow, Shopify Liquid, Astro sem React).

1) [PADRÃO] Componente React — para React, Next.js, Remix, TanStack Start, Vite + React.

   O pacote @joaosmfilho/cafe-embed-react NÃO está no npm. O próprio servidor
   hospeda o tarball pronto. Instale direto da URL (não clone, não builde nada):

     npm install https://cafe.joaosmfilho.org/downloads/cafe-embed-react-0.1.0.tgz

   Renderize UMA vez no layout raiz:

     import { CafeEmbed } from "@joaosmfilho/cafe-embed-react";
     <CafeEmbed siteSlug="SEU_SLUG" baseUrl="https://cafe.joaosmfilho.org" />

   O componente é SSR-safe (retorna null no servidor) e só injeta a tag de
   script abaixo via useEffect. Props expostas: APENAS siteSlug e baseUrl.

2) [ÚLTIMO CASO] Script tag — APENAS quando o host não é React.

   Cole antes do </body>:
     <script src="https://cafe.joaosmfilho.org/api/public/embed.js?site=SEU_SLUG" defer></script>

3) [ALTERNATIVA] Rota dedicada /cafezinho — sem modal, sem footer global.

   Use quando preferir URL real (compartilhável) e CSP mínima no hospedeiro,
   abrindo mão do footer automático e do splash. Crie uma rota /cafezinho que
   renderiza um <iframe> apontando direto para o checkout:

     <iframe src="https://cafe.joaosmfilho.org/checkout/SEU_SLUG"
             allow="payment"
             style="width:100%;height:100vh;border:0"></iframe>

   CSP do hospedeiro nesse cenário: apenas frame-src https://cafe.joaosmfilho.org.
   Opcionalmente, escute window.postMessage para reagir a "ready" / "success" / "error".

Regras importantes:
- Se o projeto usa React em qualquer forma, use o Componente React. Nunca
  inclua o <script> manualmente em um app React (vai gerar duplicação).
- Use apenas o parâmetro ?site= no script. Não invente outros parâmetros.
- Não duplique o embed: uma única instância no layout raiz basta.
- Não tente reimplementar o footer, o splash ou o checkout — tudo é renderizado
  pelo próprio script (footer #cafe-embed-footer, modal com Payment Brick do
  Mercado Pago no container #cafe-payment-brick).
- O link /cafezinho no domínio hospedeiro abre o modal automaticamente.
- Hosts com CSP estrito precisam permitir https://sdk.mercadopago.com
  (script-src) e https://api.mercadopago.com / https://*.mercadopago.com
  (connect-src / frame-src).

Tarefa: aplique a integração no meu projeto seguindo exatamente essas
instruções. Não adicione nada que não esteja descrito acima.

Baixar documentação completa

Exporte todo o conteúdo desta página para usar offline ou como referência no seu agente de IA. O arquivo SKILL.md é o que você sobe em Lovable Settings → Skills (já inclui o frontmatter YAML obrigatório).