From b414e4cf93beb2a6e224ea320f37885d1ce8c97b Mon Sep 17 00:00:00 2001
From: Livio Bieri
{{ $t("a.Hast du schon ein Konto?") }}
{{ $t("a.Anmelden") }} diff --git a/client/src/pages/onboarding/README.md b/client/src/pages/onboarding/README.md deleted file mode 100644 index 1138e281..00000000 --- a/client/src/pages/onboarding/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# Onboarding flow - -## ÜK - -### Guard - -1. Call /me - -- User logged in -> redirect to / -- Anonymous user -> Set to /onboarding/uk/step1 - -## VV - -### Guard - -1. Call /shop/vv/state - - User logged in - - If already bought -> redirect to / - - If not bought -> redirect to /onboarding/vv/step1?state="success" - - Anonymous user -> Set to /onboarding/vv/step1 diff --git a/client/src/pages/onboarding/WizardBase.vue b/client/src/pages/onboarding/WizardBase.vue index 5fdd06e5..642e6daf 100644 --- a/client/src/pages/onboarding/WizardBase.vue +++ b/client/src/pages/onboarding/WizardBase.vue @@ -4,6 +4,8 @@ import { computed } from "vue"; import mood_uk from "@/assets/images/mood_uk.jpg"; import mood_vv from "@/assets/images/mood_vv.jpg"; import { useTranslation } from "i18next-vue"; +import { startsWith } from "lodash"; +import { getVVCourseName } from "@/pages/onboarding/vv/helpers"; const props = defineProps({ courseType: { @@ -21,17 +23,17 @@ const courseData = computed(() => { imageUrl: mood_uk, }; } - if (props.courseType === "vv") { + if (startsWith(props.courseType, "vv")) { return { - name: t("a.Versicherungsvermittler/-in"), + name: getVVCourseName(props.courseType), imageUrl: mood_vv, }; + } else { + return { + name: "", + imageUrl: "", + }; } - - return { - name: "", - imageUrl: "", - }; }); diff --git a/client/src/pages/onboarding/vv/CheckoutAddress.vue b/client/src/pages/onboarding/vv/CheckoutAddress.vue index dede2509..b677a52a 100644 --- a/client/src/pages/onboarding/vv/CheckoutAddress.vue +++ b/client/src/pages/onboarding/vv/CheckoutAddress.vue @@ -10,6 +10,7 @@ import { useEntities } from "@/services/onboarding"; import { useDebounceFn, useFetch } from "@vueuse/core"; import { useRoute } from "vue-router"; import { useTranslation } from "i18next-vue"; +import { getVVCourseName } from "./helpers"; type BillingAddressType = { first_name: string; @@ -27,6 +28,13 @@ type BillingAddressType = { company_country: string; }; +const props = defineProps({ + courseType: { + type: String, + required: true, + }, +}); + const user = useUserStore(); const route = useRoute(); const { organisations } = useEntities(); @@ -193,7 +201,7 @@ const executePayment = () => { itPost("/api/shop/vv/checkout/", { redirect_url: fullHost, address: address.value, - product: `vv-${user.language}`, + product: props.courseType, }).then((res) => { console.log("Going to next page", res.next_step_url); window.location.href = res.next_step_url; @@ -210,7 +218,7 @@ const executePayment = () => { :translation="$t('a.Der Preis für den Lehrgang {course} beträgt {price}.')" > - "{{ $t("a.Versicherungsvermittler/-in") }} ({{ user.languageName }})" + "{{ getVVCourseName(props.courseType) }}" 300 CHF diff --git a/client/src/pages/onboarding/vv/CheckoutComplete.vue b/client/src/pages/onboarding/vv/CheckoutComplete.vue index 03812f8a..cceb0a5d 100644 --- a/client/src/pages/onboarding/vv/CheckoutComplete.vue +++ b/client/src/pages/onboarding/vv/CheckoutComplete.vue @@ -28,7 +28,7 @@ const user = useUserStore(); " > - {{ $t("a.Versicherungsvermittler/-in") }} ({{ user.languageName }}) + {{ $t("a.Versicherungsvermittler/-in") }} diff --git a/client/src/pages/onboarding/vv/helpers.ts b/client/src/pages/onboarding/vv/helpers.ts new file mode 100644 index 00000000..2dccab98 --- /dev/null +++ b/client/src/pages/onboarding/vv/helpers.ts @@ -0,0 +1,16 @@ +import i18next from "i18next"; + +export const getVVCourseName = (courseType: string) => { + if (!["vv-de", "vv-it", "vv-fr"].includes(courseType)) { + return ""; + } + + const lookup: { [key: string]: string } = { + "vv-de": i18next.t("a.Deutsch"), + "vv-fr": i18next.t("a.Franzosisch"), + "vv-it": i18next.t("a.Italienisch"), + }; + + const vv = i18next.t("a.Versicherungsvermittler/-in"); + return `${vv} (${lookup[courseType]})`; +}; diff --git a/client/src/router/index.ts b/client/src/router/index.ts index 4b9b5eec..ea8d4462 100644 --- a/client/src/router/index.ts +++ b/client/src/router/index.ts @@ -280,6 +280,7 @@ const router = createRouter({ path: "checkout/address", component: () => import("@/pages/onboarding/vv/CheckoutAddress.vue"), name: "checkoutAddress", + props: true, }, { path: "checkout/complete", diff --git a/client/src/services/onboarding.ts b/client/src/services/onboarding.ts index 5bd5285b..da1bdb9a 100644 --- a/client/src/services/onboarding.ts +++ b/client/src/services/onboarding.ts @@ -1,5 +1,6 @@ import { itGetCached } from "@/fetchHelpers"; import { useUserStore } from "@/stores/user"; +import { isString, startsWith } from "lodash"; import type { Ref } from "vue"; import { computed, ref } from "vue"; @@ -7,7 +8,8 @@ export function profileNextRoute(courseType: string | string[]) { if (courseType === "uk") { return "setupComplete"; } - if (courseType === "vv") { + // vv- -> vv-de, vv-fr or vv-it + if (isString(courseType) && startsWith(courseType, "vv-")) { return "checkoutAddress"; } return ""; diff --git a/server/config/settings/base.py b/server/config/settings/base.py index a36bf213..3a34d2de 100644 --- a/server/config/settings/base.py +++ b/server/config/settings/base.py @@ -559,12 +559,10 @@ else: ALLOWED_HOSTS = env.list( "IT_DJANGO_ALLOWED_HOSTS", - # FIXME @livioso: Remove ngrok -> Datatrans Testing (hope I don't commit this) default=[ "localhost", "0.0.0.0", "127.0.0.1", - "2375-2a02-21b4-9679-d800-8151-6008-2b6c-7a32.ngrok-free.app", ], ) diff --git a/server/vbv_lernwelt/shop/tests/test_checkout_api.py b/server/vbv_lernwelt/shop/tests/test_checkout_api.py index 270b3ba5..a650008d 100644 --- a/server/vbv_lernwelt/shop/tests/test_checkout_api.py +++ b/server/vbv_lernwelt/shop/tests/test_checkout_api.py @@ -84,8 +84,8 @@ class CheckoutAPITestCase(APITestCase): 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_success=f"{REDIRECT_URL}/onboarding/{VV_DE_PRODUCT_SKU}/checkout/complete", + redirect_url_error=f"{REDIRECT_URL}/onboarding/{VV_DE_PRODUCT_SKU}/checkout/address?error", redirect_url_cancel=f"{REDIRECT_URL}/", webhook_url=f"{REDIRECT_URL}/api/shop/transaction/webhook/", ) @@ -111,7 +111,7 @@ class CheckoutAPITestCase(APITestCase): self.assertEqual(response.status_code, status.HTTP_200_OK) expected = ( - f"{REDIRECT_URL}/onboarding/vv/checkout/address?error&" + f"{REDIRECT_URL}/onboarding/{VV_DE_PRODUCT_SKU}/checkout/address?error&" f"message=vv-de_product_sku_does_not_exist_needs_to_be_created_first" ) @@ -138,7 +138,7 @@ class CheckoutAPITestCase(APITestCase): # THEN self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual( - f"{REDIRECT_URL}/onboarding/vv/checkout/address?error", + f"{REDIRECT_URL}/onboarding/{VV_DE_PRODUCT_SKU}/checkout/address?error", response.json()["next_step_url"], ) diff --git a/server/vbv_lernwelt/shop/views.py b/server/vbv_lernwelt/shop/views.py index 5e22cc8e..37694d66 100644 --- a/server/vbv_lernwelt/shop/views.py +++ b/server/vbv_lernwelt/shop/views.py @@ -135,6 +135,7 @@ def checkout_vv(request): return next_step_response( url=checkout_error_url( base_url=base_redirect_url, + product_sku=sku, message=f"{sku}_product_sku_does_not_exist_needs_to_be_created_first", ), ) @@ -153,8 +154,12 @@ def checkout_vv(request): transaction_id = init_transaction( user=request.user, amount_chf_centimes=product.price, - redirect_url_success=checkout_success_url(base_redirect_url), - redirect_url_error=checkout_error_url(base_redirect_url), + redirect_url_success=checkout_success_url( + base_url=base_redirect_url, product_sku=sku + ), + redirect_url_error=checkout_error_url( + base_url=base_redirect_url, product_sku=sku + ), redirect_url_cancel=checkout_cancel_url(base_redirect_url), webhook_url=webhook_url(base_redirect_url), ) @@ -164,6 +169,7 @@ def checkout_vv(request): return next_step_response( url=checkout_error_url( base_url=base_redirect_url, + product_sku=sku, ), ) @@ -214,8 +220,10 @@ def webhook_url(base_url: str) -> str: return f"{base_url}/api/shop/transaction/webhook/" -def checkout_error_url(base_url: str, message: str | None = None) -> str: - url = f"{base_url}/onboarding/vv/checkout/address?error" +def checkout_error_url( + base_url: str, product_sku: str, message: str | None = None +) -> str: + url = f"{base_url}/onboarding/{product_sku}/checkout/address?error" if message: url += f"&message={message}" @@ -227,5 +235,5 @@ def checkout_cancel_url(base_url: str) -> str: return f"{base_url}/" -def checkout_success_url(base_url: str = "") -> str: - return f"{base_url}/onboarding/vv/checkout/complete" +def checkout_success_url(product_sku: str, base_url: str = "") -> str: + return f"{base_url}/onboarding/{product_sku}/checkout/complete" diff --git a/server/vbv_lernwelt/sso/tests/test_sso_flow.py b/server/vbv_lernwelt/sso/tests/test_sso_flow.py index 425d8844..bb3c283c 100644 --- a/server/vbv_lernwelt/sso/tests/test_sso_flow.py +++ b/server/vbv_lernwelt/sso/tests/test_sso_flow.py @@ -79,7 +79,7 @@ class TestSignInAuthorizeSSO(TestCase): } state = base64.urlsafe_b64encode( - json.dumps({"course": "vv", "next": "/shall-be-ignored"}).encode() + json.dumps({"course": "vv-de", "next": "/shall-be-ignored"}).encode() ).decode() # WHEN @@ -87,7 +87,7 @@ class TestSignInAuthorizeSSO(TestCase): # THEN self.assertEqual(302, response.status_code) - self.assertEqual("/onboarding/vv/account/create", response.url) # noqa + self.assertEqual("/onboarding/vv-de/account/create", response.url) # noqa user = User.objects.get(email=email) # noqa self.assertIsNotNone(user) @@ -206,7 +206,7 @@ class TestSignIn(TestCase): @patch("vbv_lernwelt.sso.views.oauth") def test_signin_with_course_param(self, mock_oauth): # GIVEN - course_param = "vv" + course_param = "vv-de" expected_state = {"course": course_param, "next": None} expected_state_encoded = base64.urlsafe_b64encode( @@ -229,14 +229,14 @@ class TestSignIn(TestCase): ANY, "/sso/callback", state=expected_state_encoded, - lang="de", + ui_locales="de", ) @override_settings(OAUTH_SIGNIN_REDIRECT_URI="/sso/callback") @patch("vbv_lernwelt.sso.views.oauth") def test_signin_with_state_param(self, mock_oauth): # GIVEN - state_param = "vv" + state_param = "vv-de" expected_state = {"course": state_param, "next": None} expected_state_encoded = base64.urlsafe_b64encode( @@ -259,7 +259,7 @@ class TestSignIn(TestCase): ANY, "/sso/callback", state=expected_state_encoded, - lang="de", + ui_locales="de", ) @override_settings(OAUTH_SIGNIN_REDIRECT_URI="/sso/callback") @@ -289,7 +289,7 @@ class TestSignIn(TestCase): ANY, "/sso/callback", state=expected_state_encoded, - lang=ANY, + ui_locales=ANY, ) @override_settings(OAUTH_SIGNIN_REDIRECT_URI="/sso/callback") @@ -314,7 +314,7 @@ class TestSignIn(TestCase): ANY, "/sso/callback", state=ANY, - lang=language, + ui_locales=language, ) @@ -323,7 +323,7 @@ class TestSignUp(TestCase): @patch("vbv_lernwelt.sso.views.oauth") def test_signup_with_course_param(self, mock_oauth): # GIVEN - course_param = "vv" + course_param = "vv-de" language = "fr" expected_state = {"course": course_param, "next": None} diff --git a/server/vbv_lernwelt/sso/views.py b/server/vbv_lernwelt/sso/views.py index dc15459c..b8c9b1d1 100644 --- a/server/vbv_lernwelt/sso/views.py +++ b/server/vbv_lernwelt/sso/views.py @@ -43,14 +43,18 @@ def signin(request): f"SSO Login (course={course_param}, next={next_param})", sso_login_redirect_uri=redirect_uri, ) + return oauth.signin.authorize_redirect( - request, redirect_uri, state=state_encoded, lang=request.GET.get("lang", "de") + request, + redirect_uri, + state=state_encoded, + ui_locales=request.GET.get("lang", "de"), ) -def get_redirect_uri(user: User, course: str, next_url: str): - if course == "vv" and not has_course_session_user_vv(user): - return redirect("/onboarding/vv/account/create") +def get_redirect_uri(user: User, course: str | None, next_url: str | None): + if course and course.startswith("vv") and not has_course_session_user_vv(user): + return redirect(f"/onboarding/{course}/account/create") elif ( course == "uk" and not CourseSession.objects.filter(coursesessionuser__user=user).exists()