From 46daea051153112fcb2b1290004e42fd5849c10a Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Thu, 18 Jul 2024 14:19:34 +0200 Subject: [PATCH 1/5] Enable cembra/byjuno payment --- env_secrets/caprover_vbv-develop.env | Bin 1410 -> 1410 bytes server/vbv_lernwelt/core/constants.py | 1 + .../vbv_lernwelt/core/create_default_users.py | 19 ++++++++++++++++++ server/vbv_lernwelt/shop/services.py | 6 ++---- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/env_secrets/caprover_vbv-develop.env b/env_secrets/caprover_vbv-develop.env index c633f417bff7fbf9e1da651afdf0215ac04467d3..5432d3c8d0f3325116f2c115d5b3e00b05d3e2f7 100644 GIT binary patch literal 1410 zcmV-|1%3JeM@dveQdv+`0R5m0PB9m#27BrtQzTx)WmcPXSslete|WanHJO_H|4~_O zP^cLG*XtL4OC~z@$5m_&IGL&h_JQJ|gH+5hQSp4{>H?-RSNu(A%LMc|^)u)D& zqoMe#h&R6`nYgW1niNZQJRV!nlGJJM71d4a zXb5fj0W&mrii@$Dr zb6~$Ch`Cn`pm{iLtmmv{cu9*`reNXYG0EPEm>5}eK2Jx!xd-52RUe}Z{XQJ;rN>r~ zQTuqp<1^BI105)RWpaFZn1K(pE4kPlby`GZL5+BR5HnnU8lO_ok{Hz>+V||0fO%&> zUTzCfno(qE>jU{9E`Lqi4WZjF-*@pgQQlH2*r>PZRedmIMZ09JE%OVhuP`f z9(|?;L{^b-XRf`qCIYFZze%{^2W&(Mp#mub)onH8O#d{_r-3NY+RWFWVHCV2fbg3B z)v$P*s2Bz3EaT`je|}Py(UWG)=0tl=$KTk}n~-5=lr(V3!gFD|T@OLQoY#?+@5px+ zcV2^PdKzL=TJ#>u{aaMN2S&|{kdKeyJ7M~ufHh8^AnKB|jS*DCDPLi<&FsYaRh@S~ zzbM3%GT4NC7#wuTUf+nuWoFAO%iOBfPou;>b_tOj>nBsX6}Q}PN%-*<=T!5Fez2>A zw|xDViOuevU9QfXWEV0qcOKn2R?;&S-|3^2tv&0H#uBTB@bM)Ri=A8FMH(+HRE6*f?OQWX8su^Ien`@qnd_dom`|@eKUW(Q$#e&BT z#De4lBb$-MEwE)l9U!rIU0>zkK$LoZUulYSL!ZT8*JM##eJNbm;78K|~f zbB>Z`XP`R`>Sry?a1a*IAEugrmH`9IZIS&zP}DXhzGNu67xQ>!Do?$tiz^ICk-nh} z2Oo(KyVR(yT*ysaU>N#&P^6;*_#1ww}``u5gjWb7s%&EwLsry38dxw5(Wo2x?h4oy3 z&M+{+21EE%bNplpX5mto&aIU&7gB2cEC5LNl)jY^=~4hooIYAOM;UY&L3$bkOxgx& QEL5k|q77$mx!byyB&9#T9{>OV literal 1410 zcmV-|1%3JeM@dveQdv+`0H6*;YgY}<6t)6kQ+BGQ30$%8=Z&H;LwH8|#dM(Qy-L0^4mFL{A z{NOuVz~v=y&_o7Y2`$$o!*}E<`7vnJ!af<$q!0r!FoM@Q#HAMc7Y&puEGvnhUK!Di z&>2kCU#QHjSZ*)RWr}W_-ndPAs6A9{B-bqRRv1_d$1ci^n2h{)voEKLu@-BPHHgj= zIcR*n@Gc5NE4JXOuR{(7cA#3$7&WCYHL|4UuzX<%h`hCe2C#wII#_HxS`rIm{T59> zQIqlroBTHL_d;@R67in5rPfhH=hP||Vx0)R2jyK6QaJis6y(d`{|zqSK?^wz$tQpk z*Ln%bPVBy0MRsqd^E^AV;>Ck45A054(u2={U!U*eAim-{{i!8P^4r$k9zGo;=txaC zbk8H+VA`T(h%-(QoxunK%<5euAlxhd*RHby45xd8Na&=o8V&bp6Ie=13`(wG--U3U z%6uo>T?8;FCcrtkuL_SoNb^C`C11abt?$R$rxb5)Z%Rx0~jI)abor+2+mP?$a0$`>QlwukUn(cQ5@4 zxGGGP^*jr0%RhNrwF3ULY8pz?Q3q1)+YjQav-_RqC|zkG_*1ZK0! z%8dr2?!i9*Zra&A(nkG8Ab&+uW>B>f)4;am&HjfY%OFi;A@(`zq*q>dDN#sP<(&CR`F@$$&20 z3@e~YOhE1In9IS{w8!hest|~(A;56ZAbq9}ujdpnJ6S~TH=OS(d=FmzF;6B`ls!I# z87_xkLl;Ui7k4~2DddBU2}V}r2i#fO@-L56NJ;dWt>3`M1?zebcvO@Uj1a(k2ygSC zIVrx#$;S>X`*z9m+}Xei1Igz|#i!eA`!`r6|8)RF2YG?nE)HXHvZT+b> zk6{cxE^)~X#Al4Ih%`RZf3P=8sEjg+Xu+1po@o8eSVb)V)I6n$9O2`_ZG5ED;ZXFH)nK+YnTHlca;&1`% z0jxg<@ZXhY zUpx6u^|SdDbcl|L(}T!Zq|99VcAqN4frV+d6ES4u3j7*nR)2AN1ohgj>um4M;K-H0d!IS0W+DTr QcJP3A1@w{)_wTYYEKec0#Q*>R diff --git a/server/vbv_lernwelt/core/constants.py b/server/vbv_lernwelt/core/constants.py index 75f24848..e36702b8 100644 --- a/server/vbv_lernwelt/core/constants.py +++ b/server/vbv_lernwelt/core/constants.py @@ -28,6 +28,7 @@ TEST_MENTOR1_USER_ID = "d1f5f5a9-5b0a-4e1a-9e1a-9e9b5b5e1b1b" TEST_STUDENT1_VV_USER_ID = "5ff59857-8de5-415e-a387-4449f9a0337a" TEST_STUDENT2_VV_AND_VV_MENTOR_USER_ID = "7e8ebf0b-e6e2-4022-88f4-6e663ba0a9db" TEST_USER_EMPTY_ID = "daecbabe-4ab9-4edf-a71f-4119042ccb02" +TEST_USER_DATATRANS_HANNA_ID = "6bec1a0d-f852-47aa-a4de-072df6e07ad1" TEST_COURSE_SESSION_BERN_ID = -1 TEST_COURSE_SESSION_ZURICH_ID = -2 diff --git a/server/vbv_lernwelt/core/create_default_users.py b/server/vbv_lernwelt/core/create_default_users.py index 17acd036..a384c13e 100644 --- a/server/vbv_lernwelt/core/create_default_users.py +++ b/server/vbv_lernwelt/core/create_default_users.py @@ -20,6 +20,7 @@ from vbv_lernwelt.core.constants import ( TEST_SUPERVISOR1_USER_ID, TEST_TRAINER1_USER_ID, TEST_TRAINER2_USER_ID, + TEST_USER_DATATRANS_HANNA_ID, TEST_USER_EMPTY_ID, ) from vbv_lernwelt.core.models import User @@ -202,6 +203,24 @@ def create_default_users(default_password="test", set_avatar=False): language="de", ) + hanna, _ = User.objects.get_or_create( + id=TEST_USER_DATATRANS_HANNA_ID, + ) + hanna.username = "datatrans.hanna.vbv@example.com" + hanna.email = "datatrans.hanna.vbv@example.com" + hanna.language = "de" + hanna.first_name = "Hanna" + hanna.last_name = "Vbv" + hanna.street = "Bahnstrasse" + hanna.street_number = "2" + hanna.postal_code = "8603" + hanna.city = "Schwerzenbach" + hanna.country_id = "CH" + hanna.birth_date = "1970-01-01" + hanna.phone_number = "+41792018586" + hanna.password = make_password("test") + hanna.save() + for user_data in default_users: _create_student_user(**user_data) diff --git a/server/vbv_lernwelt/shop/services.py b/server/vbv_lernwelt/shop/services.py index f5998d38..a9561d0e 100644 --- a/server/vbv_lernwelt/shop/services.py +++ b/server/vbv_lernwelt/shop/services.py @@ -7,7 +7,6 @@ import structlog from django.conf import settings from vbv_lernwelt.core.admin import User -from vbv_lernwelt.shop.const import VV_PRODUCT_NUMBER from vbv_lernwelt.shop.datatrans.datatrans_api_client import DatatransApiClient from vbv_lernwelt.shop.models import CheckoutState @@ -101,9 +100,8 @@ def init_datatrans_transaction( }, } - # FIXME: test with working cembra byjuno invoice customer? - # if with_cembra_byjuno_invoice: - # payload["paymentMethods"] = ["INT"] + if with_cembra_byjuno_invoice: + payload["paymentMethods"] = ["INT"] if datatrans_customer_data: payload["customer"] = datatrans_customer_data if datatrans_int_data: From e803e5308fc6bc948932901c92667b28c81ef513 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Thu, 18 Jul 2024 14:35:41 +0200 Subject: [PATCH 2/5] Fix unit tests --- .../vbv_lernwelt/core/create_default_users.py | 42 +++++++++++-------- .../core/management/commands/cypress_reset.py | 5 +++ 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/server/vbv_lernwelt/core/create_default_users.py b/server/vbv_lernwelt/core/create_default_users.py index a384c13e..beeaca53 100644 --- a/server/vbv_lernwelt/core/create_default_users.py +++ b/server/vbv_lernwelt/core/create_default_users.py @@ -4,6 +4,7 @@ from django.contrib.auth.models import Group, Permission from django.core.files import File from environs import Env +from vbv_lernwelt.core.model_utils import add_countries from vbv_lernwelt.media_files.models import UserImage env = Env() @@ -79,7 +80,30 @@ default_users = [ AVATAR_DIR = settings.APPS_DIR / "static" / "avatars" +def create_datatrans_hanna_user(): + hanna, _ = User.objects.get_or_create( + id=TEST_USER_DATATRANS_HANNA_ID, + ) + hanna.username = "datatrans.hanna.vbv@example.com" + hanna.email = "datatrans.hanna.vbv@example.com" + hanna.language = "de" + hanna.first_name = "Hanna" + hanna.last_name = "Vbv" + hanna.street = "Bahnstrasse" + hanna.street_number = "2" + hanna.postal_code = "8603" + hanna.city = "Schwerzenbach" + hanna.country_id = "CH" + hanna.birth_date = "1970-01-01" + hanna.phone_number = "+41792018586" + hanna.password = make_password("test") + hanna.save() + return hanna + + def create_default_users(default_password="test", set_avatar=False): + add_countries(small_set=True) + admin_group, created = Group.objects.get_or_create(name="admin_group") _content_creator_group, _created = Group.objects.get_or_create( name="content_creator_grop" @@ -203,23 +227,7 @@ def create_default_users(default_password="test", set_avatar=False): language="de", ) - hanna, _ = User.objects.get_or_create( - id=TEST_USER_DATATRANS_HANNA_ID, - ) - hanna.username = "datatrans.hanna.vbv@example.com" - hanna.email = "datatrans.hanna.vbv@example.com" - hanna.language = "de" - hanna.first_name = "Hanna" - hanna.last_name = "Vbv" - hanna.street = "Bahnstrasse" - hanna.street_number = "2" - hanna.postal_code = "8603" - hanna.city = "Schwerzenbach" - hanna.country_id = "CH" - hanna.birth_date = "1970-01-01" - hanna.phone_number = "+41792018586" - hanna.password = make_password("test") - hanna.save() + hanna = create_datatrans_hanna_user() for user_data in default_users: _create_student_user(**user_data) diff --git a/server/vbv_lernwelt/core/management/commands/cypress_reset.py b/server/vbv_lernwelt/core/management/commands/cypress_reset.py index abfae404..1a02a343 100644 --- a/server/vbv_lernwelt/core/management/commands/cypress_reset.py +++ b/server/vbv_lernwelt/core/management/commands/cypress_reset.py @@ -17,8 +17,10 @@ from vbv_lernwelt.core.constants import ( TEST_STUDENT2_VV_AND_VV_MENTOR_USER_ID, TEST_STUDENT3_USER_ID, TEST_TRAINER1_USER_ID, + TEST_USER_DATATRANS_HANNA_ID, TEST_USER_EMPTY_ID, ) +from vbv_lernwelt.core.create_default_users import create_datatrans_hanna_user from vbv_lernwelt.core.models import Organisation, User from vbv_lernwelt.course.consts import ( COURSE_TEST_ID, @@ -159,6 +161,9 @@ def command( password=make_password("test"), ) + User.objects.filter(id=TEST_USER_DATATRANS_HANNA_ID).delete() + create_datatrans_hanna_user() + cursor = connection.cursor() cursor.execute("truncate core_securityrequestresponselog;") cursor.execute("truncate core_externalapirequestlog;") From 74c4099b8b3dd25bc494c26ba7ead13b1722f996 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Thu, 18 Jul 2024 15:29:12 +0200 Subject: [PATCH 3/5] Test-Deployment improvements --- caprover_create_app.py | 8 ++++++-- env_secrets/local_daniel.env | Bin 1281 -> 1512 bytes server/config/settings/base.py | 6 ++++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/caprover_create_app.py b/caprover_create_app.py index 24dba123..e137bcf1 100644 --- a/caprover_create_app.py +++ b/caprover_create_app.py @@ -93,8 +93,12 @@ def main(app_name, image_name, environment_file): "AWS_S3_SECRET_ACCESS_KEY": env.str("AWS_S3_SECRET_ACCESS_KEY", ""), "AWS_S3_REGION_NAME": "eu-central-1", "AWS_STORAGE_BUCKET_NAME": "myvbv-dev.iterativ.ch", - "DATATRANS_HMAC_KEY": env.str("DATATRANS_HMAC_KEY", ""), - "DATATRANS_BASIC_AUTH_KEY": env.str("DATATRANS_BASIC_AUTH_KEY", ""), + "DATATRANS_HMAC_KEY": env.str("PIPELINES_DATATRANS_HMAC_KEY", ""), + "DATATRANS_BASIC_AUTH_KEY": env.str( + "PIPELINES_DATATRANS_BASIC_AUTH_KEY", "" + ), + "DATATRANS_API_ENDPOINT": "https://api.sandbox.datatrans.com", + "DATATRANS_PAY_URL": "https://pay.sandbox.datatrans.com", "FILE_UPLOAD_STORAGE": "s3", "IT_DJANGO_DEBUG": "false", "IT_SERVE_VUE": "false", diff --git a/env_secrets/local_daniel.env b/env_secrets/local_daniel.env index 7ae4f656c72a7fda433d8e81db09bbca0a48052d..703bd5e90348bdc749c714a4a40b8b13b4a5f3fb 100644 GIT binary patch literal 1512 zcmVC-quY~NB@>Kjv9hwx!L<9@4IcztOcYMjf4dlr zN{=(4ZmZ=LJHWAVZg_dR8SFER-T^A(E;wTNd_Ffaoqjxx6dA~BkVTKf zlDptT5_GKrzV^HV*6xpqTef^(SKk1azu@5Y=)vc$qN7^Qfhr2&q|tt)f8;+-*kRnZ zCi#e@ID~vZZq{ZtYbjuRPa};}^4{Nu2hdJl#GK+Rk29 zmXQUTC#j&e=T)}L+&hx-O^+1%oNDlhsa#ZzmfkV^rDKxArU`3r#3(qgL!Nef?>PU( zz;EOB8BsWUX9ioh(kY1{3ZTWjWJ!5)U5p7PunF431sD?PnCB2rrgeCkGMCNN^$;u@3WS?GSE zWPjq5k=b?&LI7L_9D>x~woW(*o47LD_5fH!WAkthP0hh6-L$PXy8fLU4W+;g2JO5aTvX$W&n@l!KbzgjdqI0skZC~XHbFz&!>OA1D{nRWDH z=*?R+sETm?u)6H!xf?>X*z6-eT6jy~W(hVO9NU_<)f*^p%^{(aCXWZgCMGFeBYur-DF#>Z|#fVPZzSze8e<^AOz7h zDhue^YZ>bCt8K*ywu;II74)@HlX8G>>$?S%Qsd`n?dkh+N{jfgB6jw!Q*T>WH1jdN zM7?Dd*%d4Gp+jx|vYw69X64f}CKbx#qp{WTL-CD1ttmkP2rEI|K!5SyR3tpKuc-qW#&(Adey5U&$9^ zRw)ZhN``&1dSqd9n4s9=i;Eb0<2qK3Q#U4dfG6=0!Tqh@=9X#w)CiUDTDABYoPZOH zq27Nv=l=sOWe5~snBc(n5^PAO zBF>DO`i))}nopj_CGh-b@b{}#Hkj77l(Ljr14+SP5qsnUfD5^EJ4w^DD!G>4A+a2= zY+^R0N)P>FMjOrsLihd7Pwl_)le$doDXh%}J}a|Jcahl6fO8Nfq3{}Mj;UFAt7!OU O%0;Y)orw|(_Ix()nD*WP literal 1281 zcmV+c1^)T~M@dveQdv+`0K~zt1aShaqph1>SC&gOXbPlOPt<_H_XF=zFMrBbRB&HK z>zIo@$>_iMEgH;_Jj`zfn7Q}W)m%2RD@&QnOlSeUhGQ^Ie&karq-#%x2ArcCTcMye zacXtbU;>n5v|($s-;&(Jfw)#UCp0(*k!b2*IepL9U)KT4ok{a(-lNo^u>?9P{CBUfv{I0GWg3vdtjSuJWUSy!9g6La*G)ut$7C8A-A-jfV@Sh<32q9E^ z^aSA0{3U^xSj4G$P$J}d-j0dX+895O2$b(tW^u@#rM+SPG;nnMI7;lfZZn)&S+7*$ zhPCb4wn**kjS>3mN-m3g+K=Y4HX8I&Yb#sAtBl=8Izg%+s;X(L(g(7+wI7Spvf#p8(@E+ zA^+tv>NM|Nx2-8=)l0f?;3OwesAAcBR&M5NhEE$jS&MZNw3-7=KgRZ4WwVD5C%ws150Btv7VWXE)*qrxAX<49+iiA!d({XH8mVo?|Ib^C*o!H}|w06GKhmDtLW5>3|`;~w?0$$=`uz4%K6&;U=kqC`g$@^@= zH0>%!Pnty${^#e6aEIEaqkb;!`I9bXJVpqp;7v+Ft@e|5rI#mUb7mBd#6n*L`8y5e z&622!O$B@CMGSQ!7+`IWiEHW+hkM-9&dyJ(s3^HMc&Yc5^_dcvNUtJl5QNW3s&6vO z0s!Al4hPApLt597{9c|>j^z}Y7EWtTiNeie8J^pi+M`>ew=U&|<{e-O3J@p%irfD{ z?kVNTGtlLFC|}U~P^dL#O%L?PQ6P}loH~0JnfB~~OspUf zhQVS^tE$t32l`>PytLxr&zUh*+V-hME-e=(KHmw&Xkvj*xkvA rA0#!9aELc67R}V0cnQxU;m+5b$i>2A4)aHay0f|KeTBvxI>C);#ZG?e diff --git a/server/config/settings/base.py b/server/config/settings/base.py index a4fda6fd..6533d160 100644 --- a/server/config/settings/base.py +++ b/server/config/settings/base.py @@ -682,10 +682,12 @@ if APP_ENVIRONMENT.startswith("prod"): DATATRANS_PAY_URL = "https://pay.datatrans.com" else: DATATRANS_API_ENDPOINT = env( - "DATATRANS_API_ENDPOINT", default="https://api.sandbox.datatrans.com" + "DATATRANS_API_ENDPOINT", + default="http://localhost:8000/server/fakeapi/datatrans/api", ) DATATRANS_PAY_URL = env( - "DATATRANS_PAY_URL", default="https://pay.sandbox.datatrans.com" + "DATATRANS_PAY_URL", + default="http://localhost:8000/server/fakeapi/datatrans/pay", ) # default settings for python sftpserver test-server From ab60eebd25fbdb3d1a96a0120988ee3a6078d94f Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Tue, 23 Jul 2024 14:02:23 +0200 Subject: [PATCH 4/5] Use order_id as refno --- server/vbv_lernwelt/shop/services.py | 6 +- .../shop/tests/test_checkout_api.py | 24 +++--- .../shop/tests/test_datatrans_service.py | 19 ++--- server/vbv_lernwelt/shop/views.py | 73 ++++++++++--------- 4 files changed, 62 insertions(+), 60 deletions(-) diff --git a/server/vbv_lernwelt/shop/services.py b/server/vbv_lernwelt/shop/services.py index a9561d0e..0e7bd7b3 100644 --- a/server/vbv_lernwelt/shop/services.py +++ b/server/vbv_lernwelt/shop/services.py @@ -1,6 +1,5 @@ import hashlib import hmac -import uuid import requests import structlog @@ -72,6 +71,7 @@ def is_signature_valid( def init_datatrans_transaction( user: User, + refno: str, amount_chf_centimes: int, redirect_url_success: str, redirect_url_error: str, @@ -90,8 +90,8 @@ def init_datatrans_transaction( "amount": amount_chf_centimes, "currency": "CHF", "language": user.language, - "refno": str(uuid.uuid4()), - "refno2": refno2, + "refno": str(refno), + "refno2": str(refno2), "webhook": {"url": webhook_url}, "redirect": { "successUrl": redirect_url_success, diff --git a/server/vbv_lernwelt/shop/tests/test_checkout_api.py b/server/vbv_lernwelt/shop/tests/test_checkout_api.py index efbee5da..f5f46b37 100644 --- a/server/vbv_lernwelt/shop/tests/test_checkout_api.py +++ b/server/vbv_lernwelt/shop/tests/test_checkout_api.py @@ -71,9 +71,8 @@ class CheckoutAPITestCase(APITestCase): # THEN self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual( - f"https://pay.sandbox.datatrans.com/v1/start/1234567890", - response.json()["next_step_url"], + self.assertTrue( + response.json()["next_step_url"].endswith("v1/start/1234567890") ) ci = CheckoutInformation.objects.first() @@ -154,9 +153,11 @@ class CheckoutAPITestCase(APITestCase): ) self.assertEqual( - 0, + 1, CheckoutInformation.objects.count(), ) + ci = CheckoutInformation.objects.first() + self.assertEqual(ci.state, CheckoutState.FAILED) def test_checkout_already_paid(self): # GIVEN @@ -217,9 +218,8 @@ class CheckoutAPITestCase(APITestCase): # THEN self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual( - f"https://pay.sandbox.datatrans.com/v1/start/{transaction_id_next}", - response.json()["next_step_url"], + self.assertTrue( + response.json()["next_step_url"].endswith(f"v1/start/{transaction_id_next}") ) # check that we have two checkouts @@ -277,9 +277,8 @@ class CheckoutAPITestCase(APITestCase): # THEN self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual( - f"https://pay.sandbox.datatrans.com/v1/start/{transaction_id}", - response.json()["next_step_url"], + self.assertTrue( + response.json()["next_step_url"].endswith(f"v1/start/{transaction_id}") ) @patch("vbv_lernwelt.shop.views.init_datatrans_transaction") @@ -310,7 +309,6 @@ class CheckoutAPITestCase(APITestCase): # THEN self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual( - f"https://pay.sandbox.datatrans.com/v1/start/{transaction_id}", - response.json()["next_step_url"], + self.assertTrue( + response.json()["next_step_url"].endswith(f"v1/start/{transaction_id}") ) diff --git a/server/vbv_lernwelt/shop/tests/test_datatrans_service.py b/server/vbv_lernwelt/shop/tests/test_datatrans_service.py index 6dc907fe..7f22c8ee 100644 --- a/server/vbv_lernwelt/shop/tests/test_datatrans_service.py +++ b/server/vbv_lernwelt/shop/tests/test_datatrans_service.py @@ -24,10 +24,8 @@ class DatatransServiceTest(TestCase): @override_settings(DATATRANS_BASIC_AUTH_KEY="BASIC_AUTH_KEY") @patch("vbv_lernwelt.shop.services.requests.post") - @patch("vbv_lernwelt.shop.services.uuid.uuid4") - def test_init_transaction_201(self, mock_uuid, mock_post): + def test_init_transaction_201(self, mock_post): # GIVEN - mock_uuid.return_value = uuid.uuid4() mock_post.return_value.status_code = 201 mock_post.return_value.json.return_value = { "transactionId": 1234567890, @@ -38,6 +36,7 @@ class DatatransServiceTest(TestCase): # WHEN transaction_id = init_datatrans_transaction( user=self.user, + refno="123321", amount_chf_centimes=324_30, redirect_url_success=f"{REDIRECT_URL}/success", redirect_url_error=f"{REDIRECT_URL}/error", @@ -64,11 +63,12 @@ class DatatransServiceTest(TestCase): with self.assertRaises(InitTransactionException): init_datatrans_transaction( user=self.user, + refno="123321", amount_chf_centimes=324_30, - redirect_url_success=f"/success", - redirect_url_error=f"/error", - redirect_url_cancel=f"/cancel", - webhook_url=f"/webhook", + redirect_url_success="/success", + redirect_url_error="/error", + redirect_url_cancel="/cancel", + webhook_url="/webhook", refno2="", ) @@ -80,7 +80,4 @@ class DatatransServiceTest(TestCase): url = get_payment_url(transaction_id) # THEN - self.assertEqual( - url, - f"https://pay.sandbox.datatrans.com/v1/start/{transaction_id}", - ) + self.assertTrue(url.endswith(f"v1/start/{transaction_id}")) diff --git a/server/vbv_lernwelt/shop/views.py b/server/vbv_lernwelt/shop/views.py index 1ce904a3..a245520a 100644 --- a/server/vbv_lernwelt/shop/views.py +++ b/server/vbv_lernwelt/shop/views.py @@ -93,7 +93,7 @@ def checkout_vv(request): sku = request.data["product"] base_redirect_url = request.data["redirect_url"] - log.info(f"Checkout requested: sku", user_id=request.user.id, sku=sku) + log.info("Checkout requested: sku", user_id=request.user.id, sku=sku) try: product = Product.objects.get(sku=sku) @@ -124,6 +124,38 @@ def checkout_vv(request): disable_save="fakeapi" in settings.DATATRANS_API_ENDPOINT ) + address_data = request.data["address"] + country_code = address_data.pop("country_code") + address_data["country_id"] = country_code + + organisation_country_code = "CH" + if "organisation_country_code" in address_data: + organisation_country_code = address_data.pop("organisation_country_code") + address_data["organisation_country_id"] = organisation_country_code + + if "birth_date" in address_data and address_data["birth_date"]: + address_data["birth_date"] = date.fromisoformat(address_data["birth_date"]) + + checkout_info = CheckoutInformation.objects.create( + user=request.user, + state=CheckoutState.ONGOING, + # product + product_sku=sku, + product_price=product.price, + product_name=product.name, + product_description=product.description, + email=email, + ip_address=ip_address, + cembra_byjuno_invoice=with_cembra_byjuno_invoice, + device_fingerprint_session_key=request.data.get( + "device_fingerprint_session_key", "" + ), + # address + **request.data["address"], + ) + + checkout_info.set_increment_abacus_order_id() + refno2 = f"{request.user.abacus_debitor_number}_{VV_PRODUCT_NUMBER}" try: @@ -138,10 +170,10 @@ def checkout_vv(request): "street": f'{request.data["address"]["street"]} {request.data["address"]["street_number"]}', "city": request.data["address"]["city"], "zipCode": request.data["address"]["postal_code"], - "country": request.data["address"]["country_code"], + "country": request.data["address"]["country_id"], "phone": request.data["address"]["phone_number"], "email": email, - "birthDate": request.data["address"]["birth_date"], + "birthDate": str(request.data["address"]["birth_date"]), "language": request.user.language, "ipAddress": ip_address, "type": "P", @@ -154,6 +186,7 @@ def checkout_vv(request): } transaction_id = init_datatrans_transaction( user=request.user, + refno=str(checkout_info.abacus_order_id), amount_chf_centimes=product.price, redirect_url_success=checkout_success_url( base_url=base_redirect_url, product_sku=sku @@ -170,6 +203,8 @@ def checkout_vv(request): with_cembra_byjuno_invoice=with_cembra_byjuno_invoice, ) except InitTransactionException as e: + checkout_info.state = CheckoutState.FAILED.value + checkout_info.save() if not settings.DEBUG: log.error("Transaction initiation failed", exc_info=True, error=str(e)) capture_exception(e) @@ -180,36 +215,8 @@ def checkout_vv(request): ), ) - address_data = request.data["address"] - country_code = address_data.pop("country_code") - address_data["country_id"] = country_code - - organisation_country_code = "CH" - if "organisation_country_code" in address_data: - organisation_country_code = address_data.pop("organisation_country_code") - address_data["organisation_country_id"] = organisation_country_code - - if "birth_date" in address_data and address_data["birth_date"]: - address_data["birth_date"] = date.fromisoformat(address_data["birth_date"]) - - checkout_info = CheckoutInformation.objects.create( - user=request.user, - state=CheckoutState.ONGOING, - transaction_id=transaction_id, - # product - product_sku=sku, - product_price=product.price, - product_name=product.name, - product_description=product.description, - email=email, - ip_address=ip_address, - cembra_byjuno_invoice=with_cembra_byjuno_invoice, - device_fingerprint_session_key=request.data.get( - "device_fingerprint_session_key", "" - ), - # address - **request.data["address"], - ) + checkout_info.transaction_id = transaction_id + checkout_info.save() return next_step_response(url=get_payment_url(transaction_id)) From 08b65f00d743fd8cc2a785280df51abc47c4339a Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Tue, 23 Jul 2024 14:39:49 +0200 Subject: [PATCH 5/5] Enable cembra/byjuno payment for PROD env --- client/src/components/onboarding/PersonalAddress.vue | 7 ------- .../learningContentPage/assignment/AssignmentView.vue | 4 ---- server/config/settings/base.py | 1 - 3 files changed, 12 deletions(-) diff --git a/client/src/components/onboarding/PersonalAddress.vue b/client/src/components/onboarding/PersonalAddress.vue index e40dd405..0430d18a 100644 --- a/client/src/components/onboarding/PersonalAddress.vue +++ b/client/src/components/onboarding/PersonalAddress.vue @@ -32,13 +32,6 @@ const paymentMethods = [ { value: "cembra_byjuno", label: t("a.Rechnung") }, ]; -// TODO: remove after cembra is ready for production -const appEnv = import.meta.env.VITE_APP_ENVIRONMENT || "local"; -if (appEnv.startsWith("prod")) { - paymentMethods.splice(1, 1); -} -// END TODO - const address = computed({ get() { return props.modelValue; diff --git a/client/src/pages/learningPath/learningContentPage/assignment/AssignmentView.vue b/client/src/pages/learningPath/learningContentPage/assignment/AssignmentView.vue index d6d79516..48ae62a3 100644 --- a/client/src/pages/learningPath/learningContentPage/assignment/AssignmentView.vue +++ b/client/src/pages/learningPath/learningContentPage/assignment/AssignmentView.vue @@ -51,10 +51,6 @@ const submissionDeadline = computed(() => { ?.submission_deadline; }); -// FIXME daniel: `useRouteQuery` from usevue is currently the reason that we have to -// fix the version of @vueuse/router and @vueuse/core to 10.1.0 -// it fails with version 10.2.0. I have a reminder to check out the situation -// at the end of July 2023 // 0 = introduction, 1 - n = tasks, n+1 = submission const stepIndex = useRouteQuery("step", "0", { transform: Number, mode: "push" }); diff --git a/server/config/settings/base.py b/server/config/settings/base.py index 6533d160..031cb063 100644 --- a/server/config/settings/base.py +++ b/server/config/settings/base.py @@ -332,7 +332,6 @@ X_FRAME_OPTIONS = "DENY" # EMAIL # ------------------------------------------------------------------------------ # https://docs.djangoproject.com/en/dev/ref/settings/#email-backend -# FIXME how to send emails? EMAIL_BACKEND = env( "DJANGO_EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend" )