diff --git a/src/tinkoff/bank.py b/src/tinkoff/bank.py index f1e86c4afaa..1b9200a69c0 100644 --- a/src/tinkoff/bank.py +++ b/src/tinkoff/bank.py @@ -20,6 +20,17 @@ class TinkoffBank(Bank): def get_initial_payment_url(self) -> str: return self.Init()["PaymentURL"] + def refund(self) -> None: + last_payment_notification = self.order.tinkoff_payment_notifications.order_by("-id")[0] + + self.call( + "Cancel", + payload={ + "PaymentId": last_payment_notification.payment_id, + "Receipt": self.get_receipt(), + }, + ) + def Init(self) -> dict: return self.call( "Init", @@ -86,7 +97,7 @@ def _get_token(request: dict) -> str: _request["Password"] = settings.TINKOFF_TERMINAL_PASSWORD sorted_request = OrderedDict(sorted(_request.items(), key=lambda key, *args: key)) - return sha256("".join(str(value) for value in sorted_request.values()).encode()).hexdigest().upper() + return sha256("".join(str(value) for value in sorted_request.values()).encode()).hexdigest() @staticmethod def get_notification_url() -> str: diff --git a/src/tinkoff/tests/bank_client/tests_tinkoff_refund.py b/src/tinkoff/tests/bank_client/tests_tinkoff_refund.py new file mode 100644 index 00000000000..8bf77852f00 --- /dev/null +++ b/src/tinkoff/tests/bank_client/tests_tinkoff_refund.py @@ -0,0 +1,50 @@ +import pytest + +from tinkoff.bank import TinkoffBank + +pytestmark = [ + pytest.mark.django_db, +] + + +@pytest.fixture +def paid_tinkoff_order(order, mixer): + order.setattr_and_save("bank_id", "tinkoff_bank") + order.set_paid() + + mixer.blend("tinkoff.PaymentNotification", order=order, payment_id=9001) + return order + + +@pytest.fixture +def refund_success_response(): + return { + "Success": True, + "ErrorCode": "0", + "TerminalKey": "k3y", + "Status": "REFUNDED", + "PaymentId": "9001", + "OrderId": "8P4GtFYc3fuaoVTTMa9EZt", + "OriginalAmount": 10050000, + "NewAmount": 0, + } + + +@pytest.fixture(autouse=True) +def add_tinkoff_refund_response(respx_mock, refund_success_response): + return respx_mock.post("https://securepay.tinkoff.ru/v2/Cancel/").respond(json=refund_success_response) + + +@pytest.fixture +def tinkoff(paid_tinkoff_order): + return TinkoffBank(order=paid_tinkoff_order) + + +def test_send_correct_refund_request(tinkoff, retrieve_request_json): + tinkoff.refund() + + refund_request = retrieve_request_json() + assert refund_request["PaymentId"] == 9001 + assert "Receipt" in refund_request + assert "TerminalKey" in refund_request + assert "Token" in refund_request diff --git a/src/tinkoff/tests/bank_client/tests_tinkoff_request_signing.py b/src/tinkoff/tests/bank_client/tests_tinkoff_request_signing.py index b7390ada17a..7c20355b853 100644 --- a/src/tinkoff/tests/bank_client/tests_tinkoff_request_signing.py +++ b/src/tinkoff/tests/bank_client/tests_tinkoff_request_signing.py @@ -34,17 +34,17 @@ def reference_request(): def test_signature(tinkoff, reference_request): - assert tinkoff._get_token(reference_request) == "597C160C8C348FB14C63C820C54B712468923A74FD111AC6B0ECDA01FB5F4716" + assert tinkoff._get_token(reference_request) == "597c160c8c348fb14c63c820c54b712468923a74fd111ac6b0ecda01fb5f4716" def test_signature_without_data_and_receipt(tinkoff, reference_request): del reference_request["DATA"] del reference_request["Receipt"] - assert tinkoff._get_token(reference_request) == "597C160C8C348FB14C63C820C54B712468923A74FD111AC6B0ECDA01FB5F4716" + assert tinkoff._get_token(reference_request) == "597c160c8c348fb14c63c820c54b712468923a74fd111ac6b0ecda01fb5f4716" def test_signature_with_integer_int(tinkoff, reference_request): reference_request["OrderId"] = 100500 - assert tinkoff._get_token(reference_request) == "2F182D493F0488BDE0750D1BDB8B1DE3A10BBE0A38358A89C8EA93972A55F453" + assert tinkoff._get_token(reference_request) == "2f182d493f0488bde0750d1bdb8b1de3a10bbe0a38358a89c8ea93972a55f453" diff --git a/src/tinkoff/tests/conftest.py b/src/tinkoff/tests/conftest.py index a92f0071daa..086b50b3048 100644 --- a/src/tinkoff/tests/conftest.py +++ b/src/tinkoff/tests/conftest.py @@ -1,5 +1,8 @@ +import json import pytest +from respx import MockRouter + @pytest.fixture def course(factory): @@ -17,3 +20,8 @@ def user(mixer): @pytest.fixture def order(factory, user, course): return factory.order(user=user, item=course, price="100500") + + +@pytest.fixture +def retrieve_request_json(respx_mock: MockRouter): + return lambda: json.loads(respx_mock.calls.last.request.content) diff --git a/src/tinkoff/tests/dolyame_client/conftest.py b/src/tinkoff/tests/dolyame_client/conftest.py index e7d691b502d..e23dcfe4ada 100644 --- a/src/tinkoff/tests/dolyame_client/conftest.py +++ b/src/tinkoff/tests/dolyame_client/conftest.py @@ -1,8 +1,5 @@ -import json import pytest -from respx import MockRouter - from tinkoff.dolyame import Dolyame pytestmark = [pytest.mark.django_db] @@ -23,8 +20,3 @@ def _absolute_host(settings): @pytest.fixture def dolyame(order): return Dolyame(order=order) - - -@pytest.fixture -def retrieve_request_json(respx_mock: MockRouter): - return lambda: json.loads(respx_mock.calls.last.request.content)