Metadata-Version: 2.4
Name: mupag-sdk
Version: 0.2.0
Summary: SDK oficial Python para integrar com a API pública da MuPag.
Project-URL: Homepage, https://docs.mupag.com.br
Project-URL: Documentation, https://docs.mupag.com.br
Author: MuPag
License: Proprietary
Keywords: mupag,payments,pix,sdk
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: httpx<1.0,>=0.27
Requires-Dist: pydantic<3.0,>=2.7
Requires-Dist: tenacity<10.0,>=8.3
Provides-Extra: dev
Requires-Dist: coverage[toml]>=7.5; extra == 'dev'
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8.2; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Requires-Dist: ruff>=0.5; extra == 'dev'
Description-Content-Type: text/markdown

# mupag-sdk

SDK oficial Python para integrar backends, automacoes, scripts e notebooks com a API publica da MuPag.

Ele foi desenhado para deixar o caminho feliz curto: instala, cria um cliente com `sk_test_*`, chama `mupag.charges.create(...)` e recebe um objeto tipado. Sem montar `httpx` na mao, sem decorar header de idempotencia, sem parsing manual de erro, sem validar webhook no improviso.

## Por que integrar com a SDK é simples

- API com cara de produto: `mupag.charges.create(...)`, `mupag.subscriptions.cancel(..., mode="immediate")` e `mupag.webhooks.construct_event(...)`.
- Tipos Pydantic v2 para validar request/response cedo, ainda no seu codigo.
- Cliente sync e async com a mesma ergonomia.
- Idempotencia automatica em operacoes mutaveis e chave explicita para identificar a operacao de negocio.
- Retry interno com a mesma chave para 429/5xx, `Retry-After` limitado e backoff curto.
- Erros tipados com `code`, `suggestion`, `documentation_url` e `request_id`, prontos para log/suporte.
- Webhook HMAC-SHA256 em tempo constante, com janela anti-replay padrao de 5 minutos.

O resultado esperado para quem integra e: poucas linhas para receber um PIX de teste e informacoes suficientes para resolver erro sem abrir ticket.

## Instalação

```bash
pip install mupag-sdk
```

## Migração da SDK MuPay

Substitua `pip install mupay-sdk` por `pip install mupag-sdk` e atualize os imports de
`from mupay_sdk` para `from mupag_sdk`. A API pública permanece a mesma nesta migração.

Requisitos:

- Python 3.10 ou superior.
- Uma API key `sk_test_*` ou `sk_prd_*`.
- Dependencias runtime pequenas: `httpx`, `pydantic` v2 e `tenacity`.

## Quickstart

```python
from mupag_sdk import MuPagClient

mupag = MuPagClient(api_key="sk_test_...", environment="test")

charge = mupag.charges.create(
    amount_cents=12000,
    payment_method="pix",
    customer={
        "id": "22222222-2222-4222-8222-222222222222",
        "name": "Ana Silva",
        "email": "ana@example.com",
        "tax_id": "12345678901",
    },
    idempotency_key="order_123_charge_1",
)

print(charge.pix_emv_code)
```

## Cliente async

```python
from mupag_sdk import AsyncMuPagClient


async def main() -> None:
    async with AsyncMuPagClient(api_key="sk_test_...", environment="test") as mupag:
        charge = await mupag.charges.create(
            amount_cents=12000,
            payment_method="pix",
            customer={
                "id": "22222222-2222-4222-8222-222222222222",
                "name": "Ana Silva",
                "email": "ana@example.com",
                "tax_id": "12345678901",
            },
        )
        print(charge.charge_id)
```

## Erros tipados

Erros HTTP da API viram `MuPagAPIError`. O erro preserva `status_code`, `code`, `suggestion`, `documentation_url` e `request_id` quando a API envia Problem Details.

A chave automatica e reutilizada apenas nos retries internos da mesma invocacao. Se uma mutacao
puder ter sido aceita e a resposta final se perder, o SDK levanta `MuPagOutcomeUnknownError` com
`outcome_unknown=True` e a `idempotency_key` efetivamente enviada. Persista essa chave e reconcilie
ou repita exatamente o mesmo payload. Para pagamentos, prefira uma chave derivada do ID imutavel da
operacao de negocio desde a primeira chamada.

Retries limitados cobrem transporte, `408`, `425`, `429`, `5xx` e
`409/idempotency_in_progress`, sempre com a mesma chave, backoff exponencial com jitter e
`Retry-After` limitado. `409/idempotency_outcome_unknown` gera unknown imediatamente;
`fingerprint_conflict` e os demais `4xx` nao classificados como ambiguos so sao definitivos quando
nenhuma tentativa anterior ficou ambigua. A ambiguidade e sticky: apenas um `2xx` parseavel e economicamente valido confirma a
mutacao; um `4xx`, `409` ou `429` posterior nao a apaga.

```python
from mupag_sdk import MuPagClient
from mupag_sdk.errors import MuPagAPIError, MuPagOutcomeUnknownError

mupag = MuPagClient(api_key="sk_test_...", environment="test")

try:
    mupag.charges.create(
        amount_cents=12000,
        payment_method="credit_card",
        card_token="tok_test",
        payer_ip="203.0.113.10",
        customer={
            "id": "22222222-2222-4222-8222-222222222222",
            "name": "Ana Silva",
            "email": "ana@example.com",
            "tax_id": "12345678901",
        },
    )
except MuPagOutcomeUnknownError as exc:
    persist_for_reconciliation(exc.idempotency_key)
except MuPagAPIError as exc:
    print(exc.code, exc.request_id, exc.suggestion)
```

Em cartão, `payer_ip` é o IP literal do pagador observado no checkout e atestado pelo merchant,
nunca o IP do servidor que chama a MuPag. O contrato atual aceita uma parcela, rejeita
`soft_descriptor` e falha fechado quando o merchant exige 3DS.

## Webhooks

Valide a assinatura antes de confiar no payload. O helper usa HMAC-SHA256 em tempo constante e bloqueia replay fora da janela configurada.

```python
from mupag_sdk.webhooks import construct_event

event = construct_event(
    request_body,
    request_headers["mupag-signature"],
    "whsec_...",
)

print(event.type)
```

O header canonico e `MuPag-Signature`, e o corpo validado segue `{ "id": string, "type": string, "data": object }`.

## Cancelamento de assinatura

O SDK envia o mesmo payload exigido pela API publica: `mode` é obrigatório e `reason` é opcional.

```python
from mupag_sdk import MuPagClient

mupag = MuPagClient(api_key="sk_test_...", environment="test")

subscription = mupag.subscriptions.cancel(
    "sub_123",
    mode="immediate",
    reason="cliente pediu cancelamento",
)

print(subscription.status)
```

## Fluxo completo em menos de 5 minutos

1. Instale o pacote: `pip install mupag-sdk`.
2. Crie uma API key sandbox no dashboard da MuPag.
3. Exporte a chave: `set MUPAG_API_KEY=sk_test_...` no Windows ou `export MUPAG_API_KEY=sk_test_...` no Linux/macOS.
4. Rode `python examples/create_pix_charge.py`.
5. Copie o `pix_emv_code` retornado para simular pagamento no sandbox.

Essa e a experiencia que a SDK precisa proteger: o integrador nao deve precisar conhecer headers internos, formato exato de Problem Details ou detalhes de retry para criar a primeira cobranca.

## Exemplos completos

- `examples/create_pix_charge.py`
- `examples/async_create_charge.py`
- `examples/verify_webhook.py`

## Desenvolvimento local

```bash
python -m pip install -e .[dev]
python -m pytest
python -m coverage run -m pytest
python -m coverage report
python -m mypy .
python -m ruff check .
```

## Publicação PyPI manual

Este PR não adiciona build/publicação automática no GitHub Actions. A publicação pública inicial deve ser manual para evitar release acidental enquanto o contrato da API ainda está amadurecendo.

Para publicar publicamente:

1. Crie conta e verifique email em PyPI e TestPyPI.
2. Garanta que o nome `mupag-sdk` está disponível ou que a organização MuPag controla esse nome.
3. Atualize `version` em `pyproject.toml` usando versionamento semântico.
4. Rode a validação local:

```bash
python -m pip install -e .[dev]
python -m ruff check .
python -m mypy .
python -m coverage run -m pytest
python -m coverage report
uv build
```

5. Publique primeiro no TestPyPI:

```bash
python -m pip install twine
python -m twine upload --repository testpypi dist/*
```

6. Instale em ambiente limpo e rode um smoke test:

```bash
python -m pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ mupag-sdk
python -c "from mupag_sdk import MuPagClient; print(MuPagClient)"
```

7. Se o smoke test passar, publique no PyPI:

```bash
python -m twine upload dist/*
```

Use token de API com escopo limitado ao projeto `mupag-sdk` quando o projeto já existir. Nunca coloque token PyPI em código, README, commit ou workflow.
