Pular para o conteúdo principal

Guia de Segurança

Melhores práticas para proteger sua integração com o PayerScan Payment Gateway.

Segurança da API Key

Nunca exponha sua API Key

Sua API Key autentica as requisições enviadas à API do PayerScan. Ela nunca deve ser visível para os usuários finais.

// ❌ ERRADO - Chamando API do frontend (API Key exposta para todos)
fetch('https://api.payerscan.com/payment/crypto', {
headers: { 'x-api-key': 'EQnhBYpknGAP...' } // API Key exposta!
})

// ✅ CORRETO - Chamando API do servidor backend
// Frontend chama sua API interna
fetch('/api/create-payment', { body: { amount: 100 } })

// Seu backend chama o PayerScan (API Key segura no servidor)
app.post('/api/create-payment', async (req, res) => {
const result = await fetch('https://api.payerscan.com/payment/crypto', {
headers: { 'x-api-key': process.env.PAYERSCAN_API_KEY }
})
res.json(result)
})

Armazene a API Key com segurança

# ✅ Use variáveis de ambiente
PAYERSCAN_API_KEY=EQnhBYpknGAP...

# ❌ Não codifique diretamente no código
const API_KEY = 'EQnhBYpknGAP...' # NÃO FAÇA ISSO

Nota: Nunca faça commit de API Keys no controle de versão (Git). Adicione .env ao seu arquivo .gitignore.

Rotacione a API Key periodicamente

  • Troque a API Key se suspeitar que foi comprometida
  • Vá para o gerenciamento da Loja → Gerar nova API Key
  • Atualize a nova API Key no seu servidor imediatamente
  • A API Key antiga será revogada imediatamente após gerar uma nova

Segurança do Webhook

Use HTTPS

Sempre use HTTPS para seu callback_url para criptografar dados em trânsito e prevenir ataques man-in-the-middle.

// ✅ CORRETO - Usar HTTPS
callback_url: 'https://your-server.com/webhook/payment'

// ❌ ERRADO - Usar HTTP (dados não criptografados, vulnerável à interceptação)
callback_url: 'http://your-server.com/webhook/payment'

Verifique as requisições do Webhook

Sempre verifique se as requisições webhook realmente vêm do PayerScan:

app.post('/webhook/payment', express.json(), async (req, res) => {
try {
const { merchant_id, api_key, trans_id, request_id, status, amount, transaction_hash } = req.body

// 1. Verificar merchant_id (completed e expired)
if (merchant_id !== process.env.PAYERSCAN_MERCHANT_ID) {
return res.status(401).json({ error: 'Invalid merchant' })
}

// 2. Verificar api_key (apenas completed — expired não inclui api_key)
if (status === 'completed' && api_key !== process.env.PAYERSCAN_API_KEY) {
return res.status(401).json({ error: 'Invalid API Key' })
}

// 3. Encontrar pedido por request_id (se fornecido) ou trans_id
let order = null
if (request_id) {
order = await db.orders.findOne({ request_id })
}
if (!order) {
order = await db.orders.findOne({ trans_id })
}
if (!order) {
return res.status(404).json({ error: 'Order not found' })
}

// 4. Processar apenas pedidos em estados atualizáveis (idempotente)
const processableStatuses = ['waiting', 'pending', 'processing']
if (!processableStatuses.includes(order.status)) {
return res.status(200).json({ message: 'Already processed' })
}

// 5. Verificar se o valor corresponde (apenas completed — prevenir adulteração)
if (status === 'completed') {
if (parseFloat(amount) !== parseFloat(order.amount)) {
return res.status(400).json({ error: 'Amount mismatch' })
}
}

// 6. Verificar se transaction_hash já foi usado (prevenir replay)
if (status === 'completed') {
const existingTx = await db.orders.findOne({ transaction_hash })
if (existingTx) {
return res.status(409).json({ error: 'Transaction hash already used' })
}
}

// 7. Atualização atômica — inclui filtro de status para prevenir condição de corrida
if (status === 'completed') {
const result = await db.orders.update(
{ trans_id, status: { $in: processableStatuses } },
{ status: 'completed', transaction_hash }
)
if (result.modifiedCount === 0) {
return res.status(200).json({ message: 'Already processed' })
}
} else if (status === 'expired') {
const result = await db.orders.update(
{ trans_id, status: { $in: processableStatuses } },
{ status: 'expired' }
)
if (result.modifiedCount === 0) {
return res.status(200).json({ message: 'Already processed' })
}
}

res.status(200).json({ success: true })
} catch (error) {
console.error('Webhook processing error:', error)
res.status(500).json({ error: 'Internal server error' })
}
})

Processamento idempotente

Webhooks podem ser reenviados até 5 vezes. Garanta que o processamento múltiplo seja seguro:

// ✅ CORRETO - Atualizar apenas pedidos em estados processáveis
const processableStatuses = ['waiting', 'pending', 'processing']
if (!processableStatuses.includes(order.status)) {
return res.status(200).json({ message: 'Already processed' })
}

// ❌ ERRADO - Sem verificação, poderia creditar o saldo várias vezes
await addBalance(user, amount) // Se webhook enviado 2 vezes → creditado 2 vezes!

Segurança dos Dados

Não registre dados sensíveis em log

// ❌ ERRADO - Registrando API Key em log
console.log('Request with API Key:', req.headers['x-api-key'])

// ✅ CORRETO - Registre apenas o necessário
console.log('Payment created:', { trans_id, amount, status })

Valide a entrada

O PayerScan valida a entrada no lado do servidor, mas você também deve validar do seu lado:

// Validar valor (deve ser positivo, máximo 1.000.000 USD)
const amount = parseFloat(req.body.amount)
if (isNaN(amount) || amount <= 0 || amount > 1000000) {
return res.status(400).json({ error: 'Invalid amount' })
}

// Validar formato do callback_url
if (req.body.callback_url) {
try {
const url = new URL(req.body.callback_url)
if (url.protocol !== 'https:') {
console.warn('callback_url deve usar HTTPS para segurança')
}
} catch {
return res.status(400).json({ error: 'Invalid callback_url' })
}
}

Proteções do lado do servidor PayerScan

O PayerScan implementa múltiplas camadas de segurança no lado do servidor:

  • Proteção SSRF: O sistema valida todos os valores de callback_url antes de enviar webhooks. IPs internos/privados e URLs suspeitas são bloqueados automaticamente.
  • Limitação de taxa: Os endpoints da API são protegidos com limites de taxa burst e sustentados para prevenir abuso. Veja Limites de Taxa para detalhes.
  • Validação de entrada: Todos os parâmetros de requisição são validados usando schemas rigorosos (faixa de valor, formato de URL, limites de comprimento de string).
  • Timeout do Webhook: Callbacks de webhook têm um timeout de 10 segundos. Se seu servidor não responder em 10 segundos, a tentativa é marcada como falha e será retentada.

Lista de verificação de segurança

Antes de ir ao ar

  • API Key armazenada em variáveis de ambiente (não codificada diretamente)
  • API Key não exposta no código frontend/client-side
  • Arquivo .env adicionado ao .gitignore
  • Endpoint do webhook usa HTTPS
  • Webhook verifica merchant_id para completed e expired
  • Webhook verifica api_key para webhooks completed
  • Webhook encontra pedido por request_id ou trans_id
  • Processamento de webhooks é idempotente (sem processamento duplicado)
  • Webhook verifica se amount corresponde ao valor do pedido
  • Webhook verifica se transaction_hash não foi usado (prevenir replay)
  • Webhook usa atualização atômica com filtro de status (prevenir condição de corrida)
  • Nenhum dado sensível registrado em log (API Key, chaves privadas)
  • Validação de entrada para valores, URLs

Contínuo

  • Revisar regularmente os logs de acesso do endpoint do webhook
  • Monitorar requisições de webhook não autorizadas ou suspeitas
  • Rotacionar API Key periodicamente ou se comprometida
  • Atualizar dependências com patches de segurança
  • Testar que o endpoint do webhook responde em 10 segundos

Resposta a incidentes de segurança

Se a API Key for comprometida

  1. Imediatamente vá ao gerenciamento da Loja → Gerar nova API Key
  2. Atualize a nova API Key em todos os servidores
  3. Revise os logs recentes da API para requisições não autorizadas
  4. Contate o suporte se transações suspeitas forem encontradas

Se webhooks fraudulentos forem detectados

  1. Verifique se seu handler de webhook verifica merchant_id e api_key
  2. Verifique os logs para incompatibilidades de amount ou tentativas de transaction_hash duplicado
  3. Bloqueie o IP de origem (se identificável)
  4. Adicione lista de permissão de IP se possível
  5. Contate o suporte para relatar o incidente

Contato com o suporte

Para problemas urgentes de segurança, contate-nos imediatamente: