chore: incomplete product setup error handling

This commit is contained in:
Livio Bieri 2023-11-20 16:36:00 +01:00 committed by Christian Cueni
parent 809c45235f
commit 33bea6c08a
3 changed files with 57 additions and 12 deletions

View File

@ -11,7 +11,6 @@ from vbv_lernwelt.shop.models import (
Product, Product,
VV_PRODUCT_SKU, VV_PRODUCT_SKU,
) )
from vbv_lernwelt.shop.services import get_payment_url
USER_USERNAME = "testuser" USER_USERNAME = "testuser"
USER_EMAIL = "test@example.com" USER_EMAIL = "test@example.com"
@ -33,8 +32,10 @@ TEST_ADDRESS_DATA = {
"company_country": "Test Company Country", "company_country": "Test Company Country",
} }
REDIRECT_URL = "http://testserver/redirect-url"
class CheckoutAPIView(APITestCase):
class CheckoutAPITestCase(APITestCase):
def setUp(self) -> None: def setUp(self) -> None:
Product.objects.create( Product.objects.create(
sku=VV_PRODUCT_SKU, sku=VV_PRODUCT_SKU,
@ -53,17 +54,16 @@ class CheckoutAPIView(APITestCase):
self.client.login(username=USER_USERNAME, password=USER_PASSWORD) self.client.login(username=USER_USERNAME, password=USER_PASSWORD)
@patch("vbv_lernwelt.shop.views.init_transaction") @patch("vbv_lernwelt.shop.views.init_transaction")
def test_checkout(self, mock_init_transaction): def test_checkout_happy_case(self, mock_init_transaction):
# GIVEN # GIVEN
mock_init_transaction.return_value = "1234567890" mock_init_transaction.return_value = "1234567890"
redirect_url = "http://testserver/redirect-url"
# WHEN # WHEN
response = self.client.post( response = self.client.post(
path=reverse("checkout-vv"), path=reverse("checkout-vv"),
format="json", format="json",
data={ data={
"redirect_url": redirect_url, "redirect_url": REDIRECT_URL,
"address": TEST_ADDRESS_DATA, "address": TEST_ADDRESS_DATA,
}, },
) )
@ -71,8 +71,8 @@ class CheckoutAPIView(APITestCase):
# THEN # THEN
self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual( self.assertEqual(
f"https://pay.sandbox.datatrans.com/v1/start/1234567890",
response.json()["next_step_url"], response.json()["next_step_url"],
get_payment_url("1234567890"),
) )
self.assertTrue( self.assertTrue(
@ -82,3 +82,38 @@ class CheckoutAPIView(APITestCase):
state=CheckoutState.INITIALIZED.value, state=CheckoutState.INITIALIZED.value,
).exists() ).exists()
) )
mock_init_transaction.assert_called_once_with(
user=self.user,
amount_chf_centimes=300_00,
redirect_url_success=f"{REDIRECT_URL}/onboarding/vv/checkout/complete",
redirect_url_error=f"{REDIRECT_URL}/onboarding/vv/checkout/address?error",
redirect_url_cancel=f"{REDIRECT_URL}/",
webhook_url=f"{REDIRECT_URL}/api/shop/transaction/webhook/",
)
@patch("vbv_lernwelt.shop.views.init_transaction")
def test_incomplete_setup(self, mock_init_transaction):
# GIVEN
Product.objects.all().delete()
mock_init_transaction.return_value = "1234567890"
# WHEN
response = self.client.post(
path=reverse("checkout-vv"),
format="json",
data={
"redirect_url": REDIRECT_URL,
"address": TEST_ADDRESS_DATA,
},
)
# THEN
self.assertEqual(response.status_code, status.HTTP_200_OK)
expected = (
f"{REDIRECT_URL}/onboarding/vv/checkout/address?error&"
f"message=vv_product_does_not_exist_needs_to_be_created"
)
self.assertEqual(expected, response.json()["next_step_url"])

View File

@ -3,7 +3,7 @@ from unittest import TestCase
from vbv_lernwelt.shop.services import is_signature_valid from vbv_lernwelt.shop.services import is_signature_valid
class DatatransWebhookSigning(TestCase): class DatatransWebhookSigningTestCase(TestCase):
# Key is from their example in the docs, not ours! :D # Key is from their example in the docs, not ours! :D
HMAC_KEY_FROM_THE_DOCS_NOT_HAZARDOUS = ( HMAC_KEY_FROM_THE_DOCS_NOT_HAZARDOUS = (
"861bbfc01e089259091927d6ad7f71c8" "861bbfc01e089259091927d6ad7f71c8"

View File

@ -107,12 +107,18 @@ def checkout_vv(request):
""" """
sku = VV_PRODUCT_SKU sku = VV_PRODUCT_SKU
logger.info(f"Checkout requested: sku={sku}", user_id=request.user.id) logger.info(f"Checkout requested: sku={sku}", user_id=request.user.id)
base_redirect_url = request.data["redirect_url"]
try: try:
product = Product.objects.get(sku=sku) product = Product.objects.get(sku=sku)
except Product.DoesNotExist: except Product.DoesNotExist:
raise Exception( return JsonResponse(
f"Required Product not found: {sku} must be created in the admin interface first.", {
"next_step_url": checkout_error_url(
base_url=base_redirect_url,
message="vv_product_does_not_exist_needs_to_be_created",
)
},
) )
checkouts = CheckoutInformation.objects.filter( checkouts = CheckoutInformation.objects.filter(
@ -131,7 +137,6 @@ def checkout_vv(request):
return JsonResponse({"next_step_url": get_payment_url(checkout.transaction_id)}) return JsonResponse({"next_step_url": get_payment_url(checkout.transaction_id)})
# not yet initialized at all, or canceled/failed # not yet initialized at all, or canceled/failed
base_redirect_url = request.data["redirect_url"]
transaction_id = init_transaction( transaction_id = init_transaction(
user=request.user, user=request.user,
amount_chf_centimes=product.price, amount_chf_centimes=product.price,
@ -162,8 +167,13 @@ def webhook_url(base_url: str) -> str:
return f"{base_url}/api/shop/transaction/webhook/" return f"{base_url}/api/shop/transaction/webhook/"
def checkout_error_url(base_url: str) -> str: def checkout_error_url(base_url: str, message: str | None = None) -> str:
return f"{base_url}/onboarding/vv/checkout/address?error" url = f"{base_url}/onboarding/vv/checkout/address?error"
if message:
url += f"&message={message}"
return url
def checkout_cancel_url(base_url: str) -> str: def checkout_cancel_url(base_url: str) -> str: