Skip to content

Commit

Permalink
Merge pull request #7 from radekholy24/compatibility
Browse files Browse the repository at this point in the history
fix backward compatibility / consistency
  • Loading branch information
PetrDlouhy authored Apr 9, 2024
2 parents 1f61405 + d2f4e90 commit 9c7454c
Show file tree
Hide file tree
Showing 5 changed files with 225 additions and 93 deletions.
2 changes: 1 addition & 1 deletion AUTHORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ Development Lead
Contributors
------------

* Radek Holý
* BlenderKit <[email protected]>
9 changes: 9 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,24 @@
History
-------

Unreleased
**********
* fix backward compatibility by making PayuProvider's get_refund_description argument optional
* make PayuProvider.refund fail if get_refund_description is not provided
* make PayuProvider.refund raise PayuApiError if an unexpected response is received
* deprecate the default value of get_refund_description; set it to a callable instead

1.3.1 (2024-03-19)
******************
* Fix description on PyPI

1.3.0 (2024-03-19)
******************
* add get_refund_description and get_refund_ext_id arguments to PayuProvider
* add PayuProvider.refund
* update payment.captured_amount only when order is completed
* subtract refunds from payment.captured_amount rather than from payment.total
* rename PayuProvider.payu_api_order_url to payu_api_orders_url
* tests for Django 2.2-5.0 Python 3.7-3.12

1.2.4 (2022-03-17)
Expand Down
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ Here are valid parameters for the provider:
:express_payments: use PayU express form
:widget_branding: tell express form to show PayU branding
:store_card: (default: False) whether PayU should store the card
:get_refund_description: A mandatory callable that is called with two keyword arguments `payment` and `amount` in order to get the string description of the particular refund whenever ``provider.refund(payment, amount)`` is called.
:get_refund_description: An optional callable that is called with two keyword arguments `payment` and `amount` in order to get the string description of the particular refund whenever ``provider.refund(payment, amount)`` is called. The callable is optional because of backwards compatibility. However, if it is not set, an attempt to refund raises an exception. A default value of `get_refund_description` is deprecated.
:get_refund_ext_id: An optional callable that is called with two keyword arguments `payment` and `amount` in order to get the External string refund ID of the particular refund whenever ``provider.refund(payment, amount)`` is called. If ``None`` is returned, no External refund ID is set. An External refund ID is not necessary if partial refunds won't be performed more than once per second. Otherwise, a unique ID is recommended since `PayuProvider.refund` is idempotent and if exactly same data will be provided, it will return the result of the already previously performed refund instead of performing a new refund. Defaults to a random UUID version 4 in the standard form.


Expand Down
23 changes: 18 additions & 5 deletions payments_payu/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import json
import logging
import uuid
import warnings
from decimal import ROUND_HALF_UP, Decimal
from urllib.parse import urljoin

Expand Down Expand Up @@ -185,7 +186,16 @@ def __init__(self, *args, **kwargs):
self.payu_shop_name = kwargs.pop("shop_name", "")
self.grant_type = kwargs.pop("grant_type", "client_credentials")
self.recurring_payments = kwargs.pop("recurring_payments", False)
self.get_refund_description = kwargs.pop("get_refund_description")
self.get_refund_description = kwargs.pop(
"get_refund_description",
# TODO: The default is deprecated. Remove in the next major release.
None,
)
if self.get_refund_description is None:
warnings.warn(
"A default value of get_refund_description is deprecated. Set it to a callable instead.",
DeprecationWarning,
)
self.get_refund_ext_id = kwargs.pop(
"get_refund_ext_id", lambda payment, amount: str(uuid.uuid4())
)
Expand Down Expand Up @@ -604,6 +614,9 @@ def process_data(self, payment, request, *args, **kwargs):
)

def refund(self, payment, amount=None):
if self.get_refund_description is None:
raise ValueError("get_refund_description not set")

request_url = self._get_payu_api_order_url(payment.transaction_id) + "/refunds"

request_data = {
Expand Down Expand Up @@ -644,7 +657,7 @@ def refund(self, payment, amount=None):
response_status = dict(response["status"])
response_status_code = response_status["statusCode"]
except Exception:
raise ValueError(
raise PayuApiError(
f"invalid response to refund {refund_id or '???'} of payment {payment.id}: {response}"
)
if response_status_code != "SUCCESS":
Expand All @@ -656,7 +669,7 @@ def refund(self, payment, amount=None):
f"statusDesc={response_status.get('statusDesc', '???')}"
)
if refund_id is None:
raise ValueError(
raise PayuApiError(
f"invalid response to refund of payment {payment.id}: {response}"
)

Expand All @@ -666,7 +679,7 @@ def refund(self, payment, amount=None):
refund_currency = refund["currencyCode"]
refund_amount = dequantize_price(refund["amount"], refund_currency)
except Exception:
raise ValueError(
raise PayuApiError(
f"invalid response to refund {refund_id} of payment {payment.id}: {response}"
)
if refund_order_id != payment.transaction_id:
Expand All @@ -677,7 +690,7 @@ def refund(self, payment, amount=None):
if refund_status == "CANCELED":
raise ValueError(f"refund {refund_id} of payment {payment.id} canceled")
elif refund_status not in {"PENDING", "FINALIZED"}:
raise ValueError(
raise PayuApiError(
f"invalid status of refund {refund_id} of payment {payment.id}"
)
if refund_currency != payment.currency:
Expand Down
Loading

0 comments on commit 9c7454c

Please sign in to comment.