From 6f90d381f3726557d568a3b0c3d3a8c60bf589d5 Mon Sep 17 00:00:00 2001 From: Livio Bieri Date: Mon, 27 Nov 2023 16:59:21 +0100 Subject: [PATCH] chore: get datatrans admin helper cleanup --- server/vbv_lernwelt/shop/admin.py | 1 - server/vbv_lernwelt/shop/services.py | 22 ++++++++++++++---- .../shop/tests/test_datatrans_webhook.py | 2 +- server/vbv_lernwelt/shop/views.py | 23 +++++++------------ 4 files changed, 26 insertions(+), 22 deletions(-) diff --git a/server/vbv_lernwelt/shop/admin.py b/server/vbv_lernwelt/shop/admin.py index db47cdb7..5432cc18 100644 --- a/server/vbv_lernwelt/shop/admin.py +++ b/server/vbv_lernwelt/shop/admin.py @@ -13,7 +13,6 @@ def generate_invoice(modeladmin, request, queryset): def sync_transaction_state(modeladmin, request, queryset): for checkout in queryset: state = get_transaction_state(transaction_id=checkout.transaction_id) - print(state) checkout.state = state.value checkout.save( update_fields=[ diff --git a/server/vbv_lernwelt/shop/services.py b/server/vbv_lernwelt/shop/services.py index 2c751ec4..3c815c6a 100644 --- a/server/vbv_lernwelt/shop/services.py +++ b/server/vbv_lernwelt/shop/services.py @@ -101,7 +101,7 @@ def init_transaction( def get_transaction_state( transaction_id: str, -) -> CheckoutState | None: +) -> CheckoutState: response = requests.get( f"{settings.DATATRANS_API_ENDPOINT}/v1/transactions/{transaction_id}", headers={ @@ -110,9 +110,6 @@ def get_transaction_state( }, ) - if response.status_code != 200: - return None - transaction_state = response.json()["status"] logger.info( @@ -121,8 +118,23 @@ def get_transaction_state( response=transaction_state, ) - return CheckoutState(transaction_state) + return datatrans_state_to_checkout_state(transaction_state) def get_payment_url(transaction_id: str): return f"{settings.DATATRANS_PAY_URL}/v1/start/{transaction_id}" + + +def datatrans_state_to_checkout_state( + transaction_state: str, +) -> CheckoutState: + if transaction_state in ["settled", "transmitted"]: + return CheckoutState.PAID + elif transaction_state == "failed": + return CheckoutState.FAILED + elif transaction_state == "canceled": + return CheckoutState.CANCELED + else: + # An intermediate state such as "initialized", "challenge_ongoing", etc. + # -> we don't care about those states, we only care about final states here. + return CheckoutState.INITIALIZED diff --git a/server/vbv_lernwelt/shop/tests/test_datatrans_webhook.py b/server/vbv_lernwelt/shop/tests/test_datatrans_webhook.py index c5c51621..c3cb3da1 100644 --- a/server/vbv_lernwelt/shop/tests/test_datatrans_webhook.py +++ b/server/vbv_lernwelt/shop/tests/test_datatrans_webhook.py @@ -73,7 +73,7 @@ class DatatransWebhookTestCase(APITestCase): ) @patch("vbv_lernwelt.shop.views.is_signature_valid") - def test_webhook_creates_course_session_user(self, mock_is_signature_valid): + def test_webhook_settled_transmitted_paid(self, mock_is_signature_valid): # GIVEN transaction_id = "1234567890" diff --git a/server/vbv_lernwelt/shop/views.py b/server/vbv_lernwelt/shop/views.py index 0f2d1c5f..afffd7b7 100644 --- a/server/vbv_lernwelt/shop/views.py +++ b/server/vbv_lernwelt/shop/views.py @@ -15,6 +15,7 @@ from vbv_lernwelt.shop.models import ( ) from vbv_lernwelt.shop.serializers import BillingAddressSerializer from vbv_lernwelt.shop.services import ( + datatrans_state_to_checkout_state, get_payment_url, init_transaction, InitTransactionException, @@ -66,7 +67,6 @@ def transaction_webhook(request): Otherwise, this webhook was/will not be called for "initialized" -> "failed" state changes For timed out transactions (cleaned up in 15 minute intervals, after 30 minutes by them), """ - logger.info("Webhook: Datatrans called transaction webhook", body=request.body) if not is_signature_valid( @@ -78,26 +78,19 @@ def transaction_webhook(request): transaction = request.data transaction_id = transaction["transactionId"] - transaction_status = transaction["status"] - # keep webhook history for debugging + # keep webhook history (for debugging) checkout_info = CheckoutInformation.objects.get(transaction_id=transaction_id) checkout_info.webhook_history.append(transaction) checkout_info.save(update_fields=["webhook_history"]) - # be aware autoSettle has implications on possible transaction states we get! - # See https://api-reference.datatrans.ch/#tag/v1transactions/operation/status - if transaction_status in ["settled", "transmitted"]: - update_checkout_state(checkout_info=checkout_info, state=CheckoutState.PAID) + # update checkout state + checkout_state = datatrans_state_to_checkout_state(transaction["status"]) + update_checkout_state(checkout_info=checkout_info, state=checkout_state) + + # handle paid + if checkout_state == CheckoutState.PAID: create_vv_course_session_user(checkout_info=checkout_info) - elif transaction_status == "failed": - update_checkout_state(checkout_info=checkout_info, state=CheckoutState.FAILED) - elif transaction_status == "canceled": - update_checkout_state(checkout_info=checkout_info, state=CheckoutState.CANCELED) - else: - logger.warning( - "Unhandled transaction status", transaction_status=transaction_status - ) return JsonResponse({"status": "ok"})