brazosd

Docs

Integracion de la API Merchant Trade

Esta guia explica como los merchants se integran con Brazosd Trade API, usando un Agent como entrada frontend y el SaaS del merchant como sistema backend de cumplimiento.

1. Activar funciones de merchant

Llama a este endpoint con un Bearer token normal de Brazosd:

http
POST /api/v1/trade/merchant
Authorization: Bearer <access_token>

Despues de activarlo, la org actual recibe un merchant profile. La comision por defecto de la plataforma es 1500 ppm, es decir 0.15%. Brazosd tambien crea una clave Ed25519 para firmar callbacks al merchant. El merchant puede llamar:

http
GET /api/v1/trade/merchant/callback-keys
Authorization: Bearer <access_token>

Usa las claves publicas devueltas para verificar las firmas de callbacks de Brazosd.

2. Generar claves de Trade API

El backend del merchant mantiene la clave privada Ed25519. Brazosd solo guarda la clave publica. La clave privada debe permanecer solo en el servidor del merchant; no la pongas en navegadores, Agent prompts, logs ni configuracion visible para usuarios.

python
from base64 import b64encode
from cryptography.hazmat.primitives.asymmetric import ed25519
from cryptography.hazmat.primitives import serialization

private_key = ed25519.Ed25519PrivateKey.generate()
public_key = private_key.public_key()

private_key_b64 = b64encode(private_key.private_bytes(
    encoding=serialization.Encoding.Raw,
    format=serialization.PrivateFormat.Raw,
    encryption_algorithm=serialization.NoEncryption(),
)).decode()

public_key_b64 = b64encode(public_key.public_bytes(
    encoding=serialization.Encoding.Raw,
    format=serialization.PublicFormat.Raw,
)).decode()

print("PRIVATE_KEY_B64=", private_key_b64)
print("PUBLIC_KEY_B64=", public_key_b64)

Registra la clave publica:

http
POST /api/v1/trade/merchant/public-keys
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "name": "prod-2026-05",
  "publicKeyB64": "<PUBLIC_KEY_B64>"
}

Guarda el id devuelto. Al crear ordenes, envialo en x-brazosd-tradeapi-pubkey-id.

3. Configurar callback URL

http
PUT /api/v1/trade/merchant/callback-url
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "url": "https://merchant.example.com/brazosd/order-callback"
}

La callback URL debe usar https://, y el host debe ser un dominio, no una direccion IP ni localhost.

4. Crear productos

Product solo guarda identidad e informacion de presentacion independiente del precio. Los precios se capturan como snapshot en las lineas de la orden cuando se crea la orden.

http
POST /api/v1/trade/products
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "name": "CRM Agent Seat",
  "description": "Monthly CRM agent seat"
}

description puede tener como maximo 200 caracteres.

5. Firmar y crear ordenes

El canonical string de la firma es:

text
METHOD
REQUEST_URI
TIMESTAMP
BODY

Por ejemplo, al llamar POST /api/v1/tradeapi/orders, REQUEST_URI es /api/v1/tradeapi/orders. TIMESTAMP esta en Unix seconds, y Brazosd valida una ventana de 5 minutos. La firma es la codificacion base64 de la firma Ed25519 raw.

python
import json
import time
import requests
from base64 import b64decode, b64encode
from cryptography.hazmat.primitives.asymmetric import ed25519

base_url = "https://brazosd.example.com"
path = "/api/v1/tradeapi/orders"
private_key = ed25519.Ed25519PrivateKey.from_private_bytes(
    b64decode("<PRIVATE_KEY_B64>")
)

body = {
    "agentId": "<agent_uuid>",
    "buyerUserId": 123,
    "outTradeNo": "merchant-order-10001",
    "description": "CRM Agent monthly usage",
    "expiresInSeconds": 1800,
    "items": [
        {
            "productId": "<product_uuid>",
            "amountMicroyuans": 199000000,
            "note": "May subscription"
        }
    ]
}
body_bytes = json.dumps(body, separators=(",", ":"), ensure_ascii=False).encode()
timestamp = str(int(time.time()))
canonical = b"POST\n" + path.encode() + b"\n" + timestamp.encode() + b"\n" + body_bytes
signature = b64encode(private_key.sign(canonical)).decode()

resp = requests.post(
    base_url + path,
    data=body_bytes,
    headers={
        "content-type": "application/json",
        "x-brazosd-tradeapi-pubkey-id": "<registered_public_key_id>",
        "x-brazosd-tradeapi-timestamp": timestamp,
        "x-brazosd-tradeapi-signature": signature,
    },
    timeout=10,
)
resp.raise_for_status()
print(resp.json())

outTradeNo es idempotente dentro del mismo merchant. El mismo outTradeNo con el mismo body devuelve la misma orden. El mismo outTradeNo con un body distinto devuelve un conflicto.

6. Autorizacion de usuario y allowlist

Despues de crear una orden, Brazosd crea un formulario de autorizacion para buyerUserId. Solo se cobra al usuario despues de confirmar. Cuando el usuario selecciona "No volver a preguntar para este tipo de orden", todos los productId de la orden se agregan a la allowlist para esa org y agent. Pagos posteriores del mismo product pueden aprobarse automaticamente.

Los gift credits solo pueden usarse para consumo de la plataforma y no para ordenes de merchants. Las ordenes de Trade API solo descuentan recharge credits de la buyer org.

7. Procesar callbacks

Cuando una orden se paga correctamente o el usuario la cancela, Brazosd envia JSON por POST a la callback URL del merchant. Brazosd firma los callbacks con Ed25519.

Headers:

text
X-Brazosd-Callback-Key-Id: <callback_key_uuid>
X-Brazosd-Callback-Timestamp: <unix_seconds>
X-Brazosd-Callback-Signature: <base64_signature>

La firma del callback usa el mismo canonical string:

text
METHOD
REQUEST_URI
TIMESTAMP
BODY

Ejemplo de verificacion:

python
from base64 import b64decode
from cryptography.hazmat.primitives.asymmetric import ed25519

def verify_brazosd_callback(method, request_uri, body_bytes, timestamp, signature_b64, public_key_b64):
    public_key = ed25519.Ed25519PublicKey.from_public_bytes(b64decode(public_key_b64))
    canonical = (
        method.upper().encode()
        + b"\n"
        + request_uri.encode()
        + b"\n"
        + timestamp.encode()
        + b"\n"
        + body_bytes
    )
    public_key.verify(b64decode(signature_b64), canonical)

El servicio del merchant debe procesar callbacks de forma idempotente por eventId o por order.id + order.status.

Si un callback falla, Brazosd reintenta la tarea una vez cada 30 minutos. Cada tarea intenta entregar hasta 3 veces con un intervalo de 5 segundos. Brazosd conserva la ultima informacion de fallo por cada callback URL. Un exito posterior no elimina ese registro de fallo; solo actualiza la hora del ultimo exito.