#!/usr/bin/env python3 from dotenv import load_dotenv from os import getenv from oauthlib.oauth2 import BackendApplicationClient from requests_oauthlib import OAuth2Session from flask import Flask, request from flask_cors import CORS from flask_restful import Resource, Api from eth_abi.packed import encode_packed import eth_account from eth_hash.auto import keccak from base64 import b85decode from urllib3 import disable_warnings from eth_utils import to_wei # Load environment variables from .env file load_dotenv() app = Flask(__name__) CORS(app) api = Api(app) class BBPay(Resource): def __init__(self): super().__init__() self.setup_oauth() def setup_oauth(self): client = BackendApplicationClient(client_id=getenv("CLIENT_ID")) self.oauth = OAuth2Session(client=client) scope = ['checkout.solicitacoes-requisicao', 'checkout.participantes-requisicao', 'checkout.solicitacoes-info', 'checkout.participantes-info'] self.oauth.fetch_token( token_url='https://oauth.hm.bb.com.br/oauth/token', client_id=getenv("CLIENT_ID"), client_secret=getenv("CLIENT_SECRET"), scope=scope, cert='cert.pem') # Url de homologação com autenticação mTLS. self.baseUrl = "https://api-bbpay.hm.bb.com.br/checkout/v2" # Url de homologação sem autenticação mTLS. #self.baseUrl = "https://api.extranet.hm.bb.com.br/checkout/v2" # Url de produção com autenticação mTLS. #self.baseUrl = "https://api-bbpay.bb.com.br/checkout/v2" self.verify_ssl = False self.params = { 'numeroConvenio': 701, 'gw-dev-app-key': getenv("DEV_APP_KEY") } class Register(BBPay): def post(self): data = request.get_json() body = { 'numeroConvenio': 701, 'nomeParticipante': data['chainID'], 'tipoDocumento': data['tipoDocumento'], 'numeroDocumento': data['numeroDocumento'], 'numeroConta': data['numeroConta'], 'numeroAgencia': data['numeroAgencia'], 'tipoConta': data['tipoConta'], 'codigoIspb': data['codigoIspb'] # Código identificador do Sistema de Pagamentos Brasileiro. Atualmente aceitamos apenas Banco do Brasil, codigoIspb igual a 0 } response = self.oauth.post( self.baseUrl+"/participantes", params=self.params, json=body, verify=self.verify_ssl, cert='cert.pem') return response.json() class Request(BBPay): def post(self): data = request.get_json() body = { "geral": { "numeroConvenio": 701, "pagamentoUnico": True, "descricaoSolicitacao": "P2Pix", "valorSolicitacao": data['amount'] }, # "devedor": { # "tipoDocumento": 1, # "numeroDocumento": 1 # }, "formasPagamento": [{ "codigoTipoPagamento": "PIX", "quantidadeParcelas": 1}], "repasse": { "tipoValorRepasse": "Percentual", "recebedores": [{ "identificadorRecebedor": data['pixTarget'], "tipoRecebedor": "Participante", "valorRepasse": 100 }] } } response = self.oauth.post( self.baseUrl+"/solicitacoes", params=self.params, json=body, verify=self.verify_ssl) return response.json() class Release(BBPay): def get(self, numeroSolicitacao): response = self.oauth.get( self.baseUrl+f"/solicitacoes/{numeroSolicitacao}", params=self.params, verify=self.verify_ssl) data = response.json() numeroParticipante = data['repasse']['recebedores'][0]['identificadorRecebedor'] pixTimestamp = b85decode(data['informacoesPix']['txId']) valorSolicitacao = to_wei(data['valorSolicitacao'], 'ether') codigoEstadoSolicitacao = data['codigoEstadoSolicitacao'] if codigoEstadoSolicitacao != 1: return 'Pix not paid', 204 response = self.oauth.get( self.baseUrl+f"/participantes/{numeroParticipante}", params=self.params, verify=self.verify_ssl) chainID = response.json()['nomeParticipante'] packed = encode_packed(['bytes32','uint80','bytes32'], (f"{chainID}-{numeroParticipante}".encode(), int(valorSolicitacao), pixTimestamp) ) signable = eth_account.messages.encode_defunct(keccak(packed)) signature = eth_account.account.Account.sign_message(signable, private_key=getenv('PRIVATE_KEY')).signature.hex() return { 'pixTarget': f"{chainID}-{numeroParticipante}", 'amount': str(valorSolicitacao), 'pixTimestamp': pixTimestamp.hex(), 'signature': signature } # (CPF, nome, conta) -> participantID # should be called before deposit api.add_resource(Register, '/register') # (amount,pixtarget) -> requestID, QRcodeText # should be called after lock api.add_resource(Request, '/request') # (requestID) -> sig(pixTarget, amount, pixTimestamp) # should be called before release api.add_resource(Release, '/release/') if __name__ == '__main__': if getenv("DEBUG"): disable_warnings() app.run(debug=True) else: from waitress import serve serve(app, host=getenv("HOST","0.0.0.0"), port=getenv("PORT",5000))