Created
March 15, 2020 21:12
-
-
Save bonomali/9912785c3ae8445419c0e430e9294689 to your computer and use it in GitHub Desktop.
Integração API Boletos - PHP
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace safra\src; | |
use Crypt_RSA; | |
use Exception; | |
use DateTime; | |
use GuzzleHttp\json_encode; | |
include("Crypt/RSA.php"); | |
include("random/lib/random.php"); | |
class Safra | |
{ | |
private $privateKeyContent; | |
private $uniqueIdSession; | |
private $kidDecoded; | |
private $uniqueIdMessage; | |
private $token; | |
private $ivBase64; | |
private $blocksize = 32; | |
//CONSTRUTOR | |
public function __construct($environment) | |
{ | |
//constroi o ambiente | |
$this->environment = ''; | |
if ($environment == 'H') { | |
$this->environment = '-hml'; | |
} | |
//url para troca de chaves | |
$this->psd001 = 'https://cobranca' . $this->environment . '.safranegocios.com.br/psd/psd001'; | |
// url Autenticação do cliente com disponibilização de token (JWT) e identificador de sessão de autenticação | |
$this->psd005 = 'https://cobranca' . $this->environment . '.safranegocios.com.br/psd/psd005'; | |
//url para renovação de token | |
$this->psd007 = 'https://cobranca' . $this->environment . '.safranegocios.com.br/psd/psd007'; | |
//url Inclusão de boletos | |
$this->psd050 = 'https://cobranca' . $this->environment . '.safranegocios.com.br/psd/psd050'; | |
//url - consulta de boletos | |
$this->psd052 = 'https://cobranca' . $this->environment . '.safranegocios.com.br/psd/psd052'; | |
$this->startServices(); | |
} | |
private function startServices() | |
{ | |
$keyHandshake = $this->executeHandshake(); | |
$keyHandshake1 = $this->executeHandshake1($keyHandshake); | |
$keyHandshake2 = $this->executeHandshake2($keyHandshake1); | |
$keyAuthorization = $this->executeAuthorization($keyHandshake2); | |
$this->executeGenerateBoleto($keyAuthorization); | |
} | |
//EXECUTE | |
private function executeHandshake() | |
{ | |
$publicKeyContent = "-----BEGIN PUBLIC KEY-----\r\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmTZZN3ghnI8D10/IXO8X\r\nY37MU5F3EfHNUBOPHgm9ANVbIkqDd+2sGIse22R4dwms+a968oEjTkq5rTT49DU2\r\nEeD3+5q2ZNGmyKpUBUn4g7xCJXU13QaiEzKrP7Xjh3F/8e1+6SpMzoa68sONbvWF\r\n289rXU272bxW+kno0HN0lL9zQTJcTmXXaBUDQVOUJNcwMrLpUjcW/iUXJJqUP+R8\r\nM6DvK3bRJD3koBRIzBgv+xS165KNE+5pEy68iEGDyQiOkL5XDKWhLhDJiPbsb7xM\r\nYgNASF+/dQN2KOPmlL55lvyygkxxUaFMNZhxM1X/1q7R1q7m2Ex6kJ7uPhuD90BC\r\nHwIDAQAB\r\n-----END PUBLIC KEY-----"; | |
$this->privateKeyContent = "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAmTZZN3ghnI8D10/IXO8XY37MU5F3EfHNUBOPHgm9ANVbIkqD\nd+2sGIse22R4dwms+a968oEjTkq5rTT49DU2EeD3+5q2ZNGmyKpUBUn4g7xCJXU1\n3QaiEzKrP7Xjh3F/8e1+6SpMzoa68sONbvWF289rXU272bxW+kno0HN0lL9zQTJc\nTmXXaBUDQVOUJNcwMrLpUjcW/iUXJJqUP+R8M6DvK3bRJD3koBRIzBgv+xS165KN\nE+5pEy68iEGDyQiOkL5XDKWhLhDJiPbsb7xMYgNASF+/dQN2KOPmlL55lvyygkxx\nUaFMNZhxM1X/1q7R1q7m2Ex6kJ7uPhuD90BCHwIDAQABAoIBAQCAhwk88TgxIAB2\n5WUOITybqMk76bHbid9Up7PN3RmC03kKQTwcpXNTe5G9NoGgSqujIkSfnxnBGgq9\ndMzMAL+FLBacKWONHBswB0NI1I735DwRNEylSGU6ea0TW0CuZNaSW0u1ylh4gKnf\n8ZB6XtmWVmCY+xb1R3wnp0z5YYGRLeXhW6GNaqCn1UGj2/lTFkFuUbgcTTDDHZ6/\nP4vl5eURFsjAw+IsX9timeqR/9i2uPcePuZevARYvYPYtqmuPjfXYFRyO9DIbDQz\n1IsRpVhFhCGOXfybi6Cu0icZWwaJE58clfPtCaod1b9PGzDgE8l09D32pl55nHyu\nsfHppDtRAoGBAMqp7KI0N2cglheKYea2PUUgOUCsUXIlAJS765rldlpwEYa9QG00\nV3vYVXD0Xf/5NuCpxQNuMxuNcD+IbpOl7/hGpbHgaAplvXQW1LKsQehoVXMR30wE\nJ6UjTsN5h9a6dWMugQ0CTlfO0a1G4j2NS/qt3Ktm7JqvzGimyKi+FrjjAoGBAMGI\numADt9InoxW6llAv6yuPjODg8+8k+Z/hQ7QkmmQmgA+nTm6D6JIscFvL3sFxYTNK\nzuwkNQtSy1i3LB2gQF77hfNXi0DONOWGNWpIUG1nugF2/F8+WEf9rnC/4c5l/Cgq\nfLleVpTRKqr63bhhmKlRfd9YIp5v6Mcxw7TKjaKVAoGBALmM+HXN/wexoxnsfhRA\nuDoo9aJL4snyLOUAZQdNnZ3Ry4okje8uJAbkm92uLr4lC2SIqzOn2q0YQzQ4Ep2m\nKN6UuDHytu8GFX5LRPRNmI5TsCCJtXsgtreQaxW3mHI2BzIGqushOF9WoUzXgF5A\nltbuBgL8WhaKbElIoqE0YRNnAoGAO1dwRkZ9k7vBnsUCl/3Jbybp/H4dbcN5oxNQ\nTik3CeRgOeyD+RELO97SaOM/HzNjqXWTswZ7GUi+vyoTJdZgRn97GB1Mvoc74vDe\n6gLYXmOCvjY8tfuI2DIpzbqYQ1A0HCDC85fAlor4OUsItyN9ytUI8nW0z73is0lI\nB+Rfv20CgYEAwGAtvMJG1380dd97g/Tzlv0xWUnITLyrHYd6DayaFTxym5KXC+Wi\n29CZ7L/l4ycbn7gBgLylunylnyvc2/g52OIEDuQj0D5uVujvTNqiJQavINZElKeV\nAArF91s/yxGrxffqe1TJqHeqan7EEQq/7fU2z0v9h4Ip1HQUmFtCQ2Q=\n-----END RSA PRIVATE KEY-----"; | |
$this->uniqueIdSession = self::generateUuid(''); | |
$this->uniqueIdMessage = self::generateUuid(''); | |
$sessionSignature = $this->generateSignatureFromCertificate($this->privateKeyContent, $this->uniqueIdSession); | |
$body = json_encode([ | |
'jwk' => base64_encode($sessionSignature) . '.' . | |
base64_encode('RSA') . '.' . | |
base64_encode('enc') . '.' . | |
base64_encode('P-256') . '.' . | |
base64_encode($publicKeyContent), | |
], JSON_UNESCAPED_SLASHES); | |
$header = [ | |
'Content-Type: application/json', | |
'accept-language: pt-BR', | |
'amc-aplicacao: PSD', | |
'amc-session-id: ' . $this->uniqueIdSession, | |
'amc-message-id: ' . $this->uniqueIdMessage, | |
]; | |
$options1 = array( | |
CURLOPT_URL => $this->psd001, | |
CURLOPT_RETURNTRANSFER => true, | |
CURLOPT_CUSTOMREQUEST => 'POST', | |
CURLOPT_POSTFIELDS => $body, | |
CURLOPT_HTTPHEADER => $header, | |
CURLOPT_SSL_VERIFYHOST => 0, | |
CURLOPT_SSL_VERIFYPEER => 0, | |
CURLOPT_SSLVERSION => 6, | |
CURLOPT_CAINFO => realpath("cert/ca-safraSafra.cer"), | |
CURLOPT_CAPATH => realpath("cert/safr.combo.pem"), | |
CURLOPT_SSLKEY => realpath("cert/safr.key"), | |
CURLOPT_PORT => 443, | |
); | |
$curl = curl_init(); | |
curl_setopt_array($curl, $options1); | |
$response = curl_exec($curl); | |
$error = curl_error($curl); | |
if ($error) { | |
new Exception('Erro ao executar handshake 2 com safra'); | |
} | |
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); | |
echo 'http code <b>' . $httpCode . '</b> handshake com safra <br>'; | |
return json_decode($response); | |
} | |
//HANDSHAKE 1 | |
private function executeHandshake1($key) | |
{ | |
//PASSO 8 | |
$serverPubKey = base64_decode(explode('.', $key->jwk)[3]); | |
$rsa = new Crypt_RSA(); | |
$rsa->loadKey($serverPubKey); | |
$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1); | |
$rsa->setHash('sha256'); | |
$sessionIdEncryptedWithPublicSafraKey = $rsa->encrypt($this->uniqueIdSession); | |
//Passo 9 | |
$sessionIdSignedWithPublicSafraKey = $this->generateSignatureFromCertificate($this->privateKeyContent, $this->uniqueIdSession); | |
$body = json_encode([ | |
'parametros' => base64_encode($sessionIdEncryptedWithPublicSafraKey) . '.' . base64_encode($sessionIdSignedWithPublicSafraKey), | |
], JSON_UNESCAPED_SLASHES); | |
$this->uniqueIdMessage = self::generateUuid(''); | |
$header = [ | |
'Content-Length: ' . \strlen($body), | |
'Content-Type: application/json', | |
'accept-language: pt-BR', | |
'amc-aplicacao: PSD', | |
'amc-session-id: ' . $this->uniqueIdSession, | |
'amc-message-id: ' . $this->uniqueIdMessage, | |
]; | |
$options2 = array( | |
CURLOPT_URL => $this->psd001, | |
CURLOPT_RETURNTRANSFER => true, | |
CURLOPT_CUSTOMREQUEST => 'POST', | |
CURLOPT_POSTFIELDS => $body, | |
CURLOPT_HTTPHEADER => $header, | |
CURLOPT_SSL_VERIFYHOST => 0, | |
CURLOPT_SSL_VERIFYPEER => 0, | |
CURLOPT_USERAGENT => false, | |
CURLOPT_PORT => 443, | |
CURLOPT_CAINFO => realpath("cert/ca-safra.cer"), | |
CURLOPT_SSLCERT => realpath("cert/safr.combo.pem"), | |
CURLOPT_SSLKEY => realpath("cert/safr.key") | |
); | |
$curl = curl_init(); | |
curl_setopt_array($curl, $options2); | |
$response = curl_exec($curl); | |
$error = curl_error($curl); | |
if ($error) { | |
new Exception('Erro ao executar handshake 2 com safra'); | |
} | |
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); | |
echo 'http code <b>' . $httpCode . '</b> handshake 1 com safra <br>'; | |
return json_decode($response); | |
} | |
//HANDSHAKE 2 | |
private function executeHandshake2($key) | |
{ | |
$jwkEncrypted = base64_decode(explode('.', $key)[0]); | |
$rsa = new Crypt_RSA(); | |
$rsa->loadKey($this->privateKeyContent); | |
$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1); | |
$rsa->setHash('sha256'); | |
$jwkDecrypted = json_decode($rsa->decrypt($jwkEncrypted)); | |
$this->kidDecoded = base64_decode($jwkDecrypted->jwk->kid); | |
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-ctr')); | |
$iv = substr(bin2hex($iv), 0, 16); | |
$this->ivBase64 = base64_encode($iv); | |
$data = json_encode([ | |
'idEstrategia' => 'autenticar/psd', | |
'versaoAplicacao' => 1, | |
'credenciais' => [], | |
'cliente' => 'BANCO SAFRA SA', | |
'parceiro' => 'BANCO SAFRA SA', | |
'userId' => 'BANCO SA' | |
], JSON_FORCE_OBJECT); | |
$cabecalho = "{ alg: 'RSA-AEP', enc: 'A256CTR' }"; | |
$hash128 = openssl_encrypt($data, 'aes-256-ctr', $this->kidDecoded, STREAM_CRYPTO_METHOD_TLS_SERVER, $iv); | |
$payload = json_encode([ | |
'data' => '.' . base64_encode($cabecalho) . '.' . $this->ivBase64 . '.' . $hash128 . '.', | |
], JSON_UNESCAPED_SLASHES); | |
$this->uniqueIdMessage = self::generateUuid(''); | |
$header = [ | |
'Content-Type: application/json', | |
'accept-language: pt-BR', | |
'amc-aplicacao: PSD', | |
'amc-session-id: ' . $this->uniqueIdSession, | |
'amc-message-id: ' . $this->uniqueIdMessage | |
]; | |
$options3 = [ | |
CURLOPT_URL => $this->psd005, | |
CURLOPT_RETURNTRANSFER => true, | |
CURLOPT_POSTFIELDS => $payload, | |
CURLOPT_HTTPHEADER => $header, | |
CURLOPT_SSL_VERIFYHOST => 0, | |
CURLOPT_SSL_VERIFYPEER => 0, | |
CURLOPT_USERAGENT => false, | |
CURLOPT_PORT => 443, | |
CURLOPT_CAINFO => realpath("cert/ca-safra.cer"), | |
CURLOPT_SSLCERT => realpath("cert/safr.combo.pem"), | |
CURLOPT_SSLKEY => realpath("cert/safr.key") | |
]; | |
$curl = curl_init(); | |
curl_setopt_array($curl, $options3); | |
$response = curl_exec($curl); | |
echo base64_decode($response); | |
$error = curl_error($curl); | |
if ($error) { | |
new Exception('Erro ao executar handshake 2 com safra'); | |
} | |
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); | |
echo 'http code <b>' . $httpCode . '</b> handshake 2 com safra <br>'; | |
return json_decode($response); | |
} | |
//GERAR AUTHORIZATION | |
private function executeAuthorization($key) | |
{ | |
$iv = base64_decode(explode('.', $key)[2]); | |
$dados = base64_decode(explode('.', $key)[3]); | |
//var_dump($dados); | |
$this->ivBase64 = base64_encode($iv); | |
$this->token = json_decode(openssl_decrypt($dados, 'aes-256-ctr', $this->kidDecoded, OPENSSL_CIPHER_AES_256_CBC, $iv)); | |
$cabecalho = "{ alg: 'RSA-AEP', enc: 'A256CTR' }"; | |
$data = [ | |
'agencia' => '00000', | |
'conta' => '0000000', | |
'documento' => [ | |
'nossonumero' => '8756025', | |
'seunumero' => '43243', | |
'diasdevolucao' => "30", | |
'especie' => '09', | |
'datavencimento' => '20191124', | |
'valor' => '510', | |
'codigomoeda' => '00', | |
'qtddiasprotesto' => '', | |
'identificacaoaceite' => 'N', | |
'desconto' => [ | |
'data' => '20191117', | |
'valor' => '102', | |
], | |
'multa' => [ | |
'juros' => '0.000333', | |
'data' => '', | |
'taxa' => '0.01', | |
], | |
'campolivre' => '', | |
'taxafidc' => '', | |
'danfe' => '', | |
'pagador' => [ | |
'nome' => 'Teste', | |
'tipopessoa' => '1', | |
'numerodocumento' => '78556802257', | |
'email' => '', | |
'endereco' => [ | |
'nome' => 'Rua Teste, 500', | |
'bairro' => 'Vila Nova', | |
'cidade' => 'SAO PAULO', | |
'uf' => 'SP', | |
'cep' => '00000000', | |
], | |
], | |
'beneficiario' => [ | |
'nome' => 'NOME BENEFICIARIO', | |
'tipopessoa' => '2', | |
'numerodocumento' => '00000000000000', | |
'endereco' => [ | |
'nome' => 'Rua Teste, 123', | |
'bairro' => 'Bela Vista', | |
'cidade' => 'Sao Paulo', | |
'uf' => 'SP', | |
'cep' => '00000000', | |
], | |
], | |
'mensagens' => [ | |
0 => [ | |
'posicao' => '1', | |
'descricao' => 'Multa após o vencimento: R$0,05.', | |
], | |
], | |
], | |
]; | |
$hash128Data = openssl_encrypt(json_encode($data, JSON_FORCE_OBJECT), 'aes-256-ctr', $this->kidDecoded, STREAM_CRYPTO_METHOD_TLS_SERVER, $iv); | |
$body = json_encode([ | |
'data' => '.' . base64_encode($cabecalho) . '.' . $this->ivBase64 . '.' . $hash128Data . '.', | |
], JSON_UNESCAPED_UNICODE); | |
$header = [ | |
'Content-Type: application/json', | |
'accept-language: pt-BR', | |
'amc-aplicacao: PSD', | |
'amc-session-id: ' . $this->uniqueIdSession, | |
'amc-message-id: ' . self::generateUuid(''), | |
'amc-work-id: ' . self::generateUuid(''), | |
'authorization: Bearer ' . $this->token->token, | |
]; | |
$options4 = array( | |
CURLOPT_URL => $this->psd050, | |
CURLOPT_RETURNTRANSFER => true, | |
CURLOPT_CUSTOMREQUEST => 'POST', | |
CURLOPT_POSTFIELDS => $body, | |
CURLOPT_HTTPHEADER => $header, | |
CURLOPT_SSL_VERIFYHOST => 0, | |
CURLOPT_SSL_VERIFYPEER => 0, | |
CURLOPT_USERAGENT => false, | |
CURLOPT_PORT => 443, | |
CURLOPT_SSLVERSION => 6, | |
CURLOPT_CAINFO => realpath("cert/ca-safra.cer"), | |
CURLOPT_CAPATH => realpath("cert/safr.combo.pem"), | |
CURLOPT_SSLKEY => realpath("cert/safr.key"), | |
); | |
$curl = curl_init(); | |
curl_setopt_array($curl, $options4); | |
$response = curl_exec($curl); | |
$error = curl_error($curl); | |
if ($error) { | |
new Exception('Erro ao executar authorization com safra'); | |
} | |
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); | |
echo 'http code <b>' . $httpCode . '</b> gerar boleto com safra <br>'; | |
return json_decode($response); | |
} | |
//GERAR BOLETO | |
public function executeGenerateBoleto($key) | |
{ | |
$iv = base64_decode(explode('.', $key)[2]); | |
$dados = base64_decode(explode('.', $key)[3]); | |
$d = json_decode(openssl_decrypt($dados, 'aes-256-ctr', $this->kidDecoded, OPENSSL_CIPHER_AES_256_CBC, $iv)); | |
$code = $d->statusProcessamento->code; | |
$message = $d->statusProcessamento->message; | |
if($code == 200) | |
{ | |
$nossoNumero = $d->documento->nossonumero; | |
$seunumero = $d->documento->seunumero; | |
$codigobarras = $d->documento->codigobarras; | |
$codesucesso = $d->statusProcessamento->code; | |
$messagesucesso = $d->statusProcessamento->message; | |
echo '{"documento":{nossonumero":' . ' "' . $nossoNumero . '" ,' . '"seunumero":' . ' "' . $seunumero . '" ,' . '"codigobarras": "' . $codigobarras . '" ,' . '{"statusProcessamento":{"code": ' . ' "' . $codesucesso . '", ' . ' "message": ' . ' "' . $messagesucesso . '" }}' ; | |
} | |
else | |
{ | |
echo '{"statusProcessamento":{"code": ' . ' "' . $code . '", ' . ' "message": ' . ' "' . $message . '" }}' ; | |
} | |
} | |
public function generateSignatureFromCertificate($certificate, $string) | |
{ | |
$rsa = new Crypt_RSA(); | |
$rsa->loadKey($certificate); | |
$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1); | |
$rsa->setHash('sha256'); | |
$returnGenerate = $rsa->sign($string); | |
return $rsa->sign($string); | |
} | |
private static function generateUuid($node) | |
{ | |
// nano second time (only micro second precision) since start of UTC | |
$time = microtime(true) * 10000000 + 0x01b21dd213814000; | |
$time = pack('H*', sprintf('%016x', $time)); | |
$sequence = random_bytes(2); | |
$sequence[0] = \chr(\ord($sequence[0]) & 0x3f | 0x80); // variant bits 10x | |
$time[0] = \chr(\ord($time[0]) & 0x0f | 0x10); // version bits 0001 | |
if (!empty($node)) { | |
// non hex string identifier | |
if (\is_string($node) && preg_match('/[^a-f0-9]/is', $node)) { | |
// base node off md5 hash for sequence | |
$node = md5($node); | |
// set multicast bit not IEEE 802 MAC | |
$node = (hexdec(substr($node, 0, 2)) | 1) . substr($node, 2, 10); | |
} | |
if (is_numeric($node)) { | |
$node = sprintf('%012x', $node); | |
} | |
if (\strlen($node) > 12) { | |
$node = substr($node, 0, 12); | |
} | |
} else { | |
// base node off random sequence | |
$node = random_bytes(6); | |
// set multicast bit not IEEE 802 MAC | |
$node[0] = \chr(\ord($node[0]) | 1); | |
$node = bin2hex($node); | |
} | |
$UUidReturn = bin2hex($time[4] . $time[5] . $time[6] . $time[7]) // time low | |
. '-' . bin2hex($time[2] . $time[3]) // time med | |
. '-' . bin2hex($time[0] . $time[1]) // time hi | |
. '-' . bin2hex($sequence) // seq | |
. '-' . $node; | |
// node | |
return $UUidReturn; | |
} | |
} | |
$safra = new Safra("H"); | |
?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment