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.
- python_getpaid_paynow-3.0.0/.github/release-drafter.yml +31 -0
- python_getpaid_paynow-3.0.0/.github/workflows/ci.yml +39 -0
- python_getpaid_paynow-3.0.0/.github/workflows/release.yml +70 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/.gitignore +1 -0
- python_getpaid_paynow-3.0.0/.pre-commit-config.yaml +15 -0
- python_getpaid_paynow-3.0.0/.python-version +1 -0
- python_getpaid_paynow-3.0.0/PKG-INFO +188 -0
- python_getpaid_paynow-3.0.0/README.md +161 -0
- python_getpaid_paynow-3.0.0/docs/changelog.md +28 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/docs/concepts.md +13 -12
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/docs/getting-started.md +3 -3
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/pyproject.toml +17 -5
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/src/getpaid_paynow/__init__.py +2 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/src/getpaid_paynow/client.py +98 -34
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/src/getpaid_paynow/processor.py +102 -66
- python_getpaid_paynow-3.0.0/src/getpaid_paynow/simulator/__init__.py +6 -0
- python_getpaid_paynow-3.0.0/src/getpaid_paynow/simulator/plugin.py +62 -0
- python_getpaid_paynow-3.0.0/src/getpaid_paynow/simulator/routes.py +421 -0
- python_getpaid_paynow-3.0.0/src/getpaid_paynow/simulator/signing.py +49 -0
- python_getpaid_paynow-3.0.0/src/getpaid_paynow/simulator/transitions.py +17 -0
- python_getpaid_paynow-3.0.0/src/getpaid_paynow/simulator/webhooks.py +59 -0
- python_getpaid_paynow-3.0.0/tests/conftest.py +119 -0
- python_getpaid_paynow-3.0.0/tests/test_callback.py +143 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/tests/test_client.py +107 -0
- python_getpaid_paynow-3.0.0/tests/test_entry_points.py +27 -0
- python_getpaid_paynow-3.0.0/tests/test_processor.py +251 -0
- python_getpaid_paynow-3.0.0/tests/test_public_api.py +25 -0
- python_getpaid_paynow-3.0.0/tests/test_simulator_plugin.py +163 -0
- python_getpaid_paynow-0.1.0/PKG-INFO +0 -154
- python_getpaid_paynow-0.1.0/README.md +0 -131
- python_getpaid_paynow-0.1.0/docs/changelog.md +0 -19
- python_getpaid_paynow-0.1.0/tests/conftest.py +0 -143
- python_getpaid_paynow-0.1.0/tests/test_callback.py +0 -315
- python_getpaid_paynow-0.1.0/tests/test_processor.py +0 -373
- python_getpaid_paynow-0.1.0/uv.lock +0 -925
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/.readthedocs.yml +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/CODE_OF_CONDUCT.md +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/CONTRIBUTING.md +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/LICENSE +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/docs/codeofconduct.md +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/docs/conf.py +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/docs/configuration.md +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/docs/contributing.md +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/docs/index.md +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/docs/license.md +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/docs/reference.md +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/docs/requirements.txt +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/src/getpaid_paynow/py.typed +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/src/getpaid_paynow/types.py +0 -0
- {python_getpaid_paynow-0.1.0 → python_getpaid_paynow-3.0.0}/tests/__init__.py +0 -0
- {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 }}
|
|
@@ -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
|
+
[](https://pypi.org/project/python-getpaid-paynow/)
|
|
31
|
+
[](https://opensource.org/licenses/MIT)
|
|
32
|
+
[](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
|
+
[](https://pypi.org/project/python-getpaid-paynow/)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](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 (
|
|
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
|
|
50
|
-
`
|
|
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
|
-
`
|
|
158
|
+
semantic `PaymentUpdate`.
|
|
158
159
|
|
|
159
|
-
| Paynow Status |
|
|
160
|
-
|
|
160
|
+
| Paynow Status | Semantic Event |
|
|
161
|
+
|---------------|----------------|
|
|
161
162
|
| `NEW` | `None` |
|
|
162
|
-
| `PENDING` | `
|
|
163
|
-
| `CONFIRMED` | `
|
|
164
|
-
| `REJECTED` | `
|
|
165
|
-
| `ERROR` | `
|
|
166
|
-
| `EXPIRED` | `
|
|
167
|
-
| `ABANDONED` | `
|
|
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
|
|
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
|
-
|
|
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
|
|
95
|
+
buyers, receiving notifications, and applying semantic payment updates.
|
|
96
96
|
|
|
97
97
|
## Sandbox vs Production
|
|
98
98
|
|