testes/app/routes/boletos.py

335 lines
11 KiB
Python

import os
import json
import requests
from time import time
from functools import wraps
from base64 import b64decode
from datetime import datetime, timedelta
from flask import Flask, Blueprint, jsonify, request, Response
from flask_mysqldb import MySQL
from flask_cors import CORS
from .auth import token_required
from dotenv import load_dotenv
from flask import send_file
import io
load_dotenv()
# Configurações da API do Banco Inter
CLIENT_ID = os.environ.get("CLIENT_ID")
CLIENT_SECRET = os.environ.get("CLIENT_SECRET")
CERT_PATH = os.environ.get("CERT_PATH")
CERT_KEY_PATH = os.environ.get("CERT_KEY_PATH")
ACCOUNT = os.environ.get("ACCOUNT", None)
API_BASE_URL = "https://cdpj.partners.bancointer.com.br"
boletos = Blueprint('boletos', __name__)
app = Flask(__name__)
cors = CORS(app, resources={r"/v1/boletos/*": {"origins": "*", "methods": ["GET", "POST", "PUT", "OPTIONS"]}})
app.config["CORS_HEADERS"] = ["Content-Type", "Authorization", "Content-Disposition", "Content-Length"]
MYSQL_HOST = os.environ.get('MYSQL_HOST')
MYSQL_PORT = os.environ.get('MYSQL_PORT')
MYSQL_USER = os.environ.get('MYSQL_USER')
MYSQL_PASSWORD = os.environ.get('MYSQL_PASSWORD')
MYSQL_DB = os.environ.get('MYSQL_DB')
MYSQL_CURSORCLASS = os.environ.get("MYSQL_CURSORCLASS")
mysql = MySQL()
token_cache = {}
def intercept_options(f):
@wraps(f)
def decorated(*args, **kwargs):
if request.method == "OPTIONS":
response = jsonify({"status": "ok"})
response.headers.add("Access-Control-Allow-Origin", "*")
response.headers.add("Access-Control-Allow-Methods", "GET,POST,PUT,OPTIONS")
response.headers.add("Access-Control-Allow-Headers", "Content-Type,Authorization,Content-Disposition,Content-Length")
return response, 200
return f(*args, **kwargs)
return decorated
def obter_token(scopes=["boleto-cobranca.write", "boleto-cobranca.read"]):
"""Obtem um token de acesso para a API do Banco Inter."""
scopes_param = " ".join(scopes)
if token_cache.get(scopes_param):
cached_token = token_cache.get(scopes_param)
# token is still valid
if cached_token.get("expires_at") > int(time()):
return cached_token["token"]
try:
request_string = f"client_id={CLIENT_ID}&client_secret={CLIENT_SECRET}&scope={scopes_param}&grant_type=client_credentials"
print("request_string", request_string)
response = requests.post(url=f"{API_BASE_URL}/oauth/v2/token",
headers={"Content-Type": "application/x-www-form-urlencoded"},
cert=(CERT_PATH, CERT_KEY_PATH),
data=request_string)
data_json = response.json()
token = data_json.get("access_token")
token_cache[scopes_param] = {
"token": token,
"expires_at": data_json.get("expires_in") + int(time()),
}
return token
except Exception as e:
print(f"Erro ao obter token de acesso: {e}")
return None
def emitir_cobranca(token, dados_cobranca):
"""Emite uma cobrança para o Banco Inter de forma assíncrona."""
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
"Accept": "application/json"
}
try:
response = requests.post(
f"{API_BASE_URL}/cobranca/v3/cobrancas",
headers=headers,
cert=(CERT_PATH, CERT_KEY_PATH),
json=dados_cobranca
)
# Captura a resposta completa
resposta_json = response.json()
# Exibe detalhes da resposta para debug
print("Código de status:", response.status_code)
print("Resposta da API:", json.dumps(resposta_json, indent=4, ensure_ascii=False))
return resposta_json
except Exception as e:
print(f"Erro ao emitir cobrança: {e}")
return None
def obter_cobranca(token, codigo_solicitacao):
"""Obtem uma cobrança."""
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "Application/json",
}
try:
response = requests.get(f"{API_BASE_URL}/cobranca/v3/cobrancas/{codigo_solicitacao}",
headers=headers,
cert=(CERT_PATH, CERT_KEY_PATH))
return response.json()
except Exception as e:
print(f"Erro ao emitir cobrança:{e}")
return None
def obter_cobranca_pdf(token, codigo_solicitacao):
"""Obtem o PDF de uma cobrança."""
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "Application/json",
}
try:
# Certifique-se de que o codigo_solicitacao seja inserido corretamente na URL
url = f"{API_BASE_URL}/cobranca/v3/cobrancas/{codigo_solicitacao}/pdf"
response = requests.get(url,
headers=headers,
cert=(CERT_PATH, CERT_KEY_PATH))
return response.json()
except Exception as e:
print(f"Erro ao emitir cobrança:{e}")
return None
def processar_cobranca_cliente(cliente, token):
"""
Processa a emissão da cobrança para um cliente específico,
retornando os dados relevantes.
"""
id_empresa = cliente["idempresa"]
hoje = datetime.now()
vencimento = (hoje + timedelta(days=5)).strftime("%Y-%m-%d")
dados_cobranca = {
"seuNumero": f"boleto-{id_empresa}",
"valorNominal": str(cliente["valor_servico"]),
"dataVencimento": vencimento,
"numDiasAgenda": 30,
"pagador": {
"cpfCnpj": cliente["cpf_cnpj"],
"tipoPessoa": cliente["tipo_pessoa"],
"nome": cliente["nome"],
"telefone": cliente["telefone"],
"endereco": cliente["endereco"],
"cidade": cliente["cidade"],
"uf": cliente["uf"],
"cep": cliente["cep"],
},
"formasRecebimento": ["BOLETO", "PIX"],
}
emissao_cobranca = emitir_cobranca(token, dados_cobranca)
codigo_solicitacao = emissao_cobranca.get("codigoSolicitacao") if emissao_cobranca else None
if not codigo_solicitacao:
return {
"cliente": cliente,
"status": "erro",
"mensagem": "Falha ao emitir cobrança."
}
cobranca = obter_cobranca(token, codigo_solicitacao)
if not cobranca:
return {
"cliente": cliente,
"status": "erro",
"mensagem": "Falha ao obter cobrança."
}
boleto_data = cobranca.get("cobranca", {}).get("boleto", {})
codigo_barras = boleto_data.get("codigoBarras")
linha_digitavel = cobranca.get("boleto", {}).get("linhaDigitavel")
pix_data = cobranca.get("pix", {})
pix_copia_e_cola = pix_data.get("pixCopiaECola")
cobranca_pdf = obter_cobranca_pdf(token, codigo_solicitacao)
if not cobranca_pdf or "pdf" not in cobranca_pdf:
return {
"cliente": cliente,
"status": "erro",
"mensagem": "Falha ao obter PDF da cobrança."
}
return {
"cliente": cliente,
"status": "sucesso",
"codigo_solicitacao": codigo_solicitacao,
"codigo_barras": codigo_barras,
"linha_digitavel": linha_digitavel,
"pix_copia_e_cola": pix_copia_e_cola,
"pdf": cobranca_pdf["pdf"],
}
@boletos.route('/', methods=['POST', 'OPTIONS'])
@intercept_options
@token_required
def gerar_boletos(data):
domain = data.get('domain')
escopos = data.get('scopes', [])
escopos.append("boleto-cobranca.write")
escopos.append("boleto-cobranca.read")
print("data", data)
print(domain)
print(escopos)
if 'boleto-cobranca.write' not in escopos:
return jsonify({"erro": "Permissão necessária: boleto-cobranca.write"}), 403
if not domain:
return jsonify({"error": "Domínio não encontrado no token"}), 400
hoje = datetime.now()
if hoje.day > 12:
return jsonify({"erro": "Boletos só podem ser gerados até o dia 12 de cada mês."}), 403
try:
cur = mysql.connection.cursor()
cur.execute(
"SELECT idempresa, nome, cpf_cnpj, tipo_pessoa, telefone, endereco, cidade, uf, cep, valor_servico FROM empresa WHERE dominio = %s",
(domain,)
)
clientes = cur.fetchall()
cur.close()
if not clientes:
return jsonify({"erro": "Nenhum cliente encontrado."}), 404
except Exception as e:
print(f"Erro ao acessar o banco de dados: {e}")
return jsonify({"erro": "Erro ao acessar o banco de dados."}), 500
token = obter_token(escopos)
if not token:
return jsonify({"erro": "Falha ao obter o token de acesso."}), 401
resultados = []
for cliente in clientes:
resultado = processar_cobranca_cliente(cliente, token)
resultados.append(resultado)
return jsonify({"resultados": resultados}), 200
@boletos.route('/<codigo_solicitacao>', methods=['GET', 'OPTIONS'])
@intercept_options
@token_required
def recuperar_cobranca(token_data, codigo_solicitacao):
print("codigo_solicitacao", codigo_solicitacao)
scopes = token_data.get('scopes', [])
print("token_data", token_data)
if 'boleto-cobranca.read' not in scopes:
return jsonify({"erro": "Permissao necessaria: boleto-cobranca.read"}), 403
token = obter_token(scopes)
if not token:
return jsonify({"erro": "Falha ao obter o token de acesso"}), 401
cobranca = obter_cobranca(token, codigo_solicitacao)
if cobranca is None:
return jsonify({"erro": "Erro ao obter PDF"}), 404
try:
codigo_barras = cobranca.get("boleto").get("codigoBarras")
linha_digitavel = cobranca.get("boleto").get("linhaDigitavel")
pix_copia_e_cola = cobranca.get("pix").get("pixCopiaECola")
return jsonify({
"codigo_solicitacao": codigo_solicitacao,
"codigo_barras": codigo_barras,
"linha_digitavel": linha_digitavel,
"pix_copia_e_cola": pix_copia_e_cola,
}), 200
except Exception as e:
return jsonify({"erro": f"Erro ao buscar cobranca: {e}"}), 500
@boletos.route('/<codigo_solicitacao>/pdf', methods=['GET', 'OPTIONS'])
@intercept_options
@token_required
def recuperar_boleto(token_data, codigo_solicitacao):
print("codigo_solicitacao", codigo_solicitacao)
scopes = token_data.get('scopes', [])
print("token_data", token_data)
if 'boleto-cobranca.read' not in scopes:
return jsonify({"erro": "Permissao necessaria: boleto-cobranca.read"}), 403
token = obter_token(scopes)
if not token:
return jsonify({"erro": "Falha ao obter o token de acesso"}), 401
cobranca_pdf = obter_cobranca_pdf(token, codigo_solicitacao)
if cobranca_pdf is None:
return jsonify({"erro": "Erro ao obter PDF"}), 404
try:
pdf_content_base64 = cobranca_pdf["pdf"]
pdf_content_bytes = b64decode(pdf_content_base64, validate=True)
return Response(pdf_content_bytes,
mimetype='application/pdf',
headers={
"Content-Disposition": "inline; filename=boleto.pdf",
"Content-Type": "application/pdf",
"Content-Length": len(pdf_content_bytes)
})
except Exception as e:
return jsonify({"erro": f"Erro ao decodificar PDF: {e}"}), 500
if __name__ == "__main__":
app.register_blueprint(boletos)
app.run(port=5000, debug=True)