Claude / Anthropic

Configure a BSPAY no Claude.ai, Claude Code e Anthropic API com Tool Use

Guia de integração via Claude.ai (web), Claude Code (CLI/IDE) e Anthropic API com Tool Use.


Claude.ai — Prompt direto

Cole no início da conversa:

Você é um especialista na 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, details? } }

## HMAC obrigatório (rotas financeiras)
Cashout, internal_transfers/payment, conversions/new exigem:
  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? }     // wallet fixa permanente
- 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? }   // PIX MED
- 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
Headers: X-BSPay-Event, X-BSPay-Signature (HMAC com callback_secret), X-BSPay-Timestamp.
Validar SEMPRE antes de processar (timing-safe compare).

## Status do ciclo
pending → confirmed | failed | cancelled | refunded

## chain vs network
- `chain` (cashin/wallet): blockchain de origem (livro-razão)
- `network` (cashout): rede de transmissão (pode ser L2: arbitrum/base/optimism)

Gere código limpo, com tratamento de erros e comentários em português.
Para operações que movem dinheiro, SEMPRE peça confirmação humana antes.

Claude Code — CLAUDE.md no projeto

Se você usa Claude Code, adicione um CLAUDE.md na raiz do seu projeto:

# BSPAY — Integração

- Base: https://api.bspay.co
- Docs completas: https://dev.bspay.co
- Auth: OAuth2 Client Credentials → Bearer JWT (1h)

## Quick reference

GET  /v2/account/balance · /info/profile · /limits · /fees
POST /v2/transactions/cashin   — { amount, currency, chain?, external_id, payer, postback_url }
POST /v2/transactions/cashout  — HMAC obrigatório, response 202 async
POST /v2/transactions/wallet   — wallet fixa BRL/MXN/cripto
POST /v2/internal_transfers/payment — HMAC
POST /v2/conversions/{rate|simulate|new}  — campos: base_currency, destination_currency
POST /v2/account/transactions/list — extrato com filtros
GET /v2/account/infractions — PIX MED (disputas BACEN)

## HMAC (rotas financeiras)

X-Signature: hex(hmac_sha256(timestamp + "." + nonce + "." + body, SIGNING_KEY))
X-Timestamp: <unix_seconds>
X-Nonce:     <uuid_v4>

Janela ±5min, nonce único 5min. Assinar BODY RAW (mesmos bytes enviados).

## Webhooks

Headers: X-BSPay-Event, X-BSPay-Signature, X-BSPay-Timestamp.
Validar HMAC com CALLBACK_SECRET antes de processar.
Eventos: cashin.confirmed, cashin.refunded, cashout.confirmed, cashout.failed,
wallet_deposit, transfer.confirmed, conversion.confirmed,
chargeback.opened, chargeback.opened|chargeback.won|chargeback.lost|chargeback.canceled.

## Convenções no projeto

- Auth: caching de token em Redis com TTL 50min (re-auth proativo)
- HMAC: helper em `lib/bspay/sign.ts` — nunca inlinar assinatura nas rotas
- Webhook: rota única `/api/webhook/bspay` valida HMAC + idempotência via tabela `webhook_events(transaction_id PRIMARY KEY)`
- Erros retryable: backoff exponencial 1s → 2s → 4s → 8s, máx 4 tentativas

Anthropic API — Tool Use

Implementação Python com auth, HMAC e tools mapeadas pra Claude.

import os, json, time, uuid, hmac, hashlib, base64
import requests
import anthropic

BASE = "https://api.bspay.co"

# ============================================================
# Cliente BSPAY com auth + HMAC
# ============================================================
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})

    # read
    def balance(self):
        return requests.get(f"{BASE}/v2/account/balance", headers=self._h()).json()

    def list_transactions(self, **f):
        return requests.post(f"{BASE}/v2/account/transactions/list", headers=self._h(), json=f).json()

    def transaction(self, transaction_id):
        return requests.post(
            f"{BASE}/v2/account/transactions/list",
            headers=self._h(), json={"transaction_id": transaction_id}
        ).json()

    # cashin
    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()

    # cashout (HMAC)
    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()

    # convert (HMAC)
    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 Anthropic
# ============================================================
TOOLS = [
    {
        "name": "balance",
        "description": "Consulta o saldo da conta em todas as moedas (available/blocked/pending).",
        "input_schema": {"type": "object", "properties": {}},
    },
    {
        "name": "cashin_pix",
        "description": "Gera QR Code PIX para receber pagamento em BRL. Retorna copy-paste e ID.",
        "input_schema": {
            "type": "object",
            "required": ["amount", "payer_name", "payer_document", "external_id"],
            "properties": {
                "amount":          {"type": "number"},
                "payer_name":      {"type": "string"},
                "payer_document":  {"type": "string", "description": "CPF/CNPJ"},
                "external_id":     {"type": "string"},
            },
        },
    },
    {
        "name": "cashout_pix",
        "description": "Envia PIX (saque). REQUER confirmação humana. Operação assíncrona — webhook chega depois.",
        "input_schema": {
            "type": "object",
            "required": ["amount", "key", "key_type", "external_id"],
            "properties": {
                "amount":      {"type": "number"},
                "key":         {"type": "string"},
                "key_type":    {"type": "string", "enum": ["cpf", "cnpj", "email", "phone", "random"]},
                "external_id": {"type": "string"},
                "description": {"type": "string"},
            },
        },
    },
    {
        "name": "list_transactions",
        "description": "Extrato paginado com filtros (status, type, currency, from_date, to_date).",
        "input_schema": {
            "type": "object",
            "properties": {
                "page":      {"type": "integer"},
                "page_size": {"type": "integer"},
                "status":    {"type": "string", "enum": ["pending", "confirmed", "cancelled", "failed"]},
                "type":      {"type": "string", "enum": ["cashin", "cashout"]},
                "currency":  {"type": "string"},
                "from_date": {"type": "string"},
                "to_date":   {"type": "string"},
            },
        },
    },
    {
        "name": "convert",
        "description": "Converte uma moeda em outra (movimenta saldo)",
        "input_schema": {
            "type": "object",
            "required": ["amount", "base_currency", "destination_currency"],
            "properties": {
                "amount":               {"type": "number"},
                "base_currency":        {"type": "string"},
                "destination_currency": {"type": "string"},            },
        },
    },
]

# ============================================================
# Loop de chat com Claude
# ============================================================
def run():
    bspay  = BSPay(
        os.environ["BSPAY_CLIENT_ID"],
        os.environ["BSPAY_CLIENT_SECRET"],
        os.environ["BSPAY_SIGNING_KEY"],
    )
    client = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])

    messages = [{"role": "user", "content": "Gere um QR Code PIX de R$ 150 para Maria Silva, CPF 12345678901."}]
    SYSTEM = (
        "Você é um assistente financeiro com acesso à BSPAY. "
        "SEMPRE confirme com o usuário antes de chamar tools que movem dinheiro (cashout/convert). "
        "Para idempotência, gere external_id como UUID v4."
    )

    while True:
        resp = client.messages.create(
            model="claude-sonnet-4-5",
            max_tokens=1024,
            system=SYSTEM,
            tools=TOOLS,
            messages=messages,
        )

        if resp.stop_reason == "end_turn":
            text = next((b.text for b in resp.content if b.type == "text"), "")
            print(text)
            return

        # tool_use loop
        messages.append({"role": "assistant", "content": resp.content})
        tool_results = []
        for block in resp.content:
            if block.type == "tool_use":
                result = getattr(bspay, block.name)(**block.input)
                tool_results.append({
                    "type": "tool_result",
                    "tool_use_id": block.id,
                    "content": json.dumps(result),
                })
        messages.append({"role": "user", "content": tool_results})

run()

MCP Server (Model Context Protocol)

Para expor a BSPAY como MCP no Claude Desktop ou Claude Code:

{
  "mcpServers": {
    "bspay": {
      "command": "node",
      "args": ["bspay-mcp-server.js"],
      "env": {
        "BSPAY_CLIENT_ID":     "seu_client_id",
        "BSPAY_CLIENT_SECRET": "seu_client_secret",
        "BSPAY_SIGNING_KEY":   "sua_signing_key"
      }
    }
  }
}

Construa o servidor com @modelcontextprotocol/sdk — exponha as mesmas operações do Tool Use acima como tools MCP.


Boas práticas para Claude

Ative extended thinking em rotas críticas

Para cashout/convert use thinking parameter — Claude valida valor, destino e fee antes de executar.

tool_choice: "any" + system prompt forte

Force Claude a chamar tools (em vez de "responder com instruções") com tool_choice: {"type": "any"}. Combine com system prompt que define limites.

external_id consistente em retries

Em loops de tool_use, persista o external_id na primeira tentativa e reuse em retries. Isso garante idempotência mesmo se Claude re-chamar a tool.

Validate webhook antes de processar

No backend que recebe webhooks, valide HMAC X-BSPay-Signature com callback_secret. Não processe nada sem isso — ou um atacante pode forjar cashin.confirmed.

Esta página foi útil?