Este guia mostra como integrar a Chargefy em uma aplicação Next.js completa, do setup inicial até o processamento de pagamentos com PIX, Cartão de Crédito e Boleto.
Pré-requisitos
Setup do projeto
1. Instalar dependências
npm install @chargefy/sdk
2. Configurar variáveis de ambiente
Crie ou atualize o arquivo .env.local:
CHARGEFY_ACCESS_TOKEN =< supabase_jwt >
CHARGEFY_WEBHOOK_SECRET = whsec_seu_secret_aqui
NEXT_PUBLIC_CHARGEFY_BASE_URL = https://api.chargefy.io
Nunca exponha o CHARGEFY_ACCESS_TOKEN no frontend. Use-o apenas em Server Components, API Routes ou Server Actions.
3. Criar cliente Chargefy
Crie um arquivo utilitário para inicializar o SDK:
import { Chargefy } from '@chargefy/sdk' ;
export const chargefy = new Chargefy ({
accessToken: process . env . CHARGEFY_ACCESS_TOKEN ! ,
server: 'production' , // ou 'sandbox' para testes
});
Listar produtos
Use um Server Component para buscar e exibir seus produtos:
import { chargefy } from '@/lib/chargefy' ;
import Link from 'next/link' ;
export default async function ProductsPage () {
const { items : products } = await chargefy . products . list ({
is_archived: false ,
});
return (
< div className = "grid grid-cols-1 md:grid-cols-3 gap-6 p-8" >
{ products . map (( product ) => (
< div key = { product . id } className = "border rounded-lg p-6" >
< h2 className = "text-xl font-bold" > { product . name } </ h2 >
< p className = "text-gray-600 mt-2" > { product . description } </ p >
{ product . prices . map (( price ) => (
< div key = { price . id } className = "mt-4" >
< span className = "text-2xl font-bold" >
{new Intl . NumberFormat ( 'pt-BR' , {
style: 'currency' ,
currency: 'BRL' ,
}). format ( price . price_amount / 100 ) }
</ span >
{ price . type === 'recurring' && (
< span className = "text-gray-500" >
/ { price . recurring_interval === 'month' ? 'mês' : 'ano' }
</ span >
) }
</ div >
)) }
< Link
href = { `/checkout?product= ${ product . id } ` }
className = "mt-4 block text-center bg-black text-white py-2 rounded-lg"
>
Comprar
</ Link >
</ div >
)) }
</ div >
);
}
Criar sessão de checkout
Server Action
Crie uma Server Action para iniciar o checkout:
'use server' ;
import { chargefy } from '@/lib/chargefy' ;
import { redirect } from 'next/navigation' ;
export async function createCheckout ( formData : FormData ) {
const productId = formData . get ( 'productId' ) as string ;
const checkout = await chargefy . checkouts . create ({
product_price_id: productId ,
success_url: ` ${ process . env . NEXT_PUBLIC_APP_URL } /checkout/success?checkout_id={CHECKOUT_ID}` ,
customer_email: formData . get ( 'email' ) as string ,
});
redirect ( checkout . url );
}
Página de checkout
import { createCheckout } from '@/app/actions/checkout' ;
export default function CheckoutPage ({
searchParams ,
} : {
searchParams : { product : string };
}) {
return (
< div className = "max-w-md mx-auto p-8" >
< h1 className = "text-2xl font-bold mb-6" > Finalizar Compra </ h1 >
< form action = { createCheckout } >
< input type = "hidden" name = "productId" value = { searchParams . product } />
< div className = "mb-4" >
< label className = "block text-sm font-medium mb-1" > Email </ label >
< input
type = "email"
name = "email"
required
className = "w-full border rounded-lg px-3 py-2"
placeholder = "[email protected] "
/>
</ div >
< button
type = "submit"
className = "w-full bg-black text-white py-3 rounded-lg font-medium"
>
Ir para pagamento
</ button >
</ form >
</ div >
);
}
Checkout via API (Server-Side)
Para controle total, crie o checkout e confirme o pagamento diretamente via API:
app/api/checkout/pix/route.ts
import { NextRequest , NextResponse } from 'next/server' ;
import { chargefy } from '@/lib/chargefy' ;
export async function POST ( req : NextRequest ) {
const { productPriceId , email , name , taxId } = await req . json ();
// 1. Criar sessão de checkout
const checkout = await chargefy . checkouts . create ({
product_price_id: productPriceId ,
customer_email: email ,
});
// 2. Confirmar com PIX
const confirmed = await chargefy . checkouts . confirm ( checkout . id , {
customer_name: name ,
customer_email: email ,
customer_tax_id: taxId , // CPF
payment_method: 'pix' ,
});
return NextResponse . json ({
checkoutId: confirmed . id ,
status: confirmed . status ,
pixQrCode: confirmed . payment_data . pix_qr_code ,
pixQrCodeBase64: confirmed . payment_data . pix_qr_code_base64 ,
pixCopyPaste: confirmed . payment_data . pix_copy_paste ,
});
}
app/api/checkout/card/route.ts
import { NextRequest , NextResponse } from 'next/server' ;
import { chargefy } from '@/lib/chargefy' ;
export async function POST ( req : NextRequest ) {
const {
productPriceId , email , name , taxId ,
cardNumber , holderName , expiryMonth , expiryYear , cvv ,
installments ,
} = await req . json ();
// 1. Criar sessão de checkout
const checkout = await chargefy . checkouts . create ({
product_price_id: productPriceId ,
customer_email: email ,
});
// 2. Confirmar com cartão
const confirmed = await chargefy . checkouts . confirm ( checkout . id , {
customer_name: name ,
customer_email: email ,
customer_tax_id: taxId ,
payment_method: 'credit_card' ,
card_number: cardNumber ,
holder_name: holderName ,
expiry_month: expiryMonth ,
expiry_year: expiryYear ,
cvv: cvv ,
installments: installments || 1 , // 1-12x
});
return NextResponse . json ({
checkoutId: confirmed . id ,
status: confirmed . status , // 'succeeded' para cartão
});
}
app/api/checkout/boleto/route.ts
import { NextRequest , NextResponse } from 'next/server' ;
import { chargefy } from '@/lib/chargefy' ;
export async function POST ( req : NextRequest ) {
const { productPriceId , email , name , taxId , dueDate } = await req . json ();
// 1. Criar sessão de checkout
const checkout = await chargefy . checkouts . create ({
product_price_id: productPriceId ,
customer_email: email ,
});
// 2. Confirmar com boleto
const confirmed = await chargefy . checkouts . confirm ( checkout . id , {
customer_name: name ,
customer_email: email ,
customer_tax_id: taxId ,
payment_method: 'boleto' ,
due_date: dueDate , // 'YYYY-MM-DD'
});
return NextResponse . json ({
checkoutId: confirmed . id ,
status: confirmed . status , // 'confirmed' (aguardando compensação)
boletoBarcode: confirmed . payment_data . boleto_barcode ,
boletoUrl: confirmed . payment_data . boleto_url ,
boletoDueDate: confirmed . payment_data . boleto_due_date ,
});
}
Página de sucesso
app/checkout/success/page.tsx
import { chargefy } from '@/lib/chargefy' ;
export default async function CheckoutSuccessPage ({
searchParams ,
} : {
searchParams : { checkout_id : string };
}) {
const checkout = await chargefy . checkouts . get ( searchParams . checkout_id );
return (
< div className = "max-w-md mx-auto p-8 text-center" >
{ checkout . status === 'succeeded' ? (
<>
< div className = "text-green-500 text-5xl mb-4" > ✓ </ div >
< h1 className = "text-2xl font-bold" > Pagamento confirmado! </ h1 >
< p className = "text-gray-600 mt-2" >
Sua cobranca # { checkout . id } foi processada com sucesso.
</ p >
</>
) : checkout . status === 'confirmed' ? (
<>
< div className = "text-yellow-500 text-5xl mb-4" > ⏳ </ div >
< h1 className = "text-2xl font-bold" > Pagamento em processamento </ h1 >
< p className = "text-gray-600 mt-2" >
Aguardando confirmação do pagamento via { ' ' }
{ checkout . payment_method === 'pix' ? 'PIX' : 'Boleto' } .
</ p >
</>
) : (
<>
< div className = "text-red-500 text-5xl mb-4" > ✗ </ div >
< h1 className = "text-2xl font-bold" > Erro no pagamento </ h1 >
< p className = "text-gray-600 mt-2" >
Houve um problema ao processar seu pagamento. Tente novamente.
</ p >
</>
) }
</ div >
);
}
Receber webhooks
Webhooks são essenciais para receber notificações de pagamentos assíncronos (PIX e Boleto) e atualizações de assinaturas.
1. Criar endpoint de webhook
app/api/webhooks/chargefy/route.ts
import { NextRequest , NextResponse } from 'next/server' ;
import crypto from 'crypto' ;
const WEBHOOK_SECRET = process . env . CHARGEFY_WEBHOOK_SECRET ! ;
function verifyWebhookSignature (
payload : string ,
signature : string ,
secret : string ,
) : boolean {
const [ timestamp , hash ] = signature . split ( ',' );
const ts = timestamp . replace ( 't=' , '' );
const sig = hash . replace ( 'v1=' , '' );
const signedContent = ` ${ ts } . ${ payload } ` ;
const expectedSig = crypto
. createHmac ( 'sha256' , secret )
. update ( signedContent )
. digest ( 'hex' );
return crypto . timingSafeEqual (
Buffer . from ( sig , 'hex' ),
Buffer . from ( expectedSig , 'hex' ),
);
}
export async function POST ( req : NextRequest ) {
const body = await req . text ();
const signature = req . headers . get ( 'webhook-signature' ) || '' ;
// Verificar assinatura HMAC
if ( ! verifyWebhookSignature ( body , signature , WEBHOOK_SECRET )) {
return NextResponse . json ({ error: 'Invalid signature' }, { status: 401 });
}
const event = JSON . parse ( body );
switch ( event . type ) {
case 'checkout.updated' :
await handleCheckoutUpdated ( event . data );
break ;
case 'subscription.active' :
await handleSubscriptionActive ( event . data );
break ;
case 'subscription.canceled' :
await handleSubscriptionCanceled ( event . data );
break ;
default :
console . log ( `Evento não tratado: ${ event . type } ` );
}
return NextResponse . json ({ received: true });
}
async function handleCheckoutUpdated ( data : any ) {
if ( data . status === 'succeeded' ) {
// Pagamento confirmado (PIX pago, boleto compensado)
console . log ( `Checkout ${ data . id } concluído com sucesso` );
// Atualizar banco de dados, enviar email, liberar acesso, etc.
}
}
async function handleSubscriptionActive ( data : any ) {
console . log ( `Assinatura ${ data . id } ativa para cliente ${ data . customer_id } ` );
// Liberar acesso ao produto recorrente
}
async function handleSubscriptionCanceled ( data : any ) {
console . log ( `Assinatura ${ data . id } cancelada` );
// Revogar acesso ao produto
}
2. Configurar webhook no dashboard
Acessar configurações
Vá até Dashboard → Configurações → Webhooks e clique em “Novo Webhook”.
Configurar URL
Insira a URL do endpoint: https://seusite.com/api/webhooks/chargefy
Selecionar eventos
Selecione os eventos que deseja receber: checkout.updated, subscription.active, subscription.canceled.
Copiar secret
Copie o Webhook Secret gerado e adicione ao seu .env.local como CHARGEFY_WEBHOOK_SECRET.
Para receber webhooks em desenvolvimento local:
# Instalar ngrok
npm install -g ngrok
# Criar tunnel para sua aplicação local
ngrok http 3000
Use a URL HTTPS gerada pelo ngrok (ex: https://abc123.ngrok.io/api/webhooks/chargefy) como URL do webhook no dashboard.
No ambiente sandbox , webhooks são enviados imediatamente, sem delay de processamento.
Gerenciar assinaturas
Listar assinaturas do cliente
app/dashboard/subscriptions/page.tsx
import { chargefy } from '@/lib/chargefy' ;
export default async function SubscriptionsPage () {
const { items : subscriptions } = await chargefy . subscriptions . list ({
active: true ,
});
return (
< div className = "p-8" >
< h1 className = "text-2xl font-bold mb-6" > Minhas Assinaturas </ h1 >
{ subscriptions . map (( sub ) => (
< div key = { sub . id } className = "border rounded-lg p-6 mb-4" >
< div className = "flex justify-between items-center" >
< div >
< h3 className = "font-bold" > { sub . product . name } </ h3 >
< p className = "text-sm text-gray-500" >
{new Intl . NumberFormat ( 'pt-BR' , {
style: 'currency' ,
currency: 'BRL' ,
}). format ( sub . amount / 100 ) }
/ { sub . recurring_interval === 'month' ? 'mês' : 'ano' }
</ p >
< p className = "text-sm text-gray-400 mt-1" >
Próxima cobrança: { ' ' }
{new Date ( sub . current_period_end ). toLocaleDateString ( 'pt-BR' ) }
</ p >
</ div >
< div >
< span
className = { `px-3 py-1 rounded-full text-sm ${
sub . status === 'active'
? 'bg-green-100 text-green-800'
: 'bg-gray-100 text-gray-800'
} ` }
>
{ sub . status === 'active' ? 'Ativa' : sub . status }
</ span >
</ div >
</ div >
</ div >
)) }
</ div >
);
}
Cancelar assinatura (Server Action)
app/actions/subscription.ts
'use server' ;
import { chargefy } from '@/lib/chargefy' ;
import { revalidatePath } from 'next/cache' ;
export async function cancelSubscription ( subscriptionId : string ) {
await chargefy . subscriptions . cancel ( subscriptionId );
revalidatePath ( '/dashboard/subscriptions' );
}
export async function reactivateSubscription ( subscriptionId : string ) {
await chargefy . subscriptions . update ( subscriptionId , {
status: 'active' ,
});
revalidatePath ( '/dashboard/subscriptions' );
}
Checkout embarcado
Para uma experiência de checkout sem redirecionamento, use o checkout embarcado:
app/components/EmbeddedCheckout.tsx
'use client' ;
import { useEffect , useRef } from 'react' ;
interface EmbeddedCheckoutProps {
checkoutId : string ;
clientSecret : string ;
}
export function EmbeddedCheckout ({
checkoutId ,
clientSecret ,
} : EmbeddedCheckoutProps ) {
const containerRef = useRef < HTMLDivElement >( null );
useEffect (() => {
// Carregar script do checkout embarcado
const script = document . createElement ( 'script' );
script . src = 'https://cdn.chargefy.io/checkout/embed.global.js' ;
script . async = true ;
script . onload = () => {
// @ts-ignore
window . ChargelyCheckout . mount ({
container: containerRef . current ,
clientSecret ,
theme: 'auto' , // 'light' | 'dark' | 'auto'
onSuccess : ( data : any ) => {
console . log ( 'Pagamento confirmado!' , data );
window . location . href = `/checkout/success?checkout_id= ${ checkoutId } ` ;
},
onError : ( error : any ) => {
console . error ( 'Erro no pagamento:' , error );
},
});
};
document . body . appendChild ( script );
return () => {
document . body . removeChild ( script );
};
}, [ checkoutId , clientSecret ]);
return < div ref = { containerRef } className = "min-h-[400px]" /> ;
}
Usar o componente
app/checkout/embedded/page.tsx
import { chargefy } from '@/lib/chargefy' ;
import { EmbeddedCheckout } from '@/app/components/EmbeddedCheckout' ;
export default async function EmbeddedCheckoutPage ({
searchParams ,
} : {
searchParams : { product : string };
}) {
const checkout = await chargefy . checkouts . create ({
product_price_id: searchParams . product ,
});
return (
< div className = "max-w-lg mx-auto p-8" >
< h1 className = "text-2xl font-bold mb-6" > Finalizar Compra </ h1 >
< EmbeddedCheckout
checkoutId = { checkout . id }
clientSecret = { checkout . client_secret }
/>
</ div >
);
}
Estrutura final do projeto
app/
├── actions/
│ ├── checkout.ts # Server Actions de checkout
│ └── subscription.ts # Server Actions de assinatura
├── api/
│ ├── checkout/
│ │ ├── pix/route.ts # API Route: checkout PIX
│ │ ├── card/route.ts # API Route: checkout cartão
│ │ └── boleto/route.ts # API Route: checkout boleto
│ └── webhooks/
│ └── chargefy/route.ts # Webhook handler
├── checkout/
│ ├── page.tsx # Página de checkout
│ ├── embedded/page.tsx # Checkout embarcado
│ └── success/page.tsx # Página de sucesso
├── components/
│ └── EmbeddedCheckout.tsx # Componente de checkout embarcado
├── dashboard/
│ └── subscriptions/
│ └── page.tsx # Gerenciamento de assinaturas
├── products/
│ └── page.tsx # Listagem de produtos
└── layout.tsx
lib/
└── chargefy.ts # Cliente Chargefy SDK
.env.local # Variáveis de ambiente
Checklist de produção
Antes de ir para produção, verifique:
Próximos passos
Checkout Embarcado Customize a experiência de checkout diretamente no seu site.
Webhooks Configure webhooks para receber notificações em tempo real.
SDK TypeScript Referência completa do SDK com todos os métodos disponíveis.
Ambiente Sandbox Teste sua integração com dados simulados antes de ir para produção.