Metadata-Version: 2.4
Name: commercexl
Version: 0.3.1
Summary: Composable commerce core for products, order items, and payments.
Author: Artasov
License-Expression: MPL-2.0
Project-URL: Homepage, https://github.com/Artasov/commercexl
Project-URL: Repository, https://github.com/Artasov/commercexl
Project-URL: Issues, https://github.com/Artasov/commercexl/issues
Keywords: commerce,fastapi,sqlalchemy,payments,orders,checkout
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Framework :: FastAPI
Classifier: Topic :: Software Development :: Libraries
Classifier: Typing :: Typed
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: NOTICE
License-File: TRADEMARKS.md
Requires-Dist: fastapi>=0.115.0
Requires-Dist: pydantic>=2.9.0
Requires-Dist: sqlalchemy>=2.0.0
Provides-Extra: test
Requires-Dist: aiosqlite>=0.20.0; extra == "test"
Requires-Dist: httpx>=0.28.0; extra == "test"
Requires-Dist: pytest>=8.0.0; extra == "test"
Requires-Dist: pytest-asyncio>=1.0.0; extra == "test"
Provides-Extra: dev
Requires-Dist: aiosqlite>=0.20.0; extra == "dev"
Requires-Dist: build>=1.2.2; extra == "dev"
Requires-Dist: httpx>=0.28.0; extra == "dev"
Requires-Dist: mypy>=1.11.0; extra == "dev"
Requires-Dist: pytest>=8.0.0; extra == "dev"
Requires-Dist: pytest-asyncio>=1.0.0; extra == "dev"
Requires-Dist: ruff>=0.11.0; extra == "dev"
Requires-Dist: twine>=6.1.0; extra == "dev"
Dynamic: license-file

<p align="right">
  <strong>English</strong> · <a href="./README.ru.md">Русский</a>
</p>

<p align="center">
  <a href="https://orcestr.com">
    <img src="./assets/orcestr-banner.webp" alt="CommerceXL banner" width="100%" />
  </a>
</p>

# CommerceXL

[![PyPI](https://img.shields.io/pypi/v/commercexl)](https://pypi.org/project/commercexl/)
[![CI](https://github.com/Artasov/commercexl/actions/workflows/ci.yml/badge.svg)](https://github.com/Artasov/commercexl/actions/workflows/ci.yml)
[![License: MPL 2.0](https://img.shields.io/badge/License-MPL_2.0-brightgreen.svg)](./LICENSE)

Composable commerce backend foundation for the [Orcestr](https://orcestr.com) ecosystem.

CommerceXL provides reusable catalog, order, balance and payment primitives for FastAPI,
Pydantic 2 and SQLAlchemy 2 applications. The host application owns the database engine,
sessions, users, authentication, CSRF policy, migrations and provider-specific callback routes.

## Status

| Item | Value |
| --- | --- |
| Package | `commercexl` |
| Version | `0.3.1` |
| Status | Beta, breaking from 0.2 |
| Runtime | Python 3.12+ |
| Frameworks | FastAPI, SQLAlchemy 2, Pydantic 2 |

## What Is Included

| Area | Includes |
| --- | --- |
| Catalog | products, exact decimal prices and extensible product services |
| Orders | multi-item orders with canonical order and item states |
| Payments | canonical payment attempts, strict provider registry and typed checkout actions |
| Lifecycle | idempotent create, verification, cancellation, refund and exact-once product finalization |
| Balances | internal credit balances and currency conversion settings |
| Promotions | promocodes and gift-certificate foundations |
| Events | transactional payment outbox and globally unique provider evidence claims |
| HTTP | auth-neutral FastAPI router assembled through `create_router(...)` |
| Persistence | typed SQLAlchemy models exposed through `CommerceBase` |

CommerceXL does not include a frontend, wallet integration, blockchain verifier or payment-provider
credentials. Those belong in separate provider packages and host adapters.

## Installation

```bash
pip install commercexl
```

Optional development dependencies:

```bash
pip install "commercexl[test]"
pip install "commercexl[dev]"
```

## Quick Start

```python
from decimal import Decimal

from commercexl import (
    BaseConfig,
    CommerceModule,
    DefaultOrderItemService,
    HandMadePaymentService,
    PaymentConfigBuilder,
    PaymentProviderRegistration,
    ProductOrderConfig,
    ProductOrderConfigBuilder,
)


class ProjectCommerceConfig(BaseConfig):
    PAYMENT_SYSTEMS = {"USD": ("handmade",)}
    MIN_TOP_UP_AMOUNTS = {"USD": Decimal("1")}
    CREDITS_CONVERTERS = {"USD": Decimal("10000")}


commerce = CommerceModule(
    config_class=ProjectCommerceConfig,
    product_orders=ProductOrderConfigBuilder(
        ProductOrderConfig(MyProductService, DefaultOrderItemService),
    ),
    payments=PaymentConfigBuilder(
        PaymentProviderRegistration(
            system="handmade",
            provider_kind="handmade",
            factory=HandMadePaymentService,
        ),
    ),
    public_base_url="https://commerce.example.com",
)
```

Provider registration is strict. A duplicate normalized `system`, a missing provider referenced by
`PAYMENT_SYSTEMS`, or a factory returning the wrong service type fails during module construction.

## FastAPI Integration

The host supplies an authenticated actor dependency and a mandatory mutation guard. For
cookie-authenticated applications, the mutation guard is the host CSRF dependency.

```python
from fastapi import Depends
from commercexl import CommerceHTTPConfig, CommerceUserActorDTO, create_router


async def get_commerce_actor(user=Depends(get_current_user)) -> CommerceUserActorDTO:
    return CommerceUserActorDTO(id=user.id, permissions=frozenset(user.permissions))


app.include_router(
    create_router(
        CommerceHTTPConfig(
            get_db_session_dependency=get_db_session,
            get_current_actor_dependency=get_commerce_actor,
            get_mutation_guard_dependency=check_csrf,
            get_commerce_module=lambda: commerce,
        ),
    ),
    prefix="/api/v1",
)
```

The checkout is intentionally two-phase:

1. `POST /orders/` creates a server-priced order and requires `Idempotency-Key`.
2. `GET /orders/{order_id}/payment-options/` returns options available to that actor and order.
3. `POST /orders/{order_id}/payment-attempts/` accepts only `payment_option_id` and another
   `Idempotency-Key`.
4. `GET /payments/{payment_public_id}/` returns authoritative state without issuing a secret.
5. `POST /payments/{payment_public_id}/checkout-action/` issues a fresh provider action.

Amounts are `Decimal` in Python and decimal strings in JSON. The client cannot submit the final
payment amount, commercial currency or arbitrary provider system when creating an attempt.

## Provider Contract

Provider packages implement `AbstractPaymentService` or `AbstractCallbackPaymentService` and are
registered through `PaymentProviderRegistration`. The stable provider-facing imports include
`PaymentCreateContext`, `PaymentCreateResult`, `PaymentOption`, `CheckoutAction`,
`PaymentVerificationResult` and `PaymentState`.

The canonical `PaymentORM` is the extension root for provider child tables. Providers do not mutate
orders or mark them paid directly. A trusted callback/reconciliation adapter returns a typed
verification result and passes it to `PaymentRuntime.apply_verification(...)` in the current DB
session.

Checkout capability URLs and transaction-request bearer values must be issued by
`get_action(...)`; CommerceXL persists only non-secret action metadata. Safe provider evidence is
claimed globally and payment changes write `PaymentOutboxEventORM` in the same transaction.

## Database Migrations

CommerceXL does not ship application migrations. Add its metadata to the host Alembic setup and
generate/review migrations in the host repository:

```python
from commercexl import CommerceBase
from my_project.db import Base

target_metadata = [Base.metadata, CommerceBase.metadata]
```

Version 0.3 is a deliberate breaking schema/API release. Follow the
[0.2 to 0.3 migration guide](./src/commercexl/docs/MIGRATION_0_3.md) before upgrading production
data.

## Documentation

- [Integration guide](./src/commercexl/docs/HOW_TO_USE.md)
- [0.3 migration guide](./src/commercexl/docs/MIGRATION_0_3.md)
- [Promocodes](./src/commercexl/docs/PROMOCODES.md)
- [Gift certificates](./src/commercexl/docs/GIFT_CERTIFICATES.md)
- [Release guide](./RELEASE_GUIDE.md)

## Development

```bash
uv sync --all-extras
uv run pytest -q
uv build
```

## License

Licensed under the [Mozilla Public License 2.0](./LICENSE). Commercial use is permitted; changes
to MPL-covered files remain subject to the MPL. See [NOTICE](./NOTICE) and
[TRADEMARKS.md](./TRADEMARKS.md).

## Orcestr Ecosystem

- [Orcestr](https://orcestr.com)
- [Orcestr Auth](https://github.com/Artasov/orcestr-auth)
- [Orcestr UI](https://github.com/Artasov/orcestr-ui)
- [Orcestr OS](https://github.com/Artasov/orcestr-os)
