chore: incomplete product setup error handling
This commit is contained in:
parent
809c45235f
commit
33bea6c08a
|
|
@ -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"])
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue