marcelofecchio / safe2pay-api
Componente PHP enxuto para a API da Safe2Pay: Pix, cartão de crédito (à vista ou parcelado), consulta, estorno e webhook.
Requires
- php: ^8.4
- ext-json: *
- php-http/discovery: ^1.19
- psr/http-client: ^1.0
- psr/http-factory: ^1.0
- psr/http-message: ^1.1 || ^2.0
- psr/log: ^2.0 || ^3.0
Requires (Dev)
- guzzlehttp/guzzle: ^7.9 || ^8.0
- pestphp/pest: ^3.0 || ^4.0
Suggests
- guzzlehttp/guzzle: Cliente HTTP PSR-18 recomendado (permite configurar o timeout).
Provides
None
Conflicts
None
Replaces
None
README
Componente PHP enxuto para a API da Safe2Pay: Pix e cartão de crédito (à vista, parcelado sem juros ou com juros do comprador), consulta de transação, estorno e webhook. Feito para checkout transparente: o cliente paga sem sair do seu site.
- PHP 8.4, sem dependência de framework. Cliente HTTP PSR-18 (Guzzle, se instalado; ou qualquer outro injetado).
- Valores sempre em centavos (
int); o componente converte para o decimal que a API espera. - Erros viram exceções tipadas, inclusive os que a Safe2Pay devolve com HTTP 200 (
HasError: true). - Log PSR-3 opcional com só metadados (método, caminho, status, tempo). Nunca o corpo, o cartão ou a chave.
Instalação
composer require marcelofecchio/safe2pay-api guzzlehttp/guzzle
Configuração
A Safe2Pay tem uma chave de API para Sandbox e outra para Produção (painel: Integração). O ambiente vai em
sandbox, que é enviado como IsSandbox em cada cobrança.
use MarceloFecchio\Safe2Pay\Configuracao; use MarceloFecchio\Safe2Pay\Safe2Pay; $safe2pay = new Safe2Pay( new Configuracao(chaveApi: getenv('SAFE2PAY_API_KEY'), sandbox: true, aplicacao: 'Minha loja', timeout: 30), logger: $logger, // opcional );
Guarde a chave fora do código (variável de ambiente ou banco criptografado). var_dump/print_r da configuração
não mostram a chave.
Comprador e itens
use MarceloFecchio\Safe2Pay\Dto\{Comprador, Endereco, Item}; $comprador = new Comprador('Maria Souza', '529.982.247-25', 'maria@exemplo.com', '(11) 98765-4321', new Endereco('01310-100', 'Avenida Paulista', '1000', 'Bela Vista', 'São Paulo', 'SP', complemento: 'Sala 1')); $itens = [new Item('Pedido 123', valorUnitarioCentavos: 2990)];
O documento aceita CPF ou CNPJ (inclusive o CNPJ alfanumérico); a API exige o endereço completo.
Pix
use MarceloFecchio\Safe2Pay\Dto\CobrancaPix; $pix = $safe2pay->pix()->criar(new CobrancaPix($comprador, $itens, referencia: 'pedido-123', // até 60 caracteres; use para achar a transação depois urlCallback: 'https://loja.com/webhooks/safe2pay', expiracaoSegundos: 1800, )); $pix->idTransacao; // guarde $pix->copiaECola; // "Pix copia e cola" $pix->urlQrCode; // imagem do QR Code
A Safe2Pay não tem sandbox para Pix: teste com valores baixos em produção e estorne.
Cartão de crédito
use MarceloFecchio\Safe2Pay\Dto\{CobrancaCartao, DadosCartao}; $cartao = new DadosCartao($titular, $numero, $validade /* MM/AA ou MM/AAAA */, $cvv); // valida Luhn, validade e CVV $pagamento = $safe2pay->cartao()->cobrar(new CobrancaCartao($comprador, $itens, $cartao, parcelas: 1, // à vista referencia: 'pedido-123', urlCallback: 'https://loja.com/webhooks/safe2pay', ipComprador: $ip, )); if ($pagamento->aprovado()) { $pagamento->idTransacao; $pagamento->bandeira; $pagamento->finalCartao(); // "1111" } else { $pagamento->mensagem; // motivo da recusa, para mostrar ao cliente }
Parcelamento
- Sem juros (o lojista absorve):
parcelas: 3. É o padrão (jurosDoComprador: false). - Com juros do comprador:
parcelas: 6, jurosDoComprador: true, taxaJuros: 2.99(taxa opcional; sem ela, vale a da conta). - Simulação para montar o seletor de parcelas:
$safe2pay->cartao()->parcelas(30000)devolvelist<Parcela>.
Token e pré-autorização
$safe2pay->cartao()->tokenizar($cartao)devolve umCartaoTokenizado; passe$token->token(string) no lugar deDadosCartaopara cobrar de novo sem pedir o cartão.preAutorizacao: truesó reserva o valor (status Pré-autorizado).- Antifraude e 3DS opcionais:
idVisitante(oVisitorIDdo script de antifraude da Safe2Pay) eautenticacao3ds(Autenticacao3ds, resultado do script de 3DS).
Sandbox do cartão
No sandbox o resultado depende do último dígito do número do cartão: 0, 1 ou 4 autoriza; 2 recusa; 3 cartão
vencido. Cartões de teste válidos no Luhn: 5105 1051 0510 5100 e 4444 2222 2222 2222.
Segurança (PCI DSS)
A Safe2Pay não oferece tokenização no navegador: no checkout transparente, o número do cartão passa pelo seu
servidor, o que coloca a aplicação no escopo do PCI DSS. O componente ajuda (o DadosCartao não pode ser
serializado nem clonado e mascara os dados em var_dump), mas cabe à aplicação:
- HTTPS em tudo; nunca gravar, logar ou pôr em sessão número, validade ou CVV;
- não manter o cartão em estado de componente (Livewire, Vue etc.): envie por formulário direto ao servidor;
- limitar tentativas por usuário e por IP (proteção contra teste de cartões roubados);
- considerar o antifraude (
idVisitante) e o 3DS.
Consulta, estorno e webhook
$transacao = $safe2pay->transacoes()->buscar($idTransacao); // ->paga(), ->status, ->valorCentavos $lista = $safe2pay->transacoes()->buscarPorReferencia('pedido-123'); $safe2pay->cartao()->estornar($idTransacao); // total $safe2pay->cartao()->estornar($idTransacao, valorCentavos: 1000); // parcial $safe2pay->pix()->estornar($idTransacao); // assíncrono: status "Em devolução" (19)
O webhook transacional manda no corpo a chave secreta da conta (SecretKey, painel: Configurações >
Integração). Valide e, por segurança, confirme o pagamento consultando a transação:
use MarceloFecchio\Safe2Pay\Servicos\Webhooks; $corpo = json_decode(file_get_contents('php://input'), true); $notificacao = $safe2pay->webhooks()->validar($corpo, getenv('SAFE2PAY_SECRET_KEY')); // ErroWebhook se não confere guardar(Webhooks::semChaveSecreta($corpo)); // nunca guarde a chave $transacao = $safe2pay->transacoes()->buscar($notificacao->idTransacao);
Responda 2xx rápido; a Safe2Pay reenvia a cada 5 horas, até 5 vezes.
Status
Enums\StatusTransacao: Pendente (1), Processamento (2), Autorizado (3), EmDisputa (5), Estornado (6),
Baixado (7), Recusado (8), Chargeback (13), PreAutorizado (14), Abortado (18), EmDevolucao (19), entre
outros. Atalhos: pago(), semPagamento(), devolvido() e rotulo() (texto em português).
Exceções
Todas implementam Excecoes\Safe2PayException:
| Exceção | Quando |
|---|---|
ErroValidacao |
dado inválido antes de chamar a API (->campo) |
ErroConfiguracao |
configuração inválida |
ErroAutenticacao |
chave de API recusada (HTTP 401/403) |
ErroApi |
erro devolvido pela API (->codigoErro, ->statusHttp, ->idRequisicao, ->temporario()) |
ErroConexao |
falha de rede ou timeout |
ErroWebhook |
notificação sem a chave secreta certa |
Testes
composer install && vendor/bin/pest
Nenhum teste chama a API real: o tests/ClienteFalso.php responde no lugar do cliente HTTP.
Licença
MIT.