Created
January 23, 2025 21:35
-
-
Save jeffersongoncalves/47b6523732a36bf7710e36331f82cc7e to your computer and use it in GitHub Desktop.
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 App\Support; | |
use Exception; | |
abstract class QRCodePixSupport | |
{ | |
/** | |
* @throws Exception | |
*/ | |
public static function make(float $value): string | |
{ | |
$px = []; | |
//Payload Format Indicator, Obrigatório, valor fixo: 01 | |
$px[00] = '01'; | |
//Indica arranjo específico; “00” (GUI) obrigatório e valor fixo: br.gov.bcb.pix | |
$px[26][00] = 'BR.GOV.BCB.PIX'; | |
// Descrição da transação, opcional. | |
$px[26][02] = ''; | |
//Chave do destinatário do pix, pode ser EVP, e-mail, CPF ou CNPJ. | |
$px[26][01] = ''; | |
//Merchant Category Code “0000” ou MCC ISO18245 | |
$px[52] = '0000'; | |
//Moeda, “986” = BRL: real brasileiro - ISO4217 | |
$px[53] = '986'; | |
//Valor da transação, se comentado o cliente especifica o valor da transação no próprio app. Utilizar o . como separador decimal. Máximo: 13 caracteres. | |
$px[54] = $value; | |
//“BR” – Código de país ISO3166-1 alpha 2 | |
$px[58] = 'BR'; | |
//Nome do beneficiário/recebedor. Máximo: 25 caracteres. | |
$px[59] = ''; | |
//Nome cidade onde é efetuada a transação. Máximo 15 caracteres. | |
$px[60] = ''; | |
//Identificador de transação, quando gerado automaticamente usar ***. Limite 25 caracteres. Vide nota abaixo. | |
$px[62][05] = '***'; | |
return self::generatePix($px); | |
} | |
/** | |
* @throws Exception | |
*/ | |
private static function generatePix(array $px = []): string | |
{ | |
$pix = self::makePix($px); | |
$pix .= '6304'; | |
$pix .= self::crcChecksum($pix); | |
return $pix; | |
} | |
/** | |
* @throws Exception | |
*/ | |
private static function makePix(array $px): string | |
{ | |
$return = ''; | |
foreach ($px as $k => $v) { | |
if (! is_array($v)) { | |
if ($k == 54) { | |
// Formata o campo valor com 2 digitos. | |
$v = number_format($v, 2, '.', ''); | |
} else { | |
$v = self::clearValue($v); | |
} | |
$return .= self::c2('' . $k) . self::cpm($v) . $v; | |
} else { | |
$text = self::makePix($v); | |
$return .= self::c2('' . $k) . self::cpm($text) . $text; | |
} | |
} | |
return $return; | |
} | |
private static function clearValue($text): array|string|null | |
{ | |
return preg_replace('/\W /', '', self::removeAccents($text)); | |
} | |
private static function removeAccents($text): array|string|null | |
{ | |
$search = explode( | |
',', | |
'à,á,â,ä,æ,ã,å,ā,ç,ć,č,è,é,ê,ë,ē,ė,ę,î,ï,í,ī,į,ì,ł,ñ,ń,ô,ö,ò,ó,œ,ø,ō,õ,ß,ś,š,û,ü,ù,ú,ū,ÿ,ž,ź,ż,À,Á,Â,Ä,Æ,Ã,Å,Ā,Ç,Ć,Č,È,É,Ê,Ë,Ē,Ė,Ę,Î,Ï,Í,Ī,Į,Ì,Ł,Ñ,Ń,Ô,Ö,Ò,Ó,Œ,Ø,Ō,Õ,Ś,Š,Û,Ü,Ù,Ú,Ū,Ÿ,Ž,Ź,Ż', | |
); | |
$replace = explode( | |
',', | |
'a,a,a,a,a,a,a,a,c,c,c,e,e,e,e,e,e,e,i,i,i,i,i,i,l,n,n,o,o,o,o,o,o,o,o,s,s,s,u,u,u,u,u,y,z,z,z,A,A,A,A,A,A,A,A,C,C,C,E,E,E,E,E,E,E,I,I,I,I,I,I,L,N,N,O,O,O,O,O,O,O,O,S,S,U,U,U,U,U,Y,Z,Z,Z', | |
); | |
return self::removeEmoji(str_replace($search, $replace, $text)); | |
} | |
private static function removeEmoji($text): array|string|null | |
{ | |
return preg_replace( | |
'%(?: | |
\xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3 | |
| [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15 | |
| \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16 | |
)%xs', | |
' ', | |
$text, | |
); | |
} | |
private static function c2(string $text): string | |
{ | |
return str_pad($text, 2, '0', STR_PAD_LEFT); | |
} | |
/** | |
* @throws Exception | |
*/ | |
private static function cpm(string $text): string | |
{ | |
if (strlen($text) > 99) { | |
throw new Exception("Tamanho máximo deve ser 99, inválido: $text possui " . strlen($text) . ' caracteres.'); | |
} | |
return self::c2('' . strlen($text)); | |
} | |
private static function crcChecksum(string $pix): string | |
{ | |
$crc = 0xFFFF; | |
$strlen = strlen($pix); | |
for ($c = 0; $c < $strlen; $c++) { | |
$crc ^= self::charCodeAt($pix, $c) << 8; | |
for ($i = 0; $i < 8; $i++) { | |
if ($crc & 0x8000) { | |
$crc = ($crc << 1) ^ 0x1021; | |
} else { | |
$crc = $crc << 1; | |
} | |
} | |
} | |
$hex = $crc & 0xFFFF; | |
$hex = dechex($hex); | |
$hex = strtoupper($hex); | |
return str_pad($hex, 4, '0', STR_PAD_LEFT); | |
} | |
private static function charCodeAt(string $pix, int $i): int | |
{ | |
return ord(substr($pix, $i, 1)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment