vanty-payments 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vanty_payments-0.1.0/.github/workflows/release.yml +56 -0
- vanty_payments-0.1.0/.github/workflows/tests.yml +63 -0
- vanty_payments-0.1.0/.gitignore +33 -0
- vanty_payments-0.1.0/PKG-INFO +238 -0
- vanty_payments-0.1.0/README.md +217 -0
- vanty_payments-0.1.0/docs/architecture.mdx +21 -0
- vanty_payments-0.1.0/docs/data-model.mdx +40 -0
- vanty_payments-0.1.0/docs/index.mdx +16 -0
- vanty_payments-0.1.0/docs/parity-target.mdx +42 -0
- vanty_payments-0.1.0/docs/sync.mdx +17 -0
- vanty_payments-0.1.0/docs/webhooks.mdx +16 -0
- vanty_payments-0.1.0/examples/reference-api/main.py +75 -0
- vanty_payments-0.1.0/pyproject.toml +80 -0
- vanty_payments-0.1.0/src/vanty_payments/__init__.py +18 -0
- vanty_payments-0.1.0/src/vanty_payments/admin/__init__.py +3 -0
- vanty_payments-0.1.0/src/vanty_payments/admin/router.py +177 -0
- vanty_payments-0.1.0/src/vanty_payments/admin/schemas.py +96 -0
- vanty_payments-0.1.0/src/vanty_payments/admin/services.py +156 -0
- vanty_payments-0.1.0/src/vanty_payments/billing/__init__.py +55 -0
- vanty_payments-0.1.0/src/vanty_payments/billing/constants.py +54 -0
- vanty_payments-0.1.0/src/vanty_payments/billing/schemas.py +158 -0
- vanty_payments-0.1.0/src/vanty_payments/billing/services.py +497 -0
- vanty_payments-0.1.0/src/vanty_payments/catalog/__init__.py +4 -0
- vanty_payments-0.1.0/src/vanty_payments/catalog/schemas.py +35 -0
- vanty_payments-0.1.0/src/vanty_payments/catalog/services.py +18 -0
- vanty_payments-0.1.0/src/vanty_payments/checkout/__init__.py +15 -0
- vanty_payments-0.1.0/src/vanty_payments/checkout/schemas.py +52 -0
- vanty_payments-0.1.0/src/vanty_payments/checkout/services.py +114 -0
- vanty_payments-0.1.0/src/vanty_payments/core/__init__.py +7 -0
- vanty_payments-0.1.0/src/vanty_payments/core/context.py +14 -0
- vanty_payments-0.1.0/src/vanty_payments/core/events.py +23 -0
- vanty_payments-0.1.0/src/vanty_payments/core/identity.py +44 -0
- vanty_payments-0.1.0/src/vanty_payments/core/schemas.py +10 -0
- vanty_payments-0.1.0/src/vanty_payments/core/stripe_client.py +179 -0
- vanty_payments-0.1.0/src/vanty_payments/core/utils.py +40 -0
- vanty_payments-0.1.0/src/vanty_payments/customers/__init__.py +4 -0
- vanty_payments-0.1.0/src/vanty_payments/customers/schemas.py +32 -0
- vanty_payments-0.1.0/src/vanty_payments/customers/services.py +107 -0
- vanty_payments-0.1.0/src/vanty_payments/db/__init__.py +3 -0
- vanty_payments-0.1.0/src/vanty_payments/db/models.py +478 -0
- vanty_payments-0.1.0/src/vanty_payments/db/parity_models.py +400 -0
- vanty_payments-0.1.0/src/vanty_payments/db/views.py +30 -0
- vanty_payments-0.1.0/src/vanty_payments/fastapi/__init__.py +4 -0
- vanty_payments-0.1.0/src/vanty_payments/fastapi/dependencies.py +18 -0
- vanty_payments-0.1.0/src/vanty_payments/fastapi/router.py +473 -0
- vanty_payments-0.1.0/src/vanty_payments/kit.py +334 -0
- vanty_payments-0.1.0/src/vanty_payments/py.typed +0 -0
- vanty_payments-0.1.0/src/vanty_payments/settings.py +51 -0
- vanty_payments-0.1.0/src/vanty_payments/sync/__init__.py +4 -0
- vanty_payments-0.1.0/src/vanty_payments/sync/parity.py +617 -0
- vanty_payments-0.1.0/src/vanty_payments/sync/schemas.py +57 -0
- vanty_payments-0.1.0/src/vanty_payments/sync/services.py +458 -0
- vanty_payments-0.1.0/src/vanty_payments/webhooks/__init__.py +4 -0
- vanty_payments-0.1.0/src/vanty_payments/webhooks/handlers.py +145 -0
- vanty_payments-0.1.0/src/vanty_payments/webhooks/schemas.py +23 -0
- vanty_payments-0.1.0/src/vanty_payments/webhooks/services.py +111 -0
- vanty_payments-0.1.0/tests/conftest.py +23 -0
- vanty_payments-0.1.0/tests/test_admin_routes.py +200 -0
- vanty_payments-0.1.0/tests/test_admin_service.py +193 -0
- vanty_payments-0.1.0/tests/test_api_routes.py +431 -0
- vanty_payments-0.1.0/tests/test_billing.py +163 -0
- vanty_payments-0.1.0/tests/test_billing_routes.py +294 -0
- vanty_payments-0.1.0/tests/test_billing_service.py +646 -0
- vanty_payments-0.1.0/tests/test_catalog_sync.py +63 -0
- vanty_payments-0.1.0/tests/test_checkout.py +127 -0
- vanty_payments-0.1.0/tests/test_core_utils.py +69 -0
- vanty_payments-0.1.0/tests/test_customer_bootstrap.py +35 -0
- vanty_payments-0.1.0/tests/test_events.py +79 -0
- vanty_payments-0.1.0/tests/test_identity.py +53 -0
- vanty_payments-0.1.0/tests/test_parity_sync.py +205 -0
- vanty_payments-0.1.0/tests/test_settings.py +46 -0
- vanty_payments-0.1.0/tests/test_stripe_client.py +140 -0
- vanty_payments-0.1.0/tests/test_sync_backfill.py +42 -0
- vanty_payments-0.1.0/tests/test_webhook_handlers.py +244 -0
- vanty_payments-0.1.0/tests/test_webhook_replay.py +100 -0
- vanty_payments-0.1.0/tests/test_webhooks.py +85 -0
- vanty_payments-0.1.0/uv.lock +648 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write
|
|
10
|
+
id-token: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
build:
|
|
14
|
+
name: Build
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
timeout-minutes: 5
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
- uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: "3.12"
|
|
22
|
+
- uses: astral-sh/setup-uv@v6
|
|
23
|
+
- run: uv build
|
|
24
|
+
- uses: actions/upload-artifact@v4
|
|
25
|
+
with:
|
|
26
|
+
name: dist
|
|
27
|
+
path: dist/
|
|
28
|
+
|
|
29
|
+
github-release:
|
|
30
|
+
name: GitHub Release
|
|
31
|
+
needs: build
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
timeout-minutes: 5
|
|
34
|
+
steps:
|
|
35
|
+
- uses: actions/checkout@v4
|
|
36
|
+
- uses: actions/download-artifact@v4
|
|
37
|
+
with:
|
|
38
|
+
name: dist
|
|
39
|
+
path: dist/
|
|
40
|
+
- uses: softprops/action-gh-release@v2
|
|
41
|
+
with:
|
|
42
|
+
generate_release_notes: true
|
|
43
|
+
files: dist/*
|
|
44
|
+
|
|
45
|
+
publish:
|
|
46
|
+
name: Publish to PyPI
|
|
47
|
+
needs: build
|
|
48
|
+
runs-on: ubuntu-latest
|
|
49
|
+
timeout-minutes: 5
|
|
50
|
+
environment: release
|
|
51
|
+
steps:
|
|
52
|
+
- uses: actions/download-artifact@v4
|
|
53
|
+
with:
|
|
54
|
+
name: dist
|
|
55
|
+
path: dist/
|
|
56
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
paths:
|
|
7
|
+
- "src/**"
|
|
8
|
+
- "tests/**"
|
|
9
|
+
- "pyproject.toml"
|
|
10
|
+
- "uv.lock"
|
|
11
|
+
- ".github/workflows/**"
|
|
12
|
+
pull_request:
|
|
13
|
+
workflow_dispatch:
|
|
14
|
+
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
lint:
|
|
20
|
+
name: Lint
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
timeout-minutes: 5
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v4
|
|
25
|
+
- uses: actions/setup-python@v5
|
|
26
|
+
with:
|
|
27
|
+
python-version: "3.12"
|
|
28
|
+
- uses: astral-sh/setup-uv@v6
|
|
29
|
+
- run: uv sync --dev
|
|
30
|
+
- run: uv run ruff check
|
|
31
|
+
|
|
32
|
+
test:
|
|
33
|
+
name: "Test: Python ${{ matrix.python-version }}"
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
timeout-minutes: 10
|
|
36
|
+
strategy:
|
|
37
|
+
matrix:
|
|
38
|
+
python-version: ["3.12", "3.13"]
|
|
39
|
+
fail-fast: false
|
|
40
|
+
steps:
|
|
41
|
+
- uses: actions/checkout@v4
|
|
42
|
+
- uses: actions/setup-python@v5
|
|
43
|
+
with:
|
|
44
|
+
python-version: ${{ matrix.python-version }}
|
|
45
|
+
- uses: astral-sh/setup-uv@v6
|
|
46
|
+
- run: uv sync --dev
|
|
47
|
+
- run: uv run pytest -q
|
|
48
|
+
|
|
49
|
+
build:
|
|
50
|
+
name: Build
|
|
51
|
+
runs-on: ubuntu-latest
|
|
52
|
+
timeout-minutes: 5
|
|
53
|
+
steps:
|
|
54
|
+
- uses: actions/checkout@v4
|
|
55
|
+
- uses: actions/setup-python@v5
|
|
56
|
+
with:
|
|
57
|
+
python-version: "3.12"
|
|
58
|
+
- uses: astral-sh/setup-uv@v6
|
|
59
|
+
- run: uv build
|
|
60
|
+
- uses: actions/upload-artifact@v4
|
|
61
|
+
with:
|
|
62
|
+
name: dist
|
|
63
|
+
path: dist/
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
.DS_Store
|
|
2
|
+
.env
|
|
3
|
+
.env.*
|
|
4
|
+
.coverage
|
|
5
|
+
|
|
6
|
+
# Local-only runtime and tool state
|
|
7
|
+
.local/
|
|
8
|
+
.omx/
|
|
9
|
+
.pgdata*
|
|
10
|
+
.pgrun
|
|
11
|
+
.pgsocket
|
|
12
|
+
|
|
13
|
+
# Python
|
|
14
|
+
.pytest_cache/
|
|
15
|
+
.ruff_cache/
|
|
16
|
+
.venv/
|
|
17
|
+
__pycache__/
|
|
18
|
+
*.db
|
|
19
|
+
*.db-shm
|
|
20
|
+
*.db-wal
|
|
21
|
+
|
|
22
|
+
# Build artifacts
|
|
23
|
+
dist/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
*.db-journal
|
|
26
|
+
*.egg-info/
|
|
27
|
+
*.pyc
|
|
28
|
+
build/
|
|
29
|
+
dist/
|
|
30
|
+
|
|
31
|
+
# Node / Frontend
|
|
32
|
+
node_modules/
|
|
33
|
+
tsconfig.tsbuildinfo
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vanty-payments
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Lean Stripe-first payments toolkit for FastAPI with Tortoise ORM
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Keywords: billing,fastapi,payments,stripe,subscriptions
|
|
7
|
+
Classifier: Development Status :: 4 - Beta
|
|
8
|
+
Classifier: Framework :: FastAPI
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
14
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
15
|
+
Requires-Python: >=3.12
|
|
16
|
+
Requires-Dist: fastapi>=0.135.1
|
|
17
|
+
Requires-Dist: pydantic-settings>=2.13.1
|
|
18
|
+
Requires-Dist: stripe>=13.0.1
|
|
19
|
+
Requires-Dist: tortoise-orm>=1.1.6
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# Vanty Payments
|
|
23
|
+
|
|
24
|
+
[](https://github.com/advantch/vanty-payments/actions/workflows/tests.yml)
|
|
25
|
+
[](https://pypi.org/project/vanty-payments/)
|
|
26
|
+
[](https://pypi.org/project/vanty-payments/)
|
|
27
|
+
|
|
28
|
+
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`.
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install vanty-payments
|
|
34
|
+
# or
|
|
35
|
+
uv pip install vanty-payments
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Quick start
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from contextlib import asynccontextmanager
|
|
42
|
+
|
|
43
|
+
from fastapi import FastAPI
|
|
44
|
+
|
|
45
|
+
from vanty_payments import PaymentsKitSettings, mount_payments_router
|
|
46
|
+
|
|
47
|
+
settings = PaymentsKitSettings(
|
|
48
|
+
database_url="sqlite://./vanty-payments.db",
|
|
49
|
+
stripe_test_secret_key="sk_test_example",
|
|
50
|
+
stripe_webhook_secret="whsec_example",
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
app = FastAPI()
|
|
54
|
+
kit = mount_payments_router(app, settings=settings)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@asynccontextmanager
|
|
58
|
+
async def lifespan(_: FastAPI):
|
|
59
|
+
await kit.init_orm(generate_schemas=True)
|
|
60
|
+
try:
|
|
61
|
+
yield
|
|
62
|
+
finally:
|
|
63
|
+
await kit.close_orm()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
app.router.lifespan_context = lifespan
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Run with `uvicorn main:app --reload` and visit `/docs` for the interactive API explorer.
|
|
70
|
+
|
|
71
|
+
## Features
|
|
72
|
+
|
|
73
|
+
- **Checkout sessions** with Stripe-hosted and embedded flows
|
|
74
|
+
- **Subscription management** with plan changes, cancellation, and reactivation
|
|
75
|
+
- **Invoice management** with PDF links and payment status tracking
|
|
76
|
+
- **Billing portal** sessions for customer self-service
|
|
77
|
+
- **Payment method** management (cards, bank accounts)
|
|
78
|
+
- **Webhook ingestion** with signature verification and idempotent processing
|
|
79
|
+
- **Manual sync** to pull Stripe state into local models on demand
|
|
80
|
+
- **Full Stripe object parity** covering customers, products, prices, coupons, promotion codes, discounts, charges, refunds, disputes, and more
|
|
81
|
+
- **Admin management** endpoints for super-admin billing operations
|
|
82
|
+
- **Identity integration** with optional `vanty-auth` or custom identity resolvers
|
|
83
|
+
|
|
84
|
+
## Architecture
|
|
85
|
+
|
|
86
|
+
`PaymentsKit` is a composition root that wires all services. Access services directly:
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
kit = mount_payments_router(app, settings=settings)
|
|
90
|
+
|
|
91
|
+
# Direct service access
|
|
92
|
+
await kit.checkout_service.create_session(customer_id=cid, price_id=pid)
|
|
93
|
+
await kit.subscription_service.list_subscriptions(customer_id=cid)
|
|
94
|
+
await kit.sync_service.sync_customer(stripe_customer_id="cus_xxx")
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Available services on `PaymentsKit`:
|
|
98
|
+
|
|
99
|
+
| Service | Purpose |
|
|
100
|
+
|---|---|
|
|
101
|
+
| `customer_service` | Customer bootstrap and management |
|
|
102
|
+
| `checkout_service` | Checkout session creation |
|
|
103
|
+
| `subscription_service` | Subscription reads and management |
|
|
104
|
+
| `invoice_service` | Invoice listing and retrieval |
|
|
105
|
+
| `payment_method_service` | Payment method listing |
|
|
106
|
+
| `portal_service` | Billing portal session creation |
|
|
107
|
+
| `webhook_service` | Webhook signature verification and dispatch |
|
|
108
|
+
| `sync_service` | Manual Stripe-to-local sync for all object types |
|
|
109
|
+
| `admin_service` | Super-admin billing operations |
|
|
110
|
+
|
|
111
|
+
## Configuration
|
|
112
|
+
|
|
113
|
+
All settings are read from environment variables with the `PAYMENTS_KIT_` prefix, or passed directly to `PaymentsKitSettings`.
|
|
114
|
+
|
|
115
|
+
| Setting | Default | Description |
|
|
116
|
+
|---|---|---|
|
|
117
|
+
| `database_url` | `sqlite://./vanty-payments.db` | Tortoise ORM database URL |
|
|
118
|
+
| `stripe_test_secret_key` | — | Stripe test mode secret key |
|
|
119
|
+
| `stripe_live_secret_key` | — | Stripe live mode secret key |
|
|
120
|
+
| `stripe_webhook_secret` | — | Webhook endpoint signing secret |
|
|
121
|
+
| `stripe_mode` | `test` | `test` or `live` |
|
|
122
|
+
| `default_currency` | `usd` | Default currency for new prices |
|
|
123
|
+
|
|
124
|
+
Set via environment: `PAYMENTS_KIT_STRIPE_TEST_SECRET_KEY=sk_test_... PAYMENTS_KIT_DATABASE_URL=postgres://...`
|
|
125
|
+
|
|
126
|
+
## API surface
|
|
127
|
+
|
|
128
|
+
### Public routes (`/payments`)
|
|
129
|
+
|
|
130
|
+
| Method | Path | Description |
|
|
131
|
+
|---|---|---|
|
|
132
|
+
| `POST` | `/customers/bootstrap` | Create or retrieve a Stripe customer |
|
|
133
|
+
| `GET` | `/catalog/products` | List available products |
|
|
134
|
+
| `GET` | `/catalog/prices` | List prices for a product |
|
|
135
|
+
| `POST` | `/checkout/sessions` | Create a checkout session |
|
|
136
|
+
| `GET` | `/subscriptions` | List subscriptions |
|
|
137
|
+
| `GET` | `/subscriptions/{id}` | Get subscription details |
|
|
138
|
+
| `POST` | `/subscriptions/{id}/cancel` | Cancel a subscription |
|
|
139
|
+
| `POST` | `/portal/sessions` | Create a billing portal session |
|
|
140
|
+
| `GET` | `/invoices` | List invoices |
|
|
141
|
+
| `GET` | `/invoices/{id}` | Get invoice details |
|
|
142
|
+
| `GET` | `/payment-methods` | List payment methods |
|
|
143
|
+
| `POST` | `/webhooks/stripe` | Stripe webhook endpoint |
|
|
144
|
+
| `POST` | `/sync` | Trigger manual sync |
|
|
145
|
+
|
|
146
|
+
### Admin routes (`/admin/payments`)
|
|
147
|
+
|
|
148
|
+
Super-admin endpoints for platform-wide billing management. Mount separately:
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
app.include_router(kit.admin_router, prefix="/admin/payments")
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Host app identity integration
|
|
155
|
+
|
|
156
|
+
The package works standalone, but a host app can supply a request-level identity adapter through `app.state.payments_identity_resolver`:
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
async def resolve_identity(request: Request) -> IdentityContext:
|
|
160
|
+
return IdentityContext(
|
|
161
|
+
user_reference=request.state.user_id,
|
|
162
|
+
organization_reference=request.state.org_id,
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
app.state.payments_identity_resolver = resolve_identity
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Routes then use those references automatically when request payloads omit them.
|
|
169
|
+
|
|
170
|
+
## Project layout
|
|
171
|
+
|
|
172
|
+
```text
|
|
173
|
+
src/vanty_payments/ Package source
|
|
174
|
+
docs/ Documentation
|
|
175
|
+
examples/reference-api/ Reference FastAPI application
|
|
176
|
+
tests/ pytest suite (unit + integration)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Development
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
git clone https://github.com/advantch/vanty-payments.git
|
|
183
|
+
cd vanty-payments
|
|
184
|
+
uv sync --dev
|
|
185
|
+
|
|
186
|
+
# Lint
|
|
187
|
+
uv run ruff check
|
|
188
|
+
|
|
189
|
+
# Test
|
|
190
|
+
uv run pytest -q
|
|
191
|
+
|
|
192
|
+
# Run reference API
|
|
193
|
+
cd examples/reference-api && uv run uvicorn main:app --reload
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Use `uv run pytest --no-cov tests/path/to/test.py` for targeted debugging without the coverage gate.
|
|
197
|
+
|
|
198
|
+
## Releasing
|
|
199
|
+
|
|
200
|
+
Releases are automated via GitHub Actions. To publish a new version:
|
|
201
|
+
|
|
202
|
+
1. Update the version in `pyproject.toml`:
|
|
203
|
+
|
|
204
|
+
```toml
|
|
205
|
+
version = "0.2.0"
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
2. Commit and tag:
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
git add pyproject.toml
|
|
212
|
+
git commit -m "release: v0.2.0"
|
|
213
|
+
git tag v0.2.0
|
|
214
|
+
git push origin main --tags
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
3. The `release.yml` workflow will automatically:
|
|
218
|
+
- Build the package
|
|
219
|
+
- Create a GitHub Release with auto-generated notes
|
|
220
|
+
- Publish to PyPI via trusted publishing (OIDC)
|
|
221
|
+
|
|
222
|
+
### PyPI trusted publishing setup
|
|
223
|
+
|
|
224
|
+
To enable automated publishing, configure a trusted publisher on PyPI:
|
|
225
|
+
|
|
226
|
+
1. Go to [pypi.org/manage/account/publishing](https://pypi.org/manage/account/publishing/)
|
|
227
|
+
2. Add a new pending publisher (or update an existing project):
|
|
228
|
+
- **PyPI project name**: `vanty-payments`
|
|
229
|
+
- **Owner**: `advantch`
|
|
230
|
+
- **Repository**: `vanty-payments`
|
|
231
|
+
- **Workflow name**: `release.yml`
|
|
232
|
+
- **Environment name**: `release`
|
|
233
|
+
|
|
234
|
+
No API tokens are needed once trusted publishing is configured.
|
|
235
|
+
|
|
236
|
+
## License
|
|
237
|
+
|
|
238
|
+
MIT
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# Vanty Payments
|
|
2
|
+
|
|
3
|
+
[](https://github.com/advantch/vanty-payments/actions/workflows/tests.yml)
|
|
4
|
+
[](https://pypi.org/project/vanty-payments/)
|
|
5
|
+
[](https://pypi.org/project/vanty-payments/)
|
|
6
|
+
|
|
7
|
+
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`.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install vanty-payments
|
|
13
|
+
# or
|
|
14
|
+
uv pip install vanty-payments
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
from contextlib import asynccontextmanager
|
|
21
|
+
|
|
22
|
+
from fastapi import FastAPI
|
|
23
|
+
|
|
24
|
+
from vanty_payments import PaymentsKitSettings, mount_payments_router
|
|
25
|
+
|
|
26
|
+
settings = PaymentsKitSettings(
|
|
27
|
+
database_url="sqlite://./vanty-payments.db",
|
|
28
|
+
stripe_test_secret_key="sk_test_example",
|
|
29
|
+
stripe_webhook_secret="whsec_example",
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
app = FastAPI()
|
|
33
|
+
kit = mount_payments_router(app, settings=settings)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@asynccontextmanager
|
|
37
|
+
async def lifespan(_: FastAPI):
|
|
38
|
+
await kit.init_orm(generate_schemas=True)
|
|
39
|
+
try:
|
|
40
|
+
yield
|
|
41
|
+
finally:
|
|
42
|
+
await kit.close_orm()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
app.router.lifespan_context = lifespan
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Run with `uvicorn main:app --reload` and visit `/docs` for the interactive API explorer.
|
|
49
|
+
|
|
50
|
+
## Features
|
|
51
|
+
|
|
52
|
+
- **Checkout sessions** with Stripe-hosted and embedded flows
|
|
53
|
+
- **Subscription management** with plan changes, cancellation, and reactivation
|
|
54
|
+
- **Invoice management** with PDF links and payment status tracking
|
|
55
|
+
- **Billing portal** sessions for customer self-service
|
|
56
|
+
- **Payment method** management (cards, bank accounts)
|
|
57
|
+
- **Webhook ingestion** with signature verification and idempotent processing
|
|
58
|
+
- **Manual sync** to pull Stripe state into local models on demand
|
|
59
|
+
- **Full Stripe object parity** covering customers, products, prices, coupons, promotion codes, discounts, charges, refunds, disputes, and more
|
|
60
|
+
- **Admin management** endpoints for super-admin billing operations
|
|
61
|
+
- **Identity integration** with optional `vanty-auth` or custom identity resolvers
|
|
62
|
+
|
|
63
|
+
## Architecture
|
|
64
|
+
|
|
65
|
+
`PaymentsKit` is a composition root that wires all services. Access services directly:
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
kit = mount_payments_router(app, settings=settings)
|
|
69
|
+
|
|
70
|
+
# Direct service access
|
|
71
|
+
await kit.checkout_service.create_session(customer_id=cid, price_id=pid)
|
|
72
|
+
await kit.subscription_service.list_subscriptions(customer_id=cid)
|
|
73
|
+
await kit.sync_service.sync_customer(stripe_customer_id="cus_xxx")
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Available services on `PaymentsKit`:
|
|
77
|
+
|
|
78
|
+
| Service | Purpose |
|
|
79
|
+
|---|---|
|
|
80
|
+
| `customer_service` | Customer bootstrap and management |
|
|
81
|
+
| `checkout_service` | Checkout session creation |
|
|
82
|
+
| `subscription_service` | Subscription reads and management |
|
|
83
|
+
| `invoice_service` | Invoice listing and retrieval |
|
|
84
|
+
| `payment_method_service` | Payment method listing |
|
|
85
|
+
| `portal_service` | Billing portal session creation |
|
|
86
|
+
| `webhook_service` | Webhook signature verification and dispatch |
|
|
87
|
+
| `sync_service` | Manual Stripe-to-local sync for all object types |
|
|
88
|
+
| `admin_service` | Super-admin billing operations |
|
|
89
|
+
|
|
90
|
+
## Configuration
|
|
91
|
+
|
|
92
|
+
All settings are read from environment variables with the `PAYMENTS_KIT_` prefix, or passed directly to `PaymentsKitSettings`.
|
|
93
|
+
|
|
94
|
+
| Setting | Default | Description |
|
|
95
|
+
|---|---|---|
|
|
96
|
+
| `database_url` | `sqlite://./vanty-payments.db` | Tortoise ORM database URL |
|
|
97
|
+
| `stripe_test_secret_key` | — | Stripe test mode secret key |
|
|
98
|
+
| `stripe_live_secret_key` | — | Stripe live mode secret key |
|
|
99
|
+
| `stripe_webhook_secret` | — | Webhook endpoint signing secret |
|
|
100
|
+
| `stripe_mode` | `test` | `test` or `live` |
|
|
101
|
+
| `default_currency` | `usd` | Default currency for new prices |
|
|
102
|
+
|
|
103
|
+
Set via environment: `PAYMENTS_KIT_STRIPE_TEST_SECRET_KEY=sk_test_... PAYMENTS_KIT_DATABASE_URL=postgres://...`
|
|
104
|
+
|
|
105
|
+
## API surface
|
|
106
|
+
|
|
107
|
+
### Public routes (`/payments`)
|
|
108
|
+
|
|
109
|
+
| Method | Path | Description |
|
|
110
|
+
|---|---|---|
|
|
111
|
+
| `POST` | `/customers/bootstrap` | Create or retrieve a Stripe customer |
|
|
112
|
+
| `GET` | `/catalog/products` | List available products |
|
|
113
|
+
| `GET` | `/catalog/prices` | List prices for a product |
|
|
114
|
+
| `POST` | `/checkout/sessions` | Create a checkout session |
|
|
115
|
+
| `GET` | `/subscriptions` | List subscriptions |
|
|
116
|
+
| `GET` | `/subscriptions/{id}` | Get subscription details |
|
|
117
|
+
| `POST` | `/subscriptions/{id}/cancel` | Cancel a subscription |
|
|
118
|
+
| `POST` | `/portal/sessions` | Create a billing portal session |
|
|
119
|
+
| `GET` | `/invoices` | List invoices |
|
|
120
|
+
| `GET` | `/invoices/{id}` | Get invoice details |
|
|
121
|
+
| `GET` | `/payment-methods` | List payment methods |
|
|
122
|
+
| `POST` | `/webhooks/stripe` | Stripe webhook endpoint |
|
|
123
|
+
| `POST` | `/sync` | Trigger manual sync |
|
|
124
|
+
|
|
125
|
+
### Admin routes (`/admin/payments`)
|
|
126
|
+
|
|
127
|
+
Super-admin endpoints for platform-wide billing management. Mount separately:
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
app.include_router(kit.admin_router, prefix="/admin/payments")
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Host app identity integration
|
|
134
|
+
|
|
135
|
+
The package works standalone, but a host app can supply a request-level identity adapter through `app.state.payments_identity_resolver`:
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
async def resolve_identity(request: Request) -> IdentityContext:
|
|
139
|
+
return IdentityContext(
|
|
140
|
+
user_reference=request.state.user_id,
|
|
141
|
+
organization_reference=request.state.org_id,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
app.state.payments_identity_resolver = resolve_identity
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Routes then use those references automatically when request payloads omit them.
|
|
148
|
+
|
|
149
|
+
## Project layout
|
|
150
|
+
|
|
151
|
+
```text
|
|
152
|
+
src/vanty_payments/ Package source
|
|
153
|
+
docs/ Documentation
|
|
154
|
+
examples/reference-api/ Reference FastAPI application
|
|
155
|
+
tests/ pytest suite (unit + integration)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Development
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
git clone https://github.com/advantch/vanty-payments.git
|
|
162
|
+
cd vanty-payments
|
|
163
|
+
uv sync --dev
|
|
164
|
+
|
|
165
|
+
# Lint
|
|
166
|
+
uv run ruff check
|
|
167
|
+
|
|
168
|
+
# Test
|
|
169
|
+
uv run pytest -q
|
|
170
|
+
|
|
171
|
+
# Run reference API
|
|
172
|
+
cd examples/reference-api && uv run uvicorn main:app --reload
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Use `uv run pytest --no-cov tests/path/to/test.py` for targeted debugging without the coverage gate.
|
|
176
|
+
|
|
177
|
+
## Releasing
|
|
178
|
+
|
|
179
|
+
Releases are automated via GitHub Actions. To publish a new version:
|
|
180
|
+
|
|
181
|
+
1. Update the version in `pyproject.toml`:
|
|
182
|
+
|
|
183
|
+
```toml
|
|
184
|
+
version = "0.2.0"
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
2. Commit and tag:
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
git add pyproject.toml
|
|
191
|
+
git commit -m "release: v0.2.0"
|
|
192
|
+
git tag v0.2.0
|
|
193
|
+
git push origin main --tags
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
3. The `release.yml` workflow will automatically:
|
|
197
|
+
- Build the package
|
|
198
|
+
- Create a GitHub Release with auto-generated notes
|
|
199
|
+
- Publish to PyPI via trusted publishing (OIDC)
|
|
200
|
+
|
|
201
|
+
### PyPI trusted publishing setup
|
|
202
|
+
|
|
203
|
+
To enable automated publishing, configure a trusted publisher on PyPI:
|
|
204
|
+
|
|
205
|
+
1. Go to [pypi.org/manage/account/publishing](https://pypi.org/manage/account/publishing/)
|
|
206
|
+
2. Add a new pending publisher (or update an existing project):
|
|
207
|
+
- **PyPI project name**: `vanty-payments`
|
|
208
|
+
- **Owner**: `advantch`
|
|
209
|
+
- **Repository**: `vanty-payments`
|
|
210
|
+
- **Workflow name**: `release.yml`
|
|
211
|
+
- **Environment name**: `release`
|
|
212
|
+
|
|
213
|
+
No API tokens are needed once trusted publishing is configured.
|
|
214
|
+
|
|
215
|
+
## License
|
|
216
|
+
|
|
217
|
+
MIT
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Architecture
|
|
3
|
+
description: Feature-oriented payments package layout.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Architecture
|
|
7
|
+
|
|
8
|
+
`PaymentsKit` is the composition root. It wires:
|
|
9
|
+
|
|
10
|
+
- settings
|
|
11
|
+
- Stripe client wrapper
|
|
12
|
+
- host-app identity adapter boundary
|
|
13
|
+
- sync service
|
|
14
|
+
- customer, catalog, checkout, billing, and webhook services
|
|
15
|
+
- FastAPI router
|
|
16
|
+
- Tortoise ORM lifecycle
|
|
17
|
+
|
|
18
|
+
The transport boundary lives in `fastapi/`. Persisted models live in `db/models.py`.
|
|
19
|
+
|
|
20
|
+
`StripeCheckoutSession` is treated as an app-owned workflow record, while the
|
|
21
|
+
other Stripe-backed models primarily act as synced local cache records.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Data Model
|
|
3
|
+
description: Persisted Stripe-backed records.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Data Model
|
|
7
|
+
|
|
8
|
+
Persisted state lives in `db/models.py`.
|
|
9
|
+
|
|
10
|
+
Primary entities:
|
|
11
|
+
|
|
12
|
+
- `StripeCustomer`
|
|
13
|
+
- `StripeProduct`
|
|
14
|
+
- `StripePrice`
|
|
15
|
+
- `StripePaymentMethod`
|
|
16
|
+
- `StripePaymentIntent`
|
|
17
|
+
- `StripeSubscription`
|
|
18
|
+
- `StripeSubscriptionItem`
|
|
19
|
+
- `StripeInvoice`
|
|
20
|
+
- `StripeInvoiceLine`
|
|
21
|
+
- `StripeCheckoutSession`
|
|
22
|
+
- `StripeWebhookEvent`
|
|
23
|
+
- `StripeWebhookDelivery`
|
|
24
|
+
- `StripeSyncRun`
|
|
25
|
+
|
|
26
|
+
Expanded parity families also include:
|
|
27
|
+
|
|
28
|
+
- account/API key models
|
|
29
|
+
- coupon/promotion/discount/plan/subscription schedule models
|
|
30
|
+
- charge/refund/dispute/balance-transaction/payout models
|
|
31
|
+
- card/bank-account/source models
|
|
32
|
+
- connect models
|
|
33
|
+
- tax models
|
|
34
|
+
- entitlements models
|
|
35
|
+
- identity verification models
|
|
36
|
+
- issuing models
|
|
37
|
+
- radar and sigma models
|
|
38
|
+
- pricing table models
|
|
39
|
+
|
|
40
|
+
Every synced Stripe-backed model stores both normalized fields and raw `stripe_data_json`.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Vanty Payments
|
|
3
|
+
description: Stripe-first payments package for FastAPI with local Tortoise ORM models.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Vanty Payments
|
|
7
|
+
|
|
8
|
+
`vanty-payments` is the Stripe-first sibling package to `vanty-auth`.
|
|
9
|
+
|
|
10
|
+
It follows the same high-level shape:
|
|
11
|
+
|
|
12
|
+
- one composition root (`PaymentsKit`)
|
|
13
|
+
- Pydantic settings (`PaymentsKitSettings`)
|
|
14
|
+
- thin `fastapi/` transport layer
|
|
15
|
+
- centralized `db/models.py`
|
|
16
|
+
- local SQLite-friendly setup for development
|