Search by

marcelofecchio / safe2pay-api

marcelofecchio

Componente PHP enxuto para a API da Safe2Pay: Pix, cartão de crédito (à vista ou parcelado), consulta, estorno e webhook.

Package info

github.com/marcelofecchio/safe2pay-api

pkg:composer/marcelofecchio/safe2pay-api

Statistics

Installs: 3

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.0 2026-10-14 22:10 UTC

This package is auto-updated.

Last update: 2026-10-14 22:12:34 UTC


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) devolve list<Parcela>.

Token e pré-autorização

  • $safe2pay->cartao()->tokenizar($cartao) devolve um CartaoTokenizado; passe $token->token (string) no lugar de DadosCartao para cobrar de novo sem pedir o cartão.
  • preAutorizacao: true só reserva o valor (status Pré-autorizado).
  • Antifraude e 3DS opcionais: idVisitante (o VisitorID do script de antifraude da Safe2Pay) e autenticacao3ds (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.