Skip to content

Instantly share code, notes, and snippets.

@jeffersongoncalves
Created January 23, 2025 21:35
Show Gist options
  • Save jeffersongoncalves/47b6523732a36bf7710e36331f82cc7e to your computer and use it in GitHub Desktop.
Save jeffersongoncalves/47b6523732a36bf7710e36331f82cc7e to your computer and use it in GitHub Desktop.
<?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