Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ name: python-redsys

on:
push:
branches: [ master ]
branches: [ master, release/1.x ]
pull_request:
branches: [ master ]
branches: [ master, release/1.x ]

jobs:
build:
Expand Down
61 changes: 61 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
name: Release

# Build and publish to PyPI when a version tag (vX.Y.Z) is pushed.
on:
push:
tags:
- "v*"

permissions:
contents: read

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"

# Guard: the tag must match [tool.poetry].version, so a mistagged release
# can never reach PyPI.
- name: Tag matches package version
run: |
version=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['tool']['poetry']['version'])")
tag="${GITHUB_REF_NAME#v}"
if [ "$version" != "$tag" ]; then
echo "::error::tag v$tag does not match pyproject version $version"
exit 1
fi

- name: Build sdist and wheel
run: |
python -m pip install --upgrade build
python -m build

- name: Check distribution metadata
run: |
python -m pip install --upgrade twine
twine check dist/*

- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/

publish:
needs: build
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/python-redsys
permissions:
id-token: write # OIDC token for PyPI Trusted Publishing (no stored secret)
steps:
- uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
<!--next-version-placeholder-->

## v1.2.1 (2026-10-04)

## Fixed

- `Request` and `Response` instances no longer share their parameters within a process: a parsed response could carry fields of a previously parsed one (for example its amount), and a request could carry fields of a previous request (for example the Bizum mobile number). Known parameters that are absent now read as `None`.

## v1.2.0 (2023-10-28)


Expand Down
6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
[tool.poetry]
name = "python-redsys"
version = "1.2.0"
version = "1.2.1"
description = "A simple, clean and less dependant client to handle payments through Redsys."
authors = ["Andrés Reverón Molina <andres@reveronmolina.me>", "David Díaz <d.diazp@gmail.com>"]
authors = ["David Díaz <d.diazp@gmail.com>", "Andrés Reverón Molina <andres@reveronmolina.me>"]
license = "MIT"
readme = "README.md"
homepage = "https://github.com/systemallica/python-redsys"
homepage = "https://github.com/ddiazpinto/python-redsys"
classifiers = [
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
Expand Down
2 changes: 1 addition & 1 deletion redsys/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
__version__ = "1.2.0"
__version__ = "1.2.1"
__version_info__ = tuple(int(num) if num.isdigit() else num for num in __version__.replace("-", ".", 1).split("."))
6 changes: 4 additions & 2 deletions redsys/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,11 @@ class Request:
their values according to the platform specifications
"""

_parameters: Dict[str, Any] = {}
_parameters: Dict[str, Any]

def __init__(self, parameters: Dict[str, Any]) -> None:
# __setattr__ only handles merchant parameters, so the per-instance store is set directly.
object.__setattr__(self, "_parameters", {})
for key, value in parameters.items():
if key in MERCHANT_PARAMETERS_MAP:
if check := getattr(self, f"check_{str(key)}", None):
Expand All @@ -90,7 +92,7 @@ def __init__(self, parameters: Dict[str, Any]) -> None:

def __getattr__(self, item: str) -> Any:
if item in MERCHANT_PARAMETERS_MAP:
return self._parameters[item]
return self._parameters.get(item)

def __setattr__(self, key, value):
if key in MERCHANT_PARAMETERS_MAP:
Expand Down
6 changes: 4 additions & 2 deletions redsys/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,11 @@ class Response:
Defines a response
"""

_parameters: Dict[str, Any] = {}
_parameters: Dict[str, Any]

def __init__(self, parameters: Dict[str, Any]):
# __setattr__ only handles merchant parameters, so the per-instance store is set directly.
object.__setattr__(self, "_parameters", {})
MERCHANT_PARAMETERS_MAP_REVERSE = {value: key for key, value in MERCHANT_PARAMETERS_MAP.items()}
for key, value in parameters.items():
reversed_parameter = MERCHANT_PARAMETERS_MAP_REVERSE.get(key, key)
Expand All @@ -104,7 +106,7 @@ def __init__(self, parameters: Dict[str, Any]):

def __getattr__(self, item: str) -> Any:
if item in MERCHANT_PARAMETERS_MAP:
return self._parameters[item]
return self._parameters.get(item)

def __setattr__(self, key: str, value: Any):
if key in MERCHANT_PARAMETERS_MAP:
Expand Down
9 changes: 9 additions & 0 deletions redsys/tests/test_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,12 @@ def test_super_long_merchant_url_throws_error(self):
merchant_url = "".join(choice("abcdefghijklmnopqrtsuvwxyz-0123456789") for _ in range(251))
with pytest.raises(ValueError):
assert Request.check_merchant_url(merchant_url)

def test_requests_do_not_share_parameters(self):
first = Request(
{"order": "000000000001", "amount": D("1.00"), "currency": EUR, "mobile_number": "+34700000000"}
)
second = Request({"order": "000000000002", "amount": D("2.00"), "currency": EUR})
assert "Ds_Merchant_Bizum_MobileNumber" not in second.prepare_parameters()
assert second.mobile_number is None
assert first.prepare_parameters()["Ds_Merchant_Bizum_MobileNumber"] == "+34700000000"
9 changes: 9 additions & 0 deletions redsys/tests/test_response.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from decimal import Decimal

from redsys.response import Response


Expand All @@ -21,3 +23,10 @@ def test_create_response(self):
assert response.is_paid is True
assert response.is_canceled is False
assert response.is_refunded is False

def test_responses_do_not_share_parameters(self):
first = Response({"Ds_Response": "0000", "Ds_Order": "000000000001", "Ds_Amount": "100"})
second = Response({"Ds_Response": "0190", "Ds_Order": "000000000002"})
assert "amount" not in second.parameters
assert second.amount is None
assert first.amount == Decimal("1.00")
Loading