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
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ concurrency:

jobs:
tests:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04

strategy:
fail-fast: false
Expand Down
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ All notable changes to this project will be documented in this file.

## [Released]

## [1.2.0]
- BTI-1701 Security update
- BTI-1351 Remove GoSettle as payment method (deprecated)

## [1.1.0]
- BTI-16 Add solution: Split Payments
- BTI-17 Add solution: PayPerEmail
Expand Down Expand Up @@ -54,7 +58,6 @@ All notable changes to this project will be documented in this file.
- BA-1003 Add payment method: Creditcards
- BA-1004 Add payment method: EPS
- BA-1005 Add payment method: Giftcards
- BA-1006 Add payment method: goSettle
- BA-1007 Add payment method: Google Pay
- BA-1008 Add payment method: External Payment
- BA-1009 Add payment method: iDEAL | Wero
Expand Down
2 changes: 1 addition & 1 deletion buckaroo/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
VERSION = "1.1.0"
VERSION = "1.2.0"
15 changes: 0 additions & 15 deletions buckaroo/builders/payments/knaken_builder.py

This file was deleted.

2 changes: 1 addition & 1 deletion buckaroo/config/buckaroo_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class BuckarooConfig:
logging_enabled: bool = True
verify_ssl: bool = True
custom_endpoint: Optional[str] = None
user_agent: str = "BuckarooSDK-Python/1.1.0"
user_agent: str = "BuckarooSDK-Python/1.2.0"
max_redirects: int = 5

def __post_init__(self):
Expand Down
2 changes: 0 additions & 2 deletions buckaroo/factories/payment_method_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
from buckaroo.builders.payments.billink_builder import BillinkBuilder
from buckaroo.builders.payments.klarna_builder import KlarnaBuilder
from buckaroo.builders.payments.klarnakp_builder import KlarnaKPBuilder
from buckaroo.builders.payments.knaken_builder import KnakenBuilder
from buckaroo.builders.payments.przelewy24_builder import Przelewy24Builder
from buckaroo.builders.payments.riverty_builder import RivertyBuilder
from buckaroo.builders.payments.sepadirectdebit_builder import SepaDirectDebitBuilder
Expand Down Expand Up @@ -73,7 +72,6 @@ class PaymentMethodFactory(BuilderFactory):
"idin": IdinBuilder,
"in3": In3Builder,
"kbc": KBCBuilder,
"knaken": KnakenBuilder,
"klarna": KlarnaBuilder,
"klarnakp": KlarnaKPBuilder,
"multibanco": MultibancoBuilder,
Expand Down
42 changes: 6 additions & 36 deletions buckaroo/http/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,27 +244,7 @@ def json(self) -> Dict[str, Any]:

def is_successful_payment(self) -> bool:
"""Check if the payment was successful based on Buckaroo response."""
if not self.success:
return False

# Check Buckaroo-specific success indicators
if self._data and "Status" in self._data:
# Buckaroo status codes for successful payments
success_statuses = [190, 490, 491, 492, 790, 791, 792, 793]
status = self._data.get("Status", {})
if status and "Code" in status:
code = status.get("Code")
# Handle nested Code structure
if isinstance(code, dict):
actual_code = code.get("Code")
elif isinstance(code, int):
actual_code = code
else:
actual_code = None

return actual_code in success_statuses if actual_code is not None else False

return self.success
return self.success and self.get_status_code() == 190

def get_payment_key(self) -> Optional[str]:
"""Get the payment key from the response."""
Expand All @@ -287,25 +267,15 @@ def get_transaction_key(self) -> Optional[str]:

def get_status_code(self) -> Optional[int]:
"""Get the Buckaroo status code."""
if not self._data:
if not isinstance(self._data, dict):
return None

status = self._data.get("Status", {})
if not status:
status = self._data.get("Status")
if not isinstance(status, dict):
return None

code = status.get("Code")
if code is None:
return None

# Handle nested Code structure: {"Code": 490, "Description": "Failed"}
if isinstance(code, dict):
return code.get("Code")
# Handle simple integer code
elif isinstance(code, int):
return code

return None
code = code.get("Code")
return code if type(code) is int else None

def get_status_message(self) -> Optional[str]:
"""Get the Buckaroo status message."""
Expand Down
46 changes: 28 additions & 18 deletions buckaroo/http/strategies/curl_strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,18 @@ def request(
cmd = self._build_curl_command(
method=method,
url=url,
headers=headers,
data=data,
timeout=timeout or self._timeout,
verify_ssl=verify_ssl,
)

# Keep credentials and payloads off the process command line.
config_lines = []
for key, value in {**self._default_headers, **(headers or {})}.items():
config_lines.append(f"header = {self._quote_config_value(f'{key}: {value}')}")
if data and method.upper() in ["POST", "PUT", "PATCH"]:
config_lines.append(f"data-raw = {self._quote_config_value(data)}")
config_input = "\n".join(config_lines) + "\n"

# Execute curl with retry logic
if self._retry_attempts <= 0:
raise Exception("Request failed after all retry attempts")
Expand All @@ -68,8 +74,10 @@ def request(
try:
result = subprocess.run(
cmd,
input=config_input,
capture_output=True,
text=True,
encoding="utf-8",
timeout=timeout or self._timeout,
check=False, # Don't raise on non-zero exit codes
)
Expand All @@ -95,8 +103,6 @@ def _build_curl_command(
self,
method: str,
url: str,
headers: Optional[Dict[str, str]] = None,
data: Optional[str] = None,
timeout: int = 30,
verify_ssl: bool = True,
) -> List[str]:
Expand All @@ -106,8 +112,6 @@ def _build_curl_command(
Args:
method: HTTP method
url: Request URL
headers: Request headers
data: Request body data
timeout: Request timeout
verify_ssl: Whether to verify SSL

Expand All @@ -116,6 +120,8 @@ def _build_curl_command(
"""
cmd = [
"curl",
"--config",
"-",
"-X",
method.upper(),
"--location", # Follow redirects
Expand All @@ -131,23 +137,27 @@ def _build_curl_command(
if not verify_ssl:
cmd.extend(["--insecure"])

# Add headers
all_headers = {**self._default_headers}
if headers:
all_headers.update(headers)

for key, value in all_headers.items():
cmd.extend(["-H", f"{key}: {value}"])

# Add data for POST/PUT requests
if data and method.upper() in ["POST", "PUT", "PATCH"]:
cmd.extend(["--data", data])

# Add URL last
cmd.append(url)

return cmd

@staticmethod
def _quote_config_value(value: str) -> str:
"""Quote a literal curl config value without allowing new options."""
if "\0" in value:
raise ValueError("curl input cannot contain NUL characters")
for original, escaped in (
("\\", "\\\\"),
('"', '\\"'),
("\n", "\\n"),
("\r", "\\r"),
("\t", "\\t"),
("\v", "\\v"),
):
value = value.replace(original, escaped)
return f'"{value}"'

def _parse_curl_output(self, result: subprocess.CompletedProcess) -> HttpResponse:
"""
Parse curl output into HttpResponse object.
Expand Down
6 changes: 4 additions & 2 deletions buckaroo/observers/logging_observer.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,12 +165,14 @@ def _format_json(self, data: Any) -> str:
try:
data = json.loads(data)
except json.JSONDecodeError:
return data
return "***BODY_OMITTED***" if self.config.mask_sensitive_data else data

masked_data = self._mask_sensitive_data(data)
if self.config.mask_sensitive_data:
return json.dumps(masked_data, indent=2)
return json.dumps(masked_data, indent=2, default=str)
except Exception:
return str(data)
return "***BODY_OMITTED***" if self.config.mask_sensitive_data else str(data)

def log_request(
self,
Expand Down
14 changes: 0 additions & 14 deletions tests/feature/payments/test_knaken.py

This file was deleted.

86 changes: 0 additions & 86 deletions tests/unit/builders/payments/test_knaken_builder.py

This file was deleted.

2 changes: 1 addition & 1 deletion tests/unit/config/test_buckaroo_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def test_defaults():
assert cfg.logging_enabled is True
assert cfg.verify_ssl is True
assert cfg.custom_endpoint is None
assert cfg.user_agent == "BuckarooSDK-Python/1.1.0"
assert cfg.user_agent == "BuckarooSDK-Python/1.2.0"
assert cfg.max_redirects == 5


Expand Down
Loading
Loading