161 lines
5.5 KiB
Python
Executable File
161 lines
5.5 KiB
Python
Executable File
#!/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/<int:numeroSolicitacao>')
|
|
|
|
|
|
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))
|
|
|
|
|