O que é o Keranos?
O Keranos é a biblioteca oficial do Autorizou para criptografia de dados de cartão de crédito no frontend. Ele garante que informações sensíveis nunca trafeguem em texto puro pela rede, atendendo aos requisitos de segurança PCI DSS.
Principais Funcionalidades
Criptografia de Cartões
Criptografia AES-256-CBC no browser
Suporte a todas as principais bandeiras
Detecção automática de bandeira do cartão
Validação de dados antes da criptografia
3D Secure 2.0
Suporte completo ao protocolo 3DS2
Interface de challenge integrada
Callbacks configuráveis para sucesso/falha
Google Pay
Integração nativa com Google Pay
Criação de botões personalizados
Suporte a transações tokenizadas
Instalação
Via CDN
<!-- Incluir na tag <head> ou antes do </body> -->
< script src = "https://js.autorizou.cloud/keranos/latest/embed.js" ></ script >
Via NPM (para projetos React/Next.js)
npm install @autorizou/keranos
# ou
yarn add @autorizou/keranos
Importante: A versão NPM ainda está em desenvolvimento. Atualmente, recomendamos usar a versão CDN para garantir compatibilidade.
Configuração
Variáveis de Ambiente
Para usar o Keranos, você precisa configurar a chave de criptografia:
# .env ou .env.local
NEXT_PUBLIC_AES_KEY=your_aes_key_here
Chave AES: Entre em contato com o suporte do Autorizou para obter sua chave de criptografia específica para cada ambiente (sandbox/production).
Uso Básico
1. Incluir o Script
<! DOCTYPE html >
< html >
< head >
< title > Meu E-commerce </ title >
<!-- Incluir Keranos -->
< script src = "https://js.autorizou.cloud/keranos/latest/embed.js" ></ script >
</ head >
< body >
<!-- Seu conteúdo -->
</ body >
</ html >
2. Criptografar Dados do Cartão
// Preparar dados do cartão
const cardData = {
number: '4111111111111111' ,
holder: 'JOAO SILVA' ,
expMonth: '12' ,
expYear: '2030' ,
securityCode: '123'
};
// Preparar dados do portador
const cardHolder = {
name: 'João Silva' ,
identification: {
type: 'CPF' ,
number: '12345678900'
}
};
// Criptografar usando callbacks
await window . Autorizou . encryptCard (
{ cardData , cardHolder },
{
onSuccess : ( response ) => {
console . log ( 'Token criptografado:' , response . token );
// Enviar response.token para sua API
},
onError : ( error ) => {
console . error ( 'Erro na criptografia:' , error . message );
}
}
);
3. Enviar para a API
const response = await fetch ( 'https://zeus-sandbox.autorizou.dev/api/v1/cards' , {
method: 'POST' ,
headers: {
'Authorization' : 'Bearer ...' ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
customer_id: 'cus_abc123' ,
encrypted: result . token
})
});
const card = await response . json ();
console . log ( 'Cartão salvo:' , card );
Integração React/Next.js
Carregar Keranos no Layout
// app/layout.js ou pages/_app.js
import Script from 'next/script' ;
export default function RootLayout ({ children }) {
return (
< html lang = "pt-BR" >
< head >
{ /* Carregar Keranos antes de qualquer script */ }
< Script
src = "https://js.autorizou.cloud/keranos/latest/embed.js"
strategy = "beforeInteractive"
/>
</ head >
< body > { children } </ body >
</ html >
);
}
'use client' ;
import { useState } from 'react' ;
export default function CardForm ({ customerId , onSuccess , onError }) {
const [ formData , setFormData ] = useState ({
number: '' ,
holder: '' ,
expMonth: '' ,
expYear: '' ,
securityCode: ''
});
const [ loading , setLoading ] = useState ( false );
const handleChange = ( e ) => {
const { name , value } = e . target ;
setFormData ( prev => ({ ... prev , [name]: value }));
};
const handleSubmit = async ( e ) => {
e . preventDefault ();
setLoading ( true );
try {
// 1. Verificar se Keranos está carregado
if ( ! window . Autorizou ?. encryptCard ) {
throw new Error ( 'Keranos não está carregado' );
}
// 2. Criptografar dados usando callbacks
await window . Autorizou . encryptCard (
{ cardData: formData },
{
onSuccess : async ( encryptResult ) => {
try {
// 3. Enviar para a API
const response = await fetch ( '/api/cards' , {
method: 'POST' ,
headers: { 'Content-Type' : 'application/json' },
body: JSON . stringify ({
customer_id: customerId ,
encrypted: encryptResult . token
})
});
if ( ! response . ok ) {
const error = await response . json ();
throw new Error ( error . message || 'Erro ao salvar cartão' );
}
const card = await response . json ();
onSuccess ?.( card );
// Limpar formulário
setFormData ({
number: '' ,
holder: '' ,
expMonth: '' ,
expYear: '' ,
securityCode: ''
});
} catch ( err ) {
console . error ( 'Erro ao salvar:' , err );
onError ?.( err );
} finally {
setLoading ( false );
}
},
onError : ( error ) => {
console . error ( 'Erro na criptografia:' , error );
onError ?.( error );
setLoading ( false );
}
}
);
} catch ( err ) {
console . error ( 'Erro:' , err );
onError ?.( err );
setLoading ( false );
}
};
return (
< form onSubmit = { handleSubmit } className = "space-y-4" >
< div >
< label htmlFor = "number" > Número do Cartão </ label >
< input
id = "number"
name = "number"
type = "text"
value = { formData . number }
onChange = { handleChange }
placeholder = "0000 0000 0000 0000"
maxLength = { 19 }
required
/>
</ div >
< div >
< label htmlFor = "holder" > Nome no Cartão </ label >
< input
id = "holder"
name = "holder"
type = "text"
value = { formData . holder }
onChange = { handleChange }
placeholder = "NOME COMPLETO"
style = { { textTransform: 'uppercase' } }
required
/>
</ div >
< div className = "grid grid-cols-2 gap-4" >
< div >
< label htmlFor = "expMonth" > Mês </ label >
< input
id = "expMonth"
name = "expMonth"
type = "text"
value = { formData . expMonth }
onChange = { handleChange }
placeholder = "MM"
maxLength = { 2 }
required
/>
</ div >
< div >
< label htmlFor = "expYear" > Ano </ label >
< input
id = "expYear"
name = "expYear"
type = "text"
value = { formData . expYear }
onChange = { handleChange }
placeholder = "AAAA"
maxLength = { 4 }
required
/>
</ div >
</ div >
< div >
< label htmlFor = "securityCode" > CVV </ label >
< input
id = "securityCode"
name = "securityCode"
type = "text"
value = { formData . securityCode }
onChange = { handleChange }
placeholder = "123"
maxLength = { 4 }
required
/>
</ div >
< button
type = "submit"
disabled = { loading }
className = "w-full bg-primary text-white py-2 rounded"
>
{ loading ? 'Processando...' : 'Salvar Cartão' }
</ button >
</ form >
);
}
API Route (Next.js)
// app/api/cards/route.js
import { NextResponse } from 'next/server' ;
export async function POST ( request ) {
try {
const body = await request . json ();
const { customer_id , encrypted } = body ;
// Validar dados
if ( ! customer_id || ! encrypted ) {
return NextResponse . json (
{ message: 'customer_id e encrypted são obrigatórios' },
{ status: 400 }
);
}
// Enviar para Zeus API
const response = await fetch ( 'https://zeus-sandbox.autorizou.dev/api/v1/cards' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ process . env . AUTORIZOU_API_KEY } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({ customer_id , encrypted })
});
const data = await response . json ();
if ( ! response . ok ) {
return NextResponse . json ( data , { status: response . status });
}
return NextResponse . json ( data );
} catch ( error ) {
console . error ( 'Erro na API de cartões:' , error );
return NextResponse . json (
{ message: 'Erro interno do servidor' },
{ status: 500 }
);
}
}
Validações e Boas Práticas
Validação de Número de Cartão
function validateCardNumber ( number ) {
// Remover espaços e caracteres não numéricos
const cleaned = number . replace ( / \D / g , '' );
// Verificar tamanho (13-19 dígitos)
if ( cleaned . length < 13 || cleaned . length > 19 ) {
return false ;
}
// Algoritmo de Luhn
let sum = 0 ;
let isEven = false ;
for ( let i = cleaned . length - 1 ; i >= 0 ; i -- ) {
let digit = parseInt ( cleaned . charAt ( i ), 10 );
if ( isEven ) {
digit *= 2 ;
if ( digit > 9 ) {
digit -= 9 ;
}
}
sum += digit ;
isEven = ! isEven ;
}
return sum % 10 === 0 ;
}
Validação de Data de Expiração
function validateExpiration ( month , year ) {
const now = new Date ();
const currentYear = now . getFullYear ();
const currentMonth = now . getMonth () + 1 ;
const expMonth = parseInt ( month , 10 );
const expYear = parseInt ( year , 10 );
// Validar mês
if ( expMonth < 1 || expMonth > 12 ) {
return false ;
}
// Validar ano (aceitar 2 ou 4 dígitos)
const fullYear = expYear < 100 ? 2000 + expYear : expYear ;
// Verificar se não está expirado
if ( fullYear < currentYear ) {
return false ;
}
if ( fullYear === currentYear && expMonth < currentMonth ) {
return false ;
}
return true ;
}
Detecção de Bandeira
function detectCardBrand ( number ) {
const cleaned = number . replace ( / \D / g , '' );
const patterns = {
visa: / ^ 4/ ,
mastercard: / ^ ( 5 [ 1-5 ] | 2 [ 2-7 ] ) / ,
amex: / ^ 3 [ 47 ] / ,
elo: / ^ ( 4011 | 4312 | 4389 | 4514 | 4576 | 5041 | 5066 | 5067 | 6277 | 6362 | 6363 | 6504 | 6505 | 6516 ) / ,
diners: / ^ ( 36 | 38 | 30 [ 0-5 ] ) / ,
discover: / ^ 6 (?: 011 | 5 ) / ,
jcb: / ^ 35/ ,
hipercard: / ^ 606282/
};
for ( const [ brand , pattern ] of Object . entries ( patterns )) {
if ( pattern . test ( cleaned )) {
return brand ;
}
}
return 'unknown' ;
}
Tratamento de Erros
Erros Comuns
try {
const result = await window . Autorizou . encryptCard ({ cardData });
if ( result . errors ) {
// Erros de validação do Keranos
switch ( result . errors . message [ 0 ]) {
case 'Número de cartão inválido' :
alert ( 'Por favor, verifique o número do cartão' );
break ;
case 'Data de expiração inválida' :
alert ( 'Cartão expirado ou data inválida' );
break ;
case 'CVV inválido' :
alert ( 'CVV deve ter 3 ou 4 dígitos' );
break ;
default :
alert ( 'Erro na validação do cartão' );
}
return ;
}
// Continuar com o envio...
} catch ( error ) {
// Erro de rede ou exceção
console . error ( 'Erro:' , error );
alert ( 'Erro ao processar cartão. Tente novamente.' );
}
Segurança
Boas Práticas
Nunca armazene dados de cartão no localStorage ou sessionStorage
Sempre use HTTPS em produção
Valide dados no frontend antes de criptografar
Implemente rate limiting no backend
Use CSP (Content Security Policy) adequado
Content Security Policy
< meta http-equiv = "Content-Security-Policy" content = "
default-src 'self';
script-src 'self' https://js.autorizou.cloud;
connect-src 'self' https://zeus.autorizou.cloud;
style-src 'self' 'unsafe-inline';
" >
Fluxo Completo de Integração
Passo a Passo
Incluir o Keranos no projeto (CDN ou NPM)
Coletar dados do cartão no formulário
Validar dados localmente antes de enviar
Criptografar usando window.Autorizou.encryptCard()
Enviar token criptografado para sua API backend
Backend encaminha para Zeus API
Processar resposta e atualizar interface
Diagrama de Fluxo
[Browser] → Keranos → [Token Criptografado] → [Seu Backend] → Zeus API
↓
[Cartão Tokenizado]
Troubleshooting
Keranos não está carregado
Problema: window.Autorizou is undefined
Solução:
// Verificar se Keranos está disponível
if ( typeof window . Autorizou === 'undefined' ) {
console . error ( 'Keranos não foi carregado. Verifique o script.' );
// Tentar recarregar ou mostrar mensagem ao usuário
}
Erro de CORS
Problema: Erro de Cross-Origin ao chamar a API
Solução:
Sempre envie o token criptografado do seu backend para o Zeus
Nunca faça chamadas diretas do browser para zeus.autorizou.cloud
Token inválido
Problema: Backend retorna “invalid_encrypted_card”
Possíveis causas:
Chave AES incorreta ou não configurada
Dados do cartão em formato incorreto
Token corrompido durante transmissão
Solução:
// Verificar se a criptografia foi bem-sucedida
await window . Autorizou . encryptCard (
{ cardData },
{
onSuccess : ( response ) => {
console . log ( 'Token válido:' , response . token );
// Verificar se token não está vazio
if ( ! response . token ) {
console . error ( 'Token vazio!' );
}
},
onError : ( error ) => {
console . error ( 'Erro na criptografia:' , error );
}
}
);
Perguntas Frequentes
Preciso armazenar a chave AES no frontend?
Sim , a chave AES deve estar disponível no frontend para que o Keranos possa criptografar os dados. Use variáveis de ambiente (NEXT_PUBLIC_AES_KEY) e nunca commite a chave no código.A chave é usada apenas para criptografar no browser. A descriptografia acontece apenas no backend seguro do Zeus.
O Keranos funciona com frameworks modernos?
Posso usar Keranos em múltiplos ambientes?
Sim , você deve usar chaves AES diferentes para cada ambiente:
NEXT_PUBLIC_AES_KEY_DEV para desenvolvimento
NEXT_PUBLIC_AES_KEY_STAGING para staging
NEXT_PUBLIC_AES_KEY_PROD para produção
Preciso validar o cartão antes de criptografar?
Recomendado mas não obrigatório . Validar localmente melhora a UX:
Algoritmo de Luhn para número do cartão
Validação de data de expiração
Formato do CVV (3-4 dígitos)
O Keranos e o Zeus também fazem validações, mas feedback imediato é melhor.
O que acontece se o usuário desabilitar JavaScript?
Sem JavaScript, o Keranos não funcionará. Considere:
Mostrar mensagem informativa
Oferecer método de pagamento alternativo
A maioria dos usuários tem JS habilitado
Boas Práticas de Segurança
✅ Faça
Use HTTPS em produção sempre
Implemente rate limiting no backend
Valide dados no frontend E backend
Use CSP (Content Security Policy)
Limpe o formulário após sucesso
Implemente timeout nas requisições
❌ Não Faça
Armazenar dados de cartão em localStorage/sessionStorage
Enviar dados de cartão em texto puro
Logar dados sensíveis no console em produção
Reutilizar tokens criptografados
Fazer chamadas diretas do browser para Zeus API
Exemplos Avançados
// Aplicar máscara no número do cartão
function formatCardNumber ( value ) {
return value
. replace ( / \s / g , '' )
. replace ( / ( \d {4} ) / g , '$1 ' )
. trim ();
}
< input
type = "text"
value = { cardNumber }
onChange = { ( e ) => {
const formatted = formatCardNumber ( e . target . value );
setCardNumber ( formatted );
} }
maxLength = { 19 }
placeholder = "0000 0000 0000 0000"
/>
Detecção Automática de Bandeira
function detectBrand ( number ) {
const cleaned = number . replace ( / \s / g , '' );
if ( / ^ 4/ . test ( cleaned )) return 'visa' ;
if ( / ^ 5 [ 1-5 ] / . test ( cleaned )) return 'mastercard' ;
if ( / ^ 3 [ 47 ] / . test ( cleaned )) return 'amex' ;
if ( / ^ 6 (?: 011 | 5 ) / . test ( cleaned )) return 'discover' ;
if ( / ^ 3 (?: 0 [ 0-5 ] | [ 68 ] ) / . test ( cleaned )) return 'diners' ;
if ( / ^ 35/ . test ( cleaned )) return 'jcb' ;
if ( / ^ ( 4011 | 4312 | 4389 | 4514 | 4576 | 5041 | 5066 | 5067 | 636368 ) / . test ( cleaned )) return 'elo' ;
if ( / ^ 606282/ . test ( cleaned )) return 'hipercard' ;
return 'unknown' ;
}
// Uso
const [ brand , setBrand ] = useState ( '' );
useEffect (() => {
if ( cardNumber . length >= 4 ) {
setBrand ( detectBrand ( cardNumber ));
}
}, [ cardNumber ]);
Loading States
const [ loadingStates , setLoadingStates ] = useState ({
encrypting: false ,
saving: false
});
const handleSubmit = async ( e ) => {
e . preventDefault ();
setLoadingStates ({ encrypting: true , saving: false });
await window . Autorizou . encryptCard (
{ cardData },
{
onSuccess : async ( encryptResult ) => {
setLoadingStates ({ encrypting: false , saving: true });
try {
const response = await fetch ( '/api/cards' , {
method: 'POST' ,
body: JSON . stringify ({
customer_id: customerId ,
encrypted: encryptResult . token
})
});
if ( ! response . ok ) throw new Error ( 'Erro ao salvar' );
// Sucesso!
setLoadingStates ({ encrypting: false , saving: false });
} catch ( err ) {
setLoadingStates ({ encrypting: false , saving: false });
console . error ( err );
}
},
onError : () => {
setLoadingStates ({ encrypting: false , saving: false });
}
}
);
};
Suporte e Recursos
Links Úteis
Dúvidas? Entre em contato com nosso time de suporte técnico. Estamos aqui para ajudar!