Gemini / Google AI
Configure a BSPAY no Gemini, AI Studio e API Google AI com Function Calling
Guia de integração via Gemini (web), AI Studio e Google AI API com Function Calling.
O catálogo completo de endpoints, HMAC, webhooks e erros vive em Integração com IA. Este guia é específico do Gemini.
Gemini — Prompt direto
Cole no início da conversa (Gemini ou AI Studio):
Contexto: BSPAY API v2
Base: https://api.bspay.co · Auth: OAuth2 Client Credentials → Bearer JWT (1h)
Resposta: { success, data, request_id, timestamp } | erro: { success:false, error:{ code, message, group, retryable } }
## HMAC obrigatório (cashout, internal_transfers/payment, conversions/new)
X-Signature: hex(hmac_sha256(timestamp + "." + nonce + "." + body, signing_key))
X-Timestamp: <unix_seconds>
X-Nonce: <uuid_v4>
## Endpoints
GET /v2/account/balance | /info/profile | /limits | /fees
POST /v2/transactions/cashin { amount, currency, chain?, external_id, payer, postback_url }
POST /v2/transactions/cashout { external_id, amount, currency, key, key_type?, network?, name?, bank_code? } [HMAC]
POST /v2/transactions/wallet { currency, chain? }
POST /v2/internal_transfers/payment { username, amount, currency, external_id? } [HMAC]
POST /v2/conversions/{rate|simulate|new} { amount, base_currency, destination_currency, external_id? }
POST /v2/account/transactions/list { page, page_size, status?, type?, source?, currency?, from_date?, to_date? }
GET /v2/account/infractions { page?, page_size?, status?, type? }
GET /v2/account/infractions/detail?id=<uuid>
POST /v2/account/infractions/reply (multipart: id, message, files[])
## Webhooks (9 eventos)
cashin.{confirmed,refunded,expired} | cashout.{confirmed,failed,refunded} | wallet_deposit
transfer.confirmed | conversion.confirmed | chargeback.opened | chargeback.opened|chargeback.won|chargeback.lost|chargeback.canceled
Validar HMAC X-BSPay-Signature com callback_secret antes de processar.
## Status do ciclo: pending → confirmed | failed | cancelled | refunded
## Moedas: BRL (PIX), MXN (SPEI), USDT, USDC, BTC, ETH, SOL, BNB
## chain (cashin/wallet) ≠ network (cashout): network pode ser L2 (arbitrum/base/optimism)
Gere código limpo, em português, e SEMPRE peça confirmação humana antes de operações que movem dinheiro.
Gemini API — Function Calling
Implementação Python com auth + HMAC + tools.
import os, json, time, uuid, hmac, hashlib, base64
import requests
import google.generativeai as genai
BASE = "https://api.bspay.co"
genai.configure(api_key=os.environ["GEMINI_API_KEY"])
# ============================================================
# Cliente BSPAY
# ============================================================
class BSPay:
def __init__(self, client_id, client_secret, signing_key):
self.signing_key = signing_key
creds = base64.b64encode(f"{client_id}:{client_secret}".encode()).decode()
r = requests.post(
f"{BASE}/v2/oauth/token",
headers={"Authorization": f"Basic {creds}", "Content-Type": "application/json"},
json={"grant_type": "client_credentials"},
)
r.raise_for_status()
self.token = r.json()["access_token"]
def _h(self, extra=None):
h = {"Authorization": f"Bearer {self.token}", "Content-Type": "application/json"}
if extra: h.update(extra)
return h
def _signed_h(self, body: str):
ts = str(int(time.time()))
nonce = str(uuid.uuid4())
sig = hmac.new(self.signing_key.encode(), f"{ts}.{nonce}.{body}".encode(), hashlib.sha256).hexdigest()
return self._h({"X-Signature": sig, "X-Timestamp": ts, "X-Nonce": nonce})
def balance(self):
return requests.get(f"{BASE}/v2/account/balance", headers=self._h()).json()
def cashin_pix(self, amount, payer_name, payer_document, external_id):
body = {
"amount": amount, "currency": "BRL",
"external_id": external_id,
"payer": {"name": payer_name, "document": payer_document},
}
return requests.post(f"{BASE}/v2/transactions/cashin", headers=self._h(), json=body).json()
def cashout_pix(self, amount, key, key_type, external_id, description=""):
body = json.dumps({
"external_id": external_id, "amount": amount, "currency": "BRL",
"key": key, "key_type": key_type, "description": description,
}, separators=(",", ":"))
return requests.post(f"{BASE}/v2/transactions/cashout", headers=self._signed_h(body), data=body).json()
def list_transactions(self, **f):
return requests.post(f"{BASE}/v2/account/transactions/list", headers=self._h(), json=f).json()
def convert(self, amount, base_currency, destination_currency, external_id=None):
body = json.dumps({
"amount": amount,
"base_currency": base_currency,
"destination_currency": destination_currency,
"external_id": external_id or str(uuid.uuid4()),
}, separators=(",", ":"))
return requests.post(f"{BASE}/v2/conversions/new", headers=self._signed_h(body), data=body).json()
# ============================================================
# Tools no formato Gemini Function Declaration
# ============================================================
TOOLS = [genai.protos.Tool(function_declarations=[
genai.protos.FunctionDeclaration(
name="balance",
description="Consulta o saldo da conta em todas as moedas (available/blocked/pending).",
parameters=genai.protos.Schema(type=genai.protos.Type.OBJECT, properties={}),
),
genai.protos.FunctionDeclaration(
name="cashin_pix",
description="Gera QR Code PIX para receber pagamento em BRL. Retorna QR + transaction_id.",
parameters=genai.protos.Schema(
type=genai.protos.Type.OBJECT,
properties={
"amount": genai.protos.Schema(type=genai.protos.Type.NUMBER),
"payer_name": genai.protos.Schema(type=genai.protos.Type.STRING),
"payer_document": genai.protos.Schema(type=genai.protos.Type.STRING, description="CPF/CNPJ"),
"external_id": genai.protos.Schema(type=genai.protos.Type.STRING),
},
required=["amount", "payer_name", "payer_document", "external_id"],
),
),
genai.protos.FunctionDeclaration(
name="cashout_pix",
description="Envia PIX (saque). REQUER confirmação humana. Operação assíncrona — webhook chega depois.",
parameters=genai.protos.Schema(
type=genai.protos.Type.OBJECT,
properties={
"amount": genai.protos.Schema(type=genai.protos.Type.NUMBER),
"key": genai.protos.Schema(type=genai.protos.Type.STRING, description="Chave PIX"),
"key_type": genai.protos.Schema(type=genai.protos.Type.STRING, description="cpf|cnpj|email|phone|random"),
"external_id": genai.protos.Schema(type=genai.protos.Type.STRING),
"description": genai.protos.Schema(type=genai.protos.Type.STRING),
},
required=["amount", "key", "key_type", "external_id"],
),
),
genai.protos.FunctionDeclaration(
name="list_transactions",
description="Extrato paginado com filtros (status, type, currency, from_date, to_date).",
parameters=genai.protos.Schema(
type=genai.protos.Type.OBJECT,
properties={
"page": genai.protos.Schema(type=genai.protos.Type.INTEGER),
"page_size": genai.protos.Schema(type=genai.protos.Type.INTEGER),
"status": genai.protos.Schema(type=genai.protos.Type.STRING),
"type": genai.protos.Schema(type=genai.protos.Type.STRING),
"currency": genai.protos.Schema(type=genai.protos.Type.STRING),
"from_date": genai.protos.Schema(type=genai.protos.Type.STRING),
"to_date": genai.protos.Schema(type=genai.protos.Type.STRING),
},
),
),
genai.protos.FunctionDeclaration(
name="convert",
description="Converte uma moeda em outra",
parameters=genai.protos.Schema(
type=genai.protos.Type.OBJECT,
properties={
"amount": genai.protos.Schema(type=genai.protos.Type.NUMBER),
"base_currency": genai.protos.Schema(type=genai.protos.Type.STRING),
"destination_currency": genai.protos.Schema(type=genai.protos.Type.STRING), },
required=["amount", "base_currency", "destination_currency"],
),
),
])]
# ============================================================
# Loop de chat
# ============================================================
def run():
bspay = BSPay(
os.environ["BSPAY_CLIENT_ID"],
os.environ["BSPAY_CLIENT_SECRET"],
os.environ["BSPAY_SIGNING_KEY"],
)
model = genai.GenerativeModel(
"gemini-2.0-flash",
tools=TOOLS,
system_instruction=(
"Você é um assistente financeiro com acesso à BSPAY. "
"SEMPRE confirme com o usuário antes de chamar funções que movem dinheiro (cashout/convert). "
"Use UUID v4 como external_id pra idempotência."
),
)
chat = model.start_chat()
resp = chat.send_message("Gere um QR Code PIX de R$ 75 para Carlos, CPF 11122233344")
while True:
fn_calls = [p.function_call for p in resp.parts if hasattr(p, "function_call") and p.function_call.name]
if not fn_calls:
print(resp.text)
return
responses = []
for fn in fn_calls:
result = getattr(bspay, fn.name)(**dict(fn.args))
responses.append(genai.protos.Part(
function_response=genai.protos.FunctionResponse(name=fn.name, response={"result": result})
))
resp = chat.send_message(genai.protos.Content(parts=responses))
run()
AI Studio (visual, sem código)
No AI Studio:
- System Instructions: cole o prompt de contexto acima
- Tools → Function Calling: cole a JSON Schema das funções
- Teste pedidos como:
- "Qual é o meu saldo?"
- "Gerar QR Code PIX de R$ 50 para João, CPF 12345678901"
- "Quanto USDT eu consigo com 500 BRL? (cotação)"
AI Studio não consegue executar HMAC nativamente. Operações com X-Signature (cashout/transfer/convert) precisam ir pelo seu backend. Use AI Studio só para read (balance, list, validate, rate, simulate).
Boas práticas para Gemini
Use gemini-2.5-pro para HMAC
Gere o helper de assinatura (JS/Python/Go) com gemini-2.5-pro — modelos menores às vezes erram a ordem timestamp + "." + nonce + "." + body.
temperature: 0 em rotas críticas
Para cashout/convert configure temperature: 0 e top_p: 0.1. Reduz o risco de Gemini "alucinar" valor ou destinatário.
Reuse chat (não generate_content)
A sessão de chat mantém o estado de tool calls — generate_content puro não. Use model.start_chat() para fluxos multi-step.
