Skip to content

Commit

Permalink
Melhorias no Parser, Payload e criação da classe Reader para extrair …
Browse files Browse the repository at this point in the history
…dados de um código Pix
  • Loading branch information
caiquearaujo committed Feb 11, 2021
1 parent 746d8d2 commit 7c5787d
Show file tree
Hide file tree
Showing 11 changed files with 901 additions and 111 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 1.1.0

* Essa é a primeira grande mudança da biblioteca. Lapidamos melhor alguns métodos da classe `Parser`, adicionamos modificadores na classe `Payload` e criamos a classe `Reader` para extrair dados de códigos Pix válidos.

## 1.0.4

* Correção de bugs.
Expand Down
111 changes: 94 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,36 @@ O **Pix** é o mais novo método de pagamento eletrônico criado pelo **Banco Ce

Essa biblioteca foi criada para ser utilizada principalmente com o plugin de **Woocommerce** [Pix por Piggly](https://wordpress.org/plugins/pix-por-piggly/). Mas, pode ser utilizada em qualquer sistema onde seja necessário a criação de payloads, códigos e QRCodes Pix.

Se você apreciar a função desta biblioteca e quiser apoiar este trabalho, sinta-se livre para fazer qualquer doação para a chave aleatória Pix `aae2196f-5f93-46e4-89e6-73bf4138427b` ❤.
> Confira também nossa micro interface pix em [piggly/php-pix-app](https://github.com/piggly-dev/php-pix-app)
> Se você apreciar a função desta biblioteca e quiser apoiar este trabalho, sinta-se livre para fazer qualquer doação para a chave aleatória Pix `aae2196f-5f93-46e4-89e6-73bf4138427b` ❤.
## Instalação

Essa biblioteca pode ser instalada via **Composer** com `composer require piggly/php-pix`;

### Atualização das versões 1.0.* para 1.1.0

Nenhum método foi alterado ou removido. As mesmas funções foram mantiadas, apenas a lógica interna de determinados métodos foram melhoradas e novas classes e recursos foram acoplados. A migração pode ser realizada tranquilamente e sem problemas. Algumas coisas que mudaram:

* Se você utiliza os métodos `validate*()` do `Parser` para validar individualmente os tipos das chaves Pix, esses métodos não retornam mais uma `Exception`, mas um `boolean`. Somente o método `validate()` continua retornando uma `Exception`. Confira a mudança da código abaixo:

```php
// !! ANTES
Parser::validateDocument($cpf);
// -> trazia uma excessão e interrompia a reprodução do código.

// !! AGORA
$valid = Parser::validateDocument($cpf);
// -> você precisa tratar excessões manualmente
if ( !$valid )
{ throw new Exception('A chave de CPF/CNPJ não é válida.'); }

// !! O MÉTODO VALIDATE() AINDA TRAZ EXCESSÕES
Parser::validate($pixKeyType,$pixKey);
// -> irá interromper o código se a chave não for válida.
```

## Como o Pix funciona?

De acordo com o [Manual do BR Code](https://www.bcb.gov.br/content/estabilidadefinanceira/SiteAssets/Manual%20do%20BR%20Code.pdf) e todas as [Especificações Técnicas](https://www.bcb.gov.br/content/estabilidadefinanceira/forumpireunioes/Anexo%20I%20-%20QRCodes%20-%20Especifica%C3%A7%C3%A3o%20-%20vers%C3%A3o%201-1.pdf), o Pix implmentado pelo Banco Central do Brasil adota todas as proposta do padrão EMV®1. As principais funções que essa biblioteca executa, são:
Expand All @@ -21,11 +45,11 @@ De acordo com o [Manual do BR Code](https://www.bcb.gov.br/content/estabilidadef

### Padrão EMV®1

Por padrão, o **BR Code** utiliza apenas caracteres alfanuméricos, identificado pelo seguinte regex `[A-Za-z0-9\$\%\*\+\-\.\/\ ]`. A estrutura do código EMV®1 é composta por três conjuntos de caracteres:
Por padrão, o **BR Code** utiliza apenas caracteres alfanuméricos, identificado pelo seguinte regex `[A-Za-z0-9\$\%\*\+\-\.\/\ \@]`. A estrutura do código EMV®1 é composta por três conjuntos de caracteres:

1. ID `[\d]{2}`;
2. Tamanho em caracteres do conteúdo `[\d]{2}`;
3. Conteúdo `[A-Za-z0-9\$\%\*\+\-\.\/\ ]`.
3. Conteúdo `[A-Za-z0-9\$\%\*\+\-\.\/\ \@]`.

#### Exemplos

Expand All @@ -39,46 +63,85 @@ O código `000200`, representa:

Cada campo **EMV®1** contém suas especificações, entre elas o tamanho do campo permitido, caracteres permitidos e afins. Além das chaves terem os tipos pré-definidos como: Chave Aleatória, CPF/CNPJ, E-mail e Telefone. Todas as validações são feitas por essa biblioteca:

* Conteúdo do campo como `[A-Za-z0-9\$\%\*\+\-\.\/\ ]`;
* Conteúdo do campo como `[A-Za-z0-9\$\%\*\+\-\.\/\ \@]`;
* Chave aleatória no formato `uuid`;
* Chave de CPF/CNPJ com um valor válido e apenas `numérico`;
* Chave de E-mail com um valor válido e substituindo `@` por ` ` espaço;
* Chave de E-mail com um valor válido;
* Chave de Telefone com um valor válido e apenas `numérico`.

> Alguns bancos podem ou não aceitar o caractere `@` para Chaves de E-mail. Atualizamos o plugin para ativar a substituição do `@` por espaço no e-mail `$pix->applyEmailWhitespace()` sinta-se livre para fazer os testes com a sua chave pix.
### Classe `Parser`

A classe `Parser` apresenta todos os métodos como `static` e segue o seguinte formato:

* Métodos com `validate` validam se o valor da chave é compatível com o formato esperado por seu tipo, retornando `Exception` quando inválido;
* Métodos com `validate` validam se o valor da chave é compatível com o formato esperado por seu tipo, retornando `true` quando verdadeiro e `false` quando inválido;
* Métodos com `parse` apenas tratam os campos retirando todos os caracteres inválidos para serem utilizando, também seguindo o tipo da chave;
* O método `validate()` que recebe a chave e o tipo da chave retornando uma `Exception` quando a chave/tipo forem incompatíveis ou inválidos;
* O método `getKeyType()` traduz a chave recebida para um dos tipos correspondentes. Trará uma `Exception` se nenhum tipo válido for identificado;
* O método `getAlias()` retorna a `label` conforme o tipo de chave. Por exemplo, ao receber `Parser::KEY_TYPE_RANDOM` retorna `Chave Aleatória`.

### Classe `Payload`

A classe `Payload` é responsável por montar o payload do Pix e segute o seguinte formato:

* O método `applyEmailWhitespace()` determina que o `@` deve ser substituido por um espaço;
* O método `applyValidCharacters()` determina que acentos e caracteres inválidos como `[\!\.\,\@\#\$\%\&\*\(\)\/\*\?]` deve ser removidos/substituídos nos campos do Pix. Esse efeito é aplicado em `description`, `merchantName`, `merchantCity` e `tid`;
* * O método `applyUppercase()` transformas todos os caracteres dos campos em maiúsculo. Esse efeito é aplicado em `description`, `merchantName`, `merchantCity` e `tid`;
* Métodos com `set` determinam valores para os atributos do Pix;
* O método `getPixCode()` retorna o código Pix em formato de texto, agora pode ser enviado o `Payload::OUTPUT_SVG` ou `Payload::OUTPUT_PNG` para o formato de saída do QR Code;
* O método `getQRCode` returna uma `string` no formato PNG `data:image/png;base64` e no formato SVG uma HTML tag.
* O método `getPixCode()` retorna o código Pix em formato de texto;
* O método `getQRCode()` retorna uma `string` formatada em `base64`. A saída pode ser controlada com os valores `Payload::OUTPUT_*` para o formato de saída do QR Code e, também, os valores `Payload::ECC_*` para controlar as porcentagens de perca de dados do QR Code.

### Classe `Reader`

> Cada banco determina as informações que a chave Pix terá. Nesses casos, quando o pix é configurado com informações incorretas, podem haver uma série de problemas de compatibilidade. Por conta disso, criamos essa classe.
A classe `Reader` nasceu para ser um tradutor dos códigos pix. O objetivo é ler um código Pix gerado por determinado banco e extrair os seguintes dados: _Chave Pix, Tipo da Chave, Descrição, ID da Transação, Nome do Titular, Cidade e Valor_.

> A classe é automática, ou seja, ao criar uma nova instância ela já irá extrair todos os dados do código Pix.
* Métodos com `get` determinam valores que podem ser obtidos do código Pix;
* O método `extract()` executa novamente a extração dos dados de um outro código Pix, por exemplo.

> Você pode utilizar esses dados para, a partir de uma chave matriz, gerar um código Pix novo com a classe `Payload`. Assim cada dado será extraído de uma chave válida e não preenchido manualmente evitando erros e incompatíbilidades.
### Os atributos do Pix

> Somente os campos `description` e `tid` continuam a conter limitações de caracteres devido a muitas incompatibilidades que estavam surgindo.
Os atributos **obrigatórios** do Pix são:

* `Pix Key` alterado pelo método `setPixKey()` com o tipo e o valor da chave Pix;
* `Merchant Name` alterado pelo método `setMerchantName()` com o nome do titular da conta como conta no banco. O tamanho máximo de caracteres foi retirado.
* `Merchant City` alterado pelo método `setMerchantCity()` com a cidade do titular da conta como conta no banco. O tamanho máximo de caracteres foi retiado.
* `Pix Key` alterado pelo método `setPixKey()` com o tipo `Parser::KEY_TYPE_*` e o valor da chave Pix;
* `Merchant Name` alterado pelo método `setMerchantName()` com o nome do titular da conta como consta na instituição bancária.
* `Merchant City` alterado pelo método `setMerchantCity()` com a cidade da agência da conta como consta na instituição bancária.

Os atributos **opcionais** do Pix são:

* `Point of Initiation Method` alterado pelo método `setAsReusable()` sendo `true` como código Pix reutilizável e `false` como código Pix utilizável apenas uma vez.
* `Point of Initiation Method` alterado pelo método `setAsReusable()` sendo `true` como código Pix reutilizável e `false` como código Pix utilizável apenas uma vez. Para utilizar `true` deve-se haver um **PSP** autorizado para controlar os **QR Code Dinâmicos**. Ainda não implementamos conexões com **APIs** de **PSPs**.
* `Merchant Account Information . Label` alterado pelo método `setDescription()` com a descrição do pagamento. Tamanho máximo de `36 caracteres`.
* `Transaction Amount` alterado pelo método `setAmount()` com o valor da transação em `float`. Tamanho máximo de `13 caracteres`.
* `Additional Data Field . Reference Label` alterado pelo método `setTid()` com o ID da transação. Tamanho máximo de `25 caracteres`.

## Incompatibilidade de Chaves

O **Pix** ainda é muito recente e, apenas das padronizações do **Banco Central do Brasil**, muitos bancos criaram algumas variações e definiram como aceitam determinadas chaves. A recomendação principal é: **utilize chaves aleatórias**.

As chaves aleatórias seguem o padrão universal `uuid`, então, não tem o que cada banco inventar aqui. Elas precisam ser aceita no formato `v4`. Não detectamos qualquer problema de incompatibilidade com esses tipos de chaves.

Algumas chaves que encontramos incompatibilidades para determinados bancos:

* E-mail: alguns bancos aceitam `@`, outros aceitam espaço e outros aceitam ambos;
* Telefone: alguns bancos aceitam `+55`, outros ignoram e outros aceitam com e sem `+55`;

### Divergências entre Pix Copia & Cola e QR Codes

Há alguns relatos que alguns bancos leem o **QR Code**, mas não leem o **Pix Copia & Cola**. Este não é um problema da biblioteca, o código Pix de ambos são o mesmo! Caso esteja curioso, abra um leitor de QR Code e leia o código é examente o mesmo que o **Pix Copia & Cola**.

Nesse caso, tente utilizar as funções corretivas como `applyEmailWhitespace()`, `applyValidCharacters()` e `applyUppercase()`. Alguns bancos podem ter leituras diferentes e, talvez, existam caracteres inválidos para a leitura do **Pix Copia & Cola**.

## Como utilizar?

Em [samples/pix.php](samples/pix.php) você encontra um exemplo de implementação. Esta biblioteca é bem simples de utilizar e tudo que você precisa fazer é solicitar ao usuário ou ter os seguintes dados para gerar o Pix:
Em [samples/payload.php](samples/payload.php) e [samples/reader.php](samples/reader.php) você encontra um exemplo de implementação. Esta biblioteca é bem simples de utilizar e tudo que você precisa fazer é solicitar ao usuário ou ter os seguintes dados para gerar o Pix:

Obrigatórios:

Expand All @@ -94,7 +157,7 @@ Opcionais:
* `$description = 'Pagamento 01';`
* `$reusable = false;`

Depois crie o objeto `Parser` e utilize os métodos `getPixCode()` ou `getQRCode()`, conforme as suas necessidades.
Depois crie o objeto `Payload` e utilize os métodos `getPixCode()` ou `getQRCode()`, conforme as suas necessidades. Você também pode criar o objeto `Reader` para extrair os dados de uma chave pix válida.

## Testes realizados

Expand All @@ -111,15 +174,29 @@ O código Pix gerado por essa biblioteca, incluindo a função **QR Code** e **P
* PagPank;
* Santander.

Como o código utiliza o padrão do Pix é possível que funcione em todos os bancos. Mas, caso encontre problemas ou dificuldades hesite em abrir uma [Issue](https://github.com/piggly-dev/php-pix/issues) ou enviar um e-mail para [[email protected]](mailto:[email protected]).
Como o código utiliza o padrão do Pix é possível que funcione em todos os bancos. Alguns bancos ainda estão resilientes em utilizar os padrões determinados. Então, caso encontre problemas ou dificuldades não hesite em abrir uma [Issue](https://github.com/piggly-dev/php-pix/issues) ou enviar um e-mail para [[email protected]](mailto:[email protected]).

Ao enviar um e-mail ou abrir uma issue, certifique-se de informar:

* Versão da Biblioteca: 1.1.0;
* Banco Emitente: NuBank;
* Banco Pagador: Inter;
* Tipo de Erro: O **QR Code** é inválido;
* Chave Pix Gerada: `00020101021126740014br.gov.bcb.pix0136aae2196f-5f93-46e4-89e6-73bf4138427b0212Pagamento 0152040000053039865406109.905802BR5913STUDIO PIGGLY6007Uberaba62130509034593-09630444C9`;

## Futuras Implementações

Queremos possibilitar o suporte para Pix Dinâmicos, esperamos em breve colocar um suporte as APIs para fazer esses tipos de transações. Por enquanto, a comunicação entre bancos e provedores de pagamento está muito complicada. Provavelmente, apenas extenderemos a class `Payload` para `DynamicPayload` permitindo a inclusão dos novos campos e criaremos interfaces e classes abstratas para cada um configurar para a API do seu PSP.

Por enquanto, na classe `Reader` extraímos apenas as informações básicas e essenciais. Em breve, tornaremos o `Payload` ainda mais flexível e permitiremos que o `Reader` leia ainda mais dados.

## Changelog

Veja o arquivo [CHANGELOG](CHANGELOG.md) para informações sobre todas as mudanças no código.

## Testes de Código

Essa biblioteca utiliza o [PHPUnit](https://phpunit.de/).
Essa biblioteca utiliza o [PHPUnit](https://phpunit.de/). Realizamos testes com todas as principais classes dessa aplicação.

```
vendor/bin/phpunit
Expand All @@ -136,7 +213,7 @@ Se você descobrir qualquer issue relacionada a segurança, por favor, envie um
## Créditos

- [Caique Araujo](https://github.com/caiquearaujo)
- [All Contributors](../../contributors)
- [Todos os colaboradores](../../contributors)

## Apoie o projeto

Expand Down
6 changes: 4 additions & 2 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions samples/pix.php → samples/payload.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
// Create the pix payload
$pix =
(new Payload())
// ->applyValidCharacters()
// ->applyUppercase()
// ->applyEmailWhitespace()
->setPixKey($keyType, $keyValue)
->setMerchantName($merchantName)
->setMerchantCity($merchantCity)
Expand Down
42 changes: 42 additions & 0 deletions samples/reader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

use Piggly\Pix\Parser;
use Piggly\Pix\Reader;
use Piggly\Pix\Payload;

// Sample
$pixCode = '00020126770014BR.GOV.BCB.PIX0136aae2196f-5f93-46e4-89e6-73bf4138427b0215Descrição Teste52040000530398654041.005802BR5922Caique Monteiro Araujo6009SAO PAULO61080540900062160512NUR1pycKbhb063046BF7';
$reader = new Reader($pixCode);

// User input
// -> Required
$keyValue = $reader->getPixKey();
$keyType = Parser::getKeyType($keyValue);
$merchantName = $reader->getMerchantName();
$merchantCity = $reader->getMerchantCity();

// -> Optional
$amount = 109.90; // Payment amount as float
$tid = '034593-09'; // Transaction id
$description = 'Pagamento 01'; // Any type of description, characters allowed
$reusable = false;

// Create the pix payload
$pix =
(new Payload())
// ->applyValidCharacters()
// ->applyUppercase()
// ->applyEmailWhitespace()
->setPixKey($keyType, $keyValue)
->setMerchantName($merchantName)
->setMerchantCity($merchantCity)
->setAmount($amount)
->setTid($tid)
->setDescription($description)
->setAsReusable($reusable);

// Prints: pix code
echo $pix->getPixCode();

// Prints: <img style="margin:12px auto" src="{{base64}}" alt="QR Code de Pagamento" />
echo '<img style="margin:12px auto" src="'.$pix->getQRCode().'" alt="QR Code de Pagamento" />';
Loading

0 comments on commit 7c5787d

Please sign in to comment.