@stamhoofd/backend 2.39.0 → 2.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/eslint.config.mjs +5 -0
  2. package/index.ts +81 -74
  3. package/jest.config.cjs +10 -0
  4. package/migrations.ts +16 -14
  5. package/package.json +11 -11
  6. package/src/crons/clear-excel-cache.test.ts +48 -50
  7. package/src/crons/clear-excel-cache.ts +18 -18
  8. package/src/crons/setup-steps.ts +2 -2
  9. package/src/crons.ts +325 -306
  10. package/src/decoders/StringArrayDecoder.ts +7 -7
  11. package/src/decoders/StringNullableDecoder.ts +1 -2
  12. package/src/email-recipient-loaders/members.ts +22 -22
  13. package/src/endpoints/admin/memberships/ChargeMembershipsEndpoint.ts +8 -9
  14. package/src/endpoints/admin/memberships/GetChargeMembershipsSummaryEndpoint.ts +39 -40
  15. package/src/endpoints/admin/organizations/GetOrganizationsCountEndpoint.ts +8 -8
  16. package/src/endpoints/admin/organizations/GetOrganizationsEndpoint.ts +44 -45
  17. package/src/endpoints/admin/organizations/PatchOrganizationsEndpoint.ts +58 -57
  18. package/src/endpoints/auth/CreateAdminEndpoint.ts +48 -45
  19. package/src/endpoints/auth/CreateTokenEndpoint.test.ts +31 -31
  20. package/src/endpoints/auth/CreateTokenEndpoint.ts +146 -147
  21. package/src/endpoints/auth/DeleteTokenEndpoint.ts +7 -7
  22. package/src/endpoints/auth/DeleteUserEndpoint.ts +15 -15
  23. package/src/endpoints/auth/ForgotPasswordEndpoint.ts +17 -18
  24. package/src/endpoints/auth/GetOtherUserEndpoint.ts +9 -10
  25. package/src/endpoints/auth/GetUserEndpoint.test.ts +32 -35
  26. package/src/endpoints/auth/GetUserEndpoint.ts +5 -6
  27. package/src/endpoints/auth/PatchApiUserEndpoint.ts +35 -33
  28. package/src/endpoints/auth/PatchUserEndpoint.ts +55 -52
  29. package/src/endpoints/auth/PollEmailVerificationEndpoint.ts +9 -9
  30. package/src/endpoints/auth/RetryEmailVerificationEndpoint.ts +8 -8
  31. package/src/endpoints/auth/SignupEndpoint.ts +37 -36
  32. package/src/endpoints/auth/VerifyEmailEndpoint.ts +29 -28
  33. package/src/endpoints/global/addresses/SearchRegionsEndpoint.ts +33 -33
  34. package/src/endpoints/global/addresses/ValidateAddressEndpoint.ts +7 -7
  35. package/src/endpoints/global/caddy/CheckDomainCertEndpoint.ts +37 -37
  36. package/src/endpoints/global/email/CreateEmailEndpoint.ts +30 -30
  37. package/src/endpoints/global/email/GetEmailAddressEndpoint.ts +13 -13
  38. package/src/endpoints/global/email/GetEmailEndpoint.ts +13 -13
  39. package/src/endpoints/global/email/ManageEmailAddressEndpoint.ts +16 -16
  40. package/src/endpoints/global/email/PatchEmailEndpoint.ts +25 -25
  41. package/src/endpoints/global/events/GetEventsEndpoint.ts +43 -44
  42. package/src/endpoints/global/events/PatchEventsEndpoint.ts +127 -172
  43. package/src/endpoints/global/files/ExportToExcelEndpoint.ts +49 -50
  44. package/src/endpoints/global/files/GetFileCache.ts +13 -13
  45. package/src/endpoints/global/files/UploadFile.ts +51 -54
  46. package/src/endpoints/global/files/UploadImage.ts +53 -53
  47. package/src/endpoints/global/groups/GetGroupsEndpoint.ts +25 -25
  48. package/src/endpoints/global/members/GetMemberFamilyEndpoint.ts +24 -23
  49. package/src/endpoints/global/members/GetMembersCountEndpoint.ts +8 -8
  50. package/src/endpoints/global/members/GetMembersEndpoint.ts +105 -102
  51. package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.ts +240 -239
  52. package/src/endpoints/global/organizations/CheckRegisterCodeEndpoint.ts +12 -14
  53. package/src/endpoints/global/organizations/CreateOrganizationEndpoint.test.ts +32 -33
  54. package/src/endpoints/global/organizations/CreateOrganizationEndpoint.ts +48 -57
  55. package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.test.ts +21 -22
  56. package/src/endpoints/global/organizations/GetOrganizationFromDomainEndpoint.ts +28 -28
  57. package/src/endpoints/global/organizations/GetOrganizationFromUriEndpoint.ts +18 -18
  58. package/src/endpoints/global/organizations/SearchOrganizationEndpoint.test.ts +20 -20
  59. package/src/endpoints/global/organizations/SearchOrganizationEndpoint.ts +17 -17
  60. package/src/endpoints/global/payments/StripeWebhookEndpoint.ts +81 -75
  61. package/src/endpoints/global/platform/GetPlatformAdminsEndpoint.ts +14 -14
  62. package/src/endpoints/global/platform/GetPlatformEnpoint.ts +11 -11
  63. package/src/endpoints/global/platform/PatchPlatformEnpoint.ts +71 -68
  64. package/src/endpoints/global/registration/GetPaymentRegistrations.ts +27 -27
  65. package/src/endpoints/global/registration/GetUserBillingStatusEndpoint.ts +30 -30
  66. package/src/endpoints/global/registration/GetUserDetailedBillingStatusEndpoint.ts +34 -34
  67. package/src/endpoints/global/registration/GetUserDocumentsEndpoint.ts +26 -26
  68. package/src/endpoints/global/registration/GetUserMembersEndpoint.ts +12 -12
  69. package/src/endpoints/global/registration/PatchUserMembersEndpoint.ts +90 -90
  70. package/src/endpoints/global/registration/RegisterMembersEndpoint.test.ts +118 -121
  71. package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +362 -350
  72. package/src/endpoints/global/registration-periods/GetRegistrationPeriodsEndpoint.ts +8 -9
  73. package/src/endpoints/global/registration-periods/PatchRegistrationPeriodsEndpoint.ts +21 -21
  74. package/src/endpoints/global/webshops/GetWebshopFromDomainEndpoint.ts +65 -65
  75. package/src/endpoints/organization/dashboard/billing/GetOrganizationBillingStatusEndpoint.ts +9 -9
  76. package/src/endpoints/organization/dashboard/billing/GetOrganizationDetailedBillingStatusEndpoint.ts +14 -14
  77. package/src/endpoints/organization/dashboard/documents/GetDocumentTemplateXML.ts +17 -17
  78. package/src/endpoints/organization/dashboard/documents/GetDocumentTemplatesEndpoint.ts +21 -21
  79. package/src/endpoints/organization/dashboard/documents/GetDocumentsEndpoint.ts +15 -15
  80. package/src/endpoints/organization/dashboard/documents/PatchDocumentEndpoint.ts +52 -52
  81. package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplateEndpoint.ts +37 -37
  82. package/src/endpoints/organization/dashboard/email/CheckEmailBouncesEndpoint.ts +14 -14
  83. package/src/endpoints/organization/dashboard/email/EmailEndpoint.ts +113 -112
  84. package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.ts +29 -29
  85. package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.ts +48 -47
  86. package/src/endpoints/organization/dashboard/mollie/CheckMollieEndpoint.ts +22 -21
  87. package/src/endpoints/organization/dashboard/mollie/ConnectMollieEndpoint.ts +13 -14
  88. package/src/endpoints/organization/dashboard/mollie/DisconnectMollieEndpoint.ts +12 -13
  89. package/src/endpoints/organization/dashboard/mollie/GetMollieDashboardEndpoint.ts +24 -24
  90. package/src/endpoints/organization/dashboard/nolt/CreateNoltTokenEndpoint.ts +10 -12
  91. package/src/endpoints/organization/dashboard/organization/GetOrganizationArchivedGroups.ts +14 -14
  92. package/src/endpoints/organization/dashboard/organization/GetOrganizationDeletedGroups.ts +13 -13
  93. package/src/endpoints/organization/dashboard/organization/GetOrganizationSSOEndpoint.ts +12 -12
  94. package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.test.ts +120 -124
  95. package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +172 -173
  96. package/src/endpoints/organization/dashboard/organization/SetOrganizationDomainEndpoint.ts +88 -89
  97. package/src/endpoints/organization/dashboard/organization/SetOrganizationSSOEndpoint.ts +12 -12
  98. package/src/endpoints/organization/dashboard/payments/GetMemberBalanceEndpoint.ts +17 -17
  99. package/src/endpoints/organization/dashboard/payments/GetPaymentsCountEndpoint.ts +8 -8
  100. package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.ts +66 -67
  101. package/src/endpoints/organization/dashboard/payments/PatchBalanceItemsEndpoint.ts +47 -47
  102. package/src/endpoints/organization/dashboard/payments/PatchPaymentsEndpoint.ts +93 -91
  103. package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.ts +16 -17
  104. package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.ts +170 -167
  105. package/src/endpoints/organization/dashboard/registration-periods/SetupStepReviewEndpoint.ts +25 -24
  106. package/src/endpoints/organization/dashboard/stripe/ConnectStripeEndpoint.ts +22 -23
  107. package/src/endpoints/organization/dashboard/stripe/DeleteStripeAccountEndpoint.ts +22 -22
  108. package/src/endpoints/organization/dashboard/stripe/GetStripeAccountLinkEndpoint.ts +17 -18
  109. package/src/endpoints/organization/dashboard/stripe/GetStripeAccountsEndpoint.ts +8 -9
  110. package/src/endpoints/organization/dashboard/stripe/GetStripeLoginLinkEndpoint.ts +17 -18
  111. package/src/endpoints/organization/dashboard/stripe/UpdateStripeAccountEndpoint.ts +14 -15
  112. package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.ts +19 -19
  113. package/src/endpoints/organization/dashboard/users/DeleteUserEndpoint.ts +19 -19
  114. package/src/endpoints/organization/dashboard/users/GetApiUsersEndpoint.ts +14 -14
  115. package/src/endpoints/organization/dashboard/users/GetOrganizationAdminsEndpoint.ts +12 -12
  116. package/src/endpoints/organization/dashboard/webshops/CreateWebshopEndpoint.ts +103 -100
  117. package/src/endpoints/organization/dashboard/webshops/DeleteWebshopEndpoint.ts +11 -12
  118. package/src/endpoints/organization/dashboard/webshops/GetDiscountCodesEndpoint.ts +15 -15
  119. package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersEndpoint.ts +14 -14
  120. package/src/endpoints/organization/dashboard/webshops/GetWebshopTicketsEndpoint.ts +14 -14
  121. package/src/endpoints/organization/dashboard/webshops/GetWebshopUriAvailabilityEndpoint.ts +23 -23
  122. package/src/endpoints/organization/dashboard/webshops/PatchDiscountCodesEndpoint.ts +54 -52
  123. package/src/endpoints/organization/dashboard/webshops/PatchWebshopEndpoint.ts +84 -81
  124. package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +120 -111
  125. package/src/endpoints/organization/dashboard/webshops/PatchWebshopTicketsEndpoint.ts +24 -24
  126. package/src/endpoints/organization/dashboard/webshops/VerifyWebshopDomainEndpoint.ts +18 -18
  127. package/src/endpoints/organization/shared/ExchangePaymentEndpoint.ts +141 -130
  128. package/src/endpoints/organization/shared/GetDocumentHtml.ts +25 -25
  129. package/src/endpoints/organization/shared/GetPaymentEndpoint.ts +18 -18
  130. package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.test.ts +36 -37
  131. package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.ts +9 -9
  132. package/src/endpoints/organization/shared/auth/OpenIDConnectCallbackEndpoint.ts +11 -11
  133. package/src/endpoints/organization/shared/auth/OpenIDConnectStartEndpoint.ts +28 -27
  134. package/src/endpoints/organization/webshops/CheckWebshopDiscountCodesEndpoint.ts +20 -20
  135. package/src/endpoints/organization/webshops/GetOrderByPaymentEndpoint.ts +22 -22
  136. package/src/endpoints/organization/webshops/GetOrderEndpoint.ts +14 -14
  137. package/src/endpoints/organization/webshops/GetTicketsEndpoint.ts +57 -56
  138. package/src/endpoints/organization/webshops/GetWebshopEndpoint.test.ts +65 -66
  139. package/src/endpoints/organization/webshops/GetWebshopEndpoint.ts +18 -17
  140. package/src/endpoints/organization/webshops/PlaceOrderEndpoint.test.ts +124 -128
  141. package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +154 -145
  142. package/src/excel-loaders/members.ts +102 -103
  143. package/src/excel-loaders/payments.ts +155 -156
  144. package/src/helpers/AddressValidator.test.ts +32 -32
  145. package/src/helpers/AddressValidator.ts +128 -122
  146. package/src/helpers/AdminPermissionChecker.ts +339 -236
  147. package/src/helpers/AuthenticatedStructures.ts +233 -134
  148. package/src/helpers/BuckarooHelper.ts +134 -134
  149. package/src/helpers/CheckSettlements.ts +94 -88
  150. package/src/helpers/Context.ts +87 -86
  151. package/src/helpers/CookieHelper.ts +23 -22
  152. package/src/helpers/EmailResumer.ts +10 -10
  153. package/src/helpers/FileCache.ts +62 -62
  154. package/src/helpers/ForwardHandler.test.ts +122 -124
  155. package/src/helpers/ForwardHandler.ts +76 -70
  156. package/src/helpers/MemberUserSyncer.ts +101 -96
  157. package/src/helpers/MembershipCharger.ts +69 -69
  158. package/src/helpers/MembershipHelper.ts +11 -12
  159. package/src/helpers/OpenIDConnectHelper.ts +85 -82
  160. package/src/helpers/PeriodHelper.ts +65 -70
  161. package/src/helpers/StripeHelper.ts +146 -137
  162. package/src/helpers/StripePayoutChecker.ts +51 -52
  163. package/src/helpers/ViesHelper.ts +46 -44
  164. package/src/helpers/fetchToAsyncIterator.ts +14 -14
  165. package/src/helpers/xlsxAddressTransformerColumnFactory.ts +50 -52
  166. package/src/middleware/ContextMiddleware.ts +5 -5
  167. package/src/migrations/1646578856-validate-addresses.ts +6 -9
  168. package/src/seeds/0000000000-example.ts +3 -5
  169. package/src/seeds/1715028563-user-permissions.ts +16 -18
  170. package/src/seeds/1722256498-group-update-occupancy.ts +12 -12
  171. package/src/seeds/1722344162-sync-member-users.ts +14 -15
  172. package/src/seeds/1722344162-update-membership.ts +6 -6
  173. package/src/seeds/1726055544-balance-item-paid.ts +4 -4
  174. package/src/seeds/1726055545-balance-item-pending.ts +4 -4
  175. package/src/seeds/1726494419-update-cached-outstanding-balance.ts +16 -16
  176. package/src/seeds/1726494420-update-cached-outstanding-balance-from-items.ts +12 -12
  177. package/src/seeds/1726572303-schedule-stock-updates.ts +12 -12
  178. package/src/seeds/1726847064-setup-steps.ts +16 -0
  179. package/src/sql-filters/balance-item-payments.ts +7 -7
  180. package/src/sql-filters/events.ts +14 -14
  181. package/src/sql-filters/members.ts +96 -96
  182. package/src/sql-filters/organizations.ts +139 -75
  183. package/src/sql-filters/payments.ts +28 -28
  184. package/src/sql-filters/registrations.ts +14 -14
  185. package/src/sql-sorters/events.ts +25 -25
  186. package/src/sql-sorters/members.ts +26 -26
  187. package/src/sql-sorters/organizations.ts +36 -36
  188. package/src/sql-sorters/payments.ts +26 -26
  189. package/tests/e2e/stock.test.ts +616 -621
  190. package/tests/e2e/tickets.test.ts +255 -260
  191. package/tests/helpers/StripeMocker.ts +177 -179
  192. package/tests/helpers/TestServer.ts +9 -9
  193. package/tests/jest.global.setup.ts +14 -13
  194. package/tests/jest.setup.ts +33 -32
  195. package/.eslintrc.js +0 -61
  196. package/jest.config.js +0 -11
  197. package/src/helpers/SetupStepsUpdater.ts +0 -359
  198. package/src/seeds/1724076679-setup-steps.ts +0 -16
@@ -1,12 +1,12 @@
1
1
  import { createMollieClient, PaymentMethod as molliePaymentMethod } from '@mollie/api-client';
2
2
  import { Decoder } from '@simonbackx/simple-encoding';
3
- import { DecodedRequest, Endpoint, Request, Response } from "@simonbackx/simple-endpoints";
3
+ import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
4
4
  import { SimpleError } from '@simonbackx/simple-errors';
5
5
  import { I18n } from '@stamhoofd/backend-i18n';
6
6
  import { Email } from '@stamhoofd/email';
7
7
  import { BalanceItem, BalanceItemPayment, MolliePayment, MollieToken, Order, PayconiqPayment, Payment, RateLimiter, Webshop, WebshopDiscountCode } from '@stamhoofd/models';
8
8
  import { QueueHandler } from '@stamhoofd/queues';
9
- import { BalanceItemStatus, Order as OrderStruct, OrderData, OrderResponse, Payment as PaymentStruct, PaymentMethod, PaymentMethodHelper, PaymentProvider, PaymentStatus, Version, Webshop as WebshopStruct,WebshopAuthType, BalanceItemType, BalanceItemRelationType, BalanceItemRelation } from "@stamhoofd/structures";
9
+ import { BalanceItemStatus, Order as OrderStruct, OrderData, OrderResponse, Payment as PaymentStruct, PaymentMethod, PaymentMethodHelper, PaymentProvider, PaymentStatus, Version, Webshop as WebshopStruct, WebshopAuthType, BalanceItemType, BalanceItemRelationType, BalanceItemRelation } from '@stamhoofd/structures';
10
10
  import { Formatter } from '@stamhoofd/utility';
11
11
 
12
12
  import { BuckarooHelper } from '../../../helpers/BuckarooHelper';
@@ -15,36 +15,36 @@ import { StripeHelper } from '../../../helpers/StripeHelper';
15
15
 
16
16
  type Params = { id: string };
17
17
  type Query = undefined;
18
- type Body = OrderData
19
- type ResponseBody = OrderResponse
18
+ type Body = OrderData;
19
+ type ResponseBody = OrderResponse;
20
20
 
21
21
  export const demoOrderLimiter = new RateLimiter({
22
22
  limits: [
23
- {
23
+ {
24
24
  // Max 10 per hour
25
25
  limit: STAMHOOFD.environment === 'development' ? 100 : 10,
26
- duration: 60 * 1000 * 60
26
+ duration: 60 * 1000 * 60,
27
27
  },
28
- {
28
+ {
29
29
  // Max 20 a day
30
30
  limit: STAMHOOFD.environment === 'development' ? 1000 : 20,
31
- duration: 24 * 60 * 1000 * 60
32
- }
33
- ]
31
+ duration: 24 * 60 * 1000 * 60,
32
+ },
33
+ ],
34
34
  });
35
35
 
36
36
  /**
37
37
  * Allow to add, patch and delete multiple members simultaneously, which is needed in order to sync relational data that is saved encrypted in multiple members (e.g. parents)
38
38
  */
39
39
  export class PlaceOrderEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
40
- bodyDecoder = OrderData as Decoder<OrderData>
40
+ bodyDecoder = OrderData as Decoder<OrderData>;
41
41
 
42
42
  protected doesMatch(request: Request): [true, Params] | [false] {
43
- if (request.method != "POST") {
43
+ if (request.method !== 'POST') {
44
44
  return [false];
45
45
  }
46
46
 
47
- const params = Endpoint.parseParameters(request.url, "/webshop/@id/order", { id: String });
47
+ const params = Endpoint.parseParameters(request.url, '/webshop/@id/order', { id: String });
48
48
 
49
49
  if (params) {
50
50
  return [true, params as Params];
@@ -54,188 +54,192 @@ export class PlaceOrderEndpoint extends Endpoint<Params, Query, Body, ResponseBo
54
54
 
55
55
  async handle(request: DecodedRequest<Params, Query, Body>) {
56
56
  const organization = await Context.setOrganizationScope();
57
- await Context.optionalAuthenticate()
57
+ await Context.optionalAuthenticate();
58
58
 
59
59
  // Read + validate + update stock in one go, to prevent race conditions
60
- const { webshop, order } = await QueueHandler.schedule("webshop-stock/"+request.params.id, async () => {
61
- const webshopWithoutOrganization = await Webshop.getByID(request.params.id)
60
+ const { webshop, order } = await QueueHandler.schedule('webshop-stock/' + request.params.id, async () => {
61
+ const webshopWithoutOrganization = await Webshop.getByID(request.params.id);
62
62
  if (!webshopWithoutOrganization || webshopWithoutOrganization.organizationId !== organization.id) {
63
63
  throw new SimpleError({
64
- code: "not_found",
65
- message: "Webshop not found",
66
- human: "Deze webshop bestaat niet (meer)"
67
- })
64
+ code: 'not_found',
65
+ message: 'Webshop not found',
66
+ human: 'Deze webshop bestaat niet (meer)',
67
+ });
68
68
  }
69
69
 
70
- //const organization = (await Organization.getByID(webshopWithoutOrganization.organizationId))!
71
- const webshop = webshopWithoutOrganization.setRelation(Webshop.organization, organization)
70
+ // const organization = (await Organization.getByID(webshopWithoutOrganization.organizationId))!
71
+ const webshop = webshopWithoutOrganization.setRelation(Webshop.organization, organization);
72
72
 
73
73
  if (webshop.meta.authType === WebshopAuthType.Required && !Context.user) {
74
74
  throw new SimpleError({
75
- code: "not_authenticated",
76
- message: "Not authenticated",
77
- human: "Je moet inloggen om een bestelling te kunnen plaatsen.",
78
- statusCode: 401
79
- })
75
+ code: 'not_authenticated',
76
+ message: 'Not authenticated',
77
+ human: 'Je moet inloggen om een bestelling te kunnen plaatsen.',
78
+ statusCode: 401,
79
+ });
80
80
  }
81
81
 
82
82
  // For non paid organizations, the limit is 10
83
83
  if (!organization.meta.packages.isPaid && STAMHOOFD.environment !== 'test') {
84
- const limiter = demoOrderLimiter
84
+ const limiter = demoOrderLimiter;
85
85
 
86
86
  try {
87
87
  limiter.track(organization.id, 1);
88
- } catch (e) {
88
+ }
89
+ catch (e) {
89
90
  Email.sendWebmaster({
90
- subject: "[Limiet] Limiet bereikt voor aantal bestellingen",
91
- text: "Beste, \nDe limiet werd bereikt voor het aantal bestellingen per dag. \nVereniging: "+organization.id+" ("+organization.name+")" + "\n\n" + e.message + "\n\nStamhoofd"
92
- })
93
-
91
+ subject: '[Limiet] Limiet bereikt voor aantal bestellingen',
92
+ text: 'Beste, \nDe limiet werd bereikt voor het aantal bestellingen per dag. \nVereniging: ' + organization.id + ' (' + organization.name + ')' + '\n\n' + e.message + '\n\nStamhoofd',
93
+ });
94
+
94
95
  throw new SimpleError({
95
- code: "too_many_emails_period",
96
- message: "Too many e-mails limited",
97
- human: "Oeps! Om spam te voorkomen limiteren we het aantal test bestellingen die je per uur of dag kan plaatsen. Probeer over een uur opnieuw of schakel over naar een betaald abonnement.",
98
- field: "recipients"
99
- })
96
+ code: 'too_many_emails_period',
97
+ message: 'Too many e-mails limited',
98
+ human: 'Oeps! Om spam te voorkomen limiteren we het aantal test bestellingen die je per uur of dag kan plaatsen. Probeer over een uur opnieuw of schakel over naar een betaald abonnement.',
99
+ field: 'recipients',
100
+ });
100
101
  }
101
102
  }
102
103
 
103
- const webshopStruct = WebshopStruct.create(webshop)
104
+ const webshopStruct = WebshopStruct.create(webshop);
104
105
 
105
- const usedCodes = request.body.discountCodes.map(c => c.code)
106
+ const usedCodes = request.body.discountCodes.map(c => c.code);
106
107
  const uniqueCodes = Formatter.uniqueArray(usedCodes);
107
108
  if (uniqueCodes.length !== usedCodes.length) {
108
109
  // Duplicate code usage is not allowed
109
110
  throw new SimpleError({
110
- code: "duplicate_codes",
111
- message: "Duplicate usage of discount codes",
112
- human: "Sommige kortingcodes werden dubbel toegepast op jouw bestelling. Kijk het even na, dit is niet toegestaan.",
113
- field: "cart.discountCodes"
114
- })
111
+ code: 'duplicate_codes',
112
+ message: 'Duplicate usage of discount codes',
113
+ human: 'Sommige kortingcodes werden dubbel toegepast op jouw bestelling. Kijk het even na, dit is niet toegestaan.',
114
+ field: 'cart.discountCodes',
115
+ });
115
116
  }
116
117
  if (uniqueCodes.length > 0) {
117
118
  // Fetch new and update them
118
- const codeModels = await WebshopDiscountCode.getActiveCodes(webshop.id, uniqueCodes)
119
+ const codeModels = await WebshopDiscountCode.getActiveCodes(webshop.id, uniqueCodes);
119
120
 
120
121
  if (codeModels.length !== uniqueCodes.length) {
121
122
  throw new SimpleError({
122
- code: "invalid_code",
123
- message: "Invalid discount code",
124
- human: "De kortingscode die je hebt toegevoegd is niet (meer) geldig",
125
- field: "cart.discountCodes"
126
- })
123
+ code: 'invalid_code',
124
+ message: 'Invalid discount code',
125
+ human: 'De kortingscode die je hebt toegevoegd is niet (meer) geldig',
126
+ field: 'cart.discountCodes',
127
+ });
127
128
  }
128
- request.body.discountCodes = codeModels.map(c => c.getStructure())
129
+ request.body.discountCodes = codeModels.map(c => c.getStructure());
129
130
  }
130
131
 
131
- request.body.validate(webshopStruct, organization.meta, request.i18n, false, Context.user?.getStructure())
132
- request.body.update(webshopStruct)
132
+ request.body.validate(webshopStruct, organization.meta, request.i18n, false, Context.user?.getStructure());
133
+ request.body.update(webshopStruct);
133
134
 
134
- const order = new Order().setRelation(Order.webshop, webshop)
135
- order.data = request.body // TODO: validate
136
- order.organizationId = organization.id
137
- order.createdAt = new Date()
138
- order.createdAt.setMilliseconds(0)
139
- order.userId = Context.user?.id ?? null
135
+ const order = new Order().setRelation(Order.webshop, webshop);
136
+ order.data = request.body; // TODO: validate
137
+ order.organizationId = organization.id;
138
+ order.createdAt = new Date();
139
+ order.createdAt.setMilliseconds(0);
140
+ order.userId = Context.user?.id ?? null;
140
141
 
141
142
  // Always reserve the stock
142
- await order.updateStock()
143
- return { webshop, order, organization }
144
- })
143
+ await order.updateStock();
144
+ return { webshop, order, organization };
145
+ });
145
146
 
146
147
  // The order now is valid, the stock is reserved for now (until the payment fails or expires)
147
- const totalPrice = request.body.totalPrice
148
+ const totalPrice = request.body.totalPrice;
148
149
 
149
150
  try {
150
151
  if (totalPrice == 0) {
151
152
  // Force unknown payment method
152
- order.data.paymentMethod = PaymentMethod.Unknown
153
+ order.data.paymentMethod = PaymentMethod.Unknown;
153
154
 
154
155
  // Mark this order as paid
155
- await order.markPaid(null, organization, webshop)
156
- await order.save()
157
- } else {
158
- const payment = new Payment()
159
- payment.organizationId = organization.id
160
- payment.method = request.body.paymentMethod
161
- payment.status = PaymentStatus.Created
162
- payment.price = totalPrice
163
- payment.paidAt = null
156
+ await order.markPaid(null, organization, webshop);
157
+ await order.save();
158
+ }
159
+ else {
160
+ const payment = new Payment();
161
+ payment.organizationId = organization.id;
162
+ payment.method = request.body.paymentMethod;
163
+ payment.status = PaymentStatus.Created;
164
+ payment.price = totalPrice;
165
+ payment.paidAt = null;
164
166
 
165
167
  // Determine the payment provider
166
168
  // Throws if invalid
167
- const {provider, stripeAccount} = await organization.getPaymentProviderFor(payment.method, webshop.privateMeta.paymentConfiguration)
168
- payment.provider = provider
169
- payment.stripeAccountId = stripeAccount?.id ?? null
169
+ const { provider, stripeAccount } = await organization.getPaymentProviderFor(payment.method, webshop.privateMeta.paymentConfiguration);
170
+ payment.provider = provider;
171
+ payment.stripeAccountId = stripeAccount?.id ?? null;
170
172
 
171
- await payment.save()
173
+ await payment.save();
172
174
 
173
175
  // Deprecated field
174
- order.paymentId = payment.id
175
- order.setRelation(Order.payment, payment)
176
+ order.paymentId = payment.id;
177
+ order.setRelation(Order.payment, payment);
176
178
 
177
179
  // Save order to get the id
178
- await order.save()
180
+ await order.save();
179
181
 
180
- const balanceItemPayments: (BalanceItemPayment & { balanceItem: BalanceItem })[] = []
182
+ const balanceItemPayments: (BalanceItemPayment & { balanceItem: BalanceItem })[] = [];
181
183
 
182
184
  // Create balance item
183
185
  const balanceItem = new BalanceItem();
184
186
  balanceItem.type = BalanceItemType.Order;
185
187
  balanceItem.orderId = order.id;
186
- balanceItem.unitPrice = totalPrice
187
- balanceItem.description = webshop.meta.name
188
- balanceItem.pricePaid = 0
188
+ balanceItem.unitPrice = totalPrice;
189
+ balanceItem.description = webshop.meta.name;
190
+ balanceItem.pricePaid = 0;
189
191
  balanceItem.organizationId = organization.id;
190
192
  balanceItem.status = BalanceItemStatus.Hidden;
191
193
  balanceItem.relations = new Map([
192
194
  [
193
- BalanceItemRelationType.Webshop,
195
+ BalanceItemRelationType.Webshop,
194
196
  BalanceItemRelation.create({
195
197
  id: webshop.id,
196
198
  name: webshop.meta.name,
197
- })
198
- ]
199
- ])
199
+ }),
200
+ ],
201
+ ]);
200
202
  await balanceItem.save();
201
203
 
202
204
  // Create one balance item payment to pay it in one payment
203
- const balanceItemPayment = new BalanceItemPayment()
205
+ const balanceItemPayment = new BalanceItemPayment();
204
206
  balanceItemPayment.balanceItemId = balanceItem.id;
205
207
  balanceItemPayment.paymentId = payment.id;
206
208
  balanceItemPayment.organizationId = organization.id;
207
209
  balanceItemPayment.price = balanceItem.price;
208
210
  await balanceItemPayment.save();
209
- balanceItemPayments.push(balanceItemPayment.setRelation(BalanceItemPayment.balanceItem, balanceItem))
211
+ balanceItemPayments.push(balanceItemPayment.setRelation(BalanceItemPayment.balanceItem, balanceItem));
210
212
 
211
- let paymentUrl: string | null = null
212
- const description = webshop.meta.name+" - "+payment.id
213
+ let paymentUrl: string | null = null;
214
+ const description = webshop.meta.name + ' - ' + payment.id;
213
215
 
214
216
  if (payment.method == PaymentMethod.Transfer) {
215
- await order.markValid(payment, [])
217
+ await order.markValid(payment, []);
216
218
 
217
219
  if (order.number) {
218
- balanceItem.description = order.generateBalanceDescription(webshop)
220
+ balanceItem.description = order.generateBalanceDescription(webshop);
219
221
  }
220
222
 
221
223
  balanceItem.status = BalanceItemStatus.Pending;
222
- await balanceItem.save()
223
- await payment.save()
224
- } else if (payment.method == PaymentMethod.PointOfSale) {
224
+ await balanceItem.save();
225
+ await payment.save();
226
+ }
227
+ else if (payment.method == PaymentMethod.PointOfSale) {
225
228
  // Not really paid, but needed to create the tickets if needed
226
- await order.markPaid(payment, organization, webshop)
229
+ await order.markPaid(payment, organization, webshop);
227
230
 
228
231
  if (order.number) {
229
- balanceItem.description = order.generateBalanceDescription(webshop)
232
+ balanceItem.description = order.generateBalanceDescription(webshop);
230
233
  }
231
-
234
+
232
235
  balanceItem.status = BalanceItemStatus.Pending;
233
- await balanceItem.save()
234
- await payment.save()
235
- } else {
236
- const cancelUrl = "https://"+webshop.getHost()+'/payment?id='+encodeURIComponent(payment.id)+"&cancel=true"
237
- const redirectUrl = "https://"+webshop.getHost()+'/payment?id='+encodeURIComponent(payment.id)
238
- const exchangeUrl = 'https://'+organization.getApiHost()+"/v"+Version+"/payments/"+encodeURIComponent(payment.id)+"?exchange=true"
236
+ await balanceItem.save();
237
+ await payment.save();
238
+ }
239
+ else {
240
+ const cancelUrl = 'https://' + webshop.getHost() + '/payment?id=' + encodeURIComponent(payment.id) + '&cancel=true';
241
+ const redirectUrl = 'https://' + webshop.getHost() + '/payment?id=' + encodeURIComponent(payment.id);
242
+ const exchangeUrl = 'https://' + organization.getApiHost() + '/v' + Version + '/payments/' + encodeURIComponent(payment.id) + '?exchange=true';
239
243
 
240
244
  if (payment.provider === PaymentProvider.Stripe) {
241
245
  const stripeResult = await StripeHelper.createPayment({
@@ -256,33 +260,34 @@ export class PlaceOrderEndpoint extends Endpoint<Params, Query, Body, ResponseBo
256
260
  customer: {
257
261
  name: order.data.customer.name,
258
262
  email: order.data.customer.email,
259
- }
263
+ },
260
264
  });
261
- paymentUrl = stripeResult.paymentUrl
262
- } else if (payment.provider === PaymentProvider.Mollie) {
265
+ paymentUrl = stripeResult.paymentUrl;
266
+ }
267
+ else if (payment.provider === PaymentProvider.Mollie) {
263
268
  // Mollie payment
264
- const token = await MollieToken.getTokenFor(webshop.organizationId)
269
+ const token = await MollieToken.getTokenFor(webshop.organizationId);
265
270
  if (!token) {
266
- throw new SimpleError({
267
- code: "",
268
- message: "Betaling via " + PaymentMethodHelper.getName(payment.method) + " is onbeschikbaar"
269
- })
271
+ throw new SimpleError({
272
+ code: '',
273
+ message: 'Betaling via ' + PaymentMethodHelper.getName(payment.method) + ' is onbeschikbaar',
274
+ });
270
275
  }
271
- const profileId = organization.privateMeta.mollieProfile?.id ?? await token.getProfileId(webshop.getHost())
276
+ const profileId = organization.privateMeta.mollieProfile?.id ?? await token.getProfileId(webshop.getHost());
272
277
  if (!profileId) {
273
278
  throw new SimpleError({
274
- code: "",
275
- message: "Betaling via " + PaymentMethodHelper.getName(payment.method) + " is tijdelijk onbeschikbaar"
276
- })
279
+ code: '',
280
+ message: 'Betaling via ' + PaymentMethodHelper.getName(payment.method) + ' is tijdelijk onbeschikbaar',
281
+ });
277
282
  }
278
283
  const mollieClient = createMollieClient({ accessToken: await token.getAccessToken() });
279
284
  const molliePayment = await mollieClient.payments.create({
280
285
  amount: {
281
286
  currency: 'EUR',
282
- value: (totalPrice / 100).toFixed(2)
287
+ value: (totalPrice / 100).toFixed(2),
283
288
  },
284
289
  method: payment.method == PaymentMethod.Bancontact ? molliePaymentMethod.bancontact : (payment.method == PaymentMethod.iDEAL ? molliePaymentMethod.ideal : molliePaymentMethod.creditcard),
285
- testmode: organization.privateMeta.useTestPayments ?? STAMHOOFD.environment != 'production',
290
+ testmode: organization.privateMeta.useTestPayments ?? STAMHOOFD.environment !== 'production',
286
291
  profileId,
287
292
  description,
288
293
  redirectUrl,
@@ -291,54 +296,58 @@ export class PlaceOrderEndpoint extends Endpoint<Params, Query, Body, ResponseBo
291
296
  order: order.id,
292
297
  organization: organization.id,
293
298
  webshop: webshop.id,
294
- payment: payment.id
299
+ payment: payment.id,
295
300
  },
296
301
  });
297
- console.log(molliePayment)
298
- paymentUrl = molliePayment.getCheckoutUrl()
302
+ console.log(molliePayment);
303
+ paymentUrl = molliePayment.getCheckoutUrl();
299
304
 
300
305
  // Save payment
301
- const dbPayment = new MolliePayment()
302
- dbPayment.paymentId = payment.id
303
- dbPayment.mollieId = molliePayment.id
306
+ const dbPayment = new MolliePayment();
307
+ dbPayment.paymentId = payment.id;
308
+ dbPayment.mollieId = molliePayment.id;
304
309
  await dbPayment.save();
305
- } else if (payment.provider == PaymentProvider.Payconiq) {
306
- paymentUrl = await PayconiqPayment.createPayment(payment, organization, description, redirectUrl, exchangeUrl)
307
- } else if (payment.provider == PaymentProvider.Buckaroo) {
310
+ }
311
+ else if (payment.provider == PaymentProvider.Payconiq) {
312
+ paymentUrl = await PayconiqPayment.createPayment(payment, organization, description, redirectUrl, exchangeUrl);
313
+ }
314
+ else if (payment.provider == PaymentProvider.Buckaroo) {
308
315
  // Increase request timeout because buckaroo is super slow
309
- request.request.request?.setTimeout(60 * 1000)
310
- const buckaroo = new BuckarooHelper(organization.privateMeta?.buckarooSettings?.key ?? "", organization.privateMeta?.buckarooSettings?.secret ?? "", organization.privateMeta.useTestPayments ?? STAMHOOFD.environment != 'production')
311
- const ip = request.request.getIP()
312
- paymentUrl = await buckaroo.createPayment(payment, ip, description, redirectUrl, exchangeUrl)
313
- await payment.save()
316
+ request.request.request?.setTimeout(60 * 1000);
317
+ const buckaroo = new BuckarooHelper(organization.privateMeta?.buckarooSettings?.key ?? '', organization.privateMeta?.buckarooSettings?.secret ?? '', organization.privateMeta.useTestPayments ?? STAMHOOFD.environment !== 'production');
318
+ const ip = request.request.getIP();
319
+ paymentUrl = await buckaroo.createPayment(payment, ip, description, redirectUrl, exchangeUrl);
320
+ await payment.save();
314
321
 
315
322
  // TypeScript doesn't understand that the status can change and isn't a const....
316
323
  if ((payment.status as any) === PaymentStatus.Failed) {
317
324
  throw new SimpleError({
318
- code: "payment_failed",
319
- message: "Betaling via " + PaymentMethodHelper.getName(payment.method) + " is onbeschikbaar"
320
- })
325
+ code: 'payment_failed',
326
+ message: 'Betaling via ' + PaymentMethodHelper.getName(payment.method) + ' is onbeschikbaar',
327
+ });
321
328
  }
322
- } else {
323
- throw new Error("Unknown payment provider")
329
+ }
330
+ else {
331
+ throw new Error('Unknown payment provider');
324
332
  }
325
333
  }
326
334
 
327
335
  return new Response(OrderResponse.create({
328
336
  paymentUrl: paymentUrl,
329
- order: OrderStruct.create({...order, payment: PaymentStruct.create(payment) })
337
+ order: OrderStruct.create({ ...order, payment: PaymentStruct.create(payment) }),
330
338
  }));
331
339
  }
332
- } catch (e) {
340
+ }
341
+ catch (e) {
333
342
  // Mark order as failed to release stock
334
343
  if (order) {
335
- await order.deleteOrderBecauseOfCreationError()
344
+ await order.deleteOrderBecauseOfCreationError();
336
345
  }
337
346
  throw e;
338
347
  }
339
-
348
+
340
349
  return new Response(OrderResponse.create({
341
- order: order.getStructureWithoutPayment()
350
+ order: order.getStructureWithoutPayment(),
342
351
  }));
343
352
  }
344
353
  }