No mundo dinâmico do desenvolvimento web, a monetização de extensões para Chrome é um tópico de crescente importância. Uma das maneiras mais eficazes de gerar receita é através de assinaturas e pagamentos únicos, e a Lemon Squeezy surge como uma solução promissora para essa finalidade. Este artigo detalha como integrar a Lemon Squeezy, uma plataforma de pagamentos projetada para criadores digitais, em sua extensão do Chrome, utilizando webhooks para gerenciar assinaturas e pagamentos de forma eficiente. Acompanhe este guia completo e descubra como simplificar o processo de monetização da sua extensão.
Parte 1: Configurando a Lemon Squeezy
Antes de mergulharmos no código, é crucial configurar sua conta Lemon Squeezy e obter as credenciais necessárias. Este processo envolve a criação de uma conta, a definição de produtos e a geração de chaves de API. Siga os passos abaixo para garantir que tudo esteja configurado corretamente.
Passo 1: Criar sua Conta Lemon Squeezy
Comece acessando o site da Lemon Squeezy em https://www.lemonsqueezy.com/ e criando uma conta de teste. A conta de teste permite que você experimente a plataforma sem incorrer em custos reais, ideal para o desenvolvimento e testes iniciais. Certifique-se de completar o processo de onboarding para ter acesso total às funcionalidades.
Passo 2: Criar Seu Produto
No painel da Lemon Squeezy, navegue até a seção "Produtos" e clique em "Novo Produto". Aqui, você criará um produto de assinatura (por exemplo, "Plano Premium - Mensal"). Defina o preço da sua assinatura (por exemplo, R$9,99/mês). Após criar o produto, clique nele para visualizar os detalhes. Copie o ID da Variante (não o ID do Produto), pois você precisará dele para criar os links de checkout na sua extensão do Chrome. O ID da variante tem um formato numérico, como: 123456.
Passo 3: Gerar a Chave da API
Acesse "Configurações" → "API" no painel da Lemon Squeezy. Clique em "Criar Chave de API". Dê um nome à chave (por exemplo, "Chrome Extension API") e copie a chave imediatamente, pois ela não será exibida novamente. Guarde-a em um local seguro.
Passo 4: Obter o ID da Sua Loja
Vá para "Configurações" → "Lojas". Você verá sua loja com um número, como #123456. Copie apenas os números (sem o símbolo #). Este é o ID da sua loja, essencial para configurar a comunicação com a API.
Passo 5: Criar Segredo do Webhook
Ao contrário de outros processadores de pagamento, a Lemon Squeezy não fornece um token secreto de webhook. Portanto, você precisa criar uma string aleatória em um arquivo `.env` para usar como segredo.
Passo 6: Configurar Variáveis de Ambiente
Crie um arquivo `.env.local` no seu projeto Next.js (ou equivalente) e adicione as seguintes variáveis:
LEMONSQUEEZY_API_KEY=sua_chave_api_aquiLEMONSQUEEZY_STORE_ID=123456LEMONSQUEEZY_WEBHOOK_SECRET=seu_segredo_aleatorioNEXT_PUBLIC_CHECKOUT_SUCCESS_URL=https://seusite.com/checkout-success
Observações importantes:
- Use o ID da Variante, não o ID do Produto.
NEXT_PUBLIC_CHECKOUT_SUCCESS_URLé onde os usuários são redirecionados após o pagamento (opcional).- Mantenha
LEMONSQUEEZY_API_KEYeWEBHOOK_SECRETseguros e nunca os exponha no código do cliente.
Parte 2: Configurando o Backend (Funções Serverless)
Para processar os pagamentos e gerenciar as assinaturas, você precisará de um backend. Funções serverless são ideais para essa finalidade, pois são fáceis de implementar e escaláveis. Criaremos duas funções serverless:
- Gerar links de checkout.
- Lidar com eventos de webhook.
Passo 1: Instalar Dependências
Utilize o gerenciador de pacotes npm para instalar a biblioteca da Lemon Squeezy:
npm install @lemonsqueezy/lemonsqueezy.js
Passo 2: Criar Cliente Lemon Squeezy
Crie o arquivo lib/lemonSqueezy/lemonSqueezyClient.ts com o seguinte código (em TypeScript):
import { lemonSqueezySetup } from "@lemonsqueezy/lemonsqueezy.js";
export function configureLemonSqueezy() {
lemonSqueezySetup({
apiKey: process.env.LEMONSQUEEZY_API_KEY!,
onError: (error) => {
console.error("Lemon Squeezy Error:", error);
throw error;
},
});
}
Passo 3: Criar Endpoint da API de Checkout
Crie o arquivo app/api/checkout/route.ts com o seguinte código (em TypeScript):
import { createCheckout } from "@lemonsqueezy/lemonsqueezy.js";
import { configureLemonSqueezy } from "@/lib/lemonSqueezy/lemonSqueezyClient";
async function createCheckoutLS(
productId: string,
userEmail?: string
) {
configureLemonSqueezy();
const storeId = process.env.LEMONSQUEEZY_STORE_ID;
if (!storeId) {
throw new Error("LEMONSQUEEZY_STORE_ID is not set");
}
const checkoutData = {
productOptions: {
redirectUrl: process.env.NEXT_PUBLIC_CHECKOUT_SUCCESS_URL,
},
checkoutData: {
email: userEmail || undefined,
},
};
const checkout = await createCheckout(
storeId,
productId,
checkoutData
);
if (checkout.error) {
throw new Error(checkout.error.message);
}
return checkout.data?.data.attributes.url;
}
export async function POST(request: Request) {
try {
const { productId, userEmail } = await request.json();
if (!productId) {
return new Response(
JSON.stringify({ error: 'Product variant ID is required' }),
{
status: 400,
headers: { 'Content-Type': 'application/json' },
}
);
}
const checkoutUrl = await createCheckoutLS(
productId,
userEmail || undefined
);
return new Response(
JSON.stringify({ checkout_url: checkoutUrl }),
{
status: 201,
headers: { 'Content-Type': 'application/json' },
}
);
} catch (error) {
console.error('Checkout creation error:', error);
return new Response(
JSON.stringify({
error: 'Failed to create checkout',
details: error instanceof Error ? error.message : 'Unknown error'
}),
{
status: 500,
headers: { 'Content-Type': 'application/json' },
}
);
}
}
Este código aceita requisições POST com productId e userEmail (opcional), cria um link de checkout personalizado através da API da Lemon Squeezy, pré-preenche o e-mail do usuário na página de checkout e retorna a URL de checkout para a sua extensão.
Parte 3: Construindo Sua Extensão Chrome
Agora, vamos integrar a Lemon Squeezy na sua extensão do Chrome. Isso envolve a criação de um manipulador de checkout, configuração do script de background e criação da interface do usuário para o upgrade premium.
Passo 1: Criar Manipulador de Checkout
Crie o arquivo src/handleCheckout.ts na sua extensão com o seguinte código:
/**
* Creates a checkout URL by calling your serverless function
* @param email - User's email to pre-fill at checkout
* @param productId - Lemon Squeezy variant ID
* @returns Checkout URL or null if failed
*/
export async function createCheckoutURL(
email: string | null,
productId: string
): Promise<string | null> {
const CHECKOUT_ENDPOINT = 'https://your-app.vercel.app/api/checkout';
try {
const response = await fetch(CHECKOUT_ENDPOINT, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
productId,
userEmail: email,
}),
});
if (!response.ok) {
console.error('Checkout creation failed:', response.statusText);
return null;
}
const results = await response.json();
return results.checkout_url;
} catch (error) {
console.error('Error creating checkout URL:', error);
return null;
}
}
/**
* Initiates checkout flow by sending message to background script
* @param email - User's email
* @param productId - Product variant ID to purchase
*/
export async function invokeCheckout(email?: string, productId?: string) {
await chrome.runtime.sendMessage({
type: 'checkout',
userEmail: email,
productId: productId || import.meta.env.WXT_LEMONSQUEEZY_PRODUCT_ID,
});
}
Adicione a seguinte linha ao seu arquivo .env.local (na pasta da sua extensão, não na pasta das funções serverless):
WXT_LEMONSQUEEZY_PRODUCT_ID=seu_id_da_variante_aqui # pro plan
Passo 2: Configurar Script de Background
Crie ou atualize o arquivo src/background.ts com o seguinte código:
import { createCheckoutURL } from './lib/handleCheckout';
type CheckoutMessage = {
type: 'checkout';
userEmail?: string;
productId: string;
};
async function handleMessages(
message: CheckoutMessage,
sender: chrome.runtime.MessageSender,
sendResponse: (response?: any) => void
) {
if (message.type === 'checkout') {
console.log('🛒 Starting checkout flow...');
// Generate the checkout link
// productId is coming from invokeCheckout <- popup.tsx
const checkoutLink = await createCheckoutURL(
message.userEmail || null,
message.productId
);
if (!checkoutLink) {
console.error('❌ Failed to create checkout link');
sendResponse({ success: false, error: 'Failed to create checkout' });
return;
}
// Redirect user to checkout page (no manifest permission needed)
chrome.tabs.create({ url: checkoutLink }, (tab) => {
console.log('✅ Redirected to checkout:', checkoutLink);
sendResponse({ success: true, tabId: tab.id });
});
}
}
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
handleMessages(request, sender, sendResponse);
return true; // Required for async sendResponse
});
Este script escuta mensagens do tipo 'checkout' vindas do popup ou scripts de conteúdo, chama sua função serverless para gerar a URL de checkout e abre uma nova aba com a página de checkout. Não são necessárias permissões de manifesto para abrir abas.
Passo 3: Criar Interface do Usuário para Upgrade Premium
Crie um componente no seu popup (src/components/PremiumCard.tsx) com o seguinte código:
import React from 'react';
import { invokeCheckout } from '../lib/handleCheckout';
export default function PremiumCard() {
async function handleUpgradeClick() {
try {
// Get user's email from your auth system or chrome.storage
await invokeCheckout('[email protected]');
} catch (error) {
console.error('Checkout error:', error);
}
}
return (
<div className="premium-card">
<div className="premium-header">
<h3>🚀 Upgrade to Premium</h3>
<p className="price">$9.99/month</p>
</div>
<ul className="features">
<li>✨ Unlimited AI generations</li>
<li>🎯 Advanced features</li>
<li>⚡ Priority support</li>
<li>🔒 Ad-free experience</li>
</ul>
<button
onClick={handleUpgradeClick}
className="upgrade-button"
>
{'Get Premium Now'}
</button>
</div>
);
}
O fluxo de usuário é o seguinte: o usuário clica em "Get Premium Now", uma mensagem é enviada ao script de background, a URL de checkout é gerada com o e-mail pré-preenchido, o usuário é redirecionado para o checkout da Lemon Squeezy e, após o pagamento, é redirecionado para a página de sucesso (opcional).
Parte 4: Configuração do Banco de Dados (Supabase)
Para armazenar informações sobre seus usuários premium e suas assinaturas, você precisará de um banco de dados. O Supabase é uma excelente opção, pois oferece uma interface intuitiva e recursos poderosos.
Passo 1: Criar Tabela no Supabase
Execute o seguinte SQL no seu editor SQL do Supabase:
CREATE TABLE PremiumUsers (
user_email TEXT PRIMARY KEY,
subscription_status TEXT NOT NULL,
plan_type TEXT,
credits_used INTEGER DEFAULT 0,
subscribed_at TIMESTAMPTZ,
expires_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW(),
subscription_id TEXT,
customer_id TEXT
);
-- Add index for faster queries
CREATE INDEX idx_subscription_status ON PremiumUsers(subscription_status);
CREATE INDEX idx_expires_at ON PremiumUsers(expires_at);
Passo 2: Configurar Cliente Supabase
Crie o arquivo lib/supabase/supabaseAdmin.ts com o seguinte código:
import { createClient } from '@supabase/supabase-js';
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!;
const supabaseServiceKey = process.env.SUPABASE_SERVICE_ROLE_KEY!;
// Use service role key for admin operations
const supabaseAdmin = createClient(supabaseUrl, supabaseServiceKey);
export default supabaseAdmin;
Adicione as seguintes linhas ao seu arquivo .env.local:
NEXT_PUBLIC_SUPABASE_URL=sua_url_do_supabase
SUPABASE_SERVICE_ROLE_KEY=sua_chave_de_servico_do_supabase
Parte 5: Webhooks
Os webhooks são essenciais para manter seu banco de dados sincronizado com a Lemon Squeezy, garantindo que as informações sobre seus usuários premium estejam sempre atualizadas.
Passo 1: Criar Definições de Tipo (Opcional)
Crie o arquivo app/api/webhooks/lemon-squeezy/types.ts com o seguinte código:
export interface LemonSqueezyWebhookPayload {