python-getpaid-paynow 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.
Files changed (51) hide show
  1. python_getpaid_paynow-3.0.0/.github/release-drafter.yml +31 -0
  2. python_getpaid_paynow-3.0.0/.github/workflows/ci.yml +39 -0
  3. python_getpaid_paynow-3.0.0/.github/workflows/release.yml +70 -0
  4. {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/.gitignore +1 -0
  5. python_getpaid_paynow-3.0.0/.pre-commit-config.yaml +15 -0
  6. python_getpaid_paynow-3.0.0/.python-version +1 -0
  7. python_getpaid_paynow-3.0.0/PKG-INFO +188 -0
  8. python_getpaid_paynow-3.0.0/README.md +161 -0
  9. python_getpaid_paynow-3.0.0/docs/changelog.md +28 -0
  10. {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/docs/concepts.md +13 -12
  11. {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/docs/getting-started.md +3 -3
  12. {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/pyproject.toml +17 -5
  13. {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/src/getpaid_paynow/__init__.py +2 -0
  14. {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/src/getpaid_paynow/client.py +98 -34
  15. {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/src/getpaid_paynow/processor.py +102 -66
  16. python_getpaid_paynow-3.0.0/src/getpaid_paynow/simulator/__init__.py +6 -0
  17. python_getpaid_paynow-3.0.0/src/getpaid_paynow/simulator/plugin.py +62 -0
  18. python_getpaid_paynow-3.0.0/src/getpaid_paynow/simulator/routes.py +421 -0
  19. python_getpaid_paynow-3.0.0/src/getpaid_paynow/simulator/signing.py +49 -0
  20. python_getpaid_paynow-3.0.0/src/getpaid_paynow/simulator/transitions.py +17 -0
  21. python_getpaid_paynow-3.0.0/src/getpaid_paynow/simulator/webhooks.py +59 -0
  22. python_getpaid_paynow-3.0.0/tests/conftest.py +119 -0
  23. python_getpaid_paynow-3.0.0/tests/test_callback.py +143 -0
  24. {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/tests/test_client.py +107 -0
  25. python_getpaid_paynow-3.0.0/tests/test_entry_points.py +27 -0
  26. python_getpaid_paynow-3.0.0/tests/test_processor.py +251 -0
  27. python_getpaid_paynow-3.0.0/tests/test_public_api.py +25 -0
  28. python_getpaid_paynow-3.0.0/tests/test_simulator_plugin.py +163 -0
  29. python_getpaid_paynow-0.1.0/PKG-INFO +0 -154
  30. python_getpaid_paynow-0.1.0/README.md +0 -131
  31. python_getpaid_paynow-0.1.0/docs/changelog.md +0 -19
  32. python_getpaid_paynow-0.1.0/tests/conftest.py +0 -143
  33. python_getpaid_paynow-0.1.0/tests/test_callback.py +0 -315
  34. python_getpaid_paynow-0.1.0/tests/test_processor.py +0 -373
  35. python_getpaid_paynow-0.1.0/uv.lock +0 -925
  36. {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/.readthedocs.yml +0 -0
  37. {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/CODE_OF_CONDUCT.md +0 -0
  38. {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/CONTRIBUTING.md +0 -0
  39. {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/LICENSE +0 -0
  40. {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/docs/codeofconduct.md +0 -0
  41. {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/docs/conf.py +0 -0
  42. {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/docs/configuration.md +0 -0
  43. {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/docs/contributing.md +0 -0
  44. {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/docs/index.md +0 -0
  45. {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/docs/license.md +0 -0
  46. {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/docs/reference.md +0 -0
  47. {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/docs/requirements.txt +0 -0
  48. {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/src/getpaid_paynow/py.typed +0 -0
  49. {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/src/getpaid_paynow/types.py +0 -0
  50. {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/tests/__init__.py +0 -0
  51. {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/tests/test_types.py +0 -0
@@ -0,0 +1,31 @@
1
+ name-template: 'v$RESOLVED_VERSION'
2
+ tag-template: 'v$RESOLVED_VERSION'
3
+ categories:
4
+ - title: ':boom: Breaking Changes'
5
+ label: 'breaking'
6
+ - title: ':rocket: Features'
7
+ label: 'enhancement'
8
+ - title: ':fire: Removals and Deprecations'
9
+ label: 'removal'
10
+ - title: ':beetle: Fixes'
11
+ label: 'bug'
12
+ - title: ':racehorse: Performance'
13
+ label: 'performance'
14
+ - title: ':rotating_light: Testing'
15
+ label: 'testing'
16
+ - title: ':construction_worker: Continuous Integration'
17
+ label: 'ci'
18
+ - title: ':books: Documentation'
19
+ label: 'documentation'
20
+ - title: ':hammer: Refactoring'
21
+ label: 'refactoring'
22
+ - title: ':lipstick: Style'
23
+ label: 'style'
24
+ - title: ':package: Dependencies'
25
+ labels:
26
+ - 'dependencies'
27
+ - 'build'
28
+ template: |
29
+ ## Changes
30
+
31
+ $CHANGES
@@ -0,0 +1,39 @@
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: Type check with ty
33
+ run: uv run ty check
34
+
35
+ - name: Audit dependencies
36
+ run: uv run pip-audit --strict
37
+
38
+ - name: Run tests
39
+ 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 }}
@@ -18,3 +18,4 @@ htmlcov/
18
18
 
19
19
  # Sphinx build output
20
20
  docs/_build/
21
+ uv.lock
@@ -0,0 +1,15 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v5.0.0
4
+ hooks:
5
+ - id: trailing-whitespace
6
+ - id: end-of-file-fixer
7
+ - id: check-yaml
8
+ - id: check-added-large-files
9
+
10
+ - repo: https://github.com/astral-sh/ruff-pre-commit
11
+ rev: v0.11.12
12
+ hooks:
13
+ - id: ruff
14
+ args: [--fix]
15
+ - id: ruff-format
@@ -0,0 +1 @@
1
+ 3.12
@@ -0,0 +1,188 @@
1
+ Metadata-Version: 2.4
2
+ Name: python-getpaid-paynow
3
+ Version: 3.0.0
4
+ Summary: Paynow payment gateway integration for python-getpaid ecosystem.
5
+ Project-URL: Homepage, https://github.com/django-getpaid/python-getpaid-paynow
6
+ Project-URL: Repository, https://github.com/django-getpaid/python-getpaid-paynow
7
+ Project-URL: Documentation, https://python-getpaid-paynow.readthedocs.io
8
+ Project-URL: Changelog, https://github.com/django-getpaid/python-getpaid-paynow/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
+ Requires-Dist: httpx>=0.27.0
22
+ Requires-Dist: python-getpaid-core>=3.0.0
23
+ Provides-Extra: simulator
24
+ Requires-Dist: litestar>=2.0; extra == 'simulator'
25
+ Requires-Dist: python-getpaid-simulator>=3.0.0; extra == 'simulator'
26
+ Description-Content-Type: text/markdown
27
+
28
+ # python-getpaid-paynow
29
+
30
+ [![PyPI version](https://img.shields.io/pypi/v/python-getpaid-paynow.svg)](https://pypi.org/project/python-getpaid-paynow/)
31
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
32
+ [![Python versions](https://img.shields.io/pypi/pyversions/python-getpaid-paynow.svg)](https://pypi.org/project/python-getpaid-paynow/)
33
+
34
+ Paynow payment processor for [python-getpaid](https://github.com/django-getpaid/python-getpaid-core) ecosystem.
35
+ Paynow is a modern Polish payment provider and a subsidiary of mBank.
36
+
37
+ ## Architecture
38
+
39
+ The plugin is split into two layers:
40
+
41
+ - **`PaynowClient`** -- low-level async HTTP client wrapping the Paynow V3 REST API. Uses `httpx.AsyncClient` with API Key authentication and HMAC-SHA256 request signing. Can be used standalone or as an async context manager for connection reuse.
42
+ - **`PaynowProcessor`** -- high-level payment processor implementing `BaseProcessor`. Orchestrates payment creation, callback/notification handling, status polling, and refunds using semantic payment updates.
43
+
44
+ ## Key Features
45
+
46
+ - **Create payment** -- register a payment and get a redirect URL
47
+ - **Notification handling** -- verify HMAC-SHA256 signature and process status changes
48
+ - **Status polling** -- fetch current payment status via API (PULL flow)
49
+ - **Refund** -- create, check, and cancel refunds
50
+ - **Payment methods** -- retrieve available payment methods
51
+ - **Sandbox mode** -- full support for testing environment
52
+
53
+ **Note:** Paynow does not support pre-authorization flows. Immediate capture is used for all transactions. The `charge()` and `release_lock()` methods raise `NotImplementedError`.
54
+
55
+ ## Supported Currencies
56
+
57
+ The processor supports the following 4 currencies:
58
+ - **PLN** (Polish Złoty)
59
+ - **EUR** (Euro)
60
+ - **GBP** (British Pound)
61
+ - **USD** (US Dollar)
62
+
63
+ ## Installation
64
+
65
+ Install the package using pip:
66
+
67
+ ```bash
68
+ pip install python-getpaid-paynow
69
+ ```
70
+
71
+ Install simulator support only when you want this package to register its local
72
+ simulator plugin with `python-getpaid-simulator`:
73
+
74
+ ```bash
75
+ pip install python-getpaid-paynow[simulator]
76
+ ```
77
+
78
+ This extra installs the simulator host and Litestar dependencies, then exposes
79
+ the `paynow` plugin through the `getpaid.simulator.providers` entry point.
80
+
81
+ ## Simulator Plugin
82
+
83
+ When `python-getpaid-paynow[simulator]` is installed alongside
84
+ `python-getpaid-simulator`, the simulator host auto-discovers the PayNow
85
+ plugin.
86
+
87
+ Typical local setup:
88
+
89
+ ```bash
90
+ pip install python-getpaid-simulator python-getpaid-paynow[simulator]
91
+ getpaid-simulator
92
+ ```
93
+
94
+ The plugin contributes:
95
+
96
+ - PayNow payment and refund simulator API routes under `/paynow/v3/...`
97
+ - PayNow authorization UI at `/sim/paynow/authorize/{payment_id}`
98
+ - PayNow-specific webhook signing and state transitions
99
+
100
+ Useful simulator environment variables:
101
+
102
+ - `SIMULATOR_PAYNOW_API_KEY`
103
+ - `SIMULATOR_PAYNOW_SIGNATURE_KEY`
104
+ - `SIMULATOR_PAYNOW_NOTIFY_URL`
105
+ - `SIMULATOR_PLUGIN_FAILURE_MODE` (`warn` or `strict`)
106
+
107
+ ## Quick Usage
108
+
109
+ ### Standalone Client
110
+
111
+ ```python
112
+ import anyio
113
+ from decimal import Decimal
114
+ from getpaid_paynow import PaynowClient
115
+
116
+ async def main():
117
+ async with PaynowClient(
118
+ api_key="your-api-key",
119
+ signature_key="your-signature-key",
120
+ api_url="https://api.sandbox.paynow.pl",
121
+ ) as client:
122
+ # Create a payment
123
+ response = await client.create_payment(
124
+ amount=Decimal("49.99"),
125
+ currency="PLN",
126
+ external_id="order-001",
127
+ description="Order #001",
128
+ buyer_email="buyer@example.com",
129
+ continue_url="https://shop.example.com/return/order-001",
130
+ )
131
+ redirect_url = response["redirectUrl"]
132
+ print(f"Redirect buyer to: {redirect_url}")
133
+
134
+ anyio.run(main)
135
+ ```
136
+
137
+ ### With python-getpaid
138
+
139
+ Register the plugin via entry point in `pyproject.toml` (if not using the pre-packaged version):
140
+
141
+ ```toml
142
+ [project.entry-points."getpaid.backends"]
143
+ paynow = "getpaid_paynow.processor:PaynowProcessor"
144
+ ```
145
+
146
+ Then configure in your project settings:
147
+
148
+ ```python
149
+ GETPAID_BACKEND_SETTINGS = {
150
+ "paynow": {
151
+ "api_key": "your-api-key",
152
+ "signature_key": "your-signature-key",
153
+ "sandbox": True,
154
+ "notification_url": "https://your-site.com/payments/{payment_id}/callback/",
155
+ "continue_url": "https://your-site.com/payments/{payment_id}/return/",
156
+ }
157
+ }
158
+ ```
159
+
160
+ ## Configuration Reference
161
+
162
+ | Key | Type | Default | Description |
163
+ |-----|------|---------|-------------|
164
+ | `api_key` | `str` | *required* | API key from Paynow merchant panel |
165
+ | `signature_key` | `str` | *required* | Signature key for HMAC calculation |
166
+ | `sandbox` | `bool` | `True` | Use sandbox or production API |
167
+ | `notification_url` | `str` | `""` | Notification URL template; use `{payment_id}` placeholder |
168
+ | `continue_url` | `str` | `""` | Return URL template; use `{payment_id}` placeholder |
169
+
170
+ ## Requirements
171
+
172
+ - Python 3.12+
173
+ - `python-getpaid-core >= 3.0.0`
174
+ - `httpx >= 0.27.0`
175
+
176
+ ## Links
177
+
178
+ - **Core Library:** [python-getpaid-core](https://github.com/django-getpaid/python-getpaid-core)
179
+ - **Official Paynow Documentation:** [docs.paynow.pl](https://docs.paynow.pl/)
180
+ - **GitHub Repository:** [django-getpaid/python-getpaid-paynow](https://github.com/django-getpaid/python-getpaid-paynow)
181
+
182
+ ## License
183
+
184
+ This project is licensed under the MIT License.
185
+
186
+ ## Credits
187
+
188
+ Created by [Dominik Kozaczko](https://github.com/dekoza).
@@ -0,0 +1,161 @@
1
+ # python-getpaid-paynow
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/python-getpaid-paynow.svg)](https://pypi.org/project/python-getpaid-paynow/)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![Python versions](https://img.shields.io/pypi/pyversions/python-getpaid-paynow.svg)](https://pypi.org/project/python-getpaid-paynow/)
6
+
7
+ Paynow payment processor for [python-getpaid](https://github.com/django-getpaid/python-getpaid-core) ecosystem.
8
+ Paynow is a modern Polish payment provider and a subsidiary of mBank.
9
+
10
+ ## Architecture
11
+
12
+ The plugin is split into two layers:
13
+
14
+ - **`PaynowClient`** -- low-level async HTTP client wrapping the Paynow V3 REST API. Uses `httpx.AsyncClient` with API Key authentication and HMAC-SHA256 request signing. Can be used standalone or as an async context manager for connection reuse.
15
+ - **`PaynowProcessor`** -- high-level payment processor implementing `BaseProcessor`. Orchestrates payment creation, callback/notification handling, status polling, and refunds using semantic payment updates.
16
+
17
+ ## Key Features
18
+
19
+ - **Create payment** -- register a payment and get a redirect URL
20
+ - **Notification handling** -- verify HMAC-SHA256 signature and process status changes
21
+ - **Status polling** -- fetch current payment status via API (PULL flow)
22
+ - **Refund** -- create, check, and cancel refunds
23
+ - **Payment methods** -- retrieve available payment methods
24
+ - **Sandbox mode** -- full support for testing environment
25
+
26
+ **Note:** Paynow does not support pre-authorization flows. Immediate capture is used for all transactions. The `charge()` and `release_lock()` methods raise `NotImplementedError`.
27
+
28
+ ## Supported Currencies
29
+
30
+ The processor supports the following 4 currencies:
31
+ - **PLN** (Polish Złoty)
32
+ - **EUR** (Euro)
33
+ - **GBP** (British Pound)
34
+ - **USD** (US Dollar)
35
+
36
+ ## Installation
37
+
38
+ Install the package using pip:
39
+
40
+ ```bash
41
+ pip install python-getpaid-paynow
42
+ ```
43
+
44
+ Install simulator support only when you want this package to register its local
45
+ simulator plugin with `python-getpaid-simulator`:
46
+
47
+ ```bash
48
+ pip install python-getpaid-paynow[simulator]
49
+ ```
50
+
51
+ This extra installs the simulator host and Litestar dependencies, then exposes
52
+ the `paynow` plugin through the `getpaid.simulator.providers` entry point.
53
+
54
+ ## Simulator Plugin
55
+
56
+ When `python-getpaid-paynow[simulator]` is installed alongside
57
+ `python-getpaid-simulator`, the simulator host auto-discovers the PayNow
58
+ plugin.
59
+
60
+ Typical local setup:
61
+
62
+ ```bash
63
+ pip install python-getpaid-simulator python-getpaid-paynow[simulator]
64
+ getpaid-simulator
65
+ ```
66
+
67
+ The plugin contributes:
68
+
69
+ - PayNow payment and refund simulator API routes under `/paynow/v3/...`
70
+ - PayNow authorization UI at `/sim/paynow/authorize/{payment_id}`
71
+ - PayNow-specific webhook signing and state transitions
72
+
73
+ Useful simulator environment variables:
74
+
75
+ - `SIMULATOR_PAYNOW_API_KEY`
76
+ - `SIMULATOR_PAYNOW_SIGNATURE_KEY`
77
+ - `SIMULATOR_PAYNOW_NOTIFY_URL`
78
+ - `SIMULATOR_PLUGIN_FAILURE_MODE` (`warn` or `strict`)
79
+
80
+ ## Quick Usage
81
+
82
+ ### Standalone Client
83
+
84
+ ```python
85
+ import anyio
86
+ from decimal import Decimal
87
+ from getpaid_paynow import PaynowClient
88
+
89
+ async def main():
90
+ async with PaynowClient(
91
+ api_key="your-api-key",
92
+ signature_key="your-signature-key",
93
+ api_url="https://api.sandbox.paynow.pl",
94
+ ) as client:
95
+ # Create a payment
96
+ response = await client.create_payment(
97
+ amount=Decimal("49.99"),
98
+ currency="PLN",
99
+ external_id="order-001",
100
+ description="Order #001",
101
+ buyer_email="buyer@example.com",
102
+ continue_url="https://shop.example.com/return/order-001",
103
+ )
104
+ redirect_url = response["redirectUrl"]
105
+ print(f"Redirect buyer to: {redirect_url}")
106
+
107
+ anyio.run(main)
108
+ ```
109
+
110
+ ### With python-getpaid
111
+
112
+ Register the plugin via entry point in `pyproject.toml` (if not using the pre-packaged version):
113
+
114
+ ```toml
115
+ [project.entry-points."getpaid.backends"]
116
+ paynow = "getpaid_paynow.processor:PaynowProcessor"
117
+ ```
118
+
119
+ Then configure in your project settings:
120
+
121
+ ```python
122
+ GETPAID_BACKEND_SETTINGS = {
123
+ "paynow": {
124
+ "api_key": "your-api-key",
125
+ "signature_key": "your-signature-key",
126
+ "sandbox": True,
127
+ "notification_url": "https://your-site.com/payments/{payment_id}/callback/",
128
+ "continue_url": "https://your-site.com/payments/{payment_id}/return/",
129
+ }
130
+ }
131
+ ```
132
+
133
+ ## Configuration Reference
134
+
135
+ | Key | Type | Default | Description |
136
+ |-----|------|---------|-------------|
137
+ | `api_key` | `str` | *required* | API key from Paynow merchant panel |
138
+ | `signature_key` | `str` | *required* | Signature key for HMAC calculation |
139
+ | `sandbox` | `bool` | `True` | Use sandbox or production API |
140
+ | `notification_url` | `str` | `""` | Notification URL template; use `{payment_id}` placeholder |
141
+ | `continue_url` | `str` | `""` | Return URL template; use `{payment_id}` placeholder |
142
+
143
+ ## Requirements
144
+
145
+ - Python 3.12+
146
+ - `python-getpaid-core >= 3.0.0`
147
+ - `httpx >= 0.27.0`
148
+
149
+ ## Links
150
+
151
+ - **Core Library:** [python-getpaid-core](https://github.com/django-getpaid/python-getpaid-core)
152
+ - **Official Paynow Documentation:** [docs.paynow.pl](https://docs.paynow.pl/)
153
+ - **GitHub Repository:** [django-getpaid/python-getpaid-paynow](https://github.com/django-getpaid/python-getpaid-paynow)
154
+
155
+ ## License
156
+
157
+ This project is licensed under the MIT License.
158
+
159
+ ## Credits
160
+
161
+ Created by [Dominik Kozaczko](https://github.com/dekoza).
@@ -0,0 +1,28 @@
1
+ # Changelog
2
+
3
+ ## v3.0.0 (2026-06-04)
4
+
5
+ Stable release of the Paynow payment gateway integration.
6
+
7
+ ### Breaking Changes
8
+
9
+ - Version bumped from `3.0.0a4` to `3.0.0` (stable).
10
+ - Development status changed from `Alpha` to `Production/Stable`.
11
+ - Core dependency floor raised to `>=3.0.0` (from `>=3.0.0a4`).
12
+
13
+ ### Features
14
+
15
+ - Full Paynow V3 REST API coverage with async HTTP client.
16
+ - HMAC-SHA256 request and notification signature verification.
17
+ - Payment creation with redirect URL support.
18
+ - Notification (PUSH) callback handling with semantic payment updates.
19
+ - Status polling (PULL) via API.
20
+ - Refund lifecycle: create, check status, cancel.
21
+ - Payment methods retrieval.
22
+ - Simulator plugin for local testing via `python-getpaid-simulator`.
23
+ - Support for 4 currencies: PLN, EUR, USD, GBP.
24
+
25
+ ### Migration from alpha
26
+
27
+ - Update dependency from `python-getpaid-paynow>=3.0.0a4` to `python-getpaid-paynow>=3.0.0`.
28
+ - No API changes — all public interfaces remain stable.
@@ -23,7 +23,7 @@ The Paynow payment flow follows a create-redirect-notify pattern:
23
23
  ┌──────────┐ ◄────────────────────────────────┘
24
24
  │ Your │
25
25
  │ Server │ 1. verify_callback (check HMAC signature)
26
- │ │ 2. handle_callback (update FSM state)
26
+ │ │ 2. handle_callback (return semantic payment update)
27
27
  └──────────┘
28
28
  ```
29
29
 
@@ -46,8 +46,9 @@ The Paynow payment flow follows a create-redirect-notify pattern:
46
46
  mismatch.
47
47
 
48
48
  5. **Handle callback** — `PaynowProcessor.handle_callback()` maps the Paynow
49
- status to FSM transitions: `CONFIRMED` triggers `confirm_payment` +
50
- `mark_as_paid`; `REJECTED`, `ERROR`, `EXPIRED`, `ABANDONED` trigger `fail`.
49
+ status to semantic updates: `CONFIRMED` returns `payment_captured`,
50
+ `PENDING` returns `prepared`, and `REJECTED`, `ERROR`, `EXPIRED`,
51
+ `ABANDONED` return `failed`.
51
52
 
52
53
  :::{note}
53
54
  Unlike Przelewy24, Paynow does **not** require a separate verification step
@@ -154,17 +155,17 @@ The plugin supports both notification models:
154
155
 
155
156
  - **PULL** — `PaynowProcessor.fetch_payment_status()` calls
156
157
  `PaynowClient.get_payment_status()` to poll the payment status. Returns a
157
- `PaymentStatusResponse` with the mapped FSM trigger.
158
+ semantic `PaymentUpdate`.
158
159
 
159
- | Paynow Status | Mapped FSM Trigger |
160
- |---------------|-------------------|
160
+ | Paynow Status | Semantic Event |
161
+ |---------------|----------------|
161
162
  | `NEW` | `None` |
162
- | `PENDING` | `confirm_prepared` |
163
- | `CONFIRMED` | `confirm_payment` |
164
- | `REJECTED` | `fail` |
165
- | `ERROR` | `fail` |
166
- | `EXPIRED` | `fail` |
167
- | `ABANDONED` | `fail` |
163
+ | `PENDING` | `prepared` |
164
+ | `CONFIRMED` | `payment_captured` |
165
+ | `REJECTED` | `failed` |
166
+ | `ERROR` | `failed` |
167
+ | `EXPIRED` | `failed` |
168
+ | `ABANDONED` | `failed` |
168
169
 
169
170
  ## Supported Operations
170
171
 
@@ -32,7 +32,7 @@ The `PaynowClient` is an async HTTP client that wraps the Paynow V3 REST API.
32
32
  Use it as an async context manager for connection reuse:
33
33
 
34
34
  ```python
35
- import asyncio
35
+ import anyio
36
36
  from decimal import Decimal
37
37
  from getpaid_paynow import PaynowClient
38
38
 
@@ -56,7 +56,7 @@ async def main():
56
56
  redirect_url = response["redirectUrl"]
57
57
  print(f"Redirect buyer to: {redirect_url}")
58
58
 
59
- asyncio.run(main())
59
+ anyio.run(main)
60
60
  ```
61
61
 
62
62
  ## Using with django-getpaid
@@ -92,7 +92,7 @@ payment ID at runtime.
92
92
  ### 3. Process payments
93
93
 
94
94
  The framework adapter handles the rest — creating payments, redirecting
95
- buyers, receiving notifications, and updating payment status via the FSM.
95
+ buyers, receiving notifications, and applying semantic payment updates.
96
96
 
97
97
  ## Sandbox vs Production
98
98