python-getpaid-core 0.1.1__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.
Files changed (84) hide show
  1. python_getpaid_core-3.0.0/.github/workflows/ci.yml +36 -0
  2. python_getpaid_core-3.0.0/.github/workflows/release.yml +70 -0
  3. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/.gitignore +2 -0
  4. python_getpaid_core-3.0.0/.sisyphus/evidence/task-22-readme-core.txt +7 -0
  5. python_getpaid_core-3.0.0/Makefile +28 -0
  6. python_getpaid_core-3.0.0/PKG-INFO +104 -0
  7. python_getpaid_core-3.0.0/README.md +82 -0
  8. python_getpaid_core-3.0.0/docs/changelog.md +50 -0
  9. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/docs/concepts.md +28 -36
  10. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/docs/getting-started.md +21 -12
  11. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/docs/reference.md +1 -1
  12. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/pyproject.toml +9 -8
  13. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/src/getpaid_core/__init__.py +15 -1
  14. python_getpaid_core-3.0.0/src/getpaid_core/backends/dummy.py +145 -0
  15. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/src/getpaid_core/enums.py +23 -4
  16. python_getpaid_core-3.0.0/src/getpaid_core/flow.py +259 -0
  17. python_getpaid_core-3.0.0/src/getpaid_core/fsm.py +331 -0
  18. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/src/getpaid_core/processor.py +20 -28
  19. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/src/getpaid_core/protocols.py +5 -8
  20. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/src/getpaid_core/registry.py +16 -16
  21. python_getpaid_core-3.0.0/src/getpaid_core/types.py +102 -0
  22. python_getpaid_core-3.0.0/src/getpaid_core/validators.py +13 -0
  23. python_getpaid_core-3.0.0/tests/conftest.py +211 -0
  24. python_getpaid_core-3.0.0/tests/test_benchmarks.py +549 -0
  25. python_getpaid_core-3.0.0/tests/test_dummy_backend.py +132 -0
  26. python_getpaid_core-3.0.0/tests/test_enums.py +47 -0
  27. python_getpaid_core-3.0.0/tests/test_flow.py +282 -0
  28. python_getpaid_core-3.0.0/tests/test_integration.py +79 -0
  29. python_getpaid_core-3.0.0/tests/test_processor.py +129 -0
  30. python_getpaid_core-3.0.0/tests/test_protocols.py +89 -0
  31. python_getpaid_core-3.0.0/tests/test_public_api.py +25 -0
  32. python_getpaid_core-3.0.0/tests/test_registry.py +173 -0
  33. python_getpaid_core-3.0.0/tests/test_state_engine.py +370 -0
  34. python_getpaid_core-3.0.0/tests/test_types.py +84 -0
  35. python_getpaid_core-3.0.0/tests/test_version.py +11 -0
  36. python_getpaid_core-0.1.1/.github/workflows/release.yml +0 -79
  37. python_getpaid_core-0.1.1/PKG-INFO +0 -105
  38. python_getpaid_core-0.1.1/README.md +0 -80
  39. python_getpaid_core-0.1.1/docs/changelog.md +0 -23
  40. python_getpaid_core-0.1.1/src/getpaid_core/backends/dummy.py +0 -95
  41. python_getpaid_core-0.1.1/src/getpaid_core/flow.py +0 -144
  42. python_getpaid_core-0.1.1/src/getpaid_core/fsm.py +0 -223
  43. python_getpaid_core-0.1.1/src/getpaid_core/types.py +0 -52
  44. python_getpaid_core-0.1.1/src/getpaid_core/validators.py +0 -21
  45. python_getpaid_core-0.1.1/tests/conftest.py +0 -132
  46. python_getpaid_core-0.1.1/tests/test_dummy_backend.py +0 -152
  47. python_getpaid_core-0.1.1/tests/test_enums.py +0 -87
  48. python_getpaid_core-0.1.1/tests/test_flow.py +0 -208
  49. python_getpaid_core-0.1.1/tests/test_fsm.py +0 -292
  50. python_getpaid_core-0.1.1/tests/test_integration.py +0 -182
  51. python_getpaid_core-0.1.1/tests/test_processor.py +0 -184
  52. python_getpaid_core-0.1.1/tests/test_protocols.py +0 -85
  53. python_getpaid_core-0.1.1/tests/test_public_api.py +0 -35
  54. python_getpaid_core-0.1.1/tests/test_registry.py +0 -190
  55. python_getpaid_core-0.1.1/tests/test_types.py +0 -97
  56. python_getpaid_core-0.1.1/uv.lock +0 -936
  57. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/.cookiecutter.json +0 -0
  58. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/.gitattributes +0 -0
  59. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/.github/dependabot.yml +0 -0
  60. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/.github/labels.yml +0 -0
  61. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/.github/release-drafter.yml +0 -0
  62. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/.github/workflows/constraints.txt +0 -0
  63. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/.github/workflows/labeler.yml +0 -0
  64. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/.github/workflows/tests.yml +0 -0
  65. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/.plans/2026-02-13-getpaid-core-design.md +0 -0
  66. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/.plans/2026-02-13-getpaid-core-implementation.md +0 -0
  67. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/.pre-commit-config.yaml +0 -0
  68. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/.readthedocs.yml +0 -0
  69. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/CODE_OF_CONDUCT.md +0 -0
  70. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/CONTRIBUTING.md +0 -0
  71. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/LICENSE +0 -0
  72. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/codecov.yml +0 -0
  73. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/docs/codeofconduct.md +0 -0
  74. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/docs/conf.py +0 -0
  75. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/docs/contributing.md +0 -0
  76. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/docs/index.md +0 -0
  77. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/docs/license.md +0 -0
  78. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/docs/requirements.txt +0 -0
  79. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/src/getpaid_core/backends/__init__.py +0 -0
  80. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/src/getpaid_core/exceptions.py +0 -0
  81. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/src/getpaid_core/py.typed +0 -0
  82. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/tests/__init__.py +0 -0
  83. {python_getpaid_core-0.1.1 → python_getpaid_core-3.0.0}/tests/test_exceptions.py +0 -0
  84. {python_getpaid_core-0.1.1 → 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 }}
@@ -6,6 +6,8 @@
6
6
  /.pytype/
7
7
  /dist/
8
8
  /docs/_build/
9
+ /_build/
9
10
  /src/*.egg-info/
10
11
  __pycache__/
11
12
  /.idea/
13
+ /uv.lock
@@ -0,0 +1,7 @@
1
+ README.md verification results:
2
+ Line count: 82
3
+ pip install count: 1
4
+ BaseProcessor count: 5
5
+ Wrappers count: 8
6
+
7
+ All verification checks PASSED.
@@ -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
+ [![PyPI version](https://img.shields.io/pypi/v/python-getpaid-core)](https://pypi.org/project/python-getpaid-core/)
26
+ [![Python version](https://img.shields.io/pypi/pyversions/python-getpaid-core)](https://pypi.org/project/python-getpaid-core/)
27
+ [![License](https://img.shields.io/pypi/l/python-getpaid-core)](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
+ [![PyPI version](https://img.shields.io/pypi/v/python-getpaid-core)](https://pypi.org/project/python-getpaid-core/)
4
+ [![Python version](https://img.shields.io/pypi/pyversions/python-getpaid-core)](https://pypi.org/project/python-getpaid-core/)
5
+ [![License](https://img.shields.io/pypi/l/python-getpaid-core)](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 State Transitions
19
+ ## Payment Events
20
20
 
21
21
  ```
22
- NEW ──────────────────────► PREPARED
23
- │ │
24
- │ confirm_lock │ confirm_lock
25
- ▼ ▼
26
- PRE_AUTH ◄─────────────────────┘
27
-
28
- ├── confirm_charge_sent ──► IN_CHARGE
29
- │ │
30
- │ confirm_payment │ confirm_payment
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 Guards
33
+ ### Transition Rules
48
34
 
49
- Some transitions have guards that raise `MachineError` if conditions aren't met:
35
+ The state engine raises `InvalidTransitionError` when an event is incompatible
36
+ with the current payment state.
50
37
 
51
- - **`mark_as_paid`** requires `is_fully_paid()` `amount_paid >= amount_required`
52
- - **`mark_as_refunded`** requires `is_fully_refunded()` `amount_refunded >= amount_paid`
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 Callbacks
42
+ ### Amount Handling
55
43
 
56
- - **`confirm_lock`** stores the locked amount via `_store_locked_amount`
57
- - **`confirm_payment`** accumulates paid amount via `_accumulate_paid_amount`
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
- | `ChargeResponse` | TypedDict with `amount_charged`, `success`, `async_call` |
166
- | `PaymentStatusResponse` | TypedDict with `amount`, `status`, `external_id` (all optional) |
167
- | `TransactionResult` | TypedDict with `redirect_url`, `form_data`, `method`, `headers` |
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 State Machine
80
+ ## Payment Updates
81
81
 
82
- Payments move through states via an FSM powered by the `transitions` library.
83
- The machine attaches trigger methods directly to payment objects:
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 getpaid_core.fsm import create_payment_machine
87
-
88
- machine = create_payment_machine(payment)
87
+ from decimal import Decimal
89
88
 
90
- # Now payment has trigger methods:
91
- payment.confirm_prepared() # NEW -> PREPARED
92
- payment.confirm_lock(amount=100) # PREPARED -> PRE_AUTH
93
- payment.confirm_payment(amount=100) # PRE_AUTH -> PARTIAL
94
- payment.mark_as_paid() # PARTIAL -> PAID (if fully paid)
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 full state diagram and transition rules.
106
+ See {doc}`concepts` for the lifecycle rules and semantic event mapping.
@@ -16,7 +16,7 @@
16
16
  :undoc-members:
17
17
  ```
18
18
 
19
- ## FSM
19
+ ## State Engine
20
20
 
21
21
  ```{eval-rst}
22
22
  .. automodule:: getpaid_core.fsm
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = 'python-getpaid-core'
3
- version = "0.1.1"
3
+ dynamic = ["version"]
4
4
  description = 'Framework-agnostic payment processing core.'
5
5
  readme = 'README.md'
6
6
  license = {text = 'MIT'}
@@ -9,7 +9,7 @@ authors = [
9
9
  ]
10
10
  requires-python = '>=3.12'
11
11
  classifiers = [
12
- 'Development Status :: 3 - Alpha',
12
+ 'Development Status :: 5 - Production/Stable',
13
13
  'Intended Audience :: Developers',
14
14
  'License :: OSI Approved :: MIT License',
15
15
  'Programming Language :: Python :: 3.12',
@@ -18,11 +18,7 @@ classifiers = [
18
18
  'Topic :: Office/Business :: Financial :: Point-Of-Sale',
19
19
  'Typing :: Typed',
20
20
  ]
21
- dependencies = [
22
- 'transitions>=0.9.0',
23
- 'httpx>=0.27.0',
24
- 'anyio>=4.0',
25
- ]
21
+ dependencies = []
26
22
 
27
23
  [dependency-groups]
28
24
  dev = [
@@ -37,6 +33,8 @@ dev = [
37
33
  "myst-parser>=4.0.1",
38
34
  "pre-commit-hooks>=6.0.0",
39
35
  "ty>=0.0.16",
36
+ "pip-audit>=2.7.0",
37
+ "pytest-benchmark>=5.0.0",
40
38
  ]
41
39
 
42
40
  [project.urls]
@@ -52,6 +50,9 @@ build-backend = 'hatchling.build'
52
50
  [tool.hatch.build.targets.wheel]
53
51
  packages = ['src/getpaid_core']
54
52
 
53
+ [tool.hatch.version]
54
+ path = "src/getpaid_core/__init__.py"
55
+
55
56
  [tool.pytest.ini_options]
56
57
  testpaths = ['tests']
57
58
  asyncio_mode = 'auto'
@@ -88,7 +89,7 @@ ignore = [
88
89
  ]
89
90
 
90
91
  [tool.ruff.lint.per-file-ignores]
91
- 'tests/**' = ['TC001', 'RUF012']
92
+ 'tests/**' = ['TC001', 'RUF012', 'E501']
92
93
 
93
94
  [tool.ruff.lint.isort]
94
95
  force-single-line = true
@@ -1,10 +1,12 @@
1
1
  """Getpaid Core -- framework-agnostic payment processing."""
2
2
 
3
- __version__ = "0.1.0"
3
+ __version__ = "3.0.0"
4
4
 
5
5
  from getpaid_core.enums import BackendMethod
6
6
  from getpaid_core.enums import ConfirmationMethod
7
+ from getpaid_core.enums import FraudEvent
7
8
  from getpaid_core.enums import FraudStatus
9
+ from getpaid_core.enums import PaymentEvent
8
10
  from getpaid_core.enums import PaymentStatus
9
11
  from getpaid_core.exceptions import ChargeFailure
10
12
  from getpaid_core.exceptions import CommunicationError
@@ -16,24 +18,36 @@ from getpaid_core.exceptions import LockFailure
16
18
  from getpaid_core.exceptions import RefundFailure
17
19
  from getpaid_core.flow import PaymentFlow
18
20
  from getpaid_core.processor import BaseProcessor
21
+ from getpaid_core.registry import PluginRegistry
19
22
  from getpaid_core.registry import registry
23
+ from getpaid_core.types import ChargeResult
24
+ from getpaid_core.types import PaymentUpdate
25
+ from getpaid_core.types import RefundResult
26
+ from getpaid_core.types import TransactionResult
20
27
 
21
28
 
22
29
  __all__ = [
23
30
  "BackendMethod",
24
31
  "BaseProcessor",
25
32
  "ChargeFailure",
33
+ "ChargeResult",
26
34
  "CommunicationError",
27
35
  "ConfirmationMethod",
28
36
  "CredentialsError",
37
+ "FraudEvent",
29
38
  "FraudStatus",
30
39
  "GetPaidException",
31
40
  "InvalidCallbackError",
32
41
  "InvalidTransitionError",
33
42
  "LockFailure",
43
+ "PaymentEvent",
34
44
  "PaymentFlow",
35
45
  "PaymentStatus",
46
+ "PaymentUpdate",
47
+ "PluginRegistry",
36
48
  "RefundFailure",
49
+ "RefundResult",
50
+ "TransactionResult",
37
51
  "__version__",
38
52
  "registry",
39
53
  ]