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.


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:

  1. System Instructions: cole o prompt de contexto acima
  2. ToolsFunction Calling: cole a JSON Schema das funções
  3. 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)"

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.

Esta página foi útil?