From 94cee1fa5ebf4e551dfeec95e852364d96bc6d02 Mon Sep 17 00:00:00 2001 From: hueso Date: Fri, 21 Mar 2025 14:30:11 -0300 Subject: [PATCH] BB Pay v0 --- bbpay.py | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++++ test_api.py | 55 ------------------- 2 files changed, 150 insertions(+), 55 deletions(-) create mode 100755 bbpay.py delete mode 100644 test_api.py diff --git a/bbpay.py b/bbpay.py new file mode 100755 index 0000000..b76570c --- /dev/null +++ b/bbpay.py @@ -0,0 +1,150 @@ +#!/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_restful import Resource, Api +from eth_abi.packed import encode_packed +import eth_account +from eth_hash.auto import keccak +from urllib3 import disable_warnings + +# Load environment variables from .env file +load_dotenv() + +app = Flask(__name__) +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) + + # 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['nomeParticipante'], + 'tipoDocumento': data['tipoDocumento'], + 'numeroDocumento': data['numeroDocumento'], + 'numeroConta': data['numeroConta'], + 'numeroAgencia': data['numeroAgencia'], + 'tipoConta': data['tipoConta'], + 'codigoIspb': 0 # 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) + 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'] + valorSolicitacao = data['valorSolicitacao'] + pixTimestamp = data['timestampCriacaoSolicitacao'] + codigoEstadoSolicitacao = data['codigoEstadoSolicitacao'] + if codigoEstadoSolicitacao != 0: + return 'Pix not paid', 204 + packed = encode_packed(['bytes32','uint80','bytes32'], + (str(numeroParticipante).encode(), int(valorSolicitacao), pixTimestamp.encode()) ) + + 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': numeroParticipante, + 'amount': valorSolicitacao, + 'pixTimestamp': pixTimestamp, + '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)) + + diff --git a/test_api.py b/test_api.py deleted file mode 100644 index a7ecc8d..0000000 --- a/test_api.py +++ /dev/null @@ -1,55 +0,0 @@ -import requests -from dotenv import load_dotenv -from os import getenv - -# Load environment variables from .env file -load_dotenv() - -url = "https://sandbox.openfinance.celcoin.dev/v5/token" -data = { - "client_id": getenv("CLIENT_ID"), - "grant_type": "client_credentials", - "client_secret": getenv("CLIENT_SECRET"), -} -response = requests.post(url, data=data) -token = response.json()["access_token"] -print("token:", token) - -url = "https://sandbox.openfinance.celcoin.dev/pix/v1/payment/endToEnd" -payload = { "dpp": "2024-11-12" } -headers = { - "accept": "application/json", - "content-type": "application/json", - "authorization": "Bearer " + token -} -response = requests.post(url, json=payload, headers=headers) -e2ee = response.json()["body"]["endToEndId"] -print("e2e:", e2ee) - - -# alternate DICT method for obtaining e2ee -url = "https://sandbox.openfinance.celcoin.dev/pix/v1/dict/v2/key" -payload = { - "payerId": "13935893000370", - "key": "93981962-9505-474d-9899-dcb0be3d40c4" -} -headers = { - "accept": "application/json", - "content-type": "application/json", - "authorization": "Bearer " + token -} -#response = requests.post(url, json=payload, headers=headers) -#print(response.text) - - -url = "https://sandbox.openfinance.celcoin.dev/pix/v1/payment" -payload = { "initiationType": "DICT" } -# headers = { -# "accept": "application/json", -# "content-type": "application/json", -# "authorization": "Bearer $beare" -# } - -# response = requests.post(url, json=payload, headers=headers) - -# print(response.text) \ No newline at end of file