Pular para o conteúdo principal

Exemplo Prático - Integração Sopague

Este guia apresenta exemplos práticos de como integrar a Biblioteca Sopague em diferentes cenários de pagamento.


Exemplo: E-commerce Completo

HTML + JavaScript

<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Checkout - E-commerce</title>
<!-- Para obter a URL do CDN da biblioteca Sopague, entre em contato com o suporte -->
<script src="https://cdn.exemplo.com/sopague/sopague.min.js"></script>
</head>
<body>
<div id="checkout-form">
<h2>Finalizar Compra</h2>

<!-- Dados do Cartão -->
<div class="form-group">
<label>Número do Cartão:</label>
<input type="text" id="cardNumber" placeholder="0000 0000 0000 0000" maxlength="19">
</div>

<div class="form-group">
<label>Nome no Cartão:</label>
<input type="text" id="cardName" placeholder="Nome como no cartão">
</div>

<div class="form-group">
<label>Validade:</label>
<input type="text" id="cardExpiry" placeholder="MM/AA" maxlength="5">
</div>

<div class="form-group">
<label>CVV:</label>
<input type="text" id="cardCvv" placeholder="123" maxlength="4">
</div>

<button id="processPayment" disabled>Processar Pagamento</button>

<div id="status"></div>
</div>

<script>
class PaymentProcessor {
constructor() {
this.publicKey = `-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC...
-----END PUBLIC KEY-----`;

this.antiFraudId = this.generateSessionId();
this.initializeSopague();
this.setupEventListeners();
}

generateSessionId() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
const r = Math.random() * 16 | 0;
const v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}

initializeSopague() {
try {
// Configurar chave pública
Sopague.setEncryptPublicKey(this.publicKey);

// Inicializar anti-fraude
Sopague.initAntiFraud(this.antiFraudId, {
orgId: 'ecommerce_org',
sessionPrefix: 'ecom_'
});

console.log('Sopague inicializado com sucesso');
this.updateStatus('Sistema de segurança carregado', 'success');
} catch (error) {
console.error('Erro ao inicializar Sopague:', error);
this.updateStatus('Erro ao carregar sistema de segurança', 'error');
}
}

setupEventListeners() {
const cardNumber = document.getElementById('cardNumber');
const processBtn = document.getElementById('processPayment');

// Formatar número do cartão
cardNumber.addEventListener('input', (e) => {
let value = e.target.value.replace(/\s/g, '').replace(/[^0-9]/gi, '');
let formattedValue = value.match(/.{1,4}/g)?.join(' ') || value;
e.target.value = formattedValue;

// Habilitar botão se cartão válido
processBtn.disabled = !this.isValidCard(value);
});

// Processar pagamento
processBtn.addEventListener('click', () => {
this.processPayment();
});
}

isValidCard(cardNumber) {
const cleanNumber = cardNumber.replace(/\s/g, '');
return cleanNumber.length >= 13 && cleanNumber.length <= 19;
}

async processPayment() {
const cardNumber = document.getElementById('cardNumber').value.replace(/\s/g, '');
const cardName = document.getElementById('cardName').value;
const cardExpiry = document.getElementById('cardExpiry').value;
const cardCvv = document.getElementById('cardCvv').value;

if (!this.validateForm(cardNumber, cardName, cardExpiry, cardCvv)) {
return;
}

this.updateStatus('Processando pagamento...', 'info');

try {
// Criptografar dados sensíveis (válido por 5 minutos)
const encryptedCard = await Sopague.encryptCard(cardNumber);
const sessionId = Sopague.getAntiFraudSessionId();

// Simular envio para API
const paymentData = {
encryptedCard: encryptedCard,
cardName: cardName,
cardExpiry: cardExpiry,
cardCvv: cardCvv,
antiFraudSessionId: sessionId,
amount: 100.00,
currency: 'BRL'
};

console.log('Dados do pagamento:', paymentData);

// Aqui você faria a chamada real para sua API
// const response = await fetch('/api/payments', {
// method: 'POST',
// headers: { 'Content-Type': 'application/json' },
// body: JSON.stringify(paymentData)
// });

this.updateStatus('Pagamento processado com sucesso!', 'success');

} catch (error) {
console.error('Erro no processamento:', error);
this.updateStatus('Erro ao processar pagamento: ' + error.message, 'error');
}
}

validateForm(cardNumber, cardName, cardExpiry, cardCvv) {
if (!cardNumber || !cardName || !cardExpiry || !cardCvv) {
this.updateStatus('Preencha todos os campos', 'error');
return false;
}

if (!this.isValidCard(cardNumber)) {
this.updateStatus('Número do cartão inválido', 'error');
return false;
}

return true;
}

updateStatus(message, type) {
const statusEl = document.getElementById('status');
statusEl.textContent = message;
statusEl.className = `status ${type}`;
}
}

// Inicializar quando a página carregar
document.addEventListener('DOMContentLoaded', () => {
new PaymentProcessor();
});
</script>

<style>
.form-group {
margin-bottom: 15px;
}

.form-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}

.form-group input {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}

#processPayment {
background: #007bff;
color: white;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
}

#processPayment:disabled {
background: #ccc;
cursor: not-allowed;
}

.status {
margin-top: 15px;
padding: 10px;
border-radius: 4px;
}

.status.success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}

.status.error {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}

.status.info {
background: #d1ecf1;
color: #0c5460;
border: 1px solid #bee5eb;
}
</style>
</body>
</html>

Exemplo: React/NextJS

Hook Personalizado

// hooks/useSopague.ts
import { useState, useEffect, useCallback } from 'react';

interface SopagueHook {
Sopague: any;
isLoaded: boolean;
error: string | null;
encryptCard: (cardNumber: string) => Promise<string>;
initAntiFraud: (sessionId: string) => string;
getSessionId: () => string | null;
}

export function useSopague(): SopagueHook {
const [isLoaded, setIsLoaded] = useState(false);
const [error, setError] = useState<string | null>(null);

useEffect(() => {
if (typeof window !== 'undefined' && window.Sopague) {
setIsLoaded(true);
return;
}

const script = document.createElement('script');
script.src = '/libs/sopague-integration-library-1.0.0.min.js';
script.async = true;

script.onload = () => {
setIsLoaded(true);
// Configurar chave pública automaticamente
if (window.Sopague && process.env.NEXT_PUBLIC_RSA_PUBLIC_KEY) {
window.Sopague.setEncryptPublicKey(process.env.NEXT_PUBLIC_RSA_PUBLIC_KEY);
}
};

script.onerror = () => {
setError('Erro ao carregar Sopague');
};

document.body.appendChild(script);

return () => {
if (document.body.contains(script)) {
document.body.removeChild(script);
}
};
}, []);

const encryptCard = useCallback(async (cardNumber: string): Promise<string> => {
if (!window.Sopague) {
throw new Error('Sopague não carregado');
}
return await window.Sopague.encryptCard(cardNumber);
}, []);

const initAntiFraud = useCallback((sessionId: string): string => {
if (!window.Sopague) {
throw new Error('Sopague não carregado');
}
return window.Sopague.initAntiFraud(sessionId, {
orgId: process.env.NEXT_PUBLIC_ORG_ID || 'default_org',
sessionPrefix: 'nextjs_'
});
}, []);

const getSessionId = useCallback((): string | null => {
if (!window.Sopague) return null;
return window.Sopague.getAntiFraudSessionId();
}, []);

return {
Sopague: typeof window !== 'undefined' ? window.Sopague : null,
isLoaded,
error,
encryptCard,
initAntiFraud,
getSessionId
};
}

Componente de Pagamento

// components/PaymentForm.tsx
import React, { useState, useEffect } from 'react';
import { useSopague } from '../hooks/useSopague';

interface PaymentFormProps {
amount: number;
onSuccess: (paymentData: any) => void;
onError: (error: string) => void;
}

export default function PaymentForm({ amount, onSuccess, onError }: PaymentFormProps) {
const { isLoaded, error, encryptCard, initAntiFraud, getSessionId } = useSopague();
const [formData, setFormData] = useState({
cardNumber: '',
cardName: '',
cardExpiry: '',
cardCvv: ''
});
const [isProcessing, setIsProcessing] = useState(false);

useEffect(() => {
if (isLoaded) {
// Inicializar anti-fraude quando a biblioteca carregar
const sessionId = initAntiFraud(`session_${Date.now()}`);
console.log('Anti-fraude inicializado:', sessionId);
}
}, [isLoaded, initAntiFraud]);

const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;

if (name === 'cardNumber') {
// Formatar número do cartão
const formatted = value.replace(/\s/g, '').replace(/(.{4})/g, '$1 ').trim();
setFormData(prev => ({ ...prev, [name]: formatted }));
} else {
setFormData(prev => ({ ...prev, [name]: value }));
}
};

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setIsProcessing(true);

try {
// Validar formulário
if (!validateForm()) {
return;
}

// Criptografar cartão (válido por 5 minutos)
const cleanCardNumber = formData.cardNumber.replace(/\s/g, '');
const encryptedCard = await encryptCard(cleanCardNumber);

// Obter session ID do anti-fraude
const antiFraudSessionId = getSessionId();

// Preparar dados do pagamento
const paymentData = {
encryptedCard,
cardName: formData.cardName,
cardExpiry: formData.cardExpiry,
cardCvv: formData.cardCvv,
antiFraudSessionId,
amount,
currency: 'BRL'
};

// Simular chamada para API
console.log('Enviando pagamento:', paymentData);

// Aqui você faria a chamada real para sua API
// const response = await fetch('/api/payments', {
// method: 'POST',
// headers: { 'Content-Type': 'application/json' },
// body: JSON.stringify(paymentData)
// });

onSuccess(paymentData);

} catch (err) {
onError(err instanceof Error ? err.message : 'Erro desconhecido');
} finally {
setIsProcessing(false);
}
};

const validateForm = (): boolean => {
const { cardNumber, cardName, cardExpiry, cardCvv } = formData;

if (!cardNumber || !cardName || !cardExpiry || !cardCvv) {
onError('Preencha todos os campos');
return false;
}

const cleanCardNumber = cardNumber.replace(/\s/g, '');
if (cleanCardNumber.length < 13 || cleanCardNumber.length > 19) {
onError('Número do cartão inválido');
return false;
}

return true;
};

if (error) {
return <div className="error">Erro ao carregar sistema de pagamento: {error}</div>;
}

if (!isLoaded) {
return <div className="loading">Carregando sistema de pagamento...</div>;
}

return (
<form onSubmit={handleSubmit} className="payment-form">
<h3>Dados do Cartão</h3>

<div className="form-group">
<label>Número do Cartão:</label>
<input
type="text"
name="cardNumber"
value={formData.cardNumber}
onChange={handleInputChange}
placeholder="0000 0000 0000 0000"
maxLength={19}
required
/>
</div>

<div className="form-group">
<label>Nome no Cartão:</label>
<input
type="text"
name="cardName"
value={formData.cardName}
onChange={handleInputChange}
placeholder="Nome como no cartão"
required
/>
</div>

<div className="form-row">
<div className="form-group">
<label>Validade:</label>
<input
type="text"
name="cardExpiry"
value={formData.cardExpiry}
onChange={handleInputChange}
placeholder="MM/AA"
maxLength={5}
required
/>
</div>

<div className="form-group">
<label>CVV:</label>
<input
type="text"
name="cardCvv"
value={formData.cardCvv}
onChange={handleInputChange}
placeholder="123"
maxLength={4}
required
/>
</div>
</div>

<div className="amount">
<strong>Valor: R$ {amount.toFixed(2)}</strong>
</div>

<button
type="submit"
disabled={isProcessing}
className="submit-button"
>
{isProcessing ? 'Processando...' : 'Pagar Agora'}
</button>
</form>
);
}

Configuração de Ambiente

Variáveis de Ambiente (NextJS)

# .env.local
NEXT_PUBLIC_RSA_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC...\n-----END PUBLIC KEY-----"
NEXT_PUBLIC_ORG_ID="sua_organizacao_id"
NEXT_PUBLIC_SOPAGUE_VERSION="1.0.0"

Configuração do Servidor

// pages/api/payments.js (NextJS)
export default async function handler(req, res) {
if (req.method !== 'POST') {
return res.status(405).json({ message: 'Method not allowed' });
}

const { encryptedCard, antiFraudSessionId, amount, currency } = req.body;

try {
// Validar dados recebidos
if (!encryptedCard || !antiFraudSessionId) {
return res.status(400).json({
message: 'Dados de segurança obrigatórios'
});
}

// Aqui você faria a integração com a API da SoPague
// const paymentResponse = await sopagueAPI.createPayment({
// encryptedCard,
// antiFraudSessionId,
// amount,
// currency
// });

res.status(200).json({
success: true,
message: 'Pagamento processado com sucesso'
});

} catch (error) {
console.error('Erro no processamento:', error);
res.status(500).json({
message: 'Erro interno do servidor'
});
}
}

Boas Práticas de Criptografia

Tempo de Validade

Importante

O token criptografado gerado pela biblioteca Sopague expira em 5 minutos. Após esse período, o token não pode ser usado para processar pagamentos.

Recomendações

  1. Criptografe apenas no momento do pagamento - Não armazene tokens criptografados
  2. Processe imediatamente - Envie o token para a API assim que gerado
  3. Trate expirações - Implemente retry com nova criptografia se necessário
  4. Valide no servidor - Sempre valide a validade do token no backend

Exemplo de Implementação Segura

async function processPaymentSecurely(cardData) {
try {
// 1. Criptografar apenas quando necessário
const encryptedCard = await Sopague.encryptCard(cardData.number);

// 2. Processar imediatamente
const paymentData = {
encryptedCard,
antiFraudSessionId: Sopague.getAntiFraudSessionId(),
amount: cardData.amount,
// ... outros dados
};

// 3. Enviar para API sem delay
const response = await fetch('/api/payments', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(paymentData)
});

return await response.json();

} catch (error) {
// 4. Tratar erros de expiração
if (error.message.includes('expired') || error.message.includes('invalid')) {
console.log('Token expirado, tentando novamente...');
// Retry com nova criptografia
return processPaymentSecurely(cardData);
}
throw error;
}
}

Testes

Teste de Criptografia

// test-encryption.js
async function testEncryption() {
const testCard = '4111111111111111';

try {
Sopague.setEncryptPublicKey(publicKey);
const encrypted = await Sopague.encryptCard(testCard);

console.log('Cartão original:', testCard);
console.log('Cartão criptografado:', encrypted);
console.log('Teste de criptografia: ✅ Sucesso');

} catch (error) {
console.error('Teste de criptografia: ❌ Falha', error);
}
}

// Executar teste
testEncryption();

Teste de Anti-Fraude

// test-antifraud.js
function testAntiFraud() {
try {
const sessionId = Sopague.initAntiFraud('test-session-123');
console.log('Session ID gerado:', sessionId);

const currentSession = Sopague.getAntiFraudSessionId();
console.log('Session ID atual:', currentSession);

console.log('Teste de anti-fraude: ✅ Sucesso');

} catch (error) {
console.error('Teste de anti-fraude: ❌ Falha', error);
}
}

// Executar teste
testAntiFraud();

Próximos Passos

  1. Implemente os exemplos em seu ambiente de desenvolvimento
  2. Configure as variáveis de ambiente necessárias
  3. Teste a integração com dados de teste
  4. Integre com sua API de pagamentos
  5. Monitore o funcionamento em produção

Para mais informações, consulte a documentação completa da Biblioteca Sopague.