@stamhoofd/backend 2.55.1 → 2.56.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.
package/index.ts CHANGED
@@ -13,6 +13,7 @@ import { sleep } from '@stamhoofd/utility';
13
13
  import { stopCrons, startCrons, waitForCrons } from '@stamhoofd/crons';
14
14
  import { resumeEmails } from './src/helpers/EmailResumer';
15
15
  import { ContextMiddleware } from './src/middleware/ContextMiddleware';
16
+ import { Platform } from '@stamhoofd/models';
16
17
 
17
18
  process.on('unhandledRejection', (error: Error) => {
18
19
  console.error('unhandledRejection');
@@ -83,6 +84,9 @@ const start = async () => {
83
84
  // Add CORS headers
84
85
  routerServer.addResponseMiddleware(CORSMiddleware);
85
86
 
87
+ // Init platform shared struct: otherwise permissions won't work with missing responsibilities
88
+ await Platform.getSharedStruct();
89
+
86
90
  // Register Excel loaders
87
91
  await import('./src/excel-loaders/members');
88
92
  await import('./src/excel-loaders/payments');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stamhoofd/backend",
3
- "version": "2.55.1",
3
+ "version": "2.56.0",
4
4
  "main": "./dist/index.js",
5
5
  "exports": {
6
6
  ".": {
@@ -31,19 +31,20 @@
31
31
  "sinon": "^18.0.0"
32
32
  },
33
33
  "dependencies": {
34
+ "@bwip-js/node": "^4.5.1",
34
35
  "@mollie/api-client": "3.7.0",
35
36
  "@simonbackx/simple-database": "1.25.0",
36
37
  "@simonbackx/simple-encoding": "2.16.6",
37
38
  "@simonbackx/simple-endpoints": "1.14.0",
38
39
  "@simonbackx/simple-logging": "^1.0.1",
39
- "@stamhoofd/backend-i18n": "2.55.1",
40
- "@stamhoofd/backend-middleware": "2.55.1",
41
- "@stamhoofd/email": "2.55.1",
42
- "@stamhoofd/models": "2.55.1",
43
- "@stamhoofd/queues": "2.55.1",
44
- "@stamhoofd/sql": "2.55.1",
45
- "@stamhoofd/structures": "2.55.1",
46
- "@stamhoofd/utility": "2.55.1",
40
+ "@stamhoofd/backend-i18n": "2.56.0",
41
+ "@stamhoofd/backend-middleware": "2.56.0",
42
+ "@stamhoofd/email": "2.56.0",
43
+ "@stamhoofd/models": "2.56.0",
44
+ "@stamhoofd/queues": "2.56.0",
45
+ "@stamhoofd/sql": "2.56.0",
46
+ "@stamhoofd/structures": "2.56.0",
47
+ "@stamhoofd/utility": "2.56.0",
47
48
  "archiver": "^7.0.1",
48
49
  "aws-sdk": "^2.885.0",
49
50
  "axios": "1.6.8",
@@ -63,5 +64,5 @@
63
64
  "publishConfig": {
64
65
  "access": "public"
65
66
  },
66
- "gitHead": "83f014895a675bef94b85eb9457411949a488326"
67
+ "gitHead": "39e791f29d992c918b83871d9dc6cf72c418b2c4"
67
68
  }
package/src/crons.ts CHANGED
@@ -13,6 +13,7 @@ import { endFunctionsOfUsersWithoutRegistration } from './crons/endFunctionsOfUs
13
13
  import { ExchangePaymentEndpoint } from './endpoints/organization/shared/ExchangePaymentEndpoint';
14
14
  import { checkSettlements } from './helpers/CheckSettlements';
15
15
  import { ForwardHandler } from './helpers/ForwardHandler';
16
+ import { PaymentService } from './services/PaymentService';
16
17
 
17
18
  // Importing postmark returns undefined (this is a bug, so we need to use require)
18
19
  // eslint-disable-next-line @typescript-eslint/no-require-imports
@@ -469,7 +470,7 @@ async function checkPayments() {
469
470
  if (payment.organizationId) {
470
471
  const organization = await Organization.getByID(payment.organizationId);
471
472
  if (organization) {
472
- await ExchangePaymentEndpoint.pollStatus(payment.id, organization);
473
+ await PaymentService.pollStatus(payment.id, organization);
473
474
  continue;
474
475
  }
475
476
  }
@@ -478,7 +479,7 @@ async function checkPayments() {
478
479
  }
479
480
 
480
481
  // Check expired
481
- if (ExchangePaymentEndpoint.isManualExpired(payment.status, payment)) {
482
+ if (PaymentService.isManualExpired(payment.status, payment)) {
482
483
  console.error('[DELAYED PAYMENTS] Could not resolve handler for expired payment, marking as failed', payment.id);
483
484
  payment.status = PaymentStatus.Failed;
484
485
  await payment.save();
@@ -542,7 +543,7 @@ async function checkFailedBuckarooPayments() {
542
543
  if (payment.organizationId) {
543
544
  const organization = await Organization.getByID(payment.organizationId);
544
545
  if (organization) {
545
- await ExchangePaymentEndpoint.pollStatus(payment.id, organization);
546
+ await PaymentService.pollStatus(payment.id, organization);
546
547
  continue;
547
548
  }
548
549
  }
@@ -0,0 +1,150 @@
1
+ import { Decoder } from '@simonbackx/simple-encoding';
2
+ import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
3
+ import { SimpleError } from '@simonbackx/simple-errors';
4
+ import { AuditLog } from '@stamhoofd/models';
5
+ import { SQL, SQLFilterDefinitions, SQLSortDefinitions, compileToSQLFilter, compileToSQLSorter } from '@stamhoofd/sql';
6
+ import { AuditLog as AuditLogStruct, CountFilteredRequest, LimitedFilteredRequest, PaginatedResponse, StamhoofdFilter, assertSort, getSortFilter } from '@stamhoofd/structures';
7
+
8
+ import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructures';
9
+ import { Context } from '../../../helpers/Context';
10
+ import { auditLogFilterCompilers } from '../../../sql-filters/audit-logs';
11
+ import { auditLogSorters } from '../../../sql-sorters/audit-logs';
12
+
13
+ type Params = Record<string, never>;
14
+ type Query = LimitedFilteredRequest;
15
+ type Body = undefined;
16
+ type ResponseBody = PaginatedResponse<AuditLogStruct[], LimitedFilteredRequest>;
17
+
18
+ const filterCompilers: SQLFilterDefinitions = auditLogFilterCompilers;
19
+ const sorters: SQLSortDefinitions<AuditLog> = auditLogSorters;
20
+
21
+ export class GetAuditLogsEndpoint extends Endpoint<Params, Query, Body, ResponseBody> {
22
+ queryDecoder = LimitedFilteredRequest as Decoder<LimitedFilteredRequest>;
23
+
24
+ protected doesMatch(request: Request): [true, Params] | [false] {
25
+ if (request.method !== 'GET') {
26
+ return [false];
27
+ }
28
+
29
+ const params = Endpoint.parseParameters(request.url, '/audit-logs', {});
30
+
31
+ if (params) {
32
+ return [true, params as Params];
33
+ }
34
+ return [false];
35
+ }
36
+
37
+ static async buildQuery(q: CountFilteredRequest | LimitedFilteredRequest) {
38
+ const organization = Context.organization;
39
+ let scopeFilter: StamhoofdFilter | undefined = undefined;
40
+
41
+ if (organization) {
42
+ if (!await Context.auth.hasFullAccess(organization.id)) {
43
+ throw Context.auth.error();
44
+ }
45
+ scopeFilter = {
46
+ organizationId: organization.id,
47
+ };
48
+ }
49
+ else {
50
+ if (!Context.auth.hasPlatformFullAccess()) {
51
+ throw Context.auth.error();
52
+ }
53
+ }
54
+
55
+ const query = SQL
56
+ .select(
57
+ SQL.wildcard(AuditLog.table),
58
+ )
59
+ .from(
60
+ SQL.table(AuditLog.table),
61
+ );
62
+
63
+ if (scopeFilter) {
64
+ query.where(compileToSQLFilter(scopeFilter, filterCompilers));
65
+ }
66
+
67
+ if (q.filter) {
68
+ query.where(compileToSQLFilter(q.filter, filterCompilers));
69
+ }
70
+
71
+ if (q.search) {
72
+ throw new SimpleError({
73
+ code: 'not_supported',
74
+ message: 'Search is not possible in audit logs',
75
+ human: 'Zoeken is niet mogelijk in audit logs',
76
+ });
77
+ }
78
+
79
+ if (q instanceof LimitedFilteredRequest) {
80
+ if (q.pageFilter) {
81
+ query.where(compileToSQLFilter(q.pageFilter, filterCompilers));
82
+ }
83
+
84
+ q.sort = assertSort(q.sort, [{ key: 'id' }]);
85
+ query.orderBy(compileToSQLSorter(q.sort, sorters));
86
+ query.limit(q.limit);
87
+ }
88
+
89
+ return query;
90
+ }
91
+
92
+ static async buildData(requestQuery: LimitedFilteredRequest) {
93
+ const query = await GetAuditLogsEndpoint.buildQuery(requestQuery);
94
+ const data = await query.fetch();
95
+
96
+ const logs = AuditLog.fromRows(data, AuditLog.table);
97
+
98
+ let next: LimitedFilteredRequest | undefined;
99
+
100
+ if (logs.length >= requestQuery.limit) {
101
+ const lastObject = logs[logs.length - 1];
102
+ const nextFilter = getSortFilter(lastObject, sorters, requestQuery.sort);
103
+
104
+ next = new LimitedFilteredRequest({
105
+ filter: requestQuery.filter,
106
+ pageFilter: nextFilter,
107
+ sort: requestQuery.sort,
108
+ limit: requestQuery.limit,
109
+ search: requestQuery.search,
110
+ });
111
+
112
+ if (JSON.stringify(nextFilter) === JSON.stringify(requestQuery.pageFilter)) {
113
+ console.error('Found infinite loading loop for', requestQuery);
114
+ next = undefined;
115
+ }
116
+ }
117
+
118
+ return new PaginatedResponse<AuditLogStruct[], LimitedFilteredRequest>({
119
+ results: await AuthenticatedStructures.auditLogs(logs),
120
+ next,
121
+ });
122
+ }
123
+
124
+ async handle(request: DecodedRequest<Params, Query, Body>) {
125
+ await Context.setOptionalOrganizationScope();
126
+ await Context.authenticate();
127
+
128
+ const maxLimit = 100;
129
+
130
+ if (request.query.limit > maxLimit) {
131
+ throw new SimpleError({
132
+ code: 'invalid_field',
133
+ field: 'limit',
134
+ message: 'Limit can not be more than ' + maxLimit,
135
+ });
136
+ }
137
+
138
+ if (request.query.limit < 1) {
139
+ throw new SimpleError({
140
+ code: 'invalid_field',
141
+ field: 'limit',
142
+ message: 'Limit can not be less than 1',
143
+ });
144
+ }
145
+
146
+ return new Response(
147
+ await GetAuditLogsEndpoint.buildData(request.query),
148
+ );
149
+ }
150
+ }
@@ -3,7 +3,7 @@ import { ConvertArrayToPatchableArray, Decoder, PatchableArrayAutoEncoder, Patch
3
3
  import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
4
4
  import { SimpleError } from '@simonbackx/simple-errors';
5
5
  import { BalanceItem, Document, Group, Member, MemberFactory, MemberPlatformMembership, MemberResponsibilityRecord, MemberWithRegistrations, mergeTwoMembers, Organization, Platform, RateLimiter, Registration, SetupStepUpdater, User } from '@stamhoofd/models';
6
- import { GroupType, MembersBlob, MemberWithRegistrationsBlob, PermissionLevel } from '@stamhoofd/structures';
6
+ import { AuditLogType, GroupType, MembersBlob, MemberWithRegistrationsBlob, PermissionLevel } from '@stamhoofd/structures';
7
7
  import { Formatter } from '@stamhoofd/utility';
8
8
 
9
9
  import { Email } from '@stamhoofd/email';
@@ -13,6 +13,7 @@ import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructure
13
13
  import { Context } from '../../../helpers/Context';
14
14
  import { MembershipCharger } from '../../../helpers/MembershipCharger';
15
15
  import { MemberUserSyncer } from '../../../helpers/MemberUserSyncer';
16
+ import { AuditLogService } from '../../../services/AuditLogService';
16
17
 
17
18
  type Params = Record<string, never>;
18
19
  type Query = undefined;
@@ -142,6 +143,13 @@ export class PatchOrganizationMembersEndpoint extends Endpoint<Params, Query, Bo
142
143
 
143
144
  // Auto link users based on data
144
145
  await MemberUserSyncer.onChangeMember(member);
146
+
147
+ if (!duplicate) {
148
+ await AuditLogService.log({
149
+ type: AuditLogType.MemberAdded,
150
+ member: member,
151
+ });
152
+ }
145
153
  }
146
154
 
147
155
  let shouldUpdateSetupSteps = false;
@@ -161,6 +169,7 @@ export class PatchOrganizationMembersEndpoint extends Endpoint<Params, Query, Bo
161
169
  }
162
170
 
163
171
  patch = await Context.auth.filterMemberPatch(member, patch);
172
+ const originalDetails = member.details.clone();
164
173
 
165
174
  if (patch.details) {
166
175
  if (patch.details.isPut()) {
@@ -196,6 +205,15 @@ export class PatchOrganizationMembersEndpoint extends Endpoint<Params, Query, Bo
196
205
 
197
206
  await member.save();
198
207
 
208
+ if (patch.details) {
209
+ await AuditLogService.log({
210
+ type: AuditLogType.MemberEdited,
211
+ member: member,
212
+ oldMemberDetails: originalDetails,
213
+ memberDetailsPatch: patch.details,
214
+ });
215
+ }
216
+
199
217
  // Update documents
200
218
  await Document.updateForMember(member.id);
201
219
 
@@ -450,14 +468,14 @@ export class PatchOrganizationMembersEndpoint extends Endpoint<Params, Query, Bo
450
468
  SQL.where('startDate', SQLWhereSign.LessEqual, put.startDate)
451
469
  .and('endDate', SQLWhereSign.GreaterEqual, put.startDate),
452
470
  )
453
- .or(
454
- SQL.where('startDate', SQLWhereSign.LessEqual, put.endDate)
455
- .and('endDate', SQLWhereSign.GreaterEqual, put.endDate),
456
- )
457
- .or(
458
- SQL.where('startDate', SQLWhereSign.GreaterEqual, put.startDate)
459
- .and('endDate', SQLWhereSign.LessEqual, put.endDate),
460
- )
471
+ .or(
472
+ SQL.where('startDate', SQLWhereSign.LessEqual, put.endDate)
473
+ .and('endDate', SQLWhereSign.GreaterEqual, put.endDate),
474
+ )
475
+ .or(
476
+ SQL.where('startDate', SQLWhereSign.GreaterEqual, put.startDate)
477
+ .and('endDate', SQLWhereSign.LessEqual, put.endDate),
478
+ ),
461
479
  )
462
480
  .first(false);
463
481
 
@@ -6,6 +6,7 @@ import { isDebouncedError, QueueHandler } from '@stamhoofd/queues';
6
6
 
7
7
  import { StripeHelper } from '../../../helpers/StripeHelper';
8
8
  import { ExchangePaymentEndpoint } from '../../organization/shared/ExchangePaymentEndpoint';
9
+ import { PaymentService } from '../../../services/PaymentService';
9
10
 
10
11
  type Params = Record<string, never>;
11
12
  class Body extends AutoEncoder {
@@ -159,7 +160,7 @@ export class StripeWebookEndpoint extends Endpoint<Params, Query, Body, Response
159
160
  await QueueHandler.debounce('stripe-webhook/payment-' + paymentId, async () => {
160
161
  const organization = await Organization.getByID(organizationId);
161
162
  if (organization) {
162
- await ExchangePaymentEndpoint.pollStatus(paymentId, organization);
163
+ await PaymentService.pollStatus(paymentId, organization);
163
164
  }
164
165
  else {
165
166
  console.warn('Could not find organization with id', organizationId);
@@ -1,7 +1,7 @@
1
1
  import { AutoEncoderPatchType, Decoder, isPatchableArray, patchObject } from '@simonbackx/simple-encoding';
2
2
  import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
3
3
  import { Organization, Platform, RegistrationPeriod, SetupStepUpdater } from '@stamhoofd/models';
4
- import { MemberResponsibility, PlatformConfig, PlatformPremiseType, Platform as PlatformStruct } from '@stamhoofd/structures';
4
+ import { AuditLogType, MemberResponsibility, PlatformConfig, PlatformPremiseType, Platform as PlatformStruct } from '@stamhoofd/structures';
5
5
 
6
6
  import { SimpleError } from '@simonbackx/simple-errors';
7
7
  import { QueueHandler } from '@stamhoofd/queues';
@@ -10,6 +10,7 @@ import { MembershipCharger } from '../../../helpers/MembershipCharger';
10
10
  import { MembershipHelper } from '../../../helpers/MembershipHelper';
11
11
  import { PeriodHelper } from '../../../helpers/PeriodHelper';
12
12
  import { TagHelper } from '../../../helpers/TagHelper';
13
+ import { AuditLogService } from '../../../services/AuditLogService';
13
14
 
14
15
  type Params = Record<string, never>;
15
16
  type Query = undefined;
@@ -90,9 +91,9 @@ export class PatchPlatformEndpoint extends Endpoint<
90
91
 
91
92
  // Update config
92
93
  if (newConfig) {
94
+ const oldConfig: PlatformConfig = platform.config.clone();
93
95
  const shouldCheckSteps = newConfig.premiseTypes || newConfig.responsibilities;
94
96
  if (shouldCheckSteps) {
95
- const oldConfig: PlatformConfig = platform.config.clone();
96
97
  platform.config = patchObject(platform.config, newConfig);
97
98
  const currentConfig: PlatformConfig = platform.config;
98
99
 
@@ -107,6 +108,12 @@ export class PatchPlatformEndpoint extends Endpoint<
107
108
  else {
108
109
  platform.config = patchObject(platform.config, newConfig);
109
110
  }
111
+
112
+ await AuditLogService.log({
113
+ type: AuditLogType.PlatformSettingChanged,
114
+ oldConfig,
115
+ patch: newConfig,
116
+ });
110
117
  }
111
118
 
112
119
  if (newConfig.tags && isPatchableArray(newConfig.tags) && newConfig.tags.changes.length > 0) {
@@ -2,12 +2,13 @@ import { AutoEncoderPatchType, Decoder, PatchableArrayAutoEncoder, PatchableArra
2
2
  import { DecodedRequest, Endpoint, Request, Response } from '@simonbackx/simple-endpoints';
3
3
  import { SimpleError } from '@simonbackx/simple-errors';
4
4
  import { Document, Member, RateLimiter } from '@stamhoofd/models';
5
- import { MemberDetails, MembersBlob, MemberWithRegistrationsBlob } from '@stamhoofd/structures';
5
+ import { AuditLogType, MemberDetails, MembersBlob, MemberWithRegistrationsBlob } from '@stamhoofd/structures';
6
6
 
7
7
  import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructures';
8
8
  import { Context } from '../../../helpers/Context';
9
9
  import { MemberUserSyncer } from '../../../helpers/MemberUserSyncer';
10
10
  import { PatchOrganizationMembersEndpoint } from '../../global/members/PatchOrganizationMembersEndpoint';
11
+ import { AuditLogService } from '../../../services/AuditLogService';
11
12
  type Params = Record<string, never>;
12
13
  type Query = undefined;
13
14
  type Body = PatchableArrayAutoEncoder<MemberWithRegistrationsBlob>;
@@ -68,6 +69,11 @@ export class PatchUserMembersEndpoint extends Endpoint<Params, Query, Body, Resp
68
69
 
69
70
  await member.save();
70
71
  addedMembers.push(member);
72
+
73
+ await AuditLogService.log({
74
+ type: AuditLogType.MemberAdded,
75
+ member: member,
76
+ });
71
77
  }
72
78
 
73
79
  // Modify members
@@ -84,6 +90,7 @@ export class PatchUserMembersEndpoint extends Endpoint<Params, Query, Body, Resp
84
90
  }
85
91
  const securityCode = struct.details?.securityCode; // will get cleared after the filter
86
92
  struct = await Context.auth.filterMemberPatch(member, struct);
93
+ const originalDetails = member.details.clone();
87
94
 
88
95
  if (struct.details) {
89
96
  if (struct.details.isPut()) {
@@ -122,6 +129,15 @@ export class PatchUserMembersEndpoint extends Endpoint<Params, Query, Body, Resp
122
129
  await member.save();
123
130
  await MemberUserSyncer.onChangeMember(member);
124
131
 
132
+ if (struct.details) {
133
+ await AuditLogService.log({
134
+ type: AuditLogType.MemberEdited,
135
+ member: member,
136
+ oldMemberDetails: originalDetails,
137
+ memberDetailsPatch: struct.details,
138
+ });
139
+ }
140
+
125
141
  // Update documents
126
142
  await Document.updateForMember(member.id);
127
143
  }
@@ -12,6 +12,8 @@ import { AuthenticatedStructures } from '../../../helpers/AuthenticatedStructure
12
12
  import { BuckarooHelper } from '../../../helpers/BuckarooHelper';
13
13
  import { Context } from '../../../helpers/Context';
14
14
  import { StripeHelper } from '../../../helpers/StripeHelper';
15
+ import { BalanceItemService } from '../../../services/BalanceItemService';
16
+ import { RegistrationService } from '../../../services/RegistrationService';
15
17
  type Params = Record<string, never>;
16
18
  type Query = undefined;
17
19
  type Body = IDRegisterCheckout;
@@ -354,16 +356,17 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
354
356
  await BalanceItem.deleteForDeletedRegistration(existingRegistration.id);
355
357
 
356
358
  // Clear the registration
357
- await existingRegistration.deactivate();
358
- deactivatedRegistrationGroupIds.push(existingRegistration.groupId);
359
-
360
- const group = groups.find(g => g.id === existingRegistration.groupId);
359
+ let group = groups.find(g => g.id === existingRegistration.groupId);
361
360
  if (!group) {
362
- const g = await Group.getByID(existingRegistration.groupId);
363
- if (g) {
364
- groups.push(g);
361
+ group = await Group.getByID(existingRegistration.groupId);
362
+ if (group) {
363
+ groups.push(group);
365
364
  }
366
365
  }
366
+ const member = members.find(m => m.id === existingRegistration.memberId);
367
+
368
+ await RegistrationService.deactivate(existingRegistration, group, member);
369
+ deactivatedRegistrationGroupIds.push(existingRegistration.groupId);
367
370
  }
368
371
 
369
372
  async function createBalanceItem({ registration, amount, unitPrice, description, type, relations }: { amount?: number; registration: RegistrationWithMemberAndGroup; unitPrice: number; description: string; relations: Map<BalanceItemRelationType, BalanceItemRelation>; type: BalanceItemType }) {
@@ -584,8 +587,8 @@ export class RegisterMembersEndpoint extends Endpoint<Params, Query, Body, Respo
584
587
  async function markValidIfNeeded() {
585
588
  if (shouldMarkValid) {
586
589
  for (const balanceItem of [...createdBalanceItems, ...unrelatedCreatedBalanceItems]) {
587
- // Mark vlaid
588
- await balanceItem.markPaid(payment, organization);
590
+ // Mark valid
591
+ await BalanceItemService.markPaid(balanceItem, payment, organization);
589
592
  }
590
593
  }
591
594
  }
@@ -8,6 +8,8 @@ import { Payment as PaymentStruct, PaymentGeneral, PaymentMethod, PaymentStatus,
8
8
  import { AuthenticatedStructures } from '../../../../helpers/AuthenticatedStructures';
9
9
  import { Context } from '../../../../helpers/Context';
10
10
  import { ExchangePaymentEndpoint } from '../../shared/ExchangePaymentEndpoint';
11
+ import { PaymentService } from '../../../../services/PaymentService';
12
+ import { BalanceItemService } from '../../../../services/BalanceItemService';
11
13
 
12
14
  type Params = Record<string, never>;
13
15
  type Query = undefined;
@@ -143,7 +145,7 @@ export class PatchPaymentsEndpoint extends Endpoint<Params, Query, Body, Respons
143
145
 
144
146
  // Mark paid or failed
145
147
  if (put.status !== PaymentStatus.Created && put.status !== PaymentStatus.Pending) {
146
- await ExchangePaymentEndpoint.handlePaymentStatusUpdate(payment, organization, put.status);
148
+ await PaymentService.handlePaymentStatusUpdate(payment, organization, put.status);
147
149
 
148
150
  if (put.status === PaymentStatus.Succeeded) {
149
151
  payment.paidAt = put.paidAt;
@@ -152,7 +154,7 @@ export class PatchPaymentsEndpoint extends Endpoint<Params, Query, Body, Respons
152
154
  }
153
155
  else {
154
156
  for (const balanceItem of balanceItems) {
155
- await balanceItem.markUpdated(payment, organization);
157
+ await BalanceItemService.markUpdated(balanceItem, payment, organization);
156
158
  }
157
159
 
158
160
  await BalanceItem.updateOutstanding(balanceItems);
@@ -222,7 +224,7 @@ export class PatchPaymentsEndpoint extends Endpoint<Params, Query, Body, Respons
222
224
  await payment.save();
223
225
 
224
226
  if (patch.status) {
225
- await ExchangePaymentEndpoint.handlePaymentStatusUpdate(payment, organization, patch.status);
227
+ await PaymentService.handlePaymentStatusUpdate(payment, organization, patch.status);
226
228
  }
227
229
 
228
230
  changedPayments.push(