Este guia mostra como integrar a Chargefy em um backend Express/Node.js, incluindo criação de checkouts, processamento de pagamentos e recebimento de webhooks.
Pré-requisitos
Setup do projeto
1. Criar projeto e instalar dependências
mkdir meu-backend && cd meu-backend
npm init -y
npm install express @chargefy/sdk dotenv
npm install -D typescript @types/express @types/node tsx
2. Configurar TypeScript
{
"compilerOptions" : {
"target" : "ES2022" ,
"module" : "NodeNext" ,
"moduleResolution" : "NodeNext" ,
"outDir" : "./dist" ,
"rootDir" : "./src" ,
"strict" : true ,
"esModuleInterop" : true ,
"skipLibCheck" : true
},
"include" : [ "src/**/*" ]
}
3. Configurar variáveis de ambiente
CHARGEFY_ACCESS_TOKEN =< supabase_jwt >
CHARGEFY_WEBHOOK_SECRET = whsec_seu_secret_aqui
PORT = 3001
Nunca commite o arquivo .env no repositório. Adicione-o ao .gitignore.
4. Criar cliente Chargefy
import { Chargefy } from '@chargefy/sdk'
export const chargefy = new Chargefy ({
accessToken: process . env . CHARGEFY_ACCESS_TOKEN ! ,
// Para sandbox:
// server: 'sandbox'
})
Estrutura do projeto
src/
├── lib/
│ └── chargefy.ts # Cliente SDK
├── routes/
│ ├── checkout.ts # Rotas de checkout
│ ├── products.ts # Rotas de produtos
│ ├── subscriptions.ts # Rotas de assinaturas
│ └── webhooks.ts # Receptor de webhooks
└── index.ts # Entry point
Criar o servidor Express
import 'dotenv/config'
import express from 'express'
import { checkoutRouter } from './routes/checkout.js'
import { productsRouter } from './routes/products.js'
import { subscriptionsRouter } from './routes/subscriptions.js'
import { webhooksRouter } from './routes/webhooks.js'
const app = express ()
const PORT = process . env . PORT || 3001
// Webhooks precisam do body raw para verificação HMAC
// Registrar ANTES do express.json()
app . use ( '/webhooks' , webhooksRouter )
// JSON parser para demais rotas
app . use ( express . json ())
app . use ( '/api/products' , productsRouter )
app . use ( '/api/checkout' , checkoutRouter )
app . use ( '/api/subscriptions' , subscriptionsRouter )
app . listen ( PORT , () => {
console . log ( `Servidor rodando em http://localhost: ${ PORT } ` )
})
A rota de webhooks é registrada antes do express.json() porque a verificação HMAC requer o body raw (não parseado).
Listar produtos
import { Router } from 'express'
import { chargefy } from '../lib/chargefy.js'
export const productsRouter = Router ()
// Listar todos os produtos
productsRouter . get ( '/' , async ( req , res ) => {
try {
const products = await chargefy . products . list ({
isArchived: false ,
limit: 20 ,
page: 1 ,
})
res . json ( products )
} catch ( error ) {
console . error ( 'Erro ao listar produtos:' , error )
res . status ( 500 ). json ({ error: 'Falha ao buscar produtos' })
}
})
// Obter produto por ID
productsRouter . get ( '/:id' , async ( req , res ) => {
try {
const product = await chargefy . products . get ( req . params . id )
res . json ( product )
} catch ( error ) {
res . status ( 404 ). json ({ error: 'Produto não encontrado' })
}
})
Criar checkout e processar pagamentos
import { Router } from 'express'
import { chargefy } from '../lib/chargefy.js'
export const checkoutRouter = Router ()
// Criar sessão de checkout
checkoutRouter . post ( '/sessions' , async ( req , res ) => {
try {
const { productPriceId , customerEmail , successUrl } = req . body
const checkout = await chargefy . checkouts . create ({
productPriceId ,
customerEmail ,
successUrl: successUrl || 'https://meusite.com.br/sucesso' ,
})
res . json ({
id: checkout . id ,
clientSecret: checkout . clientSecret ,
url: checkout . url ,
expiresAt: checkout . expiresAt ,
})
} catch ( error ) {
console . error ( 'Erro ao criar checkout:' , error )
res . status ( 400 ). json ({ error: 'Falha ao criar sessão de checkout' })
}
})
// Confirmar checkout com PIX
checkoutRouter . post ( '/sessions/:clientSecret/confirm/pix' , async ( req , res ) => {
try {
const { clientSecret } = req . params
const { customerEmail , customerName } = req . body
const result = await chargefy . checkouts . confirm ( clientSecret , {
customerEmail ,
customerName ,
paymentMethod: 'pix' ,
})
// Retorna QR Code para exibir ao cliente
res . json ({
status: result . status ,
pixQrCode: result . paymentDetails ?. pixQrCode ,
pixQrCodeBase64: result . paymentDetails ?. pixQrCodeBase64 ,
pixCopyPaste: result . paymentDetails ?. pixCopyPaste ,
expiresAt: result . paymentDetails ?. expiresAt ,
})
} catch ( error ) {
console . error ( 'Erro no pagamento PIX:' , error )
res . status ( 400 ). json ({ error: 'Falha ao processar pagamento PIX' })
}
})
// Confirmar checkout com Cartão de Crédito
checkoutRouter . post ( '/sessions/:clientSecret/confirm/card' , async ( req , res ) => {
try {
const { clientSecret } = req . params
const {
customerEmail ,
customerName ,
cardNumber ,
holderName ,
expirationMonth ,
expirationYear ,
securityCode ,
installments ,
} = req . body
const result = await chargefy . checkouts . confirm ( clientSecret , {
customerEmail ,
customerName ,
paymentMethod: 'credit_card' ,
card: {
cardNumber ,
holderName ,
expirationMonth ,
expirationYear ,
securityCode ,
},
installments: installments || 1 , // 1-12x
})
res . json ({
status: result . status ,
checkoutId: result . id ,
})
} catch ( error ) {
console . error ( 'Erro no pagamento com cartão:' , error )
res . status ( 400 ). json ({ error: 'Falha ao processar pagamento com cartão' })
}
})
Em produção, nunca trafegue dados de cartão pelo seu servidor sem certificação PCI-DSS. Use o Checkout Embed ou a tokenização client-side via Chargefy.js para enviar apenas o card_id tokenizado.
// Confirmar checkout com Boleto
checkoutRouter . post ( '/sessions/:clientSecret/confirm/boleto' , async ( req , res ) => {
try {
const { clientSecret } = req . params
const { customerEmail , customerName } = req . body
const result = await chargefy . checkouts . confirm ( clientSecret , {
customerEmail ,
customerName ,
paymentMethod: 'boleto' ,
})
res . json ({
status: result . status ,
boletoBarcode: result . paymentDetails ?. boletoBarcode ,
boletoUrl: result . paymentDetails ?. boletoUrl ,
boletoDueDate: result . paymentDetails ?. boletoDueDate ,
})
} catch ( error ) {
console . error ( 'Erro no pagamento com boleto:' , error )
res . status ( 400 ). json ({ error: 'Falha ao processar pagamento com boleto' })
}
})
Consultar status do checkout
// Consultar status de um checkout
checkoutRouter . get ( '/sessions/:clientSecret/status' , async ( req , res ) => {
try {
const checkout = await chargefy . checkouts . getByClientSecret (
req . params . clientSecret
)
res . json ({
id: checkout . id ,
status: checkout . status ,
paymentMethod: checkout . paymentMethod ,
amount: checkout . amount ,
currency: checkout . currency ,
})
} catch ( error ) {
res . status ( 404 ). json ({ error: 'Checkout não encontrado' })
}
})
Receber Webhooks
A verificação HMAC garante que o webhook veio da Chargefy e não foi alterado em trânsito.
import { Router , raw } from 'express'
import crypto from 'crypto'
export const webhooksRouter = Router ()
// Body raw necessário para verificação HMAC
webhooksRouter . use ( raw ({ type: 'application/json' }))
function verifyWebhookSignature (
payload : Buffer ,
signature : string ,
secret : string
) : boolean {
const expectedSignature = crypto
. createHmac ( 'sha256' , secret )
. update ( payload )
. digest ( 'hex' )
return crypto . timingSafeEqual (
Buffer . from ( signature ),
Buffer . from ( expectedSignature )
)
}
webhooksRouter . post ( '/' , ( req , res ) => {
const signature = req . headers [ 'webhook-signature' ] as string
const webhookSecret = process . env . CHARGEFY_WEBHOOK_SECRET !
if ( ! signature ) {
return res . status ( 401 ). json ({ error: 'Assinatura ausente' })
}
// Verificar assinatura HMAC
const isValid = verifyWebhookSignature ( req . body , signature , webhookSecret )
if ( ! isValid ) {
console . error ( 'Assinatura de webhook inválida' )
return res . status ( 401 ). json ({ error: 'Assinatura inválida' })
}
const event = JSON . parse ( req . body . toString ())
// Processar evento
switch ( event . type ) {
case 'checkout.updated' :
handleCheckoutUpdated ( event . data )
break
case 'subscription.created' :
handleSubscriptionCreated ( event . data )
break
case 'subscription.active' :
handleSubscriptionActive ( event . data )
break
case 'subscription.canceled' :
handleSubscriptionCanceled ( event . data )
break
case 'refund.created' :
handleRefundCreated ( event . data )
break
default :
console . log ( `Evento não tratado: ${ event . type } ` )
}
// Sempre responder 200 rapidamente para evitar retries
res . status ( 200 ). json ({ received: true })
})
// Handlers de eventos
function handleCheckoutUpdated ( data : any ) {
console . log ( 'Checkout atualizado:' , data . id , '→' , data . status )
if ( data . status === 'succeeded' ) {
// Pagamento confirmado — liberar acesso, enviar email, etc.
}
}
function handleSubscriptionCreated ( data : any ) {
console . log ( 'Nova assinatura:' , data . id )
// Registrar assinatura no seu banco
}
function handleSubscriptionActive ( data : any ) {
console . log ( 'Assinatura ativa:' , data . id )
// Liberar acesso ao serviço
}
function handleSubscriptionCanceled ( data : any ) {
console . log ( 'Assinatura cancelada:' , data . id )
// Revogar acesso ao final do período
}
function handleRefundCreated ( data : any ) {
console . log ( 'Reembolso criado:' , data . id )
// Processar reembolso no seu sistema
}
Responda ao webhook com status 200 o mais rápido possível . Processe lógica pesada (emails, integrações) de forma assíncrona usando filas como BullMQ .
Gerenciar assinaturas
src/routes/subscriptions.ts
import { Router } from 'express'
import { chargefy } from '../lib/chargefy.js'
export const subscriptionsRouter = Router ()
// Listar assinaturas de um cliente
subscriptionsRouter . get ( '/customer/:customerId' , async ( req , res ) => {
try {
const subscriptions = await chargefy . subscriptions . list ({
customerId: req . params . customerId ,
})
res . json ( subscriptions )
} catch ( error ) {
res . status ( 500 ). json ({ error: 'Falha ao listar assinaturas' })
}
})
// Obter detalhes de uma assinatura
subscriptionsRouter . get ( '/:id' , async ( req , res ) => {
try {
const subscription = await chargefy . subscriptions . get ( req . params . id )
res . json ( subscription )
} catch ( error ) {
res . status ( 404 ). json ({ error: 'Assinatura não encontrada' })
}
})
// Cancelar assinatura
subscriptionsRouter . post ( '/:id/cancel' , async ( req , res ) => {
try {
const subscription = await chargefy . subscriptions . cancel ( req . params . id )
res . json ({
id: subscription . id ,
status: subscription . status ,
cancelAtPeriodEnd: subscription . cancelAtPeriodEnd ,
currentPeriodEnd: subscription . currentPeriodEnd ,
})
} catch ( error ) {
res . status ( 400 ). json ({ error: 'Falha ao cancelar assinatura' })
}
})
// Reativar assinatura cancelada (antes do fim do período)
subscriptionsRouter . post ( '/:id/reactivate' , async ( req , res ) => {
try {
const subscription = await chargefy . subscriptions . update ( req . params . id , {
cancelAtPeriodEnd: false ,
})
res . json ({
id: subscription . id ,
status: subscription . status ,
})
} catch ( error ) {
res . status ( 400 ). json ({ error: 'Falha ao reativar assinatura' })
}
})
Use ngrok para expor seu servidor local e receber webhooks:
# Terminal 1 — Iniciar servidor
npx tsx src/index.ts
# Terminal 2 — Expor com ngrok
ngrok http 3001
Copie a URL gerada pelo ngrok (ex: https://abc123.ngrok.app) e configure no dashboard da Chargefy como endpoint de webhook:
https://abc123.ngrok.app/webhooks
# Criar sessão de checkout
curl -X POST http://localhost:3001/api/checkout/sessions \
-H "Content-Type: application/json" \
-d '{
"productPriceId": "price_xxxx",
"customerEmail": "[email protected] "
}'
# Confirmar pagamento com PIX
curl -X POST http://localhost:3001/api/checkout/sessions/cs_xxx/confirm/pix \
-H "Content-Type: application/json" \
-d '{
"customerEmail": "[email protected] ",
"customerName": "João Silva"
}'
Scripts do package.json
{
"scripts" : {
"dev" : "tsx watch src/index.ts" ,
"build" : "tsc" ,
"start" : "node dist/index.js"
}
}
Checklist de produção
Trocar para token de produção
Substitua o token de sandbox pelo token de produção no .env.
Configurar webhook endpoint
Registre a URL de produção no dashboard da Chargefy.
Habilitar HTTPS
Use um proxy reverso (nginx, Caddy) ou plataforma com TLS (Railway, Render, Fly.io).
Adicionar rate limiting
Use express-rate-limit para proteger seus endpoints públicos.
Processar webhooks com filas
Use BullMQ + Redis para processar eventos de forma assíncrona e resiliente.
Monitorar erros
Configure Sentry ou similar para capturar erros em produção.
Próximos passos
Checkout PIX Guia detalhado do fluxo de pagamento PIX.
Webhooks Documentação completa de webhooks e eventos.
SDK TypeScript Referência completa do SDK.
Sandbox Como testar pagamentos sem cobrar de verdade.