Skip to content

Latest commit

 

History

History
232 lines (178 loc) · 8.76 KB

efi.md

File metadata and controls

232 lines (178 loc) · 8.76 KB

Efí

Duas APIs com as mesmas credenciais, e é isso que dá ao Efí quatro das cinco capacidades:

API Host Autenticação Recursos
Cobranças cobrancas.api.efipay.com.br OAuth2 charge() — boleto
Pix pix.api.efipay.com.br OAuth2 + mTLS pix(), webhook(), subscription(), pixCharge()

Clientes ficam de fora: nenhuma das duas APIs mantém cadastro de cliente.

O gateway não faz chamada de rede no construtor. Cada API tem o seu token, pedido na primeira vez que é necessário e renovado sozinho quando expira — um gateway vivo num worker de fila não passa a tomar 401.

Cobranças (boleto)

use PHPay\Efi\EfiGateway;
use PHPay\Support\{Customer, Money};

$gateway = new EfiGateway(CLIENT_ID, CLIENT_SECRET);

$cobranca = PHPay::gateway($gateway)->charge([
    'description' => 'Assinatura PHPay',
    'expire_at'   => date('Y-m-d', strtotime('+3 days')),
])
    ->setAmount(Money::reais('100,50'))
    ->setCustomer(new Customer('Mário Lucas', '12345678909'))
    ->create();

Multa e juros: as duas escalas são diferentes

Sem configurar nada, o boleto sai com 2% de multa e 0,033% de juros ao dia — é o que este gateway sempre mandou. setConfigurations() troca isso:

$gateway->charge([...])
    ->setAmount(Money::reais('100,50'))
    ->setCustomer($cliente)
    ->setConfigurations([
        'fine'              => 250,   // 2,5%
        'interest'          => 100,   // 0,1% ao dia
        'days_to_write_off' => 40,    // baixa automática, 0 a 120 dias
    ])
    ->create();

Os dois campos usam escalas diferentes, e é a própria Efí que documenta isso: fine é percentual × 100 (200 = 2%) e interest é percentual × 1000 (33 = 0,033%). Por isso os dois são inteiros — 2,5% de multa é 250, nunca 2.5. A biblioteca recusa decimal com uma mensagem que repete as duas escalas, em vez de deixar o valor errado passar.

Boleto sem encargo:

->setConfigurations(['fine' => 0, 'interest' => 0])

Array vazio não serve para isso e é recusado: ele não diz "sem encargo", e a Efí não documenta se o objeto configurations pode ser omitido.

interest também aceita a forma de objeto, com type em daily ou monthly (omitido, a Efí calcula ao dia):

->setConfigurations(['fine' => 200, 'interest' => ['value' => 330, 'type' => 'monthly']])

A escala da forma mensal a Efí não documenta, então a biblioteca não converte nada: o valor vai como você escreveu. Campo de configurations que esta versão não modela também passa adiante — ao contrário do array de configuração do gateway, aqui o array é payload da Efí, e recusar um campo que funciona quebraria quem o manda.

Boleto com mais de um item

O payload da Efí não tem valor no nível da cobrança — tem items. O setAmount() com a description é o atalho que monta um item; para mais de uma linha, use setItems():

$gateway->charge(['expire_at' => date('Y-m-d', strtotime('+3 days'))])
    ->setCustomer($cliente)
    ->setItems([
        ['name' => 'Camiseta', 'value' => 4990, 'amount' => 2],
        ['name' => 'Frete',    'value' => 1500],
    ])
    ->create();

Com itens explícitos, description e value deixam de ser exigidos: eles pertencem ao atalho, não ao gateway. value é em centavos, como no resto da Efí.

API Pix

Toda requisição da API Pix é por mTLS, inclusive a do token. Passe o certificado .p12 (ou .pem) da aplicação, que você baixa no painel do Efí:

$gateway = new EfiGateway(CLIENT_ID, CLIENT_SECRET, certificate: '/caminho/certificado.p12');

Com senha, ou vindo de uma variável de ambiente — o comum em container e serverless:

use PHPay\Http\Certificate;

new EfiGateway(CLIENT_ID, CLIENT_SECRET, certificate: new Certificate('/caminho/certificado.p12', 'senha'));

new EfiGateway(CLIENT_ID, CLIENT_SECRET, certificate: Certificate::fromBase64(getenv('EFI_CERTIFICATE_BASE64')));

O fromBase64() grava o certificado num arquivo temporário com permissão 0600, apagado quando o processo termina. A senha nunca aparece num var_dump().

O Efí entrega certificado e chave num arquivo só. Os formatos aceitos, o par .crt + .key que outros PSPs entregam e o que o Certificate confere estão em Certificado e mTLS.

Certificado de homologação só funciona com $sandbox = true, e o de produção, com false. Sem certificado, a API de Cobranças continua funcionando e a API Pix responde com uma ValidationException clara, não com um erro de TLS.

Cobrança Pix — imediata por padrão, com vencimento quando há setDueDate():

$cobranca = $gateway->pixCharge()
    ->setAmount(Money::reais('123,45'))
    ->setKey('sua-chave-pix')                       // chave da conta Efí que recebe
    ->setCustomer(new Customer('Mário Lucas', '12345678909'))
    ->setDescription('Pedido 1234')
    ->setExpiration(3600)                           // segundos
    ->create();

$qr = $gateway->pixCharge()->qrCode($cobranca['loc']['id']);
$qr['qrcode'];         // copia e cola
$qr['imagemQrcode'];   // PNG em base64

/* com vencimento: multa, juros e desconto como num boleto */
$gateway->pixCharge()
    ->setAmount(Money::reais(250))
    ->setKey('sua-chave-pix')
    ->setCustomer(new Customer('Sixtec LTDA', '12345678000199'))
    ->setDueDate('2026-12-31', validityAfterDue: 15)
    ->create();

/* devolução, total ou parcial */
$gateway->pixCharge()->refund($endToEndId, Money::reais(10));

pixCharge() é um extra do gateway concreto, como o webhookDeliveries() do Pagar.me: o charge() da facade já é o boleto, e mudar o retorno dele quebraria quem está na v2.

Esse refund() não é a capacidade SupportsRefunds, e a Efí não a declara. São três incompatibilidades, todas da API dela e não da biblioteca: o valor é obrigatório (o BACEN exige valor na devolução, enquanto a capacidade promete que valor ausente estorna integral), o endereçamento é pelo endToEndId do Pix recebido e não pela cobrança, e a devolução vive na API Pix — o charge() da Efí é boleto, que não estorna por aqui. Declarar a capacidade e depois recusar de dentro dela seria pior que não declarar.

Chaves Pix — só chaves aleatórias (EVP) são gerenciáveis pela API:

$chave = PHPay::gateway($gateway)->pix()->createKey()['chave'];

PHPay::gateway($gateway)->pix()->getAll();
PHPay::gateway($gateway)->pix()->destroy($chave);

Sem QR Code estático. A API Pix da Efí não expõe endpoint para criar um, então aqui não existe o staticQrCode() do Asaas e da Woovi. Pelo padrão do BACEN, um QR Code estático é um BR Code composto a partir da chave, de um valor opcional e de um identificador livre — quem oferece endpoint faz por conveniência, não por necessidade. O qrCode() do pixCharge(), acima, é o QR Code de uma cobrança, que é outra coisa.

Para o estático, use StaticPixCode: ele compõe o payload a partir da chave, sem rede e sem gateway.

Webhooks — um por chave Pix, endereçado pela própria chave:

PHPay::gateway($gateway)
    ->webhook(['chave' => $chave, 'webhookUrl' => 'https://loja.com/webhook/pix'])
    ->create();

O mTLS vale nos dois sentidos: por padrão o Efí só entrega para um servidor que valide o certificado dele. Se o seu não consegue (hospedagem compartilhada, balanceador que termina o TLS), skipMtlsChecking() desliga a checagem — e aí valide a origem de outro jeito, como um hmac na URL.

Pix Automático — o pagador autoriza uma vez no app do banco, e cada ciclo é debitado sem nova aprovação:

use PHPay\Efi\Enums\{AccountTypeEnum, PeriodicityEnum};

$assinaturas = PHPay::gateway($gateway)->subscription();

/* 1. o location que o QR Code de autorização aponta */
$location = $assinaturas->createLocation();

/* 2. a recorrência: o que o pagador autoriza */
$recorrencia = $assinaturas
    ->setCustomer(new Customer('Mário Lucas', '12345678909'))
    ->setContract('CONTRATO-2026-001')              // até 35 caracteres
    ->setDescription('Plano mensal')
    ->setAmount(Money::reais('49,90'))               // ou setMinimumAmount(), para valor variável
    ->setPeriodicity(PeriodicityEnum::MONTHLY, '2026-10-01')
    ->allowRetries()                                 // até 3 tentativas em 7 dias
    ->setLocation($location['id'])
    ->create();

$assinaturas->find($recorrencia['idRec'])['dadosQR'];   // copia e cola para o pagador autorizar

/* 3. a cobrança de cada ciclo */
$assinaturas
    ->setReceiver('12345-6', AccountTypeEnum::CHECKING, '0001')
    ->createCharge($recorrencia['idRec'], Money::reais('49,90'), '2026-11-05');

$assinaturas->cancel($recorrencia['idRec']);