python-getpaid-core 0.1.0__tar.gz → 3.0.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.
- python_getpaid_core-3.0.0/.github/workflows/ci.yml +36 -0
- python_getpaid_core-3.0.0/.github/workflows/release.yml +70 -0
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/.gitignore +2 -0
- python_getpaid_core-3.0.0/.pre-commit-config.yaml +39 -0
- python_getpaid_core-3.0.0/.sisyphus/evidence/task-22-readme-core.txt +7 -0
- python_getpaid_core-3.0.0/Makefile +28 -0
- python_getpaid_core-3.0.0/PKG-INFO +104 -0
- python_getpaid_core-3.0.0/README.md +82 -0
- python_getpaid_core-3.0.0/docs/changelog.md +50 -0
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/docs/concepts.md +28 -36
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/docs/conf.py +1 -1
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/docs/getting-started.md +21 -12
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/docs/reference.md +1 -1
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/pyproject.toml +29 -14
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/src/getpaid_core/__init__.py +15 -1
- python_getpaid_core-3.0.0/src/getpaid_core/backends/dummy.py +145 -0
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/src/getpaid_core/enums.py +23 -4
- python_getpaid_core-3.0.0/src/getpaid_core/flow.py +259 -0
- python_getpaid_core-3.0.0/src/getpaid_core/fsm.py +331 -0
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/src/getpaid_core/processor.py +20 -28
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/src/getpaid_core/protocols.py +5 -8
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/src/getpaid_core/registry.py +29 -18
- python_getpaid_core-3.0.0/src/getpaid_core/types.py +102 -0
- python_getpaid_core-3.0.0/src/getpaid_core/validators.py +13 -0
- python_getpaid_core-3.0.0/tests/conftest.py +211 -0
- python_getpaid_core-3.0.0/tests/test_benchmarks.py +549 -0
- python_getpaid_core-3.0.0/tests/test_dummy_backend.py +132 -0
- python_getpaid_core-3.0.0/tests/test_enums.py +47 -0
- python_getpaid_core-3.0.0/tests/test_flow.py +282 -0
- python_getpaid_core-3.0.0/tests/test_integration.py +79 -0
- python_getpaid_core-3.0.0/tests/test_processor.py +129 -0
- python_getpaid_core-3.0.0/tests/test_protocols.py +89 -0
- python_getpaid_core-3.0.0/tests/test_public_api.py +25 -0
- python_getpaid_core-3.0.0/tests/test_registry.py +173 -0
- python_getpaid_core-3.0.0/tests/test_state_engine.py +370 -0
- python_getpaid_core-3.0.0/tests/test_types.py +84 -0
- python_getpaid_core-3.0.0/tests/test_version.py +11 -0
- python_getpaid_core-0.1.0/.github/workflows/release.yml +0 -79
- python_getpaid_core-0.1.0/.pre-commit-config.yaml +0 -14
- python_getpaid_core-0.1.0/PKG-INFO +0 -103
- python_getpaid_core-0.1.0/README.md +0 -76
- python_getpaid_core-0.1.0/docs/changelog.md +0 -23
- python_getpaid_core-0.1.0/src/getpaid_core/backends/dummy.py +0 -95
- python_getpaid_core-0.1.0/src/getpaid_core/flow.py +0 -140
- python_getpaid_core-0.1.0/src/getpaid_core/fsm.py +0 -205
- python_getpaid_core-0.1.0/src/getpaid_core/types.py +0 -52
- python_getpaid_core-0.1.0/src/getpaid_core/validators.py +0 -21
- python_getpaid_core-0.1.0/tests/conftest.py +0 -132
- python_getpaid_core-0.1.0/tests/test_dummy_backend.py +0 -152
- python_getpaid_core-0.1.0/tests/test_enums.py +0 -87
- python_getpaid_core-0.1.0/tests/test_flow.py +0 -193
- python_getpaid_core-0.1.0/tests/test_fsm.py +0 -276
- python_getpaid_core-0.1.0/tests/test_integration.py +0 -182
- python_getpaid_core-0.1.0/tests/test_processor.py +0 -184
- python_getpaid_core-0.1.0/tests/test_protocols.py +0 -85
- python_getpaid_core-0.1.0/tests/test_public_api.py +0 -35
- python_getpaid_core-0.1.0/tests/test_registry.py +0 -170
- python_getpaid_core-0.1.0/tests/test_types.py +0 -97
- python_getpaid_core-0.1.0/uv.lock +0 -1211
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/.cookiecutter.json +0 -0
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/.gitattributes +0 -0
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/.github/dependabot.yml +0 -0
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/.github/labels.yml +0 -0
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/.github/release-drafter.yml +0 -0
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/.github/workflows/constraints.txt +0 -0
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/.github/workflows/labeler.yml +0 -0
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/.github/workflows/tests.yml +0 -0
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/.plans/2026-02-13-getpaid-core-design.md +0 -0
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/.plans/2026-02-13-getpaid-core-implementation.md +0 -0
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/.readthedocs.yml +0 -0
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/CODE_OF_CONDUCT.md +0 -0
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/CONTRIBUTING.md +0 -0
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/LICENSE +0 -0
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/codecov.yml +0 -0
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/docs/codeofconduct.md +0 -0
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/docs/contributing.md +0 -0
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/docs/index.md +0 -0
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/docs/license.md +0 -0
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/docs/requirements.txt +0 -0
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/src/getpaid_core/backends/__init__.py +0 -0
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/src/getpaid_core/exceptions.py +0 -0
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/src/getpaid_core/py.typed +0 -0
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/tests/__init__.py +0 -0
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/tests/test_exceptions.py +0 -0
- {python_getpaid_core-0.1.0 → python_getpaid_core-3.0.0}/tests/test_validators.py +0 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
matrix:
|
|
13
|
+
python-version: ["3.12", "3.13"]
|
|
14
|
+
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
19
|
+
uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: ${{ matrix.python-version }}
|
|
22
|
+
|
|
23
|
+
- name: Install uv
|
|
24
|
+
run: pip install uv
|
|
25
|
+
|
|
26
|
+
- name: Install dependencies
|
|
27
|
+
run: uv sync
|
|
28
|
+
|
|
29
|
+
- name: Lint with ruff
|
|
30
|
+
run: uv run ruff check .
|
|
31
|
+
|
|
32
|
+
- name: Audit dependencies
|
|
33
|
+
run: uv run pip-audit --strict
|
|
34
|
+
|
|
35
|
+
- name: Run tests
|
|
36
|
+
run: uv run pytest --tb=short
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
- master
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
release:
|
|
11
|
+
name: Release
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- name: Check out the repository
|
|
15
|
+
uses: actions/checkout@v4
|
|
16
|
+
with:
|
|
17
|
+
fetch-depth: 0 # Full history needed for version detection
|
|
18
|
+
|
|
19
|
+
- name: Set up Python
|
|
20
|
+
uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: "3.12"
|
|
23
|
+
|
|
24
|
+
- name: Install uv
|
|
25
|
+
run: pip install uv
|
|
26
|
+
|
|
27
|
+
# Version is read from __init__.py (dynamic via hatch)
|
|
28
|
+
- name: Detect version from __init__.py
|
|
29
|
+
id: get-version
|
|
30
|
+
run: |
|
|
31
|
+
init_py=$(grep -A2 '\[tool\.hatch\.version\]' pyproject.toml | grep '^path\s*=' | head -1 | sed -E "s/path\s*=\s*['\"]([^'\"]+)['\"].*/\1/")
|
|
32
|
+
version=$(grep '__version__' "$init_py" | head -1 | sed -E "s/.*= ['\"]([^'\"]+)['\"].*/\1/")
|
|
33
|
+
echo "version=$version" >> "$GITHUB_OUTPUT"
|
|
34
|
+
|
|
35
|
+
- name: Check if tag already exists
|
|
36
|
+
id: check-tag
|
|
37
|
+
run: |
|
|
38
|
+
tag="v${{ steps.get-version.outputs.version }}"
|
|
39
|
+
if git rev-parse "$tag" >/dev/null 2>&1; then
|
|
40
|
+
echo "already_tagged=true" >> "$GITHUB_OUTPUT"
|
|
41
|
+
else
|
|
42
|
+
echo "already_tagged=false" >> "$GITHUB_OUTPUT"
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
- name: Tag new version
|
|
46
|
+
if: steps.check-tag.outputs.already_tagged == 'false'
|
|
47
|
+
run: |
|
|
48
|
+
tag="v${{ steps.get-version.outputs.version }}"
|
|
49
|
+
git config user.name "github-actions[bot]"
|
|
50
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
51
|
+
git tag -a "$tag" -m "Release $tag"
|
|
52
|
+
git push origin "$tag"
|
|
53
|
+
|
|
54
|
+
- name: Build package
|
|
55
|
+
run: uv build
|
|
56
|
+
|
|
57
|
+
- name: Publish package on PyPI
|
|
58
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
59
|
+
with:
|
|
60
|
+
password: ${{ secrets.PYPI_TOKEN }}
|
|
61
|
+
|
|
62
|
+
- name: Publish the release notes
|
|
63
|
+
if: steps.check-tag.outputs.already_tagged == 'false'
|
|
64
|
+
uses: release-drafter/release-drafter@v5.20.0
|
|
65
|
+
with:
|
|
66
|
+
publish: true
|
|
67
|
+
name: v${{ steps.get-version.outputs.version }}
|
|
68
|
+
tag: v${{ steps.get-version.outputs.version }}
|
|
69
|
+
env:
|
|
70
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: local
|
|
3
|
+
hooks:
|
|
4
|
+
- id: ruff
|
|
5
|
+
name: ruff
|
|
6
|
+
entry: uv run ruff check --fix
|
|
7
|
+
language: system
|
|
8
|
+
types: [python]
|
|
9
|
+
- id: ruff-format
|
|
10
|
+
name: ruff-format
|
|
11
|
+
entry: uv run ruff format
|
|
12
|
+
language: system
|
|
13
|
+
types: [python]
|
|
14
|
+
- id: check-toml
|
|
15
|
+
name: check-toml
|
|
16
|
+
entry: uv run check-toml
|
|
17
|
+
language: system
|
|
18
|
+
types: [toml]
|
|
19
|
+
- id: check-yaml
|
|
20
|
+
name: check-yaml
|
|
21
|
+
entry: uv run check-yaml
|
|
22
|
+
language: system
|
|
23
|
+
types: [yaml]
|
|
24
|
+
- id: end-of-file-fixer
|
|
25
|
+
name: end-of-file-fixer
|
|
26
|
+
entry: uv run end-of-file-fixer
|
|
27
|
+
language: system
|
|
28
|
+
types: [text]
|
|
29
|
+
- id: trailing-whitespace
|
|
30
|
+
name: trailing-whitespace
|
|
31
|
+
entry: uv run trailing-whitespace-fixer
|
|
32
|
+
language: system
|
|
33
|
+
types: [text]
|
|
34
|
+
- id: ty
|
|
35
|
+
name: ty
|
|
36
|
+
entry: uv run ty check
|
|
37
|
+
language: system
|
|
38
|
+
types: [python]
|
|
39
|
+
pass_filenames: false
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
.PHONY: test test-bench lint type-check audit clean
|
|
2
|
+
|
|
3
|
+
PYTHON ?= .venv/bin/python
|
|
4
|
+
|
|
5
|
+
test: ## Run all tests (unit + benchmarks)
|
|
6
|
+
$(PYTHON) -m pytest
|
|
7
|
+
|
|
8
|
+
test-bench: ## Run benchmarks only
|
|
9
|
+
$(PYTHON) -m pytest tests/test_benchmarks.py --benchmark-only --benchmark-min-rounds=10 --benchmark-sort=mean
|
|
10
|
+
|
|
11
|
+
test-bench-save: ## Run benchmarks and save results
|
|
12
|
+
$(PYTHON) -m pytest tests/test_benchmarks.py --benchmark-only --benchmark-autosave --benchmark-sort=mean
|
|
13
|
+
|
|
14
|
+
test-bench-compare: ## Compare current benchmarks against saved baseline
|
|
15
|
+
$(PYTHON) -m pytest tests/test_benchmarks.py --benchmark-only --benchmark-compare=0
|
|
16
|
+
|
|
17
|
+
lint: ## Run ruff linter
|
|
18
|
+
$(PYTHON) -m ruff check src tests
|
|
19
|
+
|
|
20
|
+
type-check: ## Run type checker
|
|
21
|
+
$(PYTHON) -m ty check src
|
|
22
|
+
|
|
23
|
+
audit: ## Audit dependencies for vulnerabilities
|
|
24
|
+
$(PYTHON) -m pip-audit
|
|
25
|
+
|
|
26
|
+
clean: ## Remove build artifacts and caches
|
|
27
|
+
rm -rf .pytest_cache .benchmarks .coverage htmlcov build dist *.egg-info
|
|
28
|
+
find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: python-getpaid-core
|
|
3
|
+
Version: 3.0.0
|
|
4
|
+
Summary: Framework-agnostic payment processing core.
|
|
5
|
+
Project-URL: Homepage, https://github.com/django-getpaid/python-getpaid-core
|
|
6
|
+
Project-URL: Repository, https://github.com/django-getpaid/python-getpaid-core
|
|
7
|
+
Project-URL: Documentation, https://getpaid-core.readthedocs.io
|
|
8
|
+
Project-URL: Changelog, https://github.com/django-getpaid/python-getpaid-core/releases
|
|
9
|
+
Author-email: Dominik Kozaczko <dominik@kozaczko.info>
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
18
|
+
Classifier: Topic :: Office/Business :: Financial :: Point-Of-Sale
|
|
19
|
+
Classifier: Typing :: Typed
|
|
20
|
+
Requires-Python: >=3.12
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# python-getpaid-core
|
|
24
|
+
|
|
25
|
+
[](https://pypi.org/project/python-getpaid-core/)
|
|
26
|
+
[](https://pypi.org/project/python-getpaid-core/)
|
|
27
|
+
[](https://github.com/django-getpaid/python-getpaid-core/blob/main/LICENSE)
|
|
28
|
+
|
|
29
|
+
**Framework-agnostic payment processing core.**
|
|
30
|
+
|
|
31
|
+
`python-getpaid-core` is the foundation of the Getpaid ecosystem. It provides the abstract interfaces, semantic payment update engine, and plugin registry needed to build a robust payment system without coupling your logic to a specific web framework or payment provider.
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install python-getpaid-core
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Quick Start: Creating a Custom Processor
|
|
40
|
+
|
|
41
|
+
To implement a new payment backend, subclass `BaseProcessor` and implement at least `prepare_transaction`.
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from getpaid_core import BaseProcessor
|
|
45
|
+
from getpaid_core.types import TransactionResult
|
|
46
|
+
|
|
47
|
+
class MyPaymentProcessor(BaseProcessor):
|
|
48
|
+
slug = "my-provider"
|
|
49
|
+
display_name = "My Payment Provider"
|
|
50
|
+
accepted_currencies = ["USD", "EUR"]
|
|
51
|
+
|
|
52
|
+
async def prepare_transaction(self, **kwargs) -> TransactionResult:
|
|
53
|
+
# Generate payment link or form data
|
|
54
|
+
return TransactionResult(
|
|
55
|
+
redirect_url=f"https://api.provider.com/pay/{self.payment.id}",
|
|
56
|
+
method="GET"
|
|
57
|
+
)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Registering your Processor
|
|
61
|
+
|
|
62
|
+
Register your processor using entry points in your `pyproject.toml` so it can be discovered by the registry:
|
|
63
|
+
|
|
64
|
+
```toml
|
|
65
|
+
[project.entry-points."getpaid.backends"]
|
|
66
|
+
my-provider = "my_package.processors:MyPaymentProcessor"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Architecture Overview
|
|
70
|
+
|
|
71
|
+
- **BaseProcessor**: The abstract base class that all payment gateway plugins must implement. It provides the standard interface for transaction preparation, callback handling, charging, and refunds.
|
|
72
|
+
- **PaymentFlow**: Manages the payment lifecycle using semantic payment update objects. It ensures that payments move between states (e.g., `NEW` -> `PREPARED` -> `PAID`) according to strict business rules.
|
|
73
|
+
- **PluginRegistry**: A central service for discovering and managing payment processors registered via `getpaid.backends` entry points.
|
|
74
|
+
- **State Engine**: Applies semantic payment and fraud events to payment objects, merges provider metadata, and tracks idempotent provider event IDs.
|
|
75
|
+
|
|
76
|
+
## API Summary
|
|
77
|
+
|
|
78
|
+
| Class / Module | Role |
|
|
79
|
+
| --- | --- |
|
|
80
|
+
| `BaseProcessor` | Abstract base for implementing payment gateways. |
|
|
81
|
+
| `PaymentFlow` | Semantic orchestration for payment lifecycles. |
|
|
82
|
+
| `PaymentStatus` | Enum for all possible payment states (NEW, PAID, FAILED, etc.). |
|
|
83
|
+
| `registry` | Singleton registry for backend discovery. |
|
|
84
|
+
| `TransactionResult` | Standard response for transaction initiation. |
|
|
85
|
+
| `GetPaidException` | Base exception for all payment-related errors. |
|
|
86
|
+
|
|
87
|
+
## Ecosystem
|
|
88
|
+
|
|
89
|
+
`getpaid-core` is the heart of a larger ecosystem designed to make payment processing easy in any Python web application.
|
|
90
|
+
|
|
91
|
+
### Framework Wrappers
|
|
92
|
+
- [django-getpaid](https://github.com/django-getpaid/django-getpaid) — Official Django integration.
|
|
93
|
+
- [litestar-getpaid](https://github.com/django-getpaid/litestar-getpaid) — Official Litestar integration.
|
|
94
|
+
- [fastapi-getpaid](https://github.com/django-getpaid/fastapi-getpaid) — Official FastAPI integration.
|
|
95
|
+
|
|
96
|
+
### Processor Plugins
|
|
97
|
+
- [python-getpaid-payu](https://github.com/django-getpaid/python-getpaid-payu) — PayU backend.
|
|
98
|
+
- [python-getpaid-paynow](https://github.com/django-getpaid/python-getpaid-paynow) — Paynow backend.
|
|
99
|
+
- [python-getpaid-bitpay](https://github.com/django-getpaid/python-getpaid-bitpay) — BitPay backend.
|
|
100
|
+
- [python-getpaid-przelewy24](https://github.com/django-getpaid/python-getpaid-przelewy24) — Przelewy24 backend.
|
|
101
|
+
|
|
102
|
+
## License
|
|
103
|
+
|
|
104
|
+
This project is licensed under the MIT License.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# python-getpaid-core
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/python-getpaid-core/)
|
|
4
|
+
[](https://pypi.org/project/python-getpaid-core/)
|
|
5
|
+
[](https://github.com/django-getpaid/python-getpaid-core/blob/main/LICENSE)
|
|
6
|
+
|
|
7
|
+
**Framework-agnostic payment processing core.**
|
|
8
|
+
|
|
9
|
+
`python-getpaid-core` is the foundation of the Getpaid ecosystem. It provides the abstract interfaces, semantic payment update engine, and plugin registry needed to build a robust payment system without coupling your logic to a specific web framework or payment provider.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install python-getpaid-core
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick Start: Creating a Custom Processor
|
|
18
|
+
|
|
19
|
+
To implement a new payment backend, subclass `BaseProcessor` and implement at least `prepare_transaction`.
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
from getpaid_core import BaseProcessor
|
|
23
|
+
from getpaid_core.types import TransactionResult
|
|
24
|
+
|
|
25
|
+
class MyPaymentProcessor(BaseProcessor):
|
|
26
|
+
slug = "my-provider"
|
|
27
|
+
display_name = "My Payment Provider"
|
|
28
|
+
accepted_currencies = ["USD", "EUR"]
|
|
29
|
+
|
|
30
|
+
async def prepare_transaction(self, **kwargs) -> TransactionResult:
|
|
31
|
+
# Generate payment link or form data
|
|
32
|
+
return TransactionResult(
|
|
33
|
+
redirect_url=f"https://api.provider.com/pay/{self.payment.id}",
|
|
34
|
+
method="GET"
|
|
35
|
+
)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Registering your Processor
|
|
39
|
+
|
|
40
|
+
Register your processor using entry points in your `pyproject.toml` so it can be discovered by the registry:
|
|
41
|
+
|
|
42
|
+
```toml
|
|
43
|
+
[project.entry-points."getpaid.backends"]
|
|
44
|
+
my-provider = "my_package.processors:MyPaymentProcessor"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Architecture Overview
|
|
48
|
+
|
|
49
|
+
- **BaseProcessor**: The abstract base class that all payment gateway plugins must implement. It provides the standard interface for transaction preparation, callback handling, charging, and refunds.
|
|
50
|
+
- **PaymentFlow**: Manages the payment lifecycle using semantic payment update objects. It ensures that payments move between states (e.g., `NEW` -> `PREPARED` -> `PAID`) according to strict business rules.
|
|
51
|
+
- **PluginRegistry**: A central service for discovering and managing payment processors registered via `getpaid.backends` entry points.
|
|
52
|
+
- **State Engine**: Applies semantic payment and fraud events to payment objects, merges provider metadata, and tracks idempotent provider event IDs.
|
|
53
|
+
|
|
54
|
+
## API Summary
|
|
55
|
+
|
|
56
|
+
| Class / Module | Role |
|
|
57
|
+
| --- | --- |
|
|
58
|
+
| `BaseProcessor` | Abstract base for implementing payment gateways. |
|
|
59
|
+
| `PaymentFlow` | Semantic orchestration for payment lifecycles. |
|
|
60
|
+
| `PaymentStatus` | Enum for all possible payment states (NEW, PAID, FAILED, etc.). |
|
|
61
|
+
| `registry` | Singleton registry for backend discovery. |
|
|
62
|
+
| `TransactionResult` | Standard response for transaction initiation. |
|
|
63
|
+
| `GetPaidException` | Base exception for all payment-related errors. |
|
|
64
|
+
|
|
65
|
+
## Ecosystem
|
|
66
|
+
|
|
67
|
+
`getpaid-core` is the heart of a larger ecosystem designed to make payment processing easy in any Python web application.
|
|
68
|
+
|
|
69
|
+
### Framework Wrappers
|
|
70
|
+
- [django-getpaid](https://github.com/django-getpaid/django-getpaid) — Official Django integration.
|
|
71
|
+
- [litestar-getpaid](https://github.com/django-getpaid/litestar-getpaid) — Official Litestar integration.
|
|
72
|
+
- [fastapi-getpaid](https://github.com/django-getpaid/fastapi-getpaid) — Official FastAPI integration.
|
|
73
|
+
|
|
74
|
+
### Processor Plugins
|
|
75
|
+
- [python-getpaid-payu](https://github.com/django-getpaid/python-getpaid-payu) — PayU backend.
|
|
76
|
+
- [python-getpaid-paynow](https://github.com/django-getpaid/python-getpaid-paynow) — Paynow backend.
|
|
77
|
+
- [python-getpaid-bitpay](https://github.com/django-getpaid/python-getpaid-bitpay) — BitPay backend.
|
|
78
|
+
- [python-getpaid-przelewy24](https://github.com/django-getpaid/python-getpaid-przelewy24) — Przelewy24 backend.
|
|
79
|
+
|
|
80
|
+
## License
|
|
81
|
+
|
|
82
|
+
This project is licensed under the MIT License.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## v3.0.0 (2026-06-04)
|
|
4
|
+
|
|
5
|
+
Major stable release — framework-agnostic payment processing core.
|
|
6
|
+
|
|
7
|
+
### Breaking Changes
|
|
8
|
+
|
|
9
|
+
- Complete rewrite as a framework-agnostic library, no longer coupled to Django
|
|
10
|
+
- `django-fsm` dependency removed — replaced by runtime FSM via `transitions`
|
|
11
|
+
- Requires Python 3.12+
|
|
12
|
+
- `can_proceed()` replaced by `may_trigger()`
|
|
13
|
+
|
|
14
|
+
### Features
|
|
15
|
+
|
|
16
|
+
- Payment status enum (`PaymentStatus`) with 9 states matching django-getpaid v2 values
|
|
17
|
+
- Fraud status enum (`FraudStatus`) with 4 states
|
|
18
|
+
- Backend method and confirmation method enums
|
|
19
|
+
- `BaseProcessor` abstract class for payment gateway plugins
|
|
20
|
+
- Semantic payment and fraud update engine
|
|
21
|
+
- Transition validation with `InvalidTransitionError`
|
|
22
|
+
- Provider metadata merging and callback idempotency tracking
|
|
23
|
+
- `PluginRegistry` with entry-point discovery and manual registration
|
|
24
|
+
- Runtime-checkable protocols: `Payment`, `Order`, `PaymentRepository`
|
|
25
|
+
- Dataclass response types: `BuyerInfo`, `ItemInfo`, `ChargeResult`,
|
|
26
|
+
`PaymentUpdate`, `RefundResult`, `TransactionResult`
|
|
27
|
+
- Structured exception hierarchy with `context` support
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## v0.1.0 (2026-02-13)
|
|
32
|
+
|
|
33
|
+
Initial release — extracted from django-getpaid v2 and redesigned as a
|
|
34
|
+
framework-agnostic library.
|
|
35
|
+
|
|
36
|
+
### Features
|
|
37
|
+
|
|
38
|
+
- Payment status enum (`PaymentStatus`) with 9 states matching django-getpaid v2
|
|
39
|
+
values for backward compatibility
|
|
40
|
+
- Fraud status enum (`FraudStatus`) with 4 states
|
|
41
|
+
- Backend method and confirmation method enums
|
|
42
|
+
- `BaseProcessor` abstract class for payment gateway plugins
|
|
43
|
+
- Semantic payment and fraud update engine
|
|
44
|
+
- Transition validation with `InvalidTransitionError`
|
|
45
|
+
- Provider metadata merging and callback idempotency tracking
|
|
46
|
+
- `PluginRegistry` with entry-point discovery and manual registration
|
|
47
|
+
- Runtime-checkable protocols: `Payment`, `Order`, `PaymentRepository`
|
|
48
|
+
- Dataclass response types: `BuyerInfo`, `ItemInfo`, `ChargeResult`,
|
|
49
|
+
`PaymentUpdate`, `RefundResult`, `TransactionResult`
|
|
50
|
+
- Structured exception hierarchy with `context` support
|
|
@@ -16,45 +16,35 @@ Payments move through these states:
|
|
|
16
16
|
| `REFUND_STARTED` | `"refund_started"` | Refund initiated |
|
|
17
17
|
| `REFUNDED` | `"refunded"` | Fully refunded or lock released |
|
|
18
18
|
|
|
19
|
-
## Payment
|
|
19
|
+
## Payment Events
|
|
20
20
|
|
|
21
21
|
```
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
▼ ▼
|
|
32
|
-
PARTIAL ◄──────────────────────┘
|
|
33
|
-
│
|
|
34
|
-
├── mark_as_paid ──────────► PAID
|
|
35
|
-
│
|
|
36
|
-
├── start_refund ──────────► REFUND_STARTED
|
|
37
|
-
│ │
|
|
38
|
-
│ cancel_refund │ confirm_refund
|
|
39
|
-
◄─────────────────────────────┘
|
|
40
|
-
│
|
|
41
|
-
└── mark_as_refunded ──────► REFUNDED
|
|
42
|
-
|
|
43
|
-
NEW/PREPARED/PRE_AUTH ──fail──► FAILED
|
|
44
|
-
PRE_AUTH ──release_lock──────► REFUNDED
|
|
22
|
+
prepared -> PREPARED
|
|
23
|
+
locked -> PRE_AUTH
|
|
24
|
+
charge_requested -> IN_CHARGE
|
|
25
|
+
payment_captured -> PARTIAL or PAID
|
|
26
|
+
failed -> FAILED
|
|
27
|
+
refund_requested -> REFUND_STARTED
|
|
28
|
+
refund_confirmed -> PARTIAL or REFUNDED
|
|
29
|
+
refund_cancelled -> active paid status
|
|
30
|
+
lock_released -> REFUNDED
|
|
45
31
|
```
|
|
46
32
|
|
|
47
|
-
### Transition
|
|
33
|
+
### Transition Rules
|
|
48
34
|
|
|
49
|
-
|
|
35
|
+
The state engine raises `InvalidTransitionError` when an event is incompatible
|
|
36
|
+
with the current payment state.
|
|
50
37
|
|
|
51
|
-
-
|
|
52
|
-
-
|
|
38
|
+
- You cannot capture a payment after it is already refunded.
|
|
39
|
+
- You cannot start a refund before the payment has been paid.
|
|
40
|
+
- Refund confirmation moves to `REFUNDED` only when `amount_refunded >= amount_paid`.
|
|
53
41
|
|
|
54
|
-
### Amount
|
|
42
|
+
### Amount Handling
|
|
55
43
|
|
|
56
|
-
-
|
|
57
|
-
-
|
|
44
|
+
- `locked_amount` stores a pre-authorized amount.
|
|
45
|
+
- `paid_amount` updates captured funds and may reduce `amount_locked`.
|
|
46
|
+
- `refunded_amount` tracks refund progress.
|
|
47
|
+
- `provider_data` stores provider-specific metadata such as refund IDs and applied callback IDs.
|
|
58
48
|
|
|
59
49
|
## Fraud Statuses
|
|
60
50
|
|
|
@@ -103,13 +93,14 @@ class Payment(Protocol):
|
|
|
103
93
|
currency: str
|
|
104
94
|
status: str
|
|
105
95
|
backend: str
|
|
106
|
-
external_id: str
|
|
107
|
-
description: str
|
|
96
|
+
external_id: str | None
|
|
97
|
+
description: str | None
|
|
108
98
|
amount_paid: Decimal
|
|
109
99
|
amount_locked: Decimal
|
|
110
100
|
amount_refunded: Decimal
|
|
111
101
|
fraud_status: str
|
|
112
102
|
fraud_message: str
|
|
103
|
+
provider_data: dict[str, Any]
|
|
113
104
|
```
|
|
114
105
|
|
|
115
106
|
### PaymentRepository Protocol
|
|
@@ -162,6 +153,7 @@ raise ChargeFailure("Gateway returned 500", context={"status_code": 500})
|
|
|
162
153
|
|------|-------------|
|
|
163
154
|
| `BuyerInfo` | TypedDict with `email`, `first_name`, `last_name`, `phone` (all optional) |
|
|
164
155
|
| `ItemInfo` | TypedDict with `name`, `quantity`, `unit_price` |
|
|
165
|
-
| `
|
|
166
|
-
| `
|
|
167
|
-
| `
|
|
156
|
+
| `ChargeResult` | Dataclass with `amount_charged`, `success`, `async_call`, `provider_data` |
|
|
157
|
+
| `PaymentUpdate` | Dataclass describing semantic payment/fraud events and amounts |
|
|
158
|
+
| `RefundResult` | Dataclass with refund amount and provider metadata |
|
|
159
|
+
| `TransactionResult` | Dataclass with redirect, method, external ID, and provider metadata |
|
|
@@ -77,21 +77,30 @@ from getpaid_core.registry import registry
|
|
|
77
77
|
registry.register(MyGatewayProcessor)
|
|
78
78
|
```
|
|
79
79
|
|
|
80
|
-
## Payment
|
|
80
|
+
## Payment Updates
|
|
81
81
|
|
|
82
|
-
Payments move through states
|
|
83
|
-
|
|
82
|
+
Payments move through states by applying semantic `PaymentUpdate` objects.
|
|
83
|
+
Processors return updates from callbacks and status polling, and the flow
|
|
84
|
+
applies them to the payment object:
|
|
84
85
|
|
|
85
86
|
```python
|
|
86
|
-
from
|
|
87
|
-
|
|
88
|
-
machine = create_payment_machine(payment)
|
|
87
|
+
from decimal import Decimal
|
|
89
88
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
payment
|
|
89
|
+
from getpaid_core.fsm import apply_payment_update
|
|
90
|
+
from getpaid_core.types import PaymentUpdate
|
|
91
|
+
|
|
92
|
+
apply_payment_update(
|
|
93
|
+
payment,
|
|
94
|
+
PaymentUpdate(payment_event="prepared"),
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
apply_payment_update(
|
|
98
|
+
payment,
|
|
99
|
+
PaymentUpdate(
|
|
100
|
+
payment_event="payment_captured",
|
|
101
|
+
paid_amount=Decimal("100.00"),
|
|
102
|
+
),
|
|
103
|
+
)
|
|
95
104
|
```
|
|
96
105
|
|
|
97
|
-
See {doc}`concepts` for the
|
|
106
|
+
See {doc}`concepts` for the lifecycle rules and semantic event mapping.
|