Pular para o conteúdo principal
Deixe o Sentra escrever seu código de integração para você.
Use nosso assistente de IA no VS Code, Cursor ou Windsurf para gerar código de SDK/API, código de integração do LLM Blueprint, webhooks e muito mais – basta descrever o que deseja.
Experimente o Sentra: Integração impulsionada por IA →
Neste tutorial, você construirá um aplicativo de chat AI com cobrança automática baseada em uso. Vamos criar tudo do zero: o medidor de cobrança, a configuração do produto e o código do aplicativo que alimenta as conversas e rastreia o uso de tokens em tempo real.
Este tutorial fornece uma aplicação completa funcional com backend e frontend. O aplicativo de chat usa a IA Gemini do Google e acompanha automaticamente o uso de tokens sem necessidade de contagem manual.
Ao final deste tutorial, você terá um aplicativo de chat em funcionamento que:
  • Alimenta conversas de IA usando o Google Gemini (AI SDK)
  • Rastreia automaticamente o uso de tokens (sem código manual)
  • Cobra dos clientes com base no consumo real de tokens
  • Inclui uma interface de chat bonita
Demonstração de Chat com IA

O Que Estamos Construindo

Vamos começar entendendo nosso serviço de chat AI:
  • Serviço: Chat alimentado por IA usando o Google Gemini (AI SDK)
  • Modelo de Preço: Pagamento por token ($0,01 por 1.000 tokens)
  • Camada Gratuita: 10.000 tokens gratuitos por cliente por mês
  • Recursos: Histórico de conversas, rastreamento automático de tokens
Antes de começarmos, certifique-se de que você possui:
  • Uma conta da Dodo Payments
  • Uma chave de API da Google AI (Pegue uma em aistudio)
  • Node.js v16+ instalado

Passo 1: Crie Seu Medidor de Uso

Começaremos criando um medidor no seu painel Dodo Payments que rastreará o uso de tokens de IA.
O que estamos construindo: Um medidor chamado “AI Token Usage Meter” que soma todos os tokens consumidos em conversas de chat.
1

Open the Meters section

  1. Faça login no painel da Dodo Payments
  2. Clique em Produtos na barra lateral esquerda
  3. Clique em Meters
  4. Clique no botão Create Meter
Criar Medidor
Você verá um formulário onde configuraremos nosso rastreamento de tokens.
2

Fill in the basic meter information

Agora vamos inserir os detalhes específicos para o nosso serviço de chat de IA:Meter NameAI Token Usage MeterDescriptionTracks token consumption from AI chat conversations using AI SDKEvent Nameai_chat_usage
O nome do evento ai_chat_usage deve corresponder exatamente ao que enviaremos do código da nossa aplicação posteriormente. Os nomes de eventos diferenciam maiúsculas de minúsculas!
3

Configure how we count tokens

Configure a agregação (como o medidor contabiliza nossos eventos):Tipo de Agregação: Selecione Soma no dropdownAggregate Over: Type → totalTokensMeasurement Unit: Type → tokens
Estamos usando “Sum” porque queremos somar todos os tokens consumidos em várias mensagens de chat. O SDK envia automaticamente totalTokens em cada evento.
4

Create your meter

  1. Verifique novamente se todas as suas configurações correspondem aos valores acima
  2. Clique em Create Meter
Configuração do Medidor
Medidor criado! Seu “AI Token Usage Meter” agora está pronto para começar a contar tokens. Em seguida, conectaremos ele a um produto de faturamento.

Passo 2: Obtenha Suas Chaves de API

Antes de construirmos o app, vamos reunir as chaves de API que precisaremos.
1

Get Dodo Payments API Key

  1. No painel da Dodo Payments, vá para DevelopersAPI Keys
  2. Clique em Create API Key
  3. Copie a chave API - ela terá a aparência test_abc123...
Guarde essa chave de API - adicionaremos ela ao nosso arquivo .env mais tarde.
2

Get Google AI API Key

  1. Visite aistudio.google.com
  2. Clique em Get API Key
  3. Crie uma nova chave API ou use uma existente
  4. Copie a chave
Mantenha essa chave segura - também a adicionaremos ao nosso arquivo .env.

Passo 3: Crie Seu Produto de Cobrança

Agora precisamos criar um produto que defina nosso preço ($0,01 por 1.000 tokens com 10.000 tokens gratuitos). Isso conecta nosso medidor à cobrança real.
O que estamos construindo: Um produto chamado “AI Chat Service” que cobra com base no consumo de tokens com um generoso nível gratuito.
1

Navigate to Products

  1. No painel da Dodo Payments, clique em Products na barra lateral esquerda
  2. Clique em Create Product
  3. Selecione Usage-Based como tipo de produto
Isso informa à Dodo Payments que o faturamento será baseado no uso do medidor, não em uma assinatura fixa.
2

Enter product details

Preencha os detalhes obrigatórios:Product Name: → AI Chat ServiceDescription: → AI-powered chat service with automatic token-based billingImagem do Produto: Faça upload de uma imagem relevante
Esses dados aparecerão nas faturas dos clientes, então torne-os claros e profissionais.
3

Connect your meter

Antes de conectar seu medidor, certifique-se de ter selecionado Usage Based Billing como tipo de preço do produto.Além disso, defina o Fixed Price para 0 para garantir que os clientes sejam cobrados apenas com base no uso, sem taxa base.Agora, vincule o medidor que você acabou de criar:
  1. Role para baixo até a seção Medidor Associado
  2. Clique em Adicionar Medidores
  3. No dropdown, selecione “Medidor de Uso de Tokens de IA” (o que você criou anteriormente)
  4. Confirme que ele aparece na configuração do seu produto
Seu medidor agora está conectado com êxito a este produto.
4

Set your pricing

Aqui definimos nosso modelo de negócios:Price Per Unit: Digite → 0.00001 (isso é 0,01por1.000tokensou0,01 por 1.000 tokens ou 0,00001 por token)Free Threshold: Digite → 10000 (os clientes recebem 10.000 tokens grátis por mês)
Precificação do Produto
Como o faturamento funciona: Se um cliente usa 25.000 tokens em um mês, será cobrado por 15.000 tokens (25.000 - 10.000 grátis) = 15.000 × 0,00001=0,00001 = 0,15
5

Save your product

  1. Revise todas as suas configurações:
    • Nome: AI Chat Service
    • Medidor: AI Token Usage Meter
    • Preço: $0,01 por 1.000 tokens
    • Nível gratuito: 10.000 tokens
  2. Clique em Save Changes
Produto criado! Seu faturamento agora está configurado. Os clientes serão cobrados automaticamente com base no consumo de tokens.

Passo 4: Faça uma Compra de Teste

Antes de começarmos a construir o app, vamos criar um cliente de teste fazendo uma compra.
1

Get your payment link

  1. No painel da Dodo Payments, vá para Products
  2. Encontre seu produto “AI Chat Service”
  3. Clique no botão Share ao lado do seu produto
  4. Copie o link de pagamento que aparecer
2

Complete a test purchase

  1. Abra o link de pagamento em uma nova aba do navegador
  2. Insira os dados de pagamento de teste e conclua a compra
Após o pagamento bem-sucedido, você terá um ID de cliente que usaremos no código da nossa aplicação.
3

Find your customer ID

  1. Volte para o painel da Dodo Payments
  2. Navegue até Sales -> Customers na barra lateral esquerda
  3. Encontre o cliente que você acabou de criar (com o e-mail de teste)
  4. Copie o ID do cliente - ele terá a aparência cus_123
Guarde esse ID de cliente - usaremos ele ao testar nosso aplicativo de chat.

Passo 5: Construa o Aplicativo de Chat

Agora que temos nossa configuração de cobrança completa e um cliente de teste criado. Vamos construir o aplicativo de chat AI com rastreamento automático de tokens.
1

Set up your project

Crie um novo diretório e inicialize o projeto:
mkdir ai-chat-app
cd ai-chat-app
npm init -y
2

Install dependencies

Instale os pacotes necessários:
npm install express ai @ai-sdk/google @dodopayments/ingestion-blueprints dotenv
npm install --save-dev typescript @types/express @types/node tsx
3

Configure TypeScript

Crie tsconfig.json:
tsconfig.json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "strict": true,
    "skipLibCheck": true,
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}
Atualize package.json para adicionar o tipo de módulo e scripts:
package.json
{
  "type": "module",
  "scripts": {
    "dev": "tsx src/server.ts",
    "build": "tsc",
    "start": "node dist/server.js"
  }
}
4

Create project structure

Crie as pastas e arquivos:
mkdir src public
5

Set up environment variables

Crie um arquivo .env na raiz do seu projeto:
.env
DODO_PAYMENTS_API_KEY=your_dodo_api_key_here
DODO_ENVIRONMENT=test_mode
GOOGLE_GENERATIVE_AI_API_KEY=your_google_api_key_here
PORT=3000
Substitua os valores de espaço reservado pelas suas chaves de API reais do Passo 2.
6

Create the backend server

Crie src/server.ts e copie este código completo do servidor:
Aqui está o servidor de chat AI completo com cobrança integrada:
import express, { Request, Response } from 'express';
import { generateText } from 'ai';
import { google } from '@ai-sdk/google';
import { createLLMTracker } from '@dodopayments/ingestion-blueprints';
import 'dotenv/config';

const app = express();
app.use(express.json());
app.use(express.static('public'));

// Replace with your test customer ID
const CUSTOMER_ID = 'cus_123';

// Create tracker once with your meter event name
const llmTracker = createLLMTracker({
  apiKey: process.env.DODO_PAYMENTS_API_KEY!,
  environment: process.env.DODO_ENVIRONMENT as 'test_mode' | 'live_mode',
  eventName: 'ai_chat_usage', // Must match your meter configuration
});

// Chat endpoint with conversation support
app.post('/chat', async (req: Request, res: Response) => {
  try {
    const { messages } = req.body;

    if (!messages || !Array.isArray(messages)) {
      return res.status(400).json({ 
        error: 'Missing required field: messages (array)' 
      });
    }

    // Wrap AI SDK with automatic token tracking
    const trackedClient = llmTracker.wrap({
      client: { generateText },
      customerId: CUSTOMER_ID
    });

    // Generate AI response - tokens are automatically tracked!
    const response = await trackedClient.generateText({
      model: google('gemini-2.5-flash'),
      messages: messages
    });

    res.json({
      message: response.text,
      usage: {
        totalTokens: response.usage.totalTokens
      }
    });

  } catch (error: any) {
    console.error('Chat error:', error);
    res.status(500).json({ 
      error: 'Failed to process chat',
      details: error.message 
    });
  }
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`🚀 Server running at http://localhost:${PORT}`);
  console.log(`📊 Tracking event: ai_chat_usage`);
  console.log(`👤 Customer ID: ${CUSTOMER_ID}`);
  console.log(`🔧 Environment: ${process.env.DODO_ENVIRONMENT}`);
});

Passo 6: Adicione a Interface de Chat

Agora vamos adicionar uma interface de chat bonita com todo o histórico de conversas! Crie public/index.html:
public/index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>AI Chat with Usage Billing</title>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }

      body {
        font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI",
          Roboto, sans-serif;
        background: #0f0f1e;
        background-image: radial-gradient(
            at 0% 0%,
            rgba(102, 126, 234, 0.15) 0px,
            transparent 50%
          ),
          radial-gradient(
            at 100% 100%,
            rgba(118, 75, 162, 0.15) 0px,
            transparent 50%
          ),
          radial-gradient(
            at 50% 50%,
            rgba(102, 126, 234, 0.05) 0px,
            transparent 50%
          );
        height: 100vh;
        display: flex;
        justify-content: center;
        align-items: center;
        padding: 0;
        position: relative;
        overflow: hidden;
        margin: 0;
      }

      .chat-container {
        background: rgba(22, 22, 35, 0.95);
        border: 1px solid rgba(102, 126, 234, 0.2);
        border-radius: 0;
        box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
        width: 100%;
        max-width: 100%;
        height: 100vh;
        display: flex;
        flex-direction: column;
        overflow: hidden;
        position: relative;
        z-index: 1;
      }

      .chat-header {
        background: linear-gradient(
          135deg,
          rgba(102, 126, 234, 0.15) 0%,
          rgba(118, 75, 162, 0.15) 100%
        );
        border-bottom: 1px solid rgba(102, 126, 234, 0.2);
        color: white;
        padding: 24px 28px;
        position: relative;
        overflow: hidden;
      }

      .chat-header h1 {
        font-size: 26px;
        margin-bottom: 6px;
        font-weight: 700;
        letter-spacing: -0.5px;
        color: #fff;
      }

      .chat-header p {
        font-size: 13px;
        opacity: 0.6;
        font-weight: 500;
        letter-spacing: 0.3px;
      }

      .chat-messages {
        flex: 1;
        overflow-y: auto;
        padding: 32px 10%;
        background: transparent;
        will-change: scroll-position;
        scroll-behavior: smooth;
      }

      .chat-messages::-webkit-scrollbar {
        width: 6px;
      }

      .chat-messages::-webkit-scrollbar-track {
        background: rgba(255, 255, 255, 0.05);
      }

      .chat-messages::-webkit-scrollbar-thumb {
        background: rgba(102, 126, 234, 0.3);
        border-radius: 3px;
      }

      .chat-messages::-webkit-scrollbar-thumb:hover {
        background: rgba(102, 126, 234, 0.5);
      }

      .message {
        margin-bottom: 20px;
        display: flex;
        gap: 12px;
        animation: slideIn 0.2s ease-out;
      }

      @keyframes slideIn {
        from {
          opacity: 0;
          transform: translateY(10px);
        }

        to {
          opacity: 1;
          transform: translateY(0);
        }
      }

      .message.user {
        flex-direction: row-reverse;
      }

      .message-avatar {
        width: 40px;
        height: 40px;
        border-radius: 12px;
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 20px;
        flex-shrink: 0;
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
      }

      .message.user .message-avatar {
        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      }

      .message.assistant .message-avatar {
        background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
      }

      .message-content {
        max-width: 65%;
      }

      .message-bubble {
        padding: 14px 18px;
        border-radius: 18px;
        line-height: 1.6;
        word-wrap: break-word;
        font-size: 15px;
        position: relative;
      }

      .message.user .message-bubble {
        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        color: white;
        border-bottom-right-radius: 6px;
        box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
      }

      .message.assistant .message-bubble {
        background: rgba(255, 255, 255, 0.05);
        color: rgba(255, 255, 255, 0.95);
        border: 1px solid rgba(255, 255, 255, 0.1);
        border-bottom-left-radius: 6px;
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
      }

      .message-meta {
        display: flex;
        gap: 10px;
        margin-top: 8px;
        font-size: 11px;
        color: rgba(255, 255, 255, 0.4);
        font-weight: 500;
      }

      .message.user .message-meta {
        justify-content: flex-end;
      }

      .token-badge {
        background: rgba(102, 126, 234, 0.2);
        color: #a8b9ff;
        padding: 4px 10px;
        border-radius: 12px;
        font-weight: 600;
        border: 1px solid rgba(102, 126, 234, 0.3);
      }

      .chat-input-area {
        padding: 24px 10% 32px;
        background: rgba(22, 22, 35, 0.95);
        border-top: 1px solid rgba(102, 126, 234, 0.2);
      }

      .input-wrapper {
        display: flex;
        gap: 12px;
        align-items: flex-end;
      }

      #messageInput {
        flex: 1;
        background: rgba(255, 255, 255, 0.05);
        border: 2px solid rgba(102, 126, 234, 0.2);
        border-radius: 16px;
        padding: 14px 20px;
        font-size: 15px;
        font-family: inherit;
        resize: none;
        max-height: 120px;
        transition: border-color 0.2s ease, background 0.2s ease;
        color: white;
        will-change: border-color;
        overflow: hidden;
        scrollbar-width: none;
        /* Firefox */
      }

      #messageInput::-webkit-scrollbar {
        display: none;
      }

      #messageInput::placeholder {
        color: rgba(255, 255, 255, 0.3);
      }

      #messageInput:focus {
        outline: none;
        border-color: #667eea;
        background: rgba(255, 255, 255, 0.08);
      }

      #sendBtn {
        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        color: white;
        border: none;
        width: 52px;
        height: 52px;
        border-radius: 16px;
        cursor: pointer;
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 22px;
        transition: transform 0.1s ease, box-shadow 0.1s ease;
        flex-shrink: 0;
        box-shadow: 0 2px 8px rgba(102, 126, 234, 0.4);
        position: relative;
      }

      #sendBtn:hover:not(:disabled) {
        transform: translateY(-1px);
        box-shadow: 0 4px 12px rgba(102, 126, 234, 0.5);
      }

      #sendBtn:active:not(:disabled) {
        transform: translateY(0);
      }

      #sendBtn:disabled {
        opacity: 0.4;
        cursor: not-allowed;
        box-shadow: none;
      }

      .typing-indicator {
        display: none;
        padding: 14px 18px;
        background: rgba(255, 255, 255, 0.05);
        border: 1px solid rgba(255, 255, 255, 0.1);
        border-radius: 18px;
        border-bottom-left-radius: 6px;
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
        width: fit-content;
      }

      .typing-indicator.show {
        display: block;
      }

      .typing-dots {
        display: flex;
        gap: 6px;
      }

      .typing-dots span {
        width: 10px;
        height: 10px;
        border-radius: 50%;
        background: #667eea;
        animation: typing 1.4s infinite ease-in-out;
        will-change: transform, opacity;
      }

      .typing-dots span:nth-child(2) {
        animation-delay: 0.2s;
      }

      .typing-dots span:nth-child(3) {
        animation-delay: 0.4s;
      }

      @keyframes typing {
        0%,
        60%,
        100% {
          transform: translateY(0) scale(1);
          opacity: 0.6;
        }

        30% {
          transform: translateY(-12px) scale(1.1);
          opacity: 1;
        }
      }

      .error-message {
        background: rgba(239, 68, 68, 0.15);
        color: #fca5a5;
        padding: 14px 18px;
        border-radius: 12px;
        margin-bottom: 12px;
        display: none;
        border: 1px solid rgba(239, 68, 68, 0.3);
        font-size: 14px;
        font-weight: 500;
      }

      .error-message.show {
        display: block;
        animation: slideIn 0.3s ease;
      }

      .empty-state {
        text-align: center;
        padding: 80px 20px;
        color: rgba(255, 255, 255, 0.5);
      }

      .empty-state-icon {
        font-size: 72px;
        margin-bottom: 20px;
        animation: float 3s ease-in-out infinite;
      }

      @keyframes float {
        0%,
        100% {
          transform: translateY(0px);
        }

        50% {
          transform: translateY(-10px);
        }
      }

      .empty-state h2 {
        font-size: 24px;
        margin-bottom: 10px;
        color: rgba(255, 255, 255, 0.9);
        font-weight: 700;
        letter-spacing: -0.5px;
      }

      .empty-state p {
        font-size: 15px;
        color: rgba(255, 255, 255, 0.4);
        font-weight: 500;
      }
    </style>
  </head>

  <body>
    <div class="chat-container">
      <div class="chat-header">
        <h1>🤖 AI Chat Assistant</h1>
        <p>Powered by AI-SDK & Dodo Payments</p>
      </div>

      <div class="chat-messages" id="chatMessages">
        <div class="empty-state" id="emptyState">
          <div class="empty-state-icon">💬</div>
          <h2>Start a Conversation</h2>
          <p>Ask me anything! Your token usage is automatically tracked.</p>
        </div>
      </div>

      <div class="chat-input-area">
        <div class="error-message" id="errorMessage"></div>
        <div class="input-wrapper">
          <textarea
            id="messageInput"
            placeholder="Type your message here..."
            rows="1"
          ></textarea>
          <button id="sendBtn" onclick="sendMessage()"></button>
        </div>
      </div>
    </div>

    <script>
      let conversationHistory = [];

      const messageInput = document.getElementById("messageInput");
      let resizeTimeout;
      messageInput.addEventListener("input", function () {
        clearTimeout(resizeTimeout);
        resizeTimeout = setTimeout(() => {
          this.style.height = "auto";
          this.style.height = Math.min(this.scrollHeight, 120) + "px";
        }, 10);
      });

      // Send message on Enter (Shift+Enter for new line)
      messageInput.addEventListener("keydown", function (e) {
        if (e.key === "Enter" && !e.shiftKey) {
          e.preventDefault();
          sendMessage();
        }
      });

      async function sendMessage() {
        const input = document.getElementById("messageInput");
        const message = input.value.trim();

        if (!message) return;

        // Hide empty state
        document.getElementById("emptyState").style.display = "none";

        // Hide error
        document.getElementById("errorMessage").classList.remove("show");

        // Add user message to UI
        addMessage("user", message);

        // Add to conversation history
        conversationHistory.push({
          role: "user",
          content: message,
        });

        // Clear input
        input.value = "";
        input.style.height = "auto";

        // Show typing indicator
        showTypingIndicator();

        // Disable send button
        const sendBtn = document.getElementById("sendBtn");
        sendBtn.disabled = true;

        try {
          const response = await fetch("/chat", {
            method: "POST",
            headers: {
              "Content-Type": "application/json",
            },
            body: JSON.stringify({
              messages: conversationHistory,
            }),
          });

          const data = await response.json();

          if (!response.ok) {
            throw new Error(data.error || "Failed to get response");
          }

          // Hide typing indicator
          hideTypingIndicator();

          // Add assistant response to UI
          addMessage("assistant", data.message, data.usage);

          // Add to conversation history
          conversationHistory.push({
            role: "assistant",
            content: data.message,
          });
        } catch (error) {
          hideTypingIndicator();
          showError(error.message);
          // Remove the last user message from history since it failed
          conversationHistory.pop();
        } finally {
          sendBtn.disabled = false;
        }
      }

      function addMessage(role, content, usage = null) {
        const messagesDiv = document.getElementById("chatMessages");

        const messageDiv = document.createElement("div");
        messageDiv.className = `message ${role}`;

        const avatar = role === "user" ? "👤" : "🤖";

        let metaHTML = "";
        if (usage) {
          metaHTML = `
                    <div class="message-meta">
                        <span class="token-badge">📊 ${usage.totalTokens} tokens</span>
                    </div>
                `;
        }

        messageDiv.innerHTML = `
                <div class="message-avatar">${avatar}</div>
                <div class="message-content">
                    <div class="message-bubble">${escapeHtml(content)}</div>
                    ${metaHTML}
                </div>
            `;

        messagesDiv.appendChild(messageDiv);
        requestAnimationFrame(() => {
          messagesDiv.scrollTop = messagesDiv.scrollHeight;
        });
      }

      function showTypingIndicator() {
        const messagesDiv = document.getElementById("chatMessages");

        const typingDiv = document.createElement("div");
        typingDiv.className = "message assistant";
        typingDiv.id = "typingIndicator";
        typingDiv.innerHTML = `
                <div class="message-avatar">🤖</div>
                <div class="typing-indicator show">
                    <div class="typing-dots">
                        <span></span>
                        <span></span>
                        <span></span>
                    </div>
                </div>
            `;

        messagesDiv.appendChild(typingDiv);
        requestAnimationFrame(() => {
          messagesDiv.scrollTop = messagesDiv.scrollHeight;
        });
      }

      function hideTypingIndicator() {
        const typingIndicator = document.getElementById("typingIndicator");
        if (typingIndicator) {
          typingIndicator.remove();
        }
      }

      function showError(message) {
        const errorDiv = document.getElementById("errorMessage");
        errorDiv.textContent = "❌ " + message;
        errorDiv.classList.add("show");
      }

      function escapeHtml(text) {
        const div = document.createElement("div");
        div.textContent = text;
        return div.innerHTML.replace(/\n/g, "<br>");
      }
    </script>
  </body>
</html>

Passo 7: Teste Seu Aplicativo de Chat

Hora de testar nosso app de chat AI e ver a cobrança em ação! Vamos garantir que tudo funcione de ponta a ponta.
O que estamos testando: Vamos ter algumas conversas com a IA, verificar se os eventos de tokens chegam à Dodo Payments e confirmar se os cálculos de faturamento estão corretos.
1

Start the server

Primeiro, certifique-se de que tudo está configurado:
  1. Verifique se seu arquivo .env possui todas as chaves API do Passo 2
  2. Inicie o servidor de desenvolvimento:
npm run dev
Você deve ver:
🚀 Server running at http://localhost:3000
📊 Tracking event: ai_chat_usage
👤 Customer ID: {YOUR CUSTOMER_ID}
🔧 Environment: test_mode
Servidor em execução! Hora de conversar.
2

Open the chat interface

  1. Abra seu navegador
  2. Vá para http://localhost:3000
  3. Você deverá ver a bela interface de chat
Certifique-se de atualizar CUSTOMER_ID em server.ts com seu ID de cliente de teste real do Passo 4.
3

Have your first conversation

Vamos testar! Experimente estas mensagens:
  1. “O que é inteligência artificial?”
  2. “Como funciona o aprendizado de máquina?”
  3. “Você pode explicar redes neurais?”
Observe a exibição do uso de tokens atualizar após cada resposta!
Se você vir a IA respondendo e as contagens de tokens aparecendo, seu aplicativo está funcionando!
4

Check your Dodo Payments dashboard

Agora vamos verificar se os eventos estão sendo recebidos:
  1. Abra seu painel Dodo Payments
  2. Vá para Cobrança por UsoMedidor de Uso de Tokens de IA
  3. Clique na aba Eventos
  4. Você deve ver seus eventos de chat listados
O que observar:
  • Nomes de evento: ai_chat_usage
  • ID do cliente: seu ID de cliente de teste
Eventos do Medidor
Você deverá ver um evento para cada mensagem enviada!
5

Verify token counting

Vamos enviar mais mensagens e verificar se a agregação de tokens está funcionando:
  1. No seu medidor, vá para a aba Clientes
  2. Encontre seu cliente de teste
  3. Verifique a coluna “Unidades Consumidas” - ela deve mostrar o total de tokens usados
Tokens do Cliente no Medidor
O medidor está somando automaticamente todos os valores totalTokens!
6

Test the free tier

Vamos usar tokens suficientes para ultrapassar o nível gratuito:
  1. Tenha várias outras conversas (visando ~15.000+ tokens no total)
  2. Verifique sua aba Clientes no painel do medidor novamente
  3. Você deve agora ver:
    • Unidades Consumidas: 15.000+ tokens
    • Unidades Cobráveis: 5.000 (10.000 tokens gratuitos aplicados)
    • Preço Total: ~$0,05
Teste do Nível Gratuito
Sucesso! Seu faturamento baseado no uso está funcionando perfeitamente. Os clientes serão cobrados automaticamente com base no consumo real de tokens.

Solução de Problemas

Problemas comuns e suas soluções:
Possíveis causas:
  • O nome do evento não corresponde exatamente à configuração do medidor
  • O ID do cliente não existe na sua conta
  • A chave de API é inválida ou expirou
  • Problemas de conectividade com a rede
Soluções:
  1. Verifique se o nome do evento corresponde exatamente à configuração do medidor (sensível a maiúsculas: ai_chat_usage)
  2. Confira se o ID do cliente existe no painel da Dodo Payments
  3. Teste a chave de API com uma chamada simples
  4. Verifique os logs do servidor em busca de mensagens de erro
Possíveis causas:
  • Modelo não retornando informações de uso
  • Versão incorreta do SDK
Soluções:
  1. Teste se o modelo retorna uso:
const response = await generateText({...});
console.log('Usage:', response.usage);
  1. Atualize para a versão mais recente do Blueprints SDK: npm install @dodopayments/ingestion-blueprints@latest
Possíveis causas:
  • Chave de API errada para o ambiente
  • Espaços extras ou aspas no arquivo .env
Soluções:
  • Garanta que a chave de teste comece com test_, a chave ao vivo começa com live_
  • Remova quaisquer aspas ao redor das chaves no arquivo .env
  • Gere uma nova chave, se necessário
Precisa de ajuda?

Parabéns! Você Construiu um App de Chat AI

Agora você tem um aplicativo de chat AI totalmente funcional com rastreamento automático de uso de tokens e cobrança alimentada pelo Dodo Payments. 🎉

Aprenda Mais