@stamhoofd/backend 2.144.0 → 2.145.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 (99) hide show
  1. package/package.json +19 -19
  2. package/src/crons/mollie-chargebacks.ts +31 -29
  3. package/src/crons/mollie-refunds.ts +61 -59
  4. package/src/endpoints/admin/organizations/PatchOrganizationsEndpoint.ts +16 -5
  5. package/src/endpoints/auth/CreateTokenEndpoint.test.ts +210 -4
  6. package/src/endpoints/auth/CreateTokenEndpoint.ts +9 -20
  7. package/src/endpoints/auth/DeleteTokenEndpoint.test.ts +29 -0
  8. package/src/endpoints/auth/DeleteTokenEndpoint.ts +2 -1
  9. package/src/endpoints/auth/GetUserEndpoint.test.ts +32 -11
  10. package/src/endpoints/auth/MFA.security.test.ts +9 -7
  11. package/src/endpoints/auth/MFA.test.ts +42 -15
  12. package/src/endpoints/auth/PatchUserEndpoint.ts +3 -2
  13. package/src/endpoints/auth/VerifyEmailEndpoint.ts +5 -4
  14. package/src/endpoints/global/email/GetAdminEmailsEndpoint.test.ts +4 -3
  15. package/src/endpoints/global/email/GetUserEmailsEndpoint.test.ts +6 -5
  16. package/src/endpoints/global/email/PatchEmailEndpoint.test.ts +5 -4
  17. package/src/endpoints/global/email-recipients/GetEmailRecipientsEndpoint.test.ts +5 -4
  18. package/src/endpoints/global/events/PatchEventNotificationsEndpoint.test.ts +2 -1
  19. package/src/endpoints/global/events/PatchEventsEndpoint.test.ts +2 -1
  20. package/src/endpoints/global/files/UploadFile.test.ts +2 -1
  21. package/src/endpoints/global/files/UploadImage.test.ts +2 -1
  22. package/src/endpoints/global/files/upload-security.test.ts +3 -2
  23. package/src/endpoints/global/groups/GetGroupsEndpoint.test.ts +4 -3
  24. package/src/endpoints/global/members/GetMembersEndpoint.test.ts +26 -25
  25. package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.test.ts +57 -56
  26. package/src/endpoints/global/members/SendMemberSecurityCodeEndpoint.test.ts +4 -3
  27. package/src/endpoints/global/platform/GetPlatformEndpoint.test.ts +6 -4
  28. package/src/endpoints/global/platform/PatchPlatformEnpoint.test.ts +6 -6
  29. package/src/endpoints/global/platform/SignOutPlatformAdminsEndpoint.test.ts +9 -8
  30. package/src/endpoints/global/registration/GetRegistrationsEndpoint.test.ts +14 -13
  31. package/src/endpoints/global/registration/PatchUserMembersEndpoint.test.ts +24 -23
  32. package/src/endpoints/global/registration/RegisterMembersEndpoint.test.ts +4 -3
  33. package/src/endpoints/global/registration-invitations/GetRegistrationInvitationsEndpoint.test.ts +2 -1
  34. package/src/endpoints/global/registration-invitations/PatchRegistrationInvitationsEndpoint.test.ts +2 -1
  35. package/src/endpoints/global/webshops/GetWebshopsEndpoint.test.ts +20 -19
  36. package/src/endpoints/organization/dashboard/balance-items/GetBalanceItemBreakdownEndpoint.test.ts +2 -1
  37. package/src/endpoints/organization/dashboard/balance-items/GetBalanceItemsEndpoint.test.ts +2 -1
  38. package/src/endpoints/organization/dashboard/billing/ApplyRegisterCodeEndpoint.test.ts +3 -2
  39. package/src/endpoints/organization/dashboard/documents/PatchDocumentTemplatesEndpoint.test.ts +4 -3
  40. package/src/endpoints/organization/dashboard/email-templates/GetEmailTemplatesEndpoint.test.ts +5 -4
  41. package/src/endpoints/organization/dashboard/email-templates/PatchEmailTemplatesEndpoint.test.ts +5 -4
  42. package/src/endpoints/organization/dashboard/invoices/GetInvoicesEndpoint.test.ts +2 -1
  43. package/src/endpoints/organization/dashboard/invoices/PatchInvoicesEndpoint.test.ts +2 -1
  44. package/src/endpoints/organization/dashboard/invoices/PatchInvoicesEndpoint.ts +0 -1
  45. package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.test.ts +23 -22
  46. package/src/endpoints/organization/dashboard/organization/PatchOrganizationEndpoint.ts +4 -4
  47. package/src/endpoints/organization/dashboard/payments/GetPaymentBreakdownEndpoint.test.ts +2 -1
  48. package/src/endpoints/organization/dashboard/payments/GetPaymentsEndpoint.test.ts +4 -3
  49. package/src/endpoints/organization/dashboard/payments/PatchPaymentsEndpoint.ts +2 -2
  50. package/src/endpoints/organization/dashboard/receivable-balances/GetReceivableBalanceEndpoint.test.ts +2 -1
  51. package/src/endpoints/organization/dashboard/registration-periods/GetOrganizationRegistrationPeriodsEndpoint.test.ts +5 -4
  52. package/src/endpoints/organization/dashboard/registration-periods/MoveRegistrationPeriods.test.ts +7 -6
  53. package/src/endpoints/organization/dashboard/registration-periods/PatchOrganizationRegistrationPeriodsEndpoint.test.ts +9 -8
  54. package/src/endpoints/organization/dashboard/settlements/SettlementsExportEndpoint.test.ts +3 -2
  55. package/src/endpoints/organization/dashboard/settlements/SettlementsSyncEndpoint.test.ts +2 -1
  56. package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.test.ts +5 -4
  57. package/src/endpoints/organization/dashboard/users/CreateApiUserEndpoint.ts +2 -1
  58. package/src/endpoints/organization/dashboard/users/GetOrganizationAdminsEndpoint.test.ts +3 -2
  59. package/src/endpoints/organization/dashboard/users/PatchApiUserEndpoint.test.ts +6 -5
  60. package/src/endpoints/organization/dashboard/users/SignOutOrganizationAdminsEndpoint.test.ts +16 -15
  61. package/src/endpoints/organization/dashboard/webshops/GetWebshopOrdersEndpoint.test.ts +2 -1
  62. package/src/endpoints/organization/dashboard/webshops/PatchWebshopEndpoint.test.ts +2 -1
  63. package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.test.ts +2 -1
  64. package/src/endpoints/organization/dashboard/webshops/WebshopReservedPathSegments.test.ts +2 -1
  65. package/src/endpoints/organization/shared/auth/GetOrganizationEndpoint.test.ts +4 -3
  66. package/src/endpoints/organization/webshops/GetWebshopEndpoint.test.ts +6 -5
  67. package/src/endpoints/organization/webshops/PlaceOrderEndpoint.test.ts +94 -8
  68. package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +18 -18
  69. package/src/helpers/Context.ts +13 -6
  70. package/src/helpers/ContextStorage.ts +5 -0
  71. package/src/helpers/StripeSettlementSync.ts +10 -7
  72. package/src/helpers/TwoFactorHelper.ts +21 -22
  73. package/src/services/AdminSessionService.ts +5 -3
  74. package/src/services/InvoiceService.ts +2 -2
  75. package/src/services/MollieService.ts +168 -126
  76. package/src/services/PaymentService.ts +2 -2
  77. package/src/services/SSOService.test.ts +25 -1
  78. package/src/services/SSOService.ts +17 -6
  79. package/src/services/SessionService.test.ts +319 -0
  80. package/src/services/SessionService.ts +357 -0
  81. package/src/services/WebshopCrowdfundingService.test.ts +4 -3
  82. package/src/sql-filters/registrations.ts +59 -1
  83. package/tests/actions/patchUserMember.ts +2 -1
  84. package/tests/e2e/api-rate-limits.test.ts +2 -1
  85. package/tests/e2e/bundle-discounts.test.ts +4 -3
  86. package/tests/e2e/charge-members.test.ts +6 -5
  87. package/tests/e2e/documents.test.ts +4 -3
  88. package/tests/e2e/private-files.test.ts +9 -8
  89. package/tests/e2e/register.test.ts +4 -3
  90. package/tests/e2e/stock.test.ts +4 -3
  91. package/tests/e2e/tickets.test.ts +4 -3
  92. package/tests/helpers/ExportSlice.ts +2 -1
  93. package/tests/helpers/MollieMocker.ts +85 -23
  94. package/tests/init/initAdmin.ts +2 -1
  95. package/tests/init/initPlatformAdmin.ts +2 -1
  96. package/src/helpers/ViesHelper.test.ts +0 -401
  97. package/src/helpers/ViesHelper.ts +0 -222
  98. package/src/services/PeppolDirectoryService.test.ts +0 -139
  99. package/src/services/PeppolDirectoryService.ts +0 -111
@@ -1,6 +1,6 @@
1
1
  import { Request } from '@simonbackx/simple-endpoints';
2
- import type { MemberWithUsersRegistrationsAndGroups, Organization, RegistrationPeriod } from '@stamhoofd/models';
3
- import { BalanceItemFactory, GroupFactory, MemberFactory, OrganizationFactory, OrganizationRegistrationPeriod, Platform, RegistrationPeriodFactory, Token, UserFactory } from '@stamhoofd/models';
2
+ import type { MemberWithUsersRegistrationsAndGroups, Organization, RegistrationPeriod, Token } from '@stamhoofd/models';
3
+ import { BalanceItemFactory, GroupFactory, MemberFactory, OrganizationFactory, OrganizationRegistrationPeriod, Platform, RegistrationPeriodFactory, UserFactory } from '@stamhoofd/models';
4
4
  import { AdministrationFeeSettings, BalanceItemCartItem, BalanceItemRelation, BalanceItemRelationType, BalanceItemStatus, BalanceItemType, BooleanStatus, DefaultAgeGroup, FreeContributionSettings, GroupOption, GroupOptionMenu, IDRegisterCart, IDRegisterCheckout, IDRegisterItem, PaymentMethod, PermissionLevel, Permissions, PlatformMembershipType, PlatformMembershipTypeConfig, ReceivableBalanceType, ReduceablePrice, RegisterItemOption, TranslatedString, Version } from '@stamhoofd/structures';
5
5
  import { TestUtils } from '@stamhoofd/test-utils';
6
6
  import { v4 as uuidv4 } from 'uuid';
@@ -11,6 +11,7 @@ import { BalanceItemService } from '../../src/services/BalanceItemService.js';
11
11
  import { PlatformMembershipService } from '../../src/services/PlatformMembershipService.js';
12
12
  import { assertBalances } from '../assertions/assertBalances.js';
13
13
  import { testServer } from '../helpers/TestServer.js';
14
+ import { SessionService } from '../../src/services/SessionService.js';
14
15
 
15
16
  describe('E2E.Register', () => {
16
17
  const registerEndpoint = new RegisterMembersEndpoint();
@@ -74,7 +75,7 @@ describe('E2E.Register', () => {
74
75
  })
75
76
  .create();
76
77
 
77
- const token = await Token.createToken(user);
78
+ const token = await SessionService.createSession(user);
78
79
 
79
80
  const member = await new MemberFactory({ organization, user }).create();
80
81
 
@@ -1,11 +1,12 @@
1
1
  import type { PatchableArrayAutoEncoder } from '@simonbackx/simple-encoding';
2
2
  import { PatchableArray } from '@simonbackx/simple-encoding';
3
3
  import { Request } from '@simonbackx/simple-endpoints';
4
- import type { Organization, StripeAccount } from '@stamhoofd/models';
5
- import { Order, OrganizationFactory, Token, UserFactory, Webshop, WebshopFactory } from '@stamhoofd/models';
4
+ import type { Organization, StripeAccount, Token } from '@stamhoofd/models';
5
+ import { Order, OrganizationFactory, UserFactory, Webshop, WebshopFactory } from '@stamhoofd/models';
6
6
  import { Address, Cart, CartItem, CartItemOption, CartReservedSeat, Customer, Option, OptionMenu, OrderData, OrderStatus, PaymentConfiguration, PaymentMethod, PermissionLevel, Permissions, PrivateOrder, PrivatePaymentConfiguration, Product, ProductPrice, ProductType, ReservedSeat, SeatingPlan, SeatingPlanRow, SeatingPlanSeat, SeatingPlanSection, TransferSettings, ValidatedAddress, WebshopDeliveryMethod, WebshopMetaData, WebshopOnSiteMethod, WebshopPrivateMetaData, WebshopTakeoutMethod, WebshopTimeSlot } from '@stamhoofd/structures';
7
7
  import { Country } from '@stamhoofd/types/Country';
8
8
  import { v4 as uuidv4 } from 'uuid';
9
+ import { SessionService } from '../../src/services/SessionService.js';
9
10
 
10
11
  import { PatchWebshopOrdersEndpoint } from '../../src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.js';
11
12
  import { PlaceOrderEndpoint } from '../../src/endpoints/organization/webshops/PlaceOrderEndpoint.js';
@@ -253,7 +254,7 @@ describe('E2E.Stock', () => {
253
254
  level: PermissionLevel.Full,
254
255
  }),
255
256
  }).create();
256
- token = await Token.createToken(user);
257
+ token = await SessionService.createSession(user);
257
258
 
258
259
  await initMembershipOrganization();
259
260
  });
@@ -1,9 +1,10 @@
1
1
  import type { PatchableArrayAutoEncoder } from '@simonbackx/simple-encoding';
2
2
  import { PatchableArray } from '@simonbackx/simple-encoding';
3
3
  import { Request } from '@simonbackx/simple-endpoints';
4
- import type { Organization, StripeAccount, Webshop } from '@stamhoofd/models';
5
- import { OrganizationFactory, Ticket, Token, UserFactory, WebshopFactory } from '@stamhoofd/models';
4
+ import type { Organization, StripeAccount, Webshop, Token } from '@stamhoofd/models';
5
+ import { OrganizationFactory, Ticket, UserFactory, WebshopFactory } from '@stamhoofd/models';
6
6
  import { Cart, CartItem, CartReservedSeat, Customer, OrderData, OrderStatus, PaymentConfiguration, PaymentMethod, PermissionLevel, Permissions, PrivateOrder, PrivatePaymentConfiguration, Product, ProductType, SeatingPlan, SeatingPlanRow, SeatingPlanSeat, SeatingPlanSection, TransferSettings, WebshopMetaData, WebshopPrivateMetaData, WebshopTicketType } from '@stamhoofd/structures';
7
+ import { SessionService } from '../../src/services/SessionService.js';
7
8
 
8
9
  import { PatchWebshopOrdersEndpoint } from '../../src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.js';
9
10
  import { PlaceOrderEndpoint } from '../../src/endpoints/organization/webshops/PlaceOrderEndpoint.js';
@@ -66,7 +67,7 @@ describe('E2E.Tickets', () => {
66
67
  level: PermissionLevel.Full,
67
68
  }),
68
69
  }).create();
69
- token = await Token.createToken(user);
70
+ token = await SessionService.createSession(user);
70
71
  });
71
72
 
72
73
  afterAll(() => {
@@ -6,6 +6,7 @@ import { ExcelExportType, LimitedFilteredRequest, SortItemDirection } from '@sta
6
6
  import { ExportToExcelEndpoint } from '../../src/endpoints/global/files/ExportToExcelEndpoint.js';
7
7
  import { Context } from '../../src/helpers/Context.js';
8
8
  import { testServer } from './TestServer.js';
9
+ import { SessionService } from '../../src/services/SessionService.js';
9
10
 
10
11
  // Registers the balance item payment export, which is what a row that holds a part of its objects is
11
12
  // written out with
@@ -56,7 +57,7 @@ class ExportSliceEndpoint extends Endpoint<Record<string, never>, undefined, und
56
57
  * How many balance item payments a filter selects, and what they are worth together.
57
58
  */
58
59
  export async function exportSlice({ organization, user, filter, search = null }: { organization: Organization; user: User; filter: StamhoofdFilter; search?: string | null }) {
59
- const token = await Token.createToken(user);
60
+ const token = await SessionService.createSession(user);
60
61
 
61
62
  const request = Request.get({
62
63
  path: '/exports/balance-item-payments',
@@ -25,6 +25,9 @@ export type MollieMockPayment = {
25
25
  mandateId: string | null;
26
26
  isCancelable: boolean;
27
27
  details: Record<string, unknown> | null;
28
+ description?: string;
29
+ profileId?: string | null;
30
+ createdAt?: string;
28
31
  };
29
32
 
30
33
  export type MollieMockMandate = {
@@ -83,9 +86,7 @@ const MOLLIE_CHECKOUT_URL = 'https://molliecheckout/';
83
86
  /**
84
87
  * Mirrors the StripeMocker (tests/helpers/StripeMocker.ts) but for Mollie.
85
88
  *
86
- * The Mollie node client (@mollie/api-client v4) performs its HTTP requests through node-fetch v2,
87
- * which uses Node's http/https module under the hood. nock can therefore intercept every call to
88
- * https://api.mollie.com - exactly like the StripeMocker does for Stripe.
89
+ * The Mollie client sends every API call to https://api.mollie.com, which nock intercepts here.
89
90
  *
90
91
  * Usage:
91
92
  * const mollieMocker = new MollieMocker();
@@ -117,6 +118,23 @@ export class MollieMocker {
117
118
 
118
119
  #forceFailure = false;
119
120
 
121
+ // The SDK validates responses with zod: these links are required on most resources
122
+ #dashboardLink = { href: 'https://www.mollie.com/dashboard', type: 'text/html' };
123
+ #documentationLink = { href: 'https://docs.mollie.com', type: 'text/html' };
124
+
125
+ /**
126
+ * A Mollie error body, complete enough for the SDK to parse it as an ErrorResponse.
127
+ */
128
+ #errorBody(status: number, title: string, detail: string, field?: string) {
129
+ return {
130
+ status,
131
+ title,
132
+ detail,
133
+ field,
134
+ _links: { documentation: this.#documentationLink },
135
+ };
136
+ }
137
+
120
138
  reset() {
121
139
  this.payments = [];
122
140
  this.customers = [];
@@ -168,15 +186,18 @@ export class MollieMocker {
168
186
  const interceptor = nock('https://api.mollie.com').persist();
169
187
 
170
188
  interceptor.get(matcher).reply((uri) => {
171
- return this.#handle('GET', uri, undefined);
189
+ const [status, body] = this.#handle('GET', uri, undefined);
190
+ return [status, body, { 'content-type': 'application/hal+json' }];
172
191
  });
173
192
 
174
193
  interceptor.post(matcher).reply((uri, body) => {
175
- return this.#handle('POST', uri, body);
194
+ const [status, responseBody] = this.#handle('POST', uri, body);
195
+ return [status, responseBody, { 'content-type': 'application/hal+json' }];
176
196
  });
177
197
 
178
198
  interceptor.delete(matcher).reply((uri, body) => {
179
- return this.#handle('DELETE', uri, body);
199
+ const [status, responseBody] = this.#handle('DELETE', uri, body);
200
+ return [status, responseBody, { 'content-type': 'application/hal+json' }];
180
201
  });
181
202
  }
182
203
 
@@ -244,6 +265,10 @@ export class MollieMocker {
244
265
  return [200, this.#onboarding()];
245
266
  }
246
267
 
268
+ if (parts[0] === 'onboarding' && method === 'POST') {
269
+ return [204];
270
+ }
271
+
247
272
  if (parts[0] === 'chargebacks' && method === 'GET') {
248
273
  return this.#listResource('chargebacks', this.chargebacks.map(c => this.#chargebackResource(c)));
249
274
  }
@@ -294,6 +319,9 @@ export class MollieMocker {
294
319
  mandateId: body.mandateId ?? null,
295
320
  isCancelable: true,
296
321
  details: null,
322
+ description: body.description ?? '',
323
+ profileId: body.profileId ?? null,
324
+ createdAt: new Date().toISOString(),
297
325
  };
298
326
  this.payments.push(payment);
299
327
  return [201, this.#paymentResource(payment)];
@@ -302,7 +330,7 @@ export class MollieMocker {
302
330
  #getPayment(id: string): [number, unknown] {
303
331
  const payment = this.payments.find(p => p.id === id);
304
332
  if (!payment) {
305
- return [404, { detail: 'Payment not found' }];
333
+ return [404, this.#errorBody(404, 'Not Found', 'No payment exists with token ' + id)];
306
334
  }
307
335
  return [200, this.#paymentResource(payment)];
308
336
  }
@@ -310,7 +338,7 @@ export class MollieMocker {
310
338
  #cancelPayment(id: string): [number, unknown] {
311
339
  const payment = this.payments.find(p => p.id === id);
312
340
  if (!payment) {
313
- return [404, { detail: 'Payment not found' }];
341
+ return [404, this.#errorBody(404, 'Not Found', 'No payment exists with token ' + id)];
314
342
  }
315
343
  payment.status = 'canceled';
316
344
  return [200, this.#paymentResource(payment)];
@@ -329,9 +357,14 @@ export class MollieMocker {
329
357
  mandateId: payment.mandateId ?? undefined,
330
358
  details: payment.details ?? undefined,
331
359
  metadata: payment.internalPaymentId ? { paymentId: payment.internalPaymentId } : undefined,
360
+ description: payment.description ?? '',
361
+ profileId: payment.profileId ?? 'pfl_test',
362
+ createdAt: payment.createdAt ?? new Date().toISOString(),
332
363
  _links: {
333
364
  self: { href: 'https://api.mollie.com/v2/payments/' + payment.id, type: 'application/hal+json' },
334
365
  checkout: { href: MOLLIE_CHECKOUT_URL, type: 'text/html' },
366
+ dashboard: this.#dashboardLink,
367
+ documentation: this.#documentationLink,
335
368
  },
336
369
  };
337
370
  }
@@ -358,9 +391,15 @@ export class MollieMocker {
358
391
  id,
359
392
  mode: 'test',
360
393
  name: body.name ?? 'Test',
361
- email: body.email ?? undefined,
362
- metadata: body.metadata ?? undefined,
363
- _links: { self: { href: 'https://api.mollie.com/v2/customers/' + id, type: 'application/hal+json' } },
394
+ email: body.email ?? null,
395
+ locale: null,
396
+ metadata: body.metadata ?? null,
397
+ createdAt: new Date().toISOString(),
398
+ _links: {
399
+ self: { href: 'https://api.mollie.com/v2/customers/' + id, type: 'application/hal+json' },
400
+ dashboard: this.#dashboardLink,
401
+ documentation: this.#documentationLink,
402
+ },
364
403
  };
365
404
  }
366
405
 
@@ -406,8 +445,14 @@ export class MollieMocker {
406
445
  method: mandate.method,
407
446
  details: mandate.details,
408
447
  customerId: mandate.customerId,
448
+ mandateReference: null,
449
+ signatureDate: mandate.createdAt.substring(0, 10),
409
450
  createdAt: mandate.createdAt,
410
- _links: { self: { href: 'https://api.mollie.com/v2/customers/' + mandate.customerId + '/mandates/' + mandate.id, type: 'application/hal+json' } },
451
+ _links: {
452
+ self: { href: 'https://api.mollie.com/v2/customers/' + mandate.customerId + '/mandates/' + mandate.id, type: 'application/hal+json' },
453
+ customer: { href: 'https://api.mollie.com/v2/customers/' + mandate.customerId, type: 'application/hal+json' },
454
+ documentation: this.#documentationLink,
455
+ },
411
456
  };
412
457
  }
413
458
 
@@ -421,8 +466,16 @@ export class MollieMocker {
421
466
  mode: 'test',
422
467
  name: 'Stamhoofd',
423
468
  website: 'https://www.stamhoofd.be',
469
+ email: 'hallo@stamhoofd.be',
470
+ phone: '+32499999999',
471
+ businessCategory: 'SOCIAL_ASSOCIATIONS',
424
472
  status: 'verified',
425
- _links: { self: { href: 'https://api.mollie.com/v2/profiles/' + id, type: 'application/hal+json' } },
473
+ createdAt: new Date().toISOString(),
474
+ _links: {
475
+ self: { href: 'https://api.mollie.com/v2/profiles/' + id, type: 'application/hal+json' },
476
+ dashboard: this.#dashboardLink,
477
+ documentation: this.#documentationLink,
478
+ },
426
479
  };
427
480
  }
428
481
 
@@ -433,6 +486,7 @@ export class MollieMocker {
433
486
  status: 'completed',
434
487
  canReceivePayments: true,
435
488
  canReceiveSettlements: true,
489
+ signedUpAt: new Date().toISOString(),
436
490
  _links: { self: { href: 'https://api.mollie.com/v2/onboarding/me', type: 'application/hal+json' } },
437
491
  };
438
492
  }
@@ -461,7 +515,11 @@ export class MollieMocker {
461
515
  amount: chargeback.amount,
462
516
  paymentId: chargeback.paymentId,
463
517
  createdAt: chargeback.createdAt,
464
- _links: { self: { href: 'https://api.mollie.com/v2/payments/' + chargeback.paymentId + '/chargebacks/' + chargeback.id, type: 'application/hal+json' } },
518
+ _links: {
519
+ self: { href: 'https://api.mollie.com/v2/payments/' + chargeback.paymentId + '/chargebacks/' + chargeback.id, type: 'application/hal+json' },
520
+ payment: { href: 'https://api.mollie.com/v2/payments/' + chargeback.paymentId, type: 'application/hal+json' },
521
+ documentation: this.#documentationLink,
522
+ },
465
523
  };
466
524
  }
467
525
 
@@ -484,20 +542,20 @@ export class MollieMocker {
484
542
  #createRefund(paymentId: string, body: Record<string, any>): [number, unknown] {
485
543
  const payment = this.payments.find(p => p.id === paymentId);
486
544
  if (!payment) {
487
- return [404, { status: 404, title: 'Not Found', detail: 'No payment exists with token ' + paymentId }];
545
+ return [404, this.#errorBody(404, 'Not Found', 'No payment exists with token ' + paymentId)];
488
546
  }
489
547
 
490
548
  if (payment.status !== 'paid') {
491
- return [422, { status: 422, title: 'Unprocessable Entity', detail: 'The payment is not paid; you can only refund paid payments' }];
549
+ return [422, this.#errorBody(422, 'Unprocessable Entity', 'The payment is not paid; you can only refund paid payments')];
492
550
  }
493
551
 
494
552
  const value = parseFloat(String(body.amount?.value ?? '0'));
495
553
  if (!(value > 0)) {
496
- return [422, { status: 422, title: 'Unprocessable Entity', detail: 'The amount is invalid', field: 'amount' }];
554
+ return [422, this.#errorBody(422, 'Unprocessable Entity', 'The amount is invalid', 'amount')];
497
555
  }
498
556
 
499
557
  if (value > this.getRemainingAmount(payment) + 0.000001) {
500
- return [422, { status: 422, title: 'Unprocessable Entity', detail: 'The amount is higher than the remaining payment amount', field: 'amount' }];
558
+ return [422, this.#errorBody(422, 'Unprocessable Entity', 'The amount is higher than the remaining payment amount', 'amount')];
501
559
  }
502
560
 
503
561
  const refund: MollieMockRefund = {
@@ -516,7 +574,7 @@ export class MollieMocker {
516
574
  #getRefund(paymentId: string, refundId: string): [number, unknown] {
517
575
  const refund = this.refunds.find(r => r.id === refundId && r.paymentId === paymentId);
518
576
  if (!refund) {
519
- return [404, { status: 404, title: 'Not Found', detail: 'No refund exists with token ' + refundId }];
577
+ return [404, this.#errorBody(404, 'Not Found', 'No refund exists with token ' + refundId)];
520
578
  }
521
579
  return [200, this.#refundResource(refund)];
522
580
  }
@@ -574,8 +632,12 @@ export class MollieMocker {
574
632
  description: refund.description,
575
633
  paymentId: refund.paymentId,
576
634
  createdAt: refund.createdAt,
577
- metadata: refund.metadata ?? undefined,
578
- _links: { self: { href: 'https://api.mollie.com/v2/payments/' + refund.paymentId + '/refunds/' + refund.id, type: 'application/hal+json' } },
635
+ metadata: refund.metadata ?? null,
636
+ _links: {
637
+ self: { href: 'https://api.mollie.com/v2/payments/' + refund.paymentId + '/refunds/' + refund.id, type: 'application/hal+json' },
638
+ payment: { href: 'https://api.mollie.com/v2/payments/' + refund.paymentId, type: 'application/hal+json' },
639
+ documentation: this.#documentationLink,
640
+ },
579
641
  };
580
642
  }
581
643
 
@@ -626,7 +688,7 @@ export class MollieMocker {
626
688
  const from = query.get('from');
627
689
  const startIndex = from ? sorted.findIndex(s => s.id === from) : 0;
628
690
  if (startIndex === -1) {
629
- return [404, { status: 404, title: 'Not Found', detail: 'No settlement exists with token ' + from }];
691
+ return [404, this.#errorBody(404, 'Not Found', 'No settlement exists with token ' + from)];
630
692
  }
631
693
 
632
694
  const page = sorted.slice(startIndex, startIndex + limit);
@@ -693,7 +755,7 @@ export class MollieMocker {
693
755
  const from = query.get('from');
694
756
  const startIndex = from ? sorted.findIndex(t => t.id === from) : 0;
695
757
  if (startIndex === -1) {
696
- return [404, { status: 404, title: 'Not Found', detail: 'No balance transaction exists with token ' + from }];
758
+ return [404, this.#errorBody(404, 'Not Found', 'No balance transaction exists with token ' + from)];
697
759
  }
698
760
 
699
761
  const page = sorted.slice(startIndex, startIndex + limit);
@@ -3,6 +3,7 @@ import { Token, UserFactory } from '@stamhoofd/models';
3
3
  import type { AccessRight } from '@stamhoofd/structures';
4
4
  import { PermissionLevel, PermissionRole, Permissions } from '@stamhoofd/structures';
5
5
  import { initPermissionRole } from './initPermissionRole.js';
6
+ import { SessionService } from '../../src/services/SessionService.js';
6
7
 
7
8
  /**
8
9
  * You cannot assign access rights directy to a user, but it can be done using roles. So when setting accessRights, a role wil be created and assigned to the user.
@@ -25,6 +26,6 @@ export async function initAdmin({ organization, permissions, accessRights }: { o
25
26
  permissions,
26
27
  }).create();
27
28
 
28
- const adminToken = await Token.createToken(admin);
29
+ const adminToken = await SessionService.createSession(admin);
29
30
  return { admin, adminToken };
30
31
  }
@@ -1,5 +1,6 @@
1
1
  import { Token, UserFactory } from '@stamhoofd/models';
2
2
  import { PermissionLevel, Permissions } from '@stamhoofd/structures';
3
+ import { SessionService } from '../../src/services/SessionService.js';
3
4
 
4
5
  export async function initPlatformAdmin() {
5
6
  const admin = await new UserFactory({
@@ -8,6 +9,6 @@ export async function initPlatformAdmin() {
8
9
  }),
9
10
  }).create();
10
11
 
11
- const adminToken = await Token.createToken(admin);
12
+ const adminToken = await SessionService.createSession(admin);
12
13
  return { admin, adminToken };
13
14
  }