Documentation Index Fetch the complete documentation index at: https://docs.chargefy.io/llms.txt
Use this file to discover all available pages before exploring further.
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 . unit_amount / 100 ) }
</ span >
{ price . type === 'recurring' && (
< span className = "text-gray-500" >
/ { price . 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_session_id={CHECKOUT_SESSION_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 = "seu@email.com"
/>
</ 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.session.async_payment_succeeded' :
await handleAsyncPaymentSucceeded ( 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 handleAsyncPaymentSucceeded ( data : any ) {
// Pagamento assíncrono 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.session.async_payment_succeeded, 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.
Ambiente Sandbox Teste sua integração com dados simulados antes de ir para produção.