Skip to content
This repository has been archived by the owner on Jan 24, 2025. It is now read-only.

Recebimento PIX #25

Merged
merged 39 commits into from
Jun 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
b503ae2
altera .env.sample
rodrigondec May 24, 2021
f9b3e16
altera script de baixa manual
rodrigondec May 24, 2021
dc84d09
adiciona md do pix
rodrigondec May 24, 2021
bf0f696
adiciona coleção postman pix
rodrigondec May 24, 2021
c4153f4
adiciona scopo pix
rodrigondec May 31, 2021
c801a22
cria o PIX Wrapper
rodrigondec May 31, 2021
b1a3901
sobe algumas alterações
rodrigondec May 31, 2021
18261f5
altera sample
rodrigondec Jun 1, 2021
0808207
refatora algumas coisas e roda todos os exemplos de cobranças
rodrigondec Jun 1, 2021
d72bfcf
ajusta documentação
rodrigondec Jun 10, 2021
f2e69a2
ajusta endpoints
rodrigondec Jun 10, 2021
630b246
adiciona PixCodeService
rodrigondec Jun 10, 2021
45f6773
refatora algumas coisas
rodrigondec Jun 10, 2021
a650825
inicia esqueleto do CRC
rodrigondec Jun 10, 2021
f7be197
fmt
rodrigondec Jun 10, 2021
d83a4be
implementa testes
rodrigondec Jun 10, 2021
aa35573
adiciona cálculo do CRC-16-CCITT-FFFF
rodrigondec Jun 10, 2021
6c44d4e
adiciona testes
rodrigondec Jun 10, 2021
7ace955
ajusta exemplos
rodrigondec Jun 11, 2021
5fea8cf
adiciona models
rodrigondec Jun 11, 2021
4c05d56
finaliza exemplos
rodrigondec Jun 11, 2021
5af5acd
altera o readme
rodrigondec Jun 11, 2021
795bcbd
altera readme
rodrigondec Jun 11, 2021
3b3f36a
altera readme
rodrigondec Jun 11, 2021
74b32f9
altera readme
rodrigondec Jun 11, 2021
a5e0263
altera readme
rodrigondec Jun 11, 2021
5b030db
altera readme
rodrigondec Jun 11, 2021
83d7d77
altera readme
rodrigondec Jun 11, 2021
8bfdb5a
altera readme
rodrigondec Jun 11, 2021
b94f9f2
altera readme
rodrigondec Jun 11, 2021
da3613b
altera readme
rodrigondec Jun 11, 2021
9cf900a
altera readme
rodrigondec Jun 11, 2021
ba213fe
altera readme
rodrigondec Jun 11, 2021
d976bb4
altera readme
rodrigondec Jun 11, 2021
029cc45
adiciona testes
rodrigondec Jun 11, 2021
5fbe42c
sobe algumas alterações
rodrigondec Jun 11, 2021
32784f8
Update bb_wrapper/wrapper/pix_cob.py
rodrigondec Jun 14, 2021
661bf2e
Update tests/services/test_brcode.py
rodrigondec Jun 14, 2021
ff28b79
Update scripts/baixa_manual.py
rodrigondec Jun 14, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
IMOBANCO_BB_IS_SANDBOX=True
IMOBANCO_BB_BASIC_TOKEN=random
IMOBANCO_BB_GW_APP_KEY=random
IMOBANCO_BB_BASIC_TOKEN=TOKEN
IMOBANCO_BB_GW_APP_KEY=d27b67790affabd01363e17d80050d56b901a5be
IMOBANCO_BB_CONVENIO=3128557
IMOBANCO_BB_CARTEIRA=17
IMOBANCO_BB_VARIACAO_CARTEIRA=35
Expand Down
62 changes: 61 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,63 @@
Cliente não oficial feito em Python, para realizar integração com a API do Banco do Brasil.
Cliente não oficial feito em Python, para realizar integração com as API's do Banco do Brasil.

`Documentação oficial do BB <https://developers.bb.com.br/>`_

Instalando
===========

Nosso pacote está hospedado no `PyPI <https://pypi.org/project/bb-wrapper/>`_

.. code-block:: bash

pip install bb-wrapper



Configuração
==================
Para utilizar o `bb-wrapper` é necessário ter algumas constantes/variáveis. sendo elas:

.. code-block:: python

IMOBANCO_BB_IS_SANDBOX='flag True ou False para indicar utilização de sandbox ou não'
IMOBANCO_BB_BASIC_TOKEN='chave de autenticação gerada para a aplicação no site developers.bb'
IMOBANCO_BB_GW_APP_KEY='chave de desenvolvimento gerada para a aplicação no site developers.bb'


Para geração de boletos é necessário:

.. code-block:: python

IMOBANCO_BB_CONVENIO='convênio do contrato para geração de boletos'
IMOBANCO_BB_CARTEIRA='carteira do contrato para geração de boletos'
IMOBANCO_BB_VARIACAO_CARTEIRA='variação da carteira do contrato para geração de boletos
IMOBANCO_BB_AGENCIA='agência da conta berço do contrato para geração de boletos'
IMOBANCO_BB_CONTA='nº da conta berço do contrato para geração de boletos'


Recomendamos criar um arquivo `.env` contendo essas varíaveis de ambiente.

::

Podem ser criadas diretamente no terminal (não recomendado).

Podem ser criadas também diretamente no `arquivo.py` (não recomendado).

Recursos disponíveis
=====================

API's
---------------------

- ☑ API de Cobrança (geração de boletos)
- ☑ API PIX (recebimento PIX) {essa API ainda está instável e incompleta no BB}
- ☐ API Arrecadação PIX {sem previsão de implementação}
- ☐ API Pagamentos {esperando lançar no BB}

Recursos auxiliares
-------------------

- ☑ Geração de imagem b64
- ☑ Geração, validação e conversão de código de barras de boleto
- ☐ Geração, validação e conversão de código de barras de convênio {sem previsão de implementação}
- ☑ Geração de QR Code PIX
10 changes: 10 additions & 0 deletions bb_wrapper/models/perfis.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,13 @@ class PessoaComEndereco(Pessoa):
bairro: constr(max_length=30)
uf: constr(max_length=2, min_length=2)
telefone: Optional[constr(max_length=30)]


class PessoaPix(BaseModel):
cpf: constr(max_length=11, min_length=11)
nome: str


class EmpresaPix(BaseModel):
cnpj: constr(max_length=14, min_length=14)
nome: str
21 changes: 21 additions & 0 deletions bb_wrapper/models/pix_cob.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from typing import Union

from pydantic import BaseModel, confloat, conint

from .perfis import PessoaPix, EmpresaPix


class Calendario(BaseModel):
expiracao: conint(gt=60) # segundos até expiração


class Valor(BaseModel):
original: confloat(gt=0.01)


class CobrancaPix(BaseModel):
calendario: Calendario
devedor: Union[PessoaPix, EmpresaPix]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

não conhecia o Union, interessante!

valor: Valor
chave: str
solicitacaoPagador: str
1 change: 1 addition & 0 deletions bb_wrapper/services/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from .barcode import BarCodeService
from .mod import ModService # noqa: F401
from .febrabran import FebrabranService # noqa: F401
from .pixcode import PixCodeService # noqa: F401


parse_unicode_to_alphanumeric = UnicodeService().parse_unicode_to_alphanumeric
Expand Down
63 changes: 63 additions & 0 deletions bb_wrapper/services/brcode.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
from crc import CrcCalculator, Configuration


class BRCodeService:
"""
BR Code é a padronização brasileira de pagamentos via QR Code.

O BR Code implementa a padronização mundial
EMV QRCPS–MPM QRCodes for Payment Systems –Merchant Presented Mode.

References:
https://www.bcb.gov.br/content/estabilidadefinanceira/SiteAssets/Manual%20do%20BR%20Code.pdf
https://www.emvco.com/terms-of-use/?u=/wp-content/uploads/documents/EMVCo-Merchant-Presented-QR-Specification-v1-1.pdf
"""

def _get_len_zfilled(self, data, qt_zfill=2):
"""
Método para retornar o tamanho de uma informação com
um zfill de 2.

Examples:
>>> len_value = BRCodeService()._get_len_zfilled('123')
>>> print(len_value)
"03"
"""

return f"{len(data)}".zfill(qt_zfill)

def create_field_string(self, _id, value):
"""
Qualquer campo do BRCode segue o seguinte padrão:
ID + LEN VALOR + VALOR

Onde LEN VALOR é a quantidade de caracteres do valor com zfill de 2.
Ou seja, LEN VALOR de '123' é '03'
"""
return f"{_id}{self._get_len_zfilled(value)}{value}"

def crc_16_ccitt_ffff(self, data: str):
"""
Método para calcular o CRC-16-CCIT-FFFF de um valor.

Retorna os dois valores hexadecimais do resultado como string.

Configurações:
CRC - algorítmo de verificação
16 - width
ccitt - polinômio 0x1021
ffff - valor inicial 0xFFFF
"""
crc_configuration = Configuration(
width=16,
polynomial=0x1021,
init_value=0xFFFF,
final_xor_value=0x0000,
reverse_input=False,
reverse_output=False,
)
crc_calculator = CrcCalculator(crc_configuration)
data_to_encode = bytes(data, encoding="utf-8")
crc_value = crc_calculator.calculate_checksum(data_to_encode)
crc_value = str(hex(crc_value))[2:].upper()
return crc_value
84 changes: 84 additions & 0 deletions bb_wrapper/services/pixcode.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
from .brcode import BRCodeService
from .qrcode import QRCodeService


class PixCodeService:
"""
Service para gerar QR Code PIX.

O QRCode PIX segue a implementação do BR Code

https://forum.developers.bb.com.br/t/duvida-sobre-a-criacao-do-qrcode-da-cobranca-pix/4503

seção 1.6.7 do II_ManualdePadroesparaIniciacaodoPix_versao2-3-0.pdf
"""

def create(self, location: str, nome_recebedor: str):
"""
Função para criar o QRCode PIX!

Args:
location: url 'location' do PIX dinâmico
nome_recebedor: nome do recebedor
"""
# campo ID 00 Payload Format Indicator
data = BRCodeService().create_field_string(_id="00", value="01")

# campo ID 01 Point of Initiation Method
data += BRCodeService().create_field_string(_id="01", value="12")
"""
12 = não deve ser pago mais de uma vez
"""

# campo ID 26 Merchant Account Information
# subcampo ID 00 GUI
merchant_account_information = BRCodeService().create_field_string(
_id="00", value="br.gov.bcb.pix"
)

# subcampo ID 25 URL
merchant_account_information += BRCodeService().create_field_string(
_id="25", value=location
)

data += BRCodeService().create_field_string(
_id="26", value=merchant_account_information
)

# campo ID 52 Merchant Category Code
data += BRCodeService().create_field_string(_id="52", value="0000")
"""
0000 = não informado
"""

# campo ID 53 Transaction Currency
data += BRCodeService().create_field_string(_id="53", value="986")
"""
986 = R$
"""

# campo ID 58 Country Code
data += BRCodeService().create_field_string(_id="58", value="BR")

# campo ID 59 Merchant Name
data += BRCodeService().create_field_string(_id="59", value=nome_recebedor)

# campo ID 60 Merchant City
data += BRCodeService().create_field_string(_id="60", value="BRASILIA")

# campo ID 62 Aditional Data Field
# subcampo ID 05 Reference Label
aditional_data_field = BRCodeService().create_field_string(
_id="05", value="***"
)

data += BRCodeService().create_field_string(
_id="62", value=aditional_data_field
)

# campo ID 63 CRC 16
data_to_encode = data + "6304"
crc_value = BRCodeService().crc_16_ccitt_ffff(data_to_encode)
data += BRCodeService().create_field_string(_id="63", value=crc_value)

return data, QRCodeService().generate_qrcode_b64image(data)
1 change: 1 addition & 0 deletions bb_wrapper/wrapper/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from .cobrancas import CobrancasBBWrapper # noqa
from .pix_cob import PIXCobBBWrapper # noqa
21 changes: 9 additions & 12 deletions bb_wrapper/wrapper/bb.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import requests

from .request import RequestsWrapper
from ..constants import IS_SANDBOX, BASIC_TOKEN, GW_APP_KEY

Expand All @@ -24,7 +22,7 @@ def __init__(self, basic_token=None, is_sandbox=None, gw_app_key=None):

self.__basic_token = basic_token
self.__gw_app_key = gw_app_key
self.__is_sandbox = is_sandbox
self._is_sandbox = is_sandbox
self.__access_token = None
self.__token_type = None

Expand All @@ -39,13 +37,15 @@ def _construct_base_url(self):
base_url = (
f"{self.BASE_SCHEMA}"
f"api"
f'{".sandbox" if self.__is_sandbox else ""}'
f'{".sandbox" if self._is_sandbox else ""}'
f"{self.BASE_DOMAIN}"
)
return base_url

def _construct_url(self, *args, search=None):
url = super()._construct_url(*args, search=search)
def _construct_url(self, *args, **kwargs):
url = super()._construct_url(*args, **kwargs)

search = kwargs.get("search")
if search is None:
url += "?"
else:
Expand All @@ -68,20 +68,17 @@ def authenticate(self):
url = (
f"{self.BASE_SCHEMA}"
f"oauth"
f'{".sandbox" if self.__is_sandbox else ""}'
f'{".sandbox" if self._is_sandbox else ""}'
f"{self.BASE_DOMAIN}"
f"/oauth/token"
)
header = {"Authorization": f"Basic {self.__basic_token}"}

data = {
"grant_type": "client_credentials",
"scope": "cobrancas.boletos-info cobrancas.boletos-requisicao",
"scope": "cobrancas.boletos-info cobrancas.boletos-requisicao cob.read cob.write pix.read pix.write", # noqa: E501
}

# https://superuser.com/a/1426579 => verify=False
response = requests.post(url, data=data, headers=header, verify=False)
response = self._process_response(response)
response = self._post(url, data, header)
self.__access_token = response.data["access_token"]
self.__token_type = response.data["token_type"]
return response
15 changes: 14 additions & 1 deletion bb_wrapper/wrapper/cobrancas.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from .bb import BaseBBWrapper
from ..constants import CONVENIO, CARTEIRA, VARIACAO_CARTEIRA, AGENCIA, CONTA
from ..models.boleto import Boleto
from ..services import parse_unicode_to_alphanumeric
from ..services import parse_unicode_to_alphanumeric, BarCodeService, QRCodeService


class CobrancasBBWrapper(BaseBBWrapper):
Expand Down Expand Up @@ -46,12 +46,25 @@ def _construct_base_url(self):
base_url += "/cobrancas/v2/boletos"
return base_url

def _injeta_b64_images(self, response):
""""""
response.data["codigo_barras_b64"] = BarCodeService().generate_barcode_b64image(
response.data["codigoBarraNumerico"]
)

qr_code = response.data.get("qrCode")
if qr_code:
response.data["qrCode"]["b64"] = QRCodeService().generate_qrcode_b64image(
qr_code["emv"]
)

def registra_boleto(self, data):
""""""
Boleto(**data)
self.authenticate()
url = self._construct_url()
response = self._post(url, data)
self._injeta_b64_images(response)
return response

def consulta_boleto(self, our_number):
Expand Down
Loading