Pular para o conteúdo principal

Autenticação 3D Secure

O SDK da FastPay fornece uma implementação completa e simplificada de autenticação 3D Secure (3DS) para transações com cartão de crédito, garantindo máxima segurança e redução de fraudes.

Instalação

Via NPM

npm install @fastpaybr/security-sdk

Via CDN

<script src="https://js.fastpaybrasil.com/security-sdk.min.js"></script>

Inicialização

Configuração Básica

import FastPayClient from '@fastpaybr/security-sdk'

const client = new FastPayClient({
credentials: {
publicKey: 'sua_api_key_publica'
},
debug: false
})

await client.initialize()

if (client.isThreeDSAvailable()) {
console.log('3DS disponível e pronto para uso')
}

Verificação de Status

// Verificar se o SDK foi inicializado
if (client.isReady()) {
console.log('SDK inicializado com sucesso')
}

// Verificar se 3DS está disponível
if (client.isThreeDSAvailable()) {
console.log('3DS pronto para uso')
}

Fluxo de Autenticação 3DS

Visão Geral do Fluxo

Processo Completo (Recomendado)

O método process() gerencia todo o fluxo de autenticação automaticamente:

try {
const result = await client.threeds.process(
{
// Dados da transação
amount: 100.00, // Valor na moeda especificada
currency: 'BRL',
installments: 1, // Número de parcelas (1-12)

// Dados do cartão
card: {
number: '4111111111111111',
holderName: 'João Silva',
expirationMonth: '12',
expirationYear: '2025'
},

// Dados do cliente (necessários para 3DS)
customer: {
name: 'João Silva',
email: 'joao@email.com',
phone: '5511987654321'
},

// Endereço do cliente
address: {
street: 'Rua das Flores',
number: '123',
complement: 'Apto 45',
zipCode: '12345678',
neighborhood: 'Centro',
city: 'São Paulo',
state: 'SP',
country: 'BRA'
}
},
{
// Opções (todas opcionais)
fingerprintingTimeout: 30000, // Timeout para fingerprinting (recomendado: 30s)
challengeTimeout: 180000, // Timeout para challenge (recomendado: 3min)
modal: {
width: '600px', // Largura do modal
height: '600px', // Altura do modal
showCloseButton: true // Mostrar botão de fechar
}
}
)

// Resultado da autenticação
console.log('Autenticação concluída:', result)

// Usar dados na criação da cobrança
const charge = await createCharge({
// ... outros dados da cobrança
threeDS: result
})
} catch (error) {
console.error('Erro na autenticação 3DS:', error)
}

Estrutura do Resultado

interface ChallengeResultResponse {
type: 'full' | 'partial' | 'none'
state: 'success' | 'failure' | 'unenrolled' | 'error' | 'unsupported_brand' | 'disabled'

// Dados da autenticação (quando state === 'success')
cryptogram?: string // CAVV/AAV
directoryServerTransactionId?: string // DS Transaction ID
threeDsServerTransactionId?: string // 3DS Server Transaction ID
acsTransactionId?: string // ACS Transaction ID
eci?: string // Electronic Commerce Indicator
version?: string // Versão do protocolo 3DS
}

Estados de Autenticação

EstadoDescriçãoAção Recomendada
success✅ Autenticação bem-sucedidaProsseguir com a cobrança
failure❌ Cliente falhou na autenticaçãoRejeitar transação ou solicitar outro método
unenrolled⚠️ Cartão não inscrito no 3DSAvaliar política de risco
error🔴 Erro no processo de autenticaçãoTentar novamente ou usar outro método
unsupported_brand🚫 Bandeira não suporta 3DSUsar outro método de pagamento
disabled🔒 3DS desabilitado para este cartãoAvaliar política de risco

Customização do Modal

Container Personalizado

Você pode fornecer seu próprio container para o iframe do challenge:

// Criar container personalizado
const customContainer = document.getElementById('my-3ds-container')

const result = await client.threeds.process({
// ... dados da transação
challengeContainer: customContainer
})
<div id="my-3ds-container" style="width: 500px; height: 500px;"></div>

Desabilitar Modal Automático

const result = await client.threeds.process(
{
// ... dados
challengeContainer: document.getElementById('my-container')
},
{
modal: false // Desabilita modal automático
}
)

Customizar Aparência do Modal

const result = await client.threeds.process(
{
/* ... dados */
},
{
modal: {
width: '800px',
height: '700px',
closeOnOverlayClick: false, // Impede fechar ao clicar fora
showCloseButton: false // Oculta botão de fechar
}
}
)

Exemplos de Implementação

React

import { useState } from 'react'
import FastPayClient from '@fastpaybr/security-sdk'

function CheckoutForm() {
const [loading, setLoading] = useState(false)
const [result, setResult] = useState(null)

const handleSubmit = async (formData) => {
setLoading(true)

try {
const client = new FastPayClient({
credentials: { publicKey: process.env.REACT_APP_FASTPAY_KEY }
})

await client.initialize()

if (!client.isThreeDSAvailable()) {
throw new Error('3DS não disponível')
}

const threeDSResult = await client.threeds.process({
amount: formData.amount,
currency: 'BRL',
card: {
number: formData.cardNumber,
holderName: formData.cardHolder,
expirationMonth: formData.expMonth,
expirationYear: formData.expYear
},
customer: {
name: formData.customerName,
email: formData.customerEmail,
phone: formData.customerPhone
},
address: formData.address
})

if (threeDSResult.state === 'success') {
// Criar cobrança com dados 3DS
const charge = await createCharge({
...formData,
threeDS: threeDSResult
})

setResult(charge)
} else {
throw new Error(`Autenticação falhou: ${threeDSResult.state}`)
}
} catch (error) {
console.error('Erro:', error)
alert(error.message)
} finally {
setLoading(false)
}
}

return (
<form onSubmit={handleSubmit}>
{/* Campos do formulário */}
<button type='submit' disabled={loading}>
{loading ? 'Processando...' : 'Finalizar Pagamento'}
</button>
</form>
)
}

Vanilla JavaScript

<!DOCTYPE html>
<html>
<head>
<title>Checkout com 3DS</title>
</head>
<body>
<form id="checkout-form">
<!-- Campos do formulário -->
<button type="submit">Pagar</button>
</form>

<script src="https://js.fastpaybrasil.com/security-sdk.min.js"></script>
<script>
let client = null

async function initializeSDK() {
client = new FastPay.FastPayClient({
credentials: {
publicKey: 'sua_chave_publica'
}
})

await client.initialize()

if (!client.isThreeDSAvailable()) {
alert('3DS não disponível no momento')
}
}

document.getElementById('checkout-form').addEventListener('submit', async (e) => {
e.preventDefault()

try {
const result = await client.threeds.process({
amount: 100.00,
currency: 'BRL',
card: {
number: document.getElementById('card-number').value,
holderName: document.getElementById('card-holder').value,
expirationMonth: document.getElementById('exp-month').value,
expirationYear: document.getElementById('exp-year').value
},
customer: {
name: document.getElementById('name').value,
email: document.getElementById('email').value,
phone: document.getElementById('phone').value
},
address: {
// ... dados do endereço
}
})

if (result.state === 'success') {
// Criar cobrança
console.log('3DS completo:', result)
}
} catch (error) {
console.error('Erro:', error)
}
})

// Inicializar ao carregar a página
initializeSDK()
</script>
</body>
</html>

Vue.js

<template>
<form @submit.prevent="handleSubmit">
<!-- Campos do formulário -->
<button type="submit" :disabled="loading">
{{ loading ? 'Processando...' : 'Pagar' }}
</button>
</form>
</template>

<script>
import FastPayClient from '@fastpaybr/security-sdk'

export default {
data() {
return {
loading: false,
client: null
}
},

async mounted() {
this.client = new FastPayClient({
credentials: {
publicKey: process.env.VUE_APP_FASTPAY_KEY
}
})

await this.client.initialize()
},

methods: {
async handleSubmit() {
if (!this.client.isThreeDSAvailable()) {
alert('3DS não disponível')
return
}

this.loading = true

try {
const result = await this.client.threeds.process({
// ... dados da transação
})

if (result.state === 'success') {
// Criar cobrança
await this.createCharge(result)
}
} catch (error) {
console.error('Erro:', error)
} finally {
this.loading = false
}
}
}
}
</script>

Métodos Disponíveis

client.initialize()

Inicializa o SDK e carrega os provedores de 3DS disponíveis.

await client.initialize()

Retorno: Promise<void>


client.isReady()

Verifica se o SDK foi inicializado com sucesso.

const ready = client.isReady()

Retorno: boolean


client.isThreeDSAvailable()

Verifica se o 3DS está disponível e pronto para uso.

const available = client.isThreeDSAvailable()

Retorno: boolean


client.threeds.process(request, options)

Executa o fluxo completo de autenticação 3DS.

Parâmetros:

interface ThreeDSProcessRequest {
amount: number // Valor na moeda especificada
currency: string // Código da moeda (ex: BRL, USD)
installments?: number // Número de parcelas (1-12)
card: {
number: string // Número do cartão
holderName?: string // Nome do titular
expirationMonth: string // Mês de expiração (1-12)
expirationYear: string // Ano de expiração (4 dígitos)
}
customer: {
name: string // Nome completo
email: string // Email do cliente
phone: string // Telefone com código do país
}
address: {
street: string // Logradouro
number: string // Número
complement?: string // Complemento (opcional)
zipCode: string // CEP
neighborhood: string // Bairro
city: string // Cidade
state: string // Estado
country: string // País (código ISO 3166-1 alpha-3)
}
challengeContainer?: HTMLElement // Container customizado para o challenge
}

interface ThreeDSProcessOptions {
fingerprintingTimeout?: number // Timeout para fingerprinting em ms (padrão: 30000)
challengeTimeout?: number // Timeout para challenge em ms (padrão: 180000)
modal?:
| {
width?: string // Largura do modal (padrão: '500px')
height?: string // Altura do modal (padrão: '600px')
closeOnOverlayClick?: boolean // Fechar ao clicar fora (padrão: true)
showCloseButton?: boolean // Mostrar botão X (padrão: true)
}
| false // false para desabilitar modal automático
}

Retorno: Promise<ChallengeResultResponse>

Integração com API de Cobranças

Após obter o resultado do 3DS, use-o na criação da cobrança:

// 1. Executar autenticação 3DS
const threeDSResult = await client.threeds.process({
amount: 100.00,
currency: 'BRL',
card: {
/* ... */
},
customer: {
/* ... */
},
address: {
/* ... */
}
})

// 2. Criar cobrança incluindo dados 3DS
const charge = await fetch('https://api-global.fastpaybrasil.com/v1/charges', {
method: 'POST',
headers: {
'Authorization': `Basic ${credentials}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
amount: 100.00,
currency: 'BRL',
customer: {
name: 'João Silva',
email: 'joao@email.com',
document: {
type: 'cpf',
id: '12345678900'
},
phone: '5511987654321'
},
paymentMethod: {
type: 'credit_card',
cardNumber: '4111111111111111',
cardholderName: 'João Silva',
expirationMonth: '12',
expirationYear: '2025',
cvv: '123',
installments: 1,
// Incluir dados 3DS
threeDS: threeDSResult
},
items: [
{
title: 'Produto XYZ',
type: 'digital',
description: 'Descrição',
unit_price: 10000,
quantity: 1
}
]
})
})

const result = await charge.json()
console.log('Cobrança criada:', result)

Cenários de Validação

SituaçãoComportamento
3DS não fornecido❌ Cobrança recusada com erro 3ds_required
state !== 'success'❌ Cobrança recusada com erro específico do estado
Dados incompletos❌ Cobrança recusada com erro 3ds_incomplete
Tudo OK✅ Cobrança prossegue normalmente

Exemplos de Erros

{
"id": "charge_id",
"status": "refused",
"reason": "3ds_required: 3D Secure authentication is required for this merchant. Please complete 3DS authentication before creating a charge."
}
{
"id": "charge_id",
"status": "refused",
"reason": "3ds_failed: The cardholder failed to authenticate. Please try again or use a different card."
}

Tratamento de Erros

Erros Comuns

try {
const result = await client.threeds.process({
/* ... */
})
} catch (error) {
if (error.message.includes('not available')) {
// 3DS provider não está disponível
console.error('3DS não disponível no momento')
} else if (error.message.includes('timeout')) {
// Timeout no processo
console.error('Timeout na autenticação')
} else if (error.message.includes('container')) {
// Container não fornecido quando necessário
console.error('Container para challenge não fornecido')
} else {
// Outros erros
console.error('Erro desconhecido:', error)
}
}

Estados de Erro no Resultado

const result = await client.threeds.process({
/* ... */
})

switch (result.state) {
case 'success':
// Autenticação bem-sucedida
break

case 'failure':
// Cliente falhou na autenticação
alert('Autenticação falhou. Tente novamente ou use outro cartão.')
break

case 'unenrolled':
// Cartão não inscrito no 3DS
// Decisão de negócio: aceitar ou recusar
break

case 'error':
// Erro no processo
alert('Erro na autenticação. Tente novamente.')
break

case 'unsupported_brand':
// Bandeira não suporta 3DS
alert('Este cartão não suporta 3D Secure.')
break

case 'disabled':
// 3DS desabilitado
alert('3D Secure está desabilitado para este cartão.')
break
}

Boas Práticas

1. Sempre Inicializar Antes de Usar

// ❌ Errado
const result = await client.threeds.process({
/* ... */
})

// ✅ Correto
await client.initialize()
if (client.isThreeDSAvailable()) {
const result = await client.threeds.process({
/* ... */
})
}

2. Tratar Todos os Estados

const result = await client.threeds.process({
/* ... */
})

// ✅ Sempre trate todos os estados possíveis
if (result.state === 'success') {
// Prosseguir com cobrança
} else {
// Lidar com falha de acordo com sua política de negócio
handleAuthenticationFailure(result.state)
}

3. Usar Variáveis de Ambiente

// ❌ Errado - chave exposta
const client = new FastPayClient({
credentials: { publicKey: 'pk_abc123' }
})

// ✅ Correto - usar variáveis de ambiente
const client = new FastPayClient({
credentials: { publicKey: process.env.FASTPAY_PUBLIC_KEY }
})

4. Implementar Loading States

// ✅ Mostrar feedback visual ao usuário
setLoading(true)
try {
const result = await client.threeds.process({
/* ... */
})
// Processar resultado
} finally {
setLoading(false)
}

5. Log de Debug em Desenvolvimento

const client = new FastPayClient({
credentials: { publicKey: 'your_key' },
debug: process.env.NODE_ENV === 'development' // Apenas em dev
})

6. Configuração de Timeouts

Os valores padrão são otimizados para produção. Ajuste apenas se necessário:

const result = await client.threeds.process(
{
/* ... */
},
{
fingerprintingTimeout: 30000, // Recomendado: 30s (não aumente além de 45s)
challengeTimeout: 180000 // Recomendado: 3min (não aumente além de 5min)
}
)

⚠️ Importante:

  • Fingerprinting: 30s é suficiente. Valores maiores podem indicar problemas de rede
  • Challenge: 3min é o ideal. Timeouts muito longos prejudicam a conversão
  • Em caso de timeout frequente, investigue a causa raiz ao invés de aumentar o valor

Troubleshooting

Possíveis causas:

  • O fluxo foi frictionless (sem challenge necessário)
  • O modal foi bloqueado por pop-up blocker
  • Container customizado com display:none

Solução:

// Verificar se challenge foi necessário
console.log('Resultado:', result)

// Garantir que container está visível
const container = document.getElementById('container')
container.style.display = 'block'

Timeout no fingerprinting

Possíveis causas:

  • Conexão de internet lenta do cliente
  • Problemas no provedor 3DS
  • Bloqueio de scripts no navegador

Solução:

// Apenas se necessário, aumente moderadamente (máx. 45s)
const result = await client.threeds.process(
{
/* ... */
},
{ fingerprintingTimeout: 45000 } // 45s (não recomendado acima disso)
)

⚠️ Importante: Timeouts frequentes indicam problemas que devem ser investigados, não apenas contornados

Erro "3DS provider not available"

Possíveis causas:

  • SDK não foi inicializado
  • Nenhum provider 3DS configurado no backend
  • Erro na inicialização do provider

Solução:

await client.initialize()

if (!client.isThreeDSAvailable()) {
console.error('3DS não disponível. Verifique a configuração no backend.')
// Implementar fallback ou notificar usuário
}

Erro "container is required"

Causa: Modal desabilitado sem fornecer container customizado

Solução:

// Opção 1: Usar modal automático (padrão)
const result = await client.threeds.process({
/* ... */
})

// Opção 2: Fornecer container se modal desabilitado
const result = await client.threeds.process(
{
/* ... */
challengeContainer: document.getElementById('my-container')
},
{ modal: false }
)

Dados do cartão inválidos

Solução:

// Validar dados antes de chamar o SDK
if (!isValidCardNumber(cardNumber)) {
alert('Número do cartão inválido')
return
}

if (!isValidExpiration(month, year)) {
alert('Data de validade inválida')
return
}