Metadata-Version: 2.4
Name: vanty-payments
Version: 0.2.0
Summary: Lean Stripe-first payments toolkit for FastAPI with Tortoise ORM
License-Expression: MIT
Keywords: billing,fastapi,payments,stripe,subscriptions
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: FastAPI
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Office/Business :: Financial
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.12
Requires-Dist: fastapi>=0.135.1
Requires-Dist: pydantic-settings>=2.13.1
Requires-Dist: stripe>=13.0.1
Requires-Dist: tortoise-orm>=1.1.6
Requires-Dist: vanty-core>=0.1.0
Description-Content-Type: text/markdown

# Vanty Payments

[![Tests](https://github.com/advantch/vanty-payments/actions/workflows/tests.yml/badge.svg)](https://github.com/advantch/vanty-payments/actions/workflows/tests.yml)
[![PyPI](https://img.shields.io/pypi/v/vanty-payments)](https://pypi.org/project/vanty-payments/)
[![Python](https://img.shields.io/pypi/pyversions/vanty-payments)](https://pypi.org/project/vanty-payments/)

Stripe-first payments toolkit for FastAPI with Tortoise ORM. Checkout, subscriptions, invoices, billing portal, webhooks, and full Stripe object sync in a single `pip install`.

## Installation

```bash
pip install vanty-payments
# or
uv pip install vanty-payments
```

## Quick start

```python
from contextlib import asynccontextmanager

from fastapi import FastAPI

from vanty_payments import PaymentsKitSettings, mount_payments_router

settings = PaymentsKitSettings(
    database_url="sqlite://./vanty-payments.db",
    stripe_test_secret_key="sk_test_example",
    stripe_webhook_secret="whsec_example",
)

app = FastAPI()
kit = mount_payments_router(app, settings=settings)


@asynccontextmanager
async def lifespan(_: FastAPI):
    await kit.init_orm(generate_schemas=True)
    try:
        yield
    finally:
        await kit.close_orm()


app.router.lifespan_context = lifespan
```

Run with `uvicorn main:app --reload` and visit `/docs` for the interactive API explorer.

## Features

- **Checkout sessions** with Stripe-hosted and embedded flows
- **Subscription management** with plan changes, cancellation, and reactivation
- **Invoice management** with PDF links and payment status tracking
- **Billing portal** sessions for customer self-service
- **Payment method** management (cards, bank accounts)
- **Webhook ingestion** with signature verification and idempotent processing
- **Manual sync** to pull Stripe state into local models on demand
- **Full Stripe object coverage** including customers, products, prices, coupons, promotion codes, discounts, charges, refunds, disputes, and more
- **Admin management** endpoints for super-admin billing operations
- **Identity integration** with custom identity resolvers

## Architecture

`PaymentsKit` is a composition root that wires all services. Access services directly:

```python
kit = mount_payments_router(app, settings=settings)

# Direct service access
await kit.checkout_service.create_session(customer_id=cid, price_id=pid)
await kit.subscription_service.list_subscriptions(customer_id=cid)
await kit.sync_service.sync_customer(stripe_customer_id="cus_xxx")
```

Available services on `PaymentsKit`:

| Service | Purpose |
|---|---|
| `customer_service` | Customer bootstrap and management |
| `checkout_service` | Checkout session creation |
| `subscription_service` | Subscription reads and management |
| `invoice_service` | Invoice listing and retrieval |
| `payment_method_service` | Payment method listing |
| `portal_service` | Billing portal session creation |
| `webhook_service` | Webhook signature verification and dispatch |
| `sync_service` | Manual Stripe-to-local sync for all object types |
| `admin_service` | Super-admin billing operations |

## Configuration

All settings are read from environment variables with the `PAYMENTS_KIT_` prefix, or passed directly to `PaymentsKitSettings`.

| Setting | Default | Description |
|---|---|---|
| `database_url` | `sqlite://./vanty-payments.db` | Tortoise ORM database URL |
| `stripe_test_secret_key` | — | Stripe test mode secret key |
| `stripe_live_secret_key` | — | Stripe live mode secret key |
| `stripe_webhook_secret` | — | Webhook endpoint signing secret |
| `stripe_mode` | `test` | `test` or `live` |
| `default_currency` | `usd` | Default currency for new prices |

Set via environment: `PAYMENTS_KIT_STRIPE_TEST_SECRET_KEY=sk_test_... PAYMENTS_KIT_DATABASE_URL=postgres://...`

## API surface

### Public routes (`/payments`)

| Method | Path | Description |
|---|---|---|
| `POST` | `/customers/bootstrap` | Create or retrieve a Stripe customer |
| `GET` | `/catalog/products` | List available products |
| `GET` | `/catalog/prices` | List prices for a product |
| `POST` | `/checkout/sessions` | Create a checkout session |
| `GET` | `/subscriptions` | List subscriptions |
| `GET` | `/subscriptions/{id}` | Get subscription details |
| `POST` | `/subscriptions/{id}/cancel` | Cancel a subscription |
| `POST` | `/portal/sessions` | Create a billing portal session |
| `GET` | `/invoices` | List invoices |
| `GET` | `/invoices/{id}` | Get invoice details |
| `GET` | `/payment-methods` | List payment methods |
| `POST` | `/webhooks/stripe` | Stripe webhook endpoint |
| `POST` | `/sync` | Trigger manual sync |

### Admin routes (`/admin/payments`)

Super-admin endpoints for platform-wide billing management. Mount separately:

```python
app.include_router(kit.admin_router, prefix="/admin/payments")
```

## Host app identity integration

The package works standalone, but a host app can supply a request-level identity adapter through `app.state.payments_identity_resolver`:

```python
async def resolve_identity(request: Request) -> IdentityContext:
    return IdentityContext(
        user_reference=request.state.user_id,
        organization_reference=request.state.org_id,
    )

app.state.payments_identity_resolver = resolve_identity
```

Routes then use those references automatically when request payloads omit them.

## Project layout

```text
src/vanty_payments/       Package source
docs/                     Documentation
examples/reference-api/   Reference FastAPI application
tests/                    pytest suite (unit + integration)
```

## Development

```bash
git clone https://github.com/advantch/vanty-payments.git
cd vanty-payments
uv sync --dev

# Lint
uv run ruff check

# Test
uv run pytest -q

# Run reference API
cd examples/reference-api && uv run uvicorn main:app --reload
```

Use `uv run pytest --no-cov tests/path/to/test.py` for targeted debugging without the coverage gate.

## Releasing

Releases are automated via GitHub Actions. To publish a new version:

1. Update the version in `pyproject.toml`:

   ```toml
   version = "0.2.0"
   ```

2. Commit and tag:

   ```bash
   git add pyproject.toml
   git commit -m "release: v0.2.0"
   git tag v0.2.0
   git push origin main --tags
   ```

3. The `release.yml` workflow will automatically:
   - Build the package
   - Create a GitHub Release with auto-generated notes
   - Publish to PyPI via trusted publishing (OIDC)

### PyPI trusted publishing setup

To enable automated publishing, configure a trusted publisher on PyPI:

1. Go to [pypi.org/manage/account/publishing](https://pypi.org/manage/account/publishing/)
2. Add a new pending publisher (or update an existing project):
   - **PyPI project name**: `vanty-payments`
   - **Owner**: `advantch`
   - **Repository**: `vanty-payments`
   - **Workflow name**: `release.yml`
   - **Environment name**: `release`

No API tokens are needed once trusted publishing is configured.

## License

MIT
