---
title: "Integração de Pagamentos com PIX em Python: Guia Prático"
url: "https://python.dev.br/blog/integracao-pagamentos-pix-python/"
markdown_url: "https://python.dev.br/blog/integracao-pagamentos-pix-python.MD"
description: "Aprenda a integrar pagamentos por PIX em Python: criar cobrança com QR Code e Copia e Cola, receber webhooks com FastAPI, validar assinatura, garantir idempotência e segurança."
date: "2026-06-27"
author: "Equipe Python Brasil"
---

# Integração de Pagamentos com PIX em Python: Guia Prático

Aprenda a integrar pagamentos por PIX em Python: criar cobrança com QR Code e Copia e Cola, receber webhooks com FastAPI, validar assinatura, garantir idempotência e segurança.


Integrar pagamentos por PIX em Python é hoje uma das habilidades mais pedidas em vagas de backend e fintech no Brasil. Desde 2020, o PIX virou o meio de pagamento padrão para transferências imediatas, e quase todo sistema que vende algo — e-commerce, SaaS, assinatura, marketplace, doação — precisa gerar uma cobrança, mostrar um QR Code e confirmar o pagamento de forma automática.

A boa notícia é que a integração não é difícil do ponto de vista de código: o fluxo gira em torno de chamadas HTTP, webhooks e uma chave de idempotência. O desafio real está nos detalhes que importam em produção — validar a assinatura do webhook, evitar cobrança duplicada, tratar concorrência, guardar segredos de forma segura e conciliar o que foi pago com o que foi prometido. Neste guia vamos montar esse fluxo inteiro em Python, com FastAPI para o webhook e boas práticas que valem para qualquer PSP (provedor de serviço de pagamento).

## Como o PIX funciona para quem integra

Do ponto de vista técnico, o fluxo de uma cobrança PIX tem quatro peças:

1. **Cobrança**: você cria uma cobrança no PSP e recebe um `txid` (identificador único da transação).
2. **BR Code / Copia e Cola**: o PSP devolve uma string no padrão EMV — o famoso "PIX Copia e Cola" — que o cliente cola no app do banco.
3. **QR Code**: a mesma string pode virar uma imagem de QR Code, lida pela câmera do celular.
4. **Webhook de confirmação**: quando o cliente paga, o PSP chama o seu endpoint avisando que o `txid` foi liquidado.

O detalhe que confunde muita gente: **a confirmação nunca vem como resposta direta da criação da cobrança**. A criação só reserva a cobrança; o dinheiro chega depois, de forma assíncrona, via webhook. Por isso, tratar webhook com idempotência é a parte mais importante de toda a integração.

## Banco Central direto ou PSP?

Você pode integrar o PIX de duas formas. A escolha muda bastante o nível de esforço:

| Caminho | Quando usar | Exige |
|---|---|---|
| **Banco Central direto (API Pix)** | Instituições financeiras, fintechs com conta institucional | Certificado ICP-Brasil, mTLS, conta regulada pelo BCB |
| **PSP (Mercado Pago, Asaas, Gerencianet, Pagar.me)** | E-commerce, SaaS, startups, qualquer empresa | Conta no PSP, access_token, HTTPS no webhook |

Para a esmagadora maioria dos projetos em Python, o caminho é o **PSP**. Ele abstrai o certificado, o mTLS e a comunicação direta com o Bacen, e expõe uma API REST simples com HTTPS. Vamos seguir por aqui.

## Preparando o ambiente

Comece com um ambiente isolado, como já mostramos no guia de [ambientes virtuais em Python](/blog/virtual-environments-python/). Com `uv`, o setup é rápido:

```bash
uv init pix-python
cd pix-python
uv add fastapi "uvicorn[standard]" httpx pydantic qrcode python-dotenv
```

Use [HTTPX](/blog/python-httpx-requests-moderno/) como cliente HTTP: ele suporta `async`, timeouts configuráveis e retries, fundamentais para não travar quando o PSP demora a responder. Para guardar chaves de API com segurança, leia o guia de [variáveis de ambiente com python-dotenv](/blog/python-dotenv-env-vars-config/) — nunca coloque `access_token` no código.

## Criando uma cobrança com QR Code e Copia e Cola

A estrutura de uma cobrança é parecida entre os PSPs. O exemplo abaixo usa um cliente HTTPX genérico que funciona com a maioria das APIs. Ajuste o `base_url` e o corpo para o PSP que você escolher (Mercado Pago, Asaas, Gerencianet e Pagar.me seguem o mesmo padrão geral):

```python
import os
import httpx
from dataclasses import dataclass

PSP_BASE_URL = os.environ["PSP_BASE_URL"]      # ex.: https://api.asaas.com ou https://api.mercadopago.com
PSP_TOKEN = os.environ["PSP_TOKEN"]            # access_token do PSP

@dataclass
class Cobranca:
    txid: str
    copia_e_cola: str           # string "PIX Copia e Cola" (BR Code)
    qr_code_base64: str         # imagem do QR Code em base64

async def criar_cobranca(cliente_id: str, valor: float, descricao: str) -> Cobranca:
    headers = {"Authorization": f"Bearer {PSP_TOKEN}", "Content-Type": "application/json"}
    payload = {
        "customer": cliente_id,
        "billingType": "PIX",
        "value": valor,
        "description": descricao,
    }
    async with httpx.AsyncClient(timeout=10.0) as client:
        resp = await client.post(f"{PSP_BASE_URL}/payments", json=payload, headers=headers)
        resp.raise_for_status()
        data = resp.json()

    # Cada PSP nomeia os campos de forma ligeiramente diferente:
    #   Asaas:       payload (Copia e Cola), encodedImage (QR Code base64)
    #   Mercado Pago:  qr_code / ticket_url + qr_code_base64
    return Cobranca(
        txid=data["id"],
        copia_e_cola=data.get("payload") or data.get("qr_code"),
        qr_code_base64=data.get("encodedImage") or data.get("qr_code_base64", ""),
    )
```

Salve o `txid` no banco associado ao pedido. Ele é a sua chave de idempotência: qualquer evento futuro (webhook, consulta de status, reembolso) referencia esse mesmo identificador.

Para gerar o QR Code localmente a partir do Copia e Cola (útil quando o PSP só devolve a string), use a biblioteca `qrcode`:

```python
import qrcode

def salvar_qrcode(copia_e_cola: str, caminho: str) -> None:
    img = qrcode.make(copia_e_cola)
    img.save(caminho)
```

## Recebendo o webhook de confirmação com FastAPI

A peça central da integração é o endpoint que recebe a notificação de pagamento. O PSP chama esse URL sempre que uma cobrança é liquidada — e pode chamar **mais de uma vez** para a mesma transação (retry em falha de rede). Por isso, idempotência é obrigatória.

O exemplo abaixo usa [FastAPI](/blog/apis-rest-com-fastapi/) e segue o mesmo padrão de robustez que mostramos no guia de [webhooks com FastAPI](/guias/webhooks-fastapi-crm/):

```python
from fastapi import FastAPI, Request, HTTPException, BackgroundTasks
from pydantic import BaseModel

app = FastAPI()

# status por txid — na prática, um banco de dados
status_pedidos: dict[str, str] = {}

class NotificacaoPix(BaseModel):
    event: str
    payment: dict | None = None

@app.post("/webhook/pix")
async def webhook_pix(req: Request, background: BackgroundTasks):
    # 1. Validar assinatura antes de confiar no conteúdo
    if not assinatura_valida(req):
        raise HTTPException(status_code=401, detail="Assinatura inválida")

    body = await req.json()
    txid = body.get("payment", {}).get("id")
    if not txid:
        raise HTTPException(status_code=400, detail="txid ausente")

    # 2. Idempotência: ignorar se já processado
    if status_pedidos.get(txid) == "paid":
        return {"status": "ok", "duplicado": True}

    # 3. Confirmar de forma resiliente, fora do ciclo do webhook
    background.add_task(confirmar_pagamento, txid)
    return {"status": "ok"}

async def confirmar_pagamento(txid: str) -> None:
    # Consulte o PSP para confirmar o status real (não confie só no webhook)
    status = await consultar_status_no_psp(txid)
    if status == "paid":
        status_pedidos[txid] = "paid"
        # aqui: liberar pedido, enviar e-mail, atualizar estoque...
```

Do pontos críticos desse código:

- **Sempre valide a assinatura** do webhook (header de assinatura/HMAC enviado pelo PSP). Sem isso, qualquer um pode simular um pagamento pago.
- **Sempre confirme consultando o PSP** antes de liberar o pedido. O webhook avisa; a consulta confirma. Isso protege contra webhooks forjados e contra falsos positivos.
- **Processe de forma assíncrona** (`BackgroundTasks` ou uma fila). O webhook precisa responder rápido (`200 OK`), senão o PSP faz retry e você processa o mesmo evento várias vezes.

## Segurança que não pode faltar

Integração financeira é [segurança aplicada](/blog/python-seguranca-automacao-cloud/). Some pontos não negociáveis:

- **Chaves em variáveis de ambiente**, nunca no código nem no Git. Use `python-dotenv` local e o cofre de segredos do provedor (AWS Secrets Manager, Cloudflare, etc.) em produção.
- **Valide a assinatura de todo webhook**. Cada PSP documenta qual header carrega a assinatura e qual segredo usar no HMAC.
- **Não registre dados sensíveis** em log: CPF, e-mail, número de cartão, chave PIX de terceiros. O próprio Bacen recomenda anonimizar. O guia de [conciliação financeira com Python](/blog/conciliacao-financeira-python/) mostra como usar dados sintéticos em testes para nunca vazar informação real.
- **Use HTTPS em todo o caminho**, do PSP até o seu banco de dados.

## Conciliação, testes e erros comuns

Mesmo com webhook perfeito, você ainda precisa **conciliar** periodicamente: comparar o que o PSP diz que foi pago com o que o seu sistema registrou. PIX pode cair no mesmo dia, mas aparecer com descrição diferente; webhook pode se perder; cliente pode pagar após o vencimento. Esse casamento de dados é exatamente o que cobrimos no guia de [conciliação financeira](/blog/conciliacao-financeira-python/) — leia em conjunto.

Para testar, cubra o fluxo inteiro com [pytest](/guias/testes-com-pytest/): criação de cobrança, tratamento de webhook válido, webhook duplicado (idempotência) e assinatura inválida (rejeição). Os PSPs oferecem ambientes de sandbox com chaves de teste — use-os sempre.

Erros comuns que valem o checklist final:

- confiar só no webhook sem consultar o PSP (falso positivo);
- não tratar `txid` duplicado (cobrança processada duas vezes);
- responder o webhook com lentidão (PSP dispara retries);
- deixar o `access_token` no repositório (vazamento de credencial);
- não validar a assinatura (qualquer pessoa simula pagamento).

## Portfólio e carreira

Uma integração de PIX ponta a ponta — cobrança, QR Code, webhook com idempotência, validação de assinatura e conciliação — é um dos projetos mais fortes que você pode colocar em um portfólio para vagas de backend Python. Ela mostra domínio de APIs assíncronas, segurança aplicada, modelagem de dados e atenção a detalhes de produção. Se quiser montar isso como peça de portfólio, o guia de [projetos de portfólio em Python](/carreira/projetos-portfolio-python/) traz a estrutura certa, e o [roadmap Python 2026](/carreira/roadmap-python-2026/) mostra onde pagamentos se encaixam na trilha de carreira.

Vagas de fintech e backend frequentemente listam "integração de meios de pagamento" como requisito — confira as oportunidades na nossa página de [vagas de Python em fintech](/vagas/fintech/). Dominar esse fluxo coloca você à frente de boa parte dos candidatos.

## Conclusão

Integrar PIX em Python é, no fundo, um exercício de disciplina: HTTPX para criar a cobrança, QR Code para o cliente, webhook com FastAPI para confirmar, idempotência por `txid` para evitar duplicidade e validação de assinatura para segurança. O código é curto; o que sustenta a integração em produção são as decisões de idempotência, conciliação e proteção de dados. Comece com um PSP em sandbox, monte o fluxo completo ponta a ponta e adicione testes e conciliação antes de qualquer coisa ir ao ar.

### Perguntas frequentes

#### Preciso de certificado digital para integrar PIX em Python?
Depende do caminho. A integração direta com o Banco Central (API Pix) exige conta institucional e certificado ICP-Brasil com mTLS, o que não se aplica a pessoas físicas ou empresas comuns. Integrar via PSP (Mercado Pago, Asaas, Gerencianet, Pagar.me) usa apenas HTTPS e access_token, sem certificado.

#### Como evito processar o mesmo pagamento duas vezes?
Use o `txid` da cobrança como chave de idempotência e persista o status do pagamento no banco antes de atualizar o pedido. Se o webhook chegar repetido, a segunda ocorrência encontra o status já confirmado e é ignorada.

#### Qual biblioteca Python uso para gerar o QR Code do PIX?
A maioria dos PSPs já retorna a string Copia e Cola e o QR Code em base64 na resposta da cobrança, então você apenas salva a imagem. Para gerar o BR Code localmente, a biblioteca `qrcode` do Python monta a imagem a partir do payload.

#### Posso receber webhook de PIX em produção sem domínio HTTPS?
Não. O PSP exige um endpoint público com HTTPS válido para entregar a notificação. Em desenvolvimento, use um túnel como ngrok ou Cloudflare Tunnel para expor o FastAPI local.
