Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 8 additions & 3 deletions pixqrcode/service/generate_code.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from pixqrcode.model.merchant_account import MerchantAccount
from pixqrcode.model.pix import Pix
from pixqrcode.utils.crc16 import crc16

from pixqrcode.service.validate_pix import ValidatePix
import re

class GenerateCode:
def __init__(self):
Expand All @@ -11,7 +12,11 @@ def left_zero(self, text: str):
return str(len(text)).zfill(2)

def generate(self, pix: Pix):
return f"00020126360014{self.merchant.globally_unique_identifier}0114{pix.mobile}520400005303986540" \
valPix = ValidatePix(pix)
detect_type = valPix.detect_type_key
code = detect_type()

return f"00020126360014{self.merchant.globally_unique_identifier}{code}{pix.mobile}520400005303986540" \
f"{len(pix.amount.__str__())}{pix.amount.__str__()}5802{pix.country_code}59{self.left_zero(pix.name)}" \
f"{pix.name}60{self.left_zero(pix.city)}{pix.city}" \
f"62{str(len(pix.reference_label) + 4).zfill(2)}05{self.left_zero(pix.reference_label)}" \
Expand All @@ -22,4 +27,4 @@ def crc16code(self, query: str) -> str:

def format_code(self, pix: Pix):
hash_payment = self.generate(pix)
return f"{hash_payment}{self.crc16code(hash_payment).upper()[2:]}".strip()
return f"{hash_payment}{self.crc16code(hash_payment).upper()[2:]}".strip()
97 changes: 82 additions & 15 deletions pixqrcode/service/validate_pix.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,25 +42,92 @@ def amount(self):
return True

def reference_label(self):
if not self.pix.reference_label:
self.pix.reference_label = "***"
self.pix.reference_label = (
FormatValues.texts_no_space(self.pix.reference_label)
if self.pix.reference_label
else "***"
)

def validateCPF(self):
cpf = self.pix.mobile
cpf = re.sub('\D', '', cpf)

numbers = [int(digit) for digit in cpf if digit.isdigit()]
if len(numbers) != 11 or len(set(numbers)) == 1:
return False

sum_of_products = sum(a*b for a, b in zip(numbers[:9], range(10, 1, -1)))
expected_digit = (sum_of_products * 10 % 11) % 10
if numbers[9] != expected_digit:
return False

sum_of_products = sum(a*b for a, b in zip(numbers[:10], range(11, 1, -1)))
expected_digit = (sum_of_products * 10 % 11) % 10
if numbers[10] != expected_digit:
return False

return True

"""
Valida o tipo de chave e retorna o codigo de acordo com o BC
"""
def detect_type_key(self):
key = self.pix.mobile
regex = re.search(r'^[a-zA-Z0-9._-]+@[a-zA-Z0-9]+\.[a-zA-Z\.a-zA-Z]{1,10}$', key)
# 0111 - CPF
# 0111 - CNPJ
# 0125 - EMAIL
# 0114 - Telefone

code = ""
if regex is None:
key_numbers = re.sub('\D', '', key)
if len(key_numbers) == 10:
#valida se é um telefone fixo
code = '0114'

if len(key_numbers) == 12:
#valida se é um telefone com DDI
code = '0114'

if len(key_numbers) == 13:
#valida se é um celular com DDI
code = '0114'

elif self.validateCPF():
#valida se é um cpf com base nos calculos de digitos
code = '0111'
elif len(key_numbers) == 11:
#se na ultima validacao nao retornou true é bem provavel que seja celular

code = '0114'
elif len(key_numbers) == 14:
#verifica se é cnpj
code = '0111' #creio que o codigo esteja errado, nao achei nada sobre
else:
raise Exception("A chave informada não foi identificada")
else:
self.pix.reference_label = FormatValues.texts_no_space(self.pix.reference_label)
# É email
code = '0125'

return code

def mobile(self):
if not self.pix.mobile:
raise PixError("telefone nao informado")

self.pix.mobile = FormatValues.mobile(self.pix.mobile)
if not re.match(r'^.55[\d]{3}', self.pix.mobile):
if not re.match(r'^55[\d]{3}', self.pix.mobile):
self.pix.mobile = f"+55{self.pix.mobile}"
else:
self.pix.mobile = f"+{self.pix.mobile}"
raise PixError("Chave nao informado")
code = self.detect_type_key()
if code == '0114':
self.pix.mobile = FormatValues.mobile(self.pix.mobile)
if not re.match(r'^.55[\d]{3}', self.pix.mobile):
self.pix.mobile = (
f"+{self.pix.mobile}"
if re.match(r'^55[\d]{3}', self.pix.mobile)
else f"+55{self.pix.mobile}"
)

if 14 > len(self.pix.mobile) < 14:
raise PixError("telefone curto ou longo")
if 14 > len(self.pix.mobile) < 14:
raise PixError("telefone curto ou longo")

if not re.match(r'^.+55[\d]{3}', self.pix.mobile):
raise PixError("telefone sem o DDD")
if not re.match(r'^.+55[\d]{3}', self.pix.mobile):
raise PixError("telefone sem o DDD")
return True