335 lines
11 KiB
Python
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)
|
|
|
|
|
|
|
|
|
|
|
|
|