> ## Documentation Index
> Fetch the complete documentation index at: https://dev.valorapayments.com.br/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Valora Payments API (Brazil: Pix, card, boleto). Base URL https://api.valorapayments.com.br, Authorization: Bearer sk_vl_... used only on the server. Request amounts are reais as strings ("149.90"); response amounts are integer cents (14990). Use POST /v1/checkouts for card and boleto; POST /v1/payments only creates Pix and should send an Idempotency-Key. Confirm payments only through the payment.succeeded webhook after verifying X-Valora-Signature (hex HMAC-SHA256 of "<t>.<raw body>"), dedupe by event.id and check livemode. Never invent endpoints or fields: check /llms.txt and /openapi.json.

# Python com FastAPI

> Integre a Valora numa API FastAPI com httpx: crie compras e Pix e confirme os pagamentos pelo webhook.

Neste tutorial, você monta numa API FastAPI:

* `POST /checkout`: cria a compra e devolve o link do checkout;
* `POST /pix`: cria uma cobrança Pix;
* `POST /api/webhooks/valora`: recebe e verifica os eventos.

## Pré-requisitos

* Python 3.10+.
* As variáveis de [Antes de começar](https://dev.valorapayments.com.br/frameworks/visao-geral.md#antes-de-começar).

```bash theme={null}
pip install fastapi uvicorn httpx python-dotenv
```

## Integração

**1. Crie o cliente da Valora**

```python valora.py theme={null}
import os
import httpx

BASE_URL = "https://api.valorapayments.com.br"


class ValoraError(Exception):
    def __init__(self, status: int, type_: str | None, message: str):
        super().__init__(message)
        self.status, self.type = status, type_


async def valora(method: str, path: str, json: dict | None = None, idempotency_key: str | None = None) -> dict:
    headers = {"Authorization": f"Bearer {os.environ['VALORA_API_KEY']}"}
    if idempotency_key:
        headers["Idempotency-Key"] = idempotency_key
    async with httpx.AsyncClient(base_url=BASE_URL, timeout=20) as client:
        res = await client.request(method, path, json=json, headers=headers)
    data = res.json()
    if res.is_error:
        err = data.get("error", {})
        raise ValoraError(res.status_code, err.get("type"), err.get("message", f"HTTP {res.status_code}"))
    return data
```

**2. Crie as rotas**

```python main.py theme={null}
import hashlib
import hmac
import json
import os
import time
import uuid

from dotenv import load_dotenv
from fastapi import FastAPI, Header, HTTPException, Request
from pydantic import BaseModel

from valora import ValoraError, valora

load_dotenv()
app = FastAPI()


class CheckoutIn(BaseModel):
    email: str | None = None


@app.post("/checkout")
async def checkout(body: CheckoutIn):
    order_id = str(uuid.uuid4())
    try:
        session = await valora("POST", "/v1/checkouts", json={
            "product_cart": [{"product_id": os.environ["VALORA_PRODUCT_ID"], "quantity": 1}],
            **({"customer": {"email": body.email}} if body.email else {}),
            "success_url": f"{os.environ['APP_URL']}/pedido/{order_id}",
            "cancel_url": f"{os.environ['APP_URL']}/",
            "external_reference": order_id,
        })
    except ValoraError as e:
        raise HTTPException(status_code=502 if e.status >= 500 else e.status, detail=e.type)
    return {"url": session["checkout_url"]}


class PixIn(BaseModel):
    order_id: str


@app.post("/pix")
async def pix(body: PixIn):
    amount = "149.90"  # calcule no servidor a partir do pedido
    payment = await valora("POST", "/v1/payments", json={
        "method": "pix",
        "amount": amount,
        "external_reference": f"pedido-{body.order_id}",
    }, idempotency_key=f"pedido-{body.order_id}")
    return {"id": payment["id"], "qr_image": payment["pix"]["qr_image"], "brcode": payment["pix"]["brcode"]}


def signature_ok(raw: bytes, header: str | None, secret: str, tolerance: int = 300) -> bool:
    parts = dict(p.split("=", 1) for p in (header or "").split(",") if "=" in p)
    try:
        t = int(parts["t"])
    except (KeyError, ValueError):
        return False
    if abs(time.time() - t) > tolerance:
        return False
    expected = hmac.new(secret.encode(), f"{t}.".encode() + raw, hashlib.sha256).hexdigest()
    return hmac.compare_digest(parts.get("v1", ""), expected)


@app.post("/api/webhooks/valora")
async def webhook(request: Request, x_valora_signature: str | None = Header(default=None)):
    raw = await request.body()  # corpo cru, antes de qualquer parse
    if not signature_ok(raw, x_valora_signature, os.environ["VALORA_WEBHOOK_SECRET"]):
        raise HTTPException(status_code=400, detail="assinatura inválida")

    event = json.loads(raw)
    # Ignore eventos repetidos pelo event["id"] antes de agir.
    if event["type"] == "payment.succeeded":
        payment = event["data"]["object"]
        # await orders.mark_paid(payment["external_reference"], payment["id"])
    return {"ok": True}
```

## Teste

```bash theme={null}
uvicorn main:app --reload --port 8000
ngrok http 8000
```

Cadastre `https://SEU-TUNEL/api/webhooks/valora` em **Desenvolvedores → Webhooks**, faça uma compra de teste e confira a entrega com status `200`.

> [!TIP]
>
> Usa Django? A lógica é a mesma: leia o corpo cru com `request.body`, marque a view com `@csrf_exempt` e use a função `signature_ok` acima.

## Próximos passos

> [card] [Webhooks](https://dev.valorapayments.com.br/integrar/webhooks.md)
>
> Novas tentativas, reenvio e boas práticas.

> [card] [Checklist de produção](https://dev.valorapayments.com.br/integrar/producao.md)
>
> O que conferir antes de vender de verdade.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.