@commercetools/checkout-payments-processor-sdk 0.0.6

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 (105) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +42 -0
  3. package/biome.json +38 -0
  4. package/package.json +48 -0
  5. package/src/api/context/request-context.helper.test.ts +98 -0
  6. package/src/api/context/request-context.helper.ts +89 -0
  7. package/src/api/context/request-context.provider.ts +27 -0
  8. package/src/api/context/types/request-context.type.ts +18 -0
  9. package/src/api/handlers/payment-components.handler.test.ts +378 -0
  10. package/src/api/handlers/payment-components.handler.ts +80 -0
  11. package/src/api/handlers/payment-intents.handler.test.ts +475 -0
  12. package/src/api/handlers/payment-intents.handler.ts +169 -0
  13. package/src/api/handlers/status.handler.test.ts +551 -0
  14. package/src/api/handlers/status.handler.ts +216 -0
  15. package/src/api/handlers/transaction.handler.ts +86 -0
  16. package/src/api/handlers/types/handler.type.ts +49 -0
  17. package/src/api/hooks/authorize.hook.test.ts +62 -0
  18. package/src/api/hooks/authorize.hook.ts +51 -0
  19. package/src/api/hooks/jwt-auth.hook.test.ts +54 -0
  20. package/src/api/hooks/jwt-auth.hook.ts +56 -0
  21. package/src/api/hooks/oauth2-auth.hook.test.ts +173 -0
  22. package/src/api/hooks/oauth2-auth.hook.ts +46 -0
  23. package/src/api/hooks/session-header-auth.hook.ts +47 -0
  24. package/src/api/hooks/session-query-param-auth.hook.ts +46 -0
  25. package/src/api/hooks/types/hook.type.ts +12 -0
  26. package/src/api/index.ts +11 -0
  27. package/src/api/types.ts +3 -0
  28. package/src/commercetools/api/base-api.ts +28 -0
  29. package/src/commercetools/api/cart-api.test.ts +95 -0
  30. package/src/commercetools/api/cart-api.ts +55 -0
  31. package/src/commercetools/api/custom-type-api.ts +39 -0
  32. package/src/commercetools/api/order-api.ts +22 -0
  33. package/src/commercetools/api/payment-api.ts +64 -0
  34. package/src/commercetools/api/payment-method-api.ts +118 -0
  35. package/src/commercetools/api/root-api.ts +109 -0
  36. package/src/commercetools/errors/ct-api.error.ts +27 -0
  37. package/src/commercetools/helpers/currency.converter.test.ts +121 -0
  38. package/src/commercetools/helpers/currency.converter.test_data.json +2379 -0
  39. package/src/commercetools/helpers/currency.converter.ts +102 -0
  40. package/src/commercetools/helpers/taxrate.converter.test.ts +48 -0
  41. package/src/commercetools/helpers/taxrate.converter.ts +39 -0
  42. package/src/commercetools/index.ts +22 -0
  43. package/src/commercetools/services/ct-cart.service.test.ts +1616 -0
  44. package/src/commercetools/services/ct-cart.service.ts +316 -0
  45. package/src/commercetools/services/ct-custom-type.service.ts +62 -0
  46. package/src/commercetools/services/ct-order.service.test.ts +136 -0
  47. package/src/commercetools/services/ct-order.service.ts +47 -0
  48. package/src/commercetools/services/ct-payment-method.service.test.ts +878 -0
  49. package/src/commercetools/services/ct-payment-method.service.ts +223 -0
  50. package/src/commercetools/services/ct-payment.service.test.ts +1123 -0
  51. package/src/commercetools/services/ct-payment.service.ts +574 -0
  52. package/src/commercetools/types/api.type.ts +162 -0
  53. package/src/commercetools/types/cart.type.ts +83 -0
  54. package/src/commercetools/types/custom-type.type.ts +21 -0
  55. package/src/commercetools/types/order.type.ts +19 -0
  56. package/src/commercetools/types/payment-method.type.ts +130 -0
  57. package/src/commercetools/types/payment.type.ts +74 -0
  58. package/src/commercetools/types/predefined-custom-types.type.ts +240 -0
  59. package/src/commercetools/types.ts +7 -0
  60. package/src/errorx/errorx.ts +625 -0
  61. package/src/errorx/index.ts +1 -0
  62. package/src/errorx/types.ts +5 -0
  63. package/src/fetch/decorators/base.decorator.ts +13 -0
  64. package/src/fetch/decorators/monitoring.decorator.ts +41 -0
  65. package/src/fetch/fetch.test.ts +57 -0
  66. package/src/fetch/index.ts +3 -0
  67. package/src/fetch/types/fetch.type.ts +11 -0
  68. package/src/fetch/types.ts +1 -0
  69. package/src/index.ts +15 -0
  70. package/src/logger/commercetools-logger.ts +69 -0
  71. package/src/logger/index.ts +1 -0
  72. package/src/logger/types/logger.type.ts +6 -0
  73. package/src/logger/types.ts +1 -0
  74. package/src/mocks/auth.mock.ts +6 -0
  75. package/src/mocks/cart.mock.ts +239 -0
  76. package/src/mocks/ct-api-error.mock.ts +23 -0
  77. package/src/mocks/index.ts +4 -0
  78. package/src/mocks/payment.mock.ts +35 -0
  79. package/src/payment-processor.ts +317 -0
  80. package/src/security/authn/authns.ts +219 -0
  81. package/src/security/authn/bearer-utils.test.ts +35 -0
  82. package/src/security/authn/bearer-utils.ts +28 -0
  83. package/src/security/authn/jwt-authn-manager.ts +69 -0
  84. package/src/security/authn/oauth2-authn-manager.ts +105 -0
  85. package/src/security/authn/session-header-authn-manager.ts +58 -0
  86. package/src/security/authn/session-query-param-authn-manager.ts +53 -0
  87. package/src/security/authn/types/authn.type.ts +50 -0
  88. package/src/security/authz/authorization-manager.ts +39 -0
  89. package/src/security/authz/types/authz.type.ts +13 -0
  90. package/src/security/index.ts +13 -0
  91. package/src/security/services/authorization.service.ts +54 -0
  92. package/src/security/services/jwt.service.test.ts +27 -0
  93. package/src/security/services/jwt.service.ts +45 -0
  94. package/src/security/services/oauth2.service.ts +60 -0
  95. package/src/security/services/session.service.ts +141 -0
  96. package/src/security/services/types/authorization.type.ts +10 -0
  97. package/src/security/services/types/jwt.type.ts +3 -0
  98. package/src/security/services/types/oauth2.type.ts +15 -0
  99. package/src/security/services/types/session.type.ts +38 -0
  100. package/src/security/types.ts +6 -0
  101. package/tsconfig.json +28 -0
  102. package/tsconfig.prod.json +9 -0
  103. package/typedoc.json +6 -0
  104. package/vitest.config.ts +15 -0
  105. package/wiki/getting-started.md +13 -0
@@ -0,0 +1,878 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { RequestContextProvider } from '@api';
3
+ import type {
4
+ PaymentMethod,
5
+ PaymentMethodPagedQueryResponse,
6
+ PaymentMethodUpdateAction,
7
+ } from '@commercetools/platform-sdk';
8
+ import {
9
+ ErrorInternalConstraintViolated,
10
+ ErrorResourceNotFound,
11
+ } from '@errorx';
12
+ import { CommercetoolsLogger } from '@logger';
13
+ import { CommercetoolsPaymentMethodAPI } from '@/commercetools/api/payment-method-api.js';
14
+ import { DefaultCommercetoolsAPI } from '@/commercetools/api/root-api.js';
15
+ import { DefaultPaymentMethodService } from '@/commercetools/services/ct-payment-method.service.js';
16
+ import type { SavePaymentMethodDraft } from '@/commercetools/types/payment-method.type.js';
17
+
18
+ const projectKey = 'test-payment-methods';
19
+ const contextProvider = new RequestContextProvider({
20
+ getContextFn: () => ({
21
+ correlationId: 'correlation-id',
22
+ requestId: 'request-id',
23
+ }),
24
+ updateContextFn: () => {},
25
+ });
26
+ const logger = new CommercetoolsLogger({ contextProvider, projectKey });
27
+
28
+ const ctAPI = new DefaultCommercetoolsAPI({
29
+ apiUrl: 'http://api.test.com',
30
+ authUrl: 'http://auth.test.com',
31
+ clientId: 'clientId',
32
+ clientSecret: 'clientSecret',
33
+ projectKey,
34
+ contextProvider,
35
+ logger,
36
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
37
+ httpClient: (input: string | URL | Request, init?: RequestInit) => {
38
+ return fetch(input, init);
39
+ },
40
+ });
41
+
42
+ const paymentMethodAPI = new CommercetoolsPaymentMethodAPI({
43
+ client: ctAPI.client,
44
+ });
45
+ ctAPI.paymentMethod = paymentMethodAPI;
46
+
47
+ const ctPaymentMethodService = new DefaultPaymentMethodService({
48
+ ctAPI,
49
+ logger,
50
+ });
51
+
52
+ describe('commercetools payment-method service', () => {
53
+ beforeEach(() => {
54
+ vi.clearAllMocks();
55
+ });
56
+
57
+ describe('get', () => {
58
+ test('should return a payment method', async () => {
59
+ //Given
60
+ const paymentId = randomUUID();
61
+ const customerId = randomUUID();
62
+ const paymentInterface = 'psp-interface';
63
+ const paymentMethod: PaymentMethod = {
64
+ id: paymentId,
65
+ version: 1,
66
+ default: false,
67
+ paymentMethodStatus: 'Active',
68
+ createdAt: '',
69
+ lastModifiedAt: '',
70
+ customer: {
71
+ id: customerId,
72
+ typeId: 'customer',
73
+ },
74
+ paymentInterface,
75
+ };
76
+
77
+ const spyer = vi.spyOn(paymentMethodAPI, 'find').mockResolvedValueOnce({
78
+ count: 1,
79
+ limit: 100,
80
+ offset: 0,
81
+ results: [paymentMethod],
82
+ });
83
+
84
+ //When
85
+ const result = await ctPaymentMethodService.get({
86
+ id: paymentId,
87
+ customerId: customerId,
88
+ paymentInterface,
89
+ });
90
+
91
+ //Then
92
+ expect(result).toEqual(paymentMethod);
93
+
94
+ expect(spyer).toHaveBeenCalledWith({
95
+ queryString: `customer(id="${customerId}") and paymentInterface="${paymentInterface}"`,
96
+ });
97
+ });
98
+
99
+ test('should return the payment-method even if the interfaceAccount is set on the payment-method but it is not provided as a query param since it is an optional value', async () => {
100
+ //Given
101
+ const paymentId = randomUUID();
102
+ const customerId = randomUUID();
103
+ const paymentInterface = 'psp-interface';
104
+ const interfaceAccountValue = 'interfaceaccount-value';
105
+ const paymentMethod: PaymentMethod = {
106
+ id: paymentId,
107
+ version: 1,
108
+ default: false,
109
+ paymentMethodStatus: 'Active',
110
+ createdAt: '',
111
+ lastModifiedAt: '',
112
+ customer: {
113
+ id: customerId,
114
+ typeId: 'customer',
115
+ },
116
+ paymentInterface: paymentInterface,
117
+ interfaceAccount: interfaceAccountValue,
118
+ };
119
+
120
+ const spyer = vi.spyOn(paymentMethodAPI, 'find').mockResolvedValueOnce({
121
+ count: 1,
122
+ limit: 100,
123
+ offset: 0,
124
+ results: [paymentMethod],
125
+ });
126
+
127
+ //When
128
+ const result = await ctPaymentMethodService.get({
129
+ id: paymentId,
130
+ customerId: customerId,
131
+ paymentInterface: paymentInterface,
132
+ });
133
+
134
+ //Then
135
+ expect(result).toEqual(paymentMethod);
136
+
137
+ expect(spyer).toHaveBeenCalledWith({
138
+ queryString: `customer(id="${customerId}") and paymentInterface="${paymentInterface}"`,
139
+ });
140
+ });
141
+
142
+ test('should throw an ErrorResourceNotFound if no payment-method could be found', async () => {
143
+ //Given
144
+ const paymentId = randomUUID();
145
+ const customerId = randomUUID();
146
+ const paymentInterface = 'psp-interface';
147
+
148
+ const spyer = vi.spyOn(paymentMethodAPI, 'find').mockResolvedValueOnce({
149
+ count: 0,
150
+ limit: 100,
151
+ offset: 0,
152
+ results: [],
153
+ });
154
+
155
+ //When
156
+ const result = ctPaymentMethodService.get({
157
+ id: paymentId,
158
+ customerId: customerId,
159
+ paymentInterface,
160
+ });
161
+
162
+ //Then
163
+ await expect(result).rejects.toThrow(
164
+ new ErrorResourceNotFound('payment-method', paymentId),
165
+ );
166
+ expect(spyer).toHaveBeenCalledWith({
167
+ queryString: `customer(id="${customerId}") and paymentInterface="${paymentInterface}"`,
168
+ });
169
+ });
170
+ });
171
+
172
+ describe('find', () => {
173
+ test('should return an empty list if nothing matches', async () => {
174
+ //Given
175
+ const customerId = randomUUID();
176
+ const paymentInterfaceValue = 'paymentInterface-value';
177
+
178
+ const queryResults: PaymentMethodPagedQueryResponse = {
179
+ limit: 20,
180
+ offset: 0,
181
+ count: 0,
182
+ total: 0,
183
+ results: [],
184
+ };
185
+
186
+ const spyer = vi
187
+ .spyOn(paymentMethodAPI, 'find')
188
+ .mockResolvedValueOnce(queryResults);
189
+
190
+ //When
191
+ const result = await ctPaymentMethodService.find({
192
+ customerId: customerId,
193
+ paymentInterface: paymentInterfaceValue,
194
+ });
195
+
196
+ //Then
197
+ expect(spyer).toHaveBeenCalledWith({
198
+ queryString: `customer(id="${customerId}") and paymentInterface="${paymentInterfaceValue}"`,
199
+ });
200
+ expect(result).toEqual(queryResults);
201
+ });
202
+
203
+ test('should return a list of payment methods that match with the given "customerId" and "paymentInterface"', async () => {
204
+ //Given
205
+ const customerId = randomUUID();
206
+ const paymentId = randomUUID();
207
+ const paymentInterfaceValue = 'paymentInterface-value';
208
+
209
+ const queryResults: PaymentMethodPagedQueryResponse = {
210
+ limit: 100,
211
+ offset: 0,
212
+ count: 1,
213
+ total: 1,
214
+ results: [
215
+ {
216
+ id: paymentId,
217
+ version: 1,
218
+ default: false,
219
+ paymentMethodStatus: 'Active',
220
+ paymentInterface: paymentInterfaceValue,
221
+ createdAt: '',
222
+ lastModifiedAt: '',
223
+ customer: {
224
+ id: customerId,
225
+ typeId: 'customer',
226
+ },
227
+ },
228
+ ],
229
+ };
230
+
231
+ const spyer = vi
232
+ .spyOn(paymentMethodAPI, 'find')
233
+ .mockResolvedValueOnce(queryResults);
234
+
235
+ //When
236
+ const result = await ctPaymentMethodService.find({
237
+ customerId: customerId,
238
+ paymentInterface: paymentInterfaceValue,
239
+ });
240
+
241
+ //Then
242
+ expect(spyer).toHaveBeenCalledWith({
243
+ queryString: `customer(id="${customerId}") and paymentInterface="${paymentInterfaceValue}"`,
244
+ });
245
+ expect(result).toEqual(queryResults);
246
+ });
247
+
248
+ test('should return a list of payment methods that match with the given "customerId", "paymentInterface" and "interfaceAccount"', async () => {
249
+ //Given
250
+ const customerId = randomUUID();
251
+ const paymentId = randomUUID();
252
+ const paymentInterfaceValue = 'paymentInterface-value';
253
+ const interfaceAccountValue = 'interfaceAccount-value';
254
+
255
+ const queryResults: PaymentMethodPagedQueryResponse = {
256
+ limit: 100,
257
+ offset: 0,
258
+ count: 1,
259
+ total: 1,
260
+ results: [
261
+ {
262
+ id: paymentId,
263
+ version: 1,
264
+ default: false,
265
+ paymentMethodStatus: 'Active',
266
+ paymentInterface: paymentInterfaceValue,
267
+ interfaceAccount: interfaceAccountValue,
268
+ createdAt: '',
269
+ lastModifiedAt: '',
270
+ customer: {
271
+ id: customerId,
272
+ typeId: 'customer',
273
+ },
274
+ },
275
+ ],
276
+ };
277
+
278
+ const spyer = vi
279
+ .spyOn(paymentMethodAPI, 'find')
280
+ .mockResolvedValueOnce(queryResults);
281
+
282
+ //When
283
+ const result = await ctPaymentMethodService.find({
284
+ customerId: customerId,
285
+ paymentInterface: paymentInterfaceValue,
286
+ interfaceAccount: interfaceAccountValue,
287
+ });
288
+
289
+ //Then
290
+ expect(spyer).toHaveBeenCalledWith({
291
+ queryString: `customer(id="${customerId}") and paymentInterface="${paymentInterfaceValue}" and interfaceAccount="${interfaceAccountValue}"`,
292
+ });
293
+ expect(result).toEqual(queryResults);
294
+ });
295
+ });
296
+
297
+ describe('save', () => {
298
+ test('should throw an error if an payment method already exists for that customer with the same token value', async () => {
299
+ //Given
300
+ const draft: SavePaymentMethodDraft = {
301
+ customerId: 'customerId',
302
+ method: 'scheme',
303
+ paymentInterface: 'paymentInterface',
304
+ token: 'token',
305
+ interfaceAccount: 'interfaceAccount',
306
+ };
307
+
308
+ const spyer = vi
309
+ .spyOn(ctPaymentMethodService, 'doesTokenBelongsToCustomer')
310
+ .mockResolvedValueOnce(true);
311
+
312
+ //When
313
+ const result = ctPaymentMethodService.save(draft);
314
+
315
+ //Then
316
+ expect(spyer).toHaveBeenCalledWith({
317
+ customerId: 'customerId',
318
+ paymentInterface: 'paymentInterface',
319
+ interfaceAccount: 'interfaceAccount',
320
+ tokenValue: 'token',
321
+ });
322
+ await expect(result).rejects.toThrow(
323
+ new ErrorInternalConstraintViolated(
324
+ 'A payment method with the same "token.value" already exists.',
325
+ {
326
+ privateFields: {
327
+ customerId: 'customerId',
328
+ interfaceAccount: 'interfaceAccount',
329
+ paymentInterface: 'paymentInterface',
330
+ },
331
+ },
332
+ ),
333
+ );
334
+ });
335
+
336
+ test('should pass the draft to the underlying API layer and return the result unchanged', async () => {
337
+ //Given
338
+ const draft: SavePaymentMethodDraft = {
339
+ customerId: 'customerId',
340
+ method: 'scheme',
341
+ paymentInterface: 'paymentInterface',
342
+ token: 'token',
343
+ interfaceAccount: 'interfaceAccount',
344
+ };
345
+
346
+ const paymentId = randomUUID();
347
+
348
+ const paymentMethod: PaymentMethod = {
349
+ id: paymentId,
350
+ version: 1,
351
+ default: false,
352
+ paymentMethodStatus: 'Active',
353
+ createdAt: '',
354
+ lastModifiedAt: '',
355
+ customer: {
356
+ id: 'customerId',
357
+ typeId: 'customer',
358
+ },
359
+ paymentInterface: 'paymentInterface',
360
+ interfaceAccount: 'interfaceAccount',
361
+ token: {
362
+ value: 'token',
363
+ },
364
+ };
365
+
366
+ vi.spyOn(
367
+ ctPaymentMethodService,
368
+ 'doesTokenBelongsToCustomer',
369
+ ).mockResolvedValueOnce(false);
370
+
371
+ const spyer = vi
372
+ .spyOn(paymentMethodAPI, 'create')
373
+ .mockResolvedValueOnce(paymentMethod);
374
+
375
+ //When
376
+ const result = await ctPaymentMethodService.save(draft);
377
+
378
+ //Then
379
+ expect(spyer).toHaveBeenCalledWith({
380
+ customer: {
381
+ id: 'customerId',
382
+ typeId: 'customer',
383
+ },
384
+ paymentInterface: 'paymentInterface',
385
+ interfaceAccount: 'interfaceAccount',
386
+ method: 'scheme',
387
+ default: false,
388
+ paymentMethodStatus: 'Active',
389
+ token: {
390
+ value: 'token',
391
+ },
392
+ });
393
+ expect(result).toStrictEqual(paymentMethod);
394
+ });
395
+ });
396
+
397
+ describe('update', () => {
398
+ test('should throw an ErrorInternalConstraintViolated if the provided customerId does not match the the given paymentMethodId', async () => {
399
+ //Given
400
+ const paymentId = randomUUID();
401
+ const customerId = randomUUID();
402
+
403
+ const spyer = vi
404
+ .spyOn(paymentMethodAPI, 'checkIfExistsByPredicate')
405
+ .mockResolvedValueOnce(false);
406
+
407
+ //When
408
+ const result = ctPaymentMethodService.update({
409
+ actions: [],
410
+ customerId: customerId,
411
+ resource: {
412
+ id: paymentId,
413
+ version: 1,
414
+ },
415
+ });
416
+
417
+ //Then
418
+ await expect(result).rejects.toThrow(
419
+ new ErrorInternalConstraintViolated(
420
+ `The provided payment-method '${paymentId}' does not have a customer '${customerId}' set`,
421
+ {
422
+ privateFields: {
423
+ predicateQueryString: `id="${paymentId}" and customer(id="${customerId}")`,
424
+ },
425
+ fields: {
426
+ customerId,
427
+ paymentMethodId: paymentId,
428
+ },
429
+ },
430
+ ),
431
+ );
432
+
433
+ expect(spyer).toHaveBeenCalledWith({
434
+ queryString: `id="${paymentId}" and customer(id="${customerId}")`,
435
+ });
436
+ });
437
+
438
+ test('should pass the update actions to the underlying API layer and return the result unchanged', async () => {
439
+ //Given
440
+ const paymentId = randomUUID();
441
+ const customerId = randomUUID();
442
+
443
+ const paymentMethod: PaymentMethod = {
444
+ id: paymentId,
445
+ version: 1,
446
+ default: false,
447
+ paymentMethodStatus: 'Active',
448
+ createdAt: '',
449
+ lastModifiedAt: '',
450
+ customer: {
451
+ id: customerId,
452
+ typeId: 'customer',
453
+ },
454
+ };
455
+
456
+ vi.spyOn(
457
+ paymentMethodAPI,
458
+ 'checkIfExistsByPredicate',
459
+ ).mockResolvedValueOnce(true);
460
+ const spyer = vi
461
+ .spyOn(paymentMethodAPI, 'update')
462
+ .mockResolvedValueOnce(paymentMethod);
463
+
464
+ //When
465
+ const updateActions: PaymentMethodUpdateAction[] = [
466
+ {
467
+ action: 'setInterfaceAccount',
468
+ interfaceAccount: 'new-interfaceAccount-value',
469
+ },
470
+ ];
471
+ const result = await ctPaymentMethodService.update({
472
+ actions: updateActions,
473
+ customerId: customerId,
474
+ resource: {
475
+ id: paymentId,
476
+ version: 1,
477
+ },
478
+ });
479
+
480
+ //Then
481
+ expect(result).toEqual(paymentMethod);
482
+ expect(spyer).toHaveBeenCalledWith({
483
+ actions: updateActions,
484
+ resource: {
485
+ id: paymentId,
486
+ version: paymentMethod.version,
487
+ },
488
+ });
489
+ });
490
+ });
491
+
492
+ describe('delete', () => {
493
+ test('should throw an ErrorInternalConstraintViolated if the provided customerId does not match the the given paymentMethodId', async () => {
494
+ //Given
495
+ const paymentId = randomUUID();
496
+ const customerId = randomUUID();
497
+
498
+ const spyer = vi
499
+ .spyOn(paymentMethodAPI, 'checkIfExistsByPredicate')
500
+ .mockResolvedValueOnce(false);
501
+
502
+ //When
503
+ const result = ctPaymentMethodService.delete({
504
+ customerId: customerId,
505
+ id: paymentId,
506
+ version: 1,
507
+ });
508
+
509
+ //Then
510
+ await expect(result).rejects.toThrow(
511
+ new ErrorInternalConstraintViolated(
512
+ `The provided payment-method '${paymentId}' does not have a customer '${customerId}' set`,
513
+ {
514
+ privateFields: {
515
+ predicateQueryString: `id="${paymentId}" and customer(id="${customerId}")`,
516
+ },
517
+ fields: {
518
+ customerId: customerId,
519
+ paymentMethodId: paymentId,
520
+ },
521
+ },
522
+ ),
523
+ );
524
+
525
+ expect(spyer).toHaveBeenCalledWith({
526
+ queryString: `id="${paymentId}" and customer(id="${customerId}")`,
527
+ });
528
+ });
529
+
530
+ test('should pass the delete params to the underlying API layer and return the result unchanged', async () => {
531
+ //Given
532
+ const paymentId = randomUUID();
533
+ const customerId = randomUUID();
534
+
535
+ const paymentMethod: PaymentMethod = {
536
+ id: paymentId,
537
+ version: 1,
538
+ default: false,
539
+ paymentMethodStatus: 'Active',
540
+ createdAt: '',
541
+ lastModifiedAt: '',
542
+ customer: {
543
+ id: customerId,
544
+ typeId: 'customer',
545
+ },
546
+ };
547
+
548
+ vi.spyOn(
549
+ paymentMethodAPI,
550
+ 'checkIfExistsByPredicate',
551
+ ).mockResolvedValueOnce(true);
552
+ const spyer = vi
553
+ .spyOn(paymentMethodAPI, 'delete')
554
+ .mockResolvedValueOnce(paymentMethod);
555
+
556
+ //When
557
+ const result = await ctPaymentMethodService.delete({
558
+ customerId: customerId,
559
+ id: paymentId,
560
+ version: 1,
561
+ });
562
+
563
+ //Then
564
+ expect(result).toStrictEqual(paymentMethod);
565
+ expect(spyer).toHaveBeenCalledWith({
566
+ id: paymentId,
567
+ version: 1,
568
+ });
569
+ });
570
+ });
571
+
572
+ describe('doesTokenBelongsToCustomer', () => {
573
+ test('should return "true" if the token belongs to the customer', async () => {
574
+ //Given
575
+ const paymentId = randomUUID();
576
+ const customerId = randomUUID();
577
+ const paymentInterfaceValue = 'psp-interface';
578
+ const interfaceAccountValue = 'interfaceaccount-value';
579
+ const tokenValue = 'tokenValue';
580
+
581
+ const queryResults: PaymentMethodPagedQueryResponse = {
582
+ limit: 100,
583
+ offset: 0,
584
+ count: 2,
585
+ total: 2,
586
+ results: [
587
+ {
588
+ id: paymentId,
589
+ version: 1,
590
+ default: false,
591
+ paymentMethodStatus: 'Active',
592
+ paymentInterface: paymentInterfaceValue,
593
+ interfaceAccount: interfaceAccountValue,
594
+ createdAt: '',
595
+ lastModifiedAt: '',
596
+ customer: {
597
+ id: customerId,
598
+ typeId: 'customer',
599
+ },
600
+ token: {
601
+ value: 'differentTokenValue',
602
+ },
603
+ },
604
+ {
605
+ id: 'db745569-56d8-4a2f-b78e-3f0cee37dd86',
606
+ version: 1,
607
+ default: false,
608
+ paymentMethodStatus: 'Active',
609
+ paymentInterface: paymentInterfaceValue,
610
+ interfaceAccount: interfaceAccountValue,
611
+ createdAt: '',
612
+ lastModifiedAt: '',
613
+ customer: {
614
+ id: customerId,
615
+ typeId: 'customer',
616
+ },
617
+ token: {
618
+ value: tokenValue,
619
+ },
620
+ },
621
+ ],
622
+ };
623
+
624
+ const spyer = vi
625
+ .spyOn(paymentMethodAPI, 'find')
626
+ .mockResolvedValueOnce(queryResults);
627
+
628
+ //When
629
+ const result = await ctPaymentMethodService.doesTokenBelongsToCustomer({
630
+ tokenValue: tokenValue,
631
+ interfaceAccount: interfaceAccountValue,
632
+ customerId: customerId,
633
+ paymentInterface: paymentInterfaceValue,
634
+ });
635
+
636
+ //Then
637
+ expect(result).toBeTruthy();
638
+ expect(spyer).toHaveBeenCalledWith({
639
+ queryString: `customer(id="${customerId}") and paymentInterface="${paymentInterfaceValue}" and interfaceAccount="${interfaceAccountValue}"`,
640
+ });
641
+ });
642
+
643
+ test('should return "false" if token does not belong to the customer', async () => {
644
+ //Given
645
+ const paymentId = randomUUID();
646
+ const customerId = randomUUID();
647
+ const paymentInterfaceValue = 'psp-interface';
648
+ const interfaceAccountValue = 'interfaceaccount-value';
649
+ const tokenValue = 'tokenValue';
650
+
651
+ const queryResults: PaymentMethodPagedQueryResponse = {
652
+ limit: 100,
653
+ offset: 0,
654
+ count: 1,
655
+ total: 1,
656
+ results: [
657
+ {
658
+ id: paymentId,
659
+ version: 1,
660
+ default: false,
661
+ paymentMethodStatus: 'Active',
662
+ paymentInterface: paymentInterfaceValue,
663
+ interfaceAccount: interfaceAccountValue,
664
+ createdAt: '',
665
+ lastModifiedAt: '',
666
+ customer: {
667
+ id: customerId,
668
+ typeId: 'customer',
669
+ },
670
+ token: {
671
+ value: tokenValue,
672
+ },
673
+ },
674
+ ],
675
+ };
676
+
677
+ const spyer = vi
678
+ .spyOn(paymentMethodAPI, 'find')
679
+ .mockResolvedValueOnce(queryResults);
680
+
681
+ //When
682
+ const result = await ctPaymentMethodService.doesTokenBelongsToCustomer({
683
+ tokenValue: 'differentTokenValue',
684
+ interfaceAccount: interfaceAccountValue,
685
+ customerId: customerId,
686
+ paymentInterface: paymentInterfaceValue,
687
+ });
688
+
689
+ //Then
690
+ expect(result).toBeFalsy();
691
+ expect(spyer).toHaveBeenCalledWith({
692
+ queryString: `customer(id="${customerId}") and paymentInterface="${paymentInterfaceValue}" and interfaceAccount="${interfaceAccountValue}"`,
693
+ });
694
+ });
695
+ });
696
+
697
+ describe('getByTokenValue', () => {
698
+ test('should return the payment method if the provided tokenValue matches with an payment method which has the corresponding token value set', async () => {
699
+ //Given
700
+ const paymentId = randomUUID();
701
+ const customerId = randomUUID();
702
+ const paymentInterfaceValue = 'psp-interface';
703
+ const interfaceAccountValue = 'interfaceaccount-value';
704
+ const tokenValue = 'tokenValue';
705
+
706
+ const queryResults: PaymentMethodPagedQueryResponse = {
707
+ limit: 100,
708
+ offset: 0,
709
+ count: 1,
710
+ total: 1,
711
+ results: [
712
+ {
713
+ id: paymentId,
714
+ version: 1,
715
+ default: false,
716
+ paymentMethodStatus: 'Active',
717
+ paymentInterface: paymentInterfaceValue,
718
+ interfaceAccount: interfaceAccountValue,
719
+ createdAt: '',
720
+ lastModifiedAt: '',
721
+ customer: {
722
+ id: customerId,
723
+ typeId: 'customer',
724
+ },
725
+ token: {
726
+ value: tokenValue,
727
+ },
728
+ },
729
+ ],
730
+ };
731
+
732
+ const spyer = vi
733
+ .spyOn(paymentMethodAPI, 'find')
734
+ .mockResolvedValueOnce(queryResults);
735
+
736
+ //When
737
+ const result = await ctPaymentMethodService.getByTokenValue({
738
+ tokenValue: tokenValue,
739
+ interfaceAccount: interfaceAccountValue,
740
+ customerId: customerId,
741
+ paymentInterface: paymentInterfaceValue,
742
+ });
743
+
744
+ //Then
745
+ expect(result).toStrictEqual(queryResults.results[0]);
746
+ expect(spyer).toHaveBeenCalledWith({
747
+ queryString: `customer(id="${customerId}") and paymentInterface="${paymentInterfaceValue}" and interfaceAccount="${interfaceAccountValue}"`,
748
+ });
749
+ });
750
+
751
+ test.skip('should throw an "ErrorInternalConstraintViolated" if more then one payment method with the same token value is found', async () => {
752
+ //Given
753
+ const paymentId = randomUUID();
754
+ const customerId = randomUUID();
755
+ const paymentInterfaceValue = 'psp-interface';
756
+ const interfaceAccountValue = 'interfaceaccount-value';
757
+ const tokenValue = 'tokenValue';
758
+
759
+ const queryResults: PaymentMethodPagedQueryResponse = {
760
+ limit: 100,
761
+ offset: 0,
762
+ count: 2,
763
+ total: 2,
764
+ results: [
765
+ {
766
+ id: paymentId,
767
+ version: 1,
768
+ default: false,
769
+ paymentMethodStatus: 'Active',
770
+ paymentInterface: paymentInterfaceValue,
771
+ interfaceAccount: interfaceAccountValue,
772
+ createdAt: '',
773
+ lastModifiedAt: '',
774
+ customer: {
775
+ id: customerId,
776
+ typeId: 'customer',
777
+ },
778
+ token: {
779
+ value: tokenValue,
780
+ },
781
+ },
782
+ {
783
+ id: '28e2fadb-2f3d-493c-ab75-d912cc2375f5',
784
+ version: 1,
785
+ default: false,
786
+ paymentMethodStatus: 'Active',
787
+ paymentInterface: paymentInterfaceValue,
788
+ interfaceAccount: interfaceAccountValue,
789
+ createdAt: '',
790
+ lastModifiedAt: '',
791
+ customer: {
792
+ id: customerId,
793
+ typeId: 'customer',
794
+ },
795
+ token: {
796
+ value: tokenValue,
797
+ },
798
+ },
799
+ ],
800
+ };
801
+
802
+ const spyer = vi
803
+ .spyOn(paymentMethodAPI, 'find')
804
+ .mockResolvedValueOnce(queryResults);
805
+
806
+ //When
807
+ const result = ctPaymentMethodService.getByTokenValue({
808
+ tokenValue: tokenValue,
809
+ interfaceAccount: interfaceAccountValue,
810
+ customerId: customerId,
811
+ paymentInterface: paymentInterfaceValue,
812
+ });
813
+
814
+ //Then
815
+ await expect(result).rejects.toThrow(
816
+ new ErrorInternalConstraintViolated(
817
+ 'Found more then one payment method with the same token value.',
818
+ {
819
+ privateFields: {
820
+ customerId,
821
+ interfaceAccount: 'interfaceaccount-value',
822
+ paymentInterface: 'psp-interface',
823
+ paymentMethodIds: [
824
+ paymentId,
825
+ '28e2fadb-2f3d-493c-ab75-d912cc2375f5',
826
+ ],
827
+ },
828
+ },
829
+ ),
830
+ );
831
+ expect(spyer).toHaveBeenCalledWith({
832
+ queryString: `customer(id="${customerId}") and paymentInterface="${paymentInterfaceValue}" and interfaceAccount="${interfaceAccountValue}"`,
833
+ });
834
+ });
835
+
836
+ test.skip('should throw an "ErrorResourceNotFound" if no payment method could be found that matches with the given tokenValue', async () => {
837
+ //Given
838
+ const customerId = randomUUID();
839
+ const paymentInterfaceValue = 'psp-interface';
840
+ const interfaceAccountValue = 'interfaceaccount-value';
841
+ const tokenValue = 'tokenValue';
842
+
843
+ const queryResults: PaymentMethodPagedQueryResponse = {
844
+ limit: 100,
845
+ offset: 0,
846
+ count: 0,
847
+ total: 0,
848
+ results: [],
849
+ };
850
+
851
+ const spyer = vi
852
+ .spyOn(paymentMethodAPI, 'find')
853
+ .mockResolvedValueOnce(queryResults);
854
+
855
+ //When
856
+ const result = ctPaymentMethodService.getByTokenValue({
857
+ tokenValue: tokenValue,
858
+ interfaceAccount: interfaceAccountValue,
859
+ customerId: customerId,
860
+ paymentInterface: paymentInterfaceValue,
861
+ });
862
+
863
+ //Then
864
+ await expect(result).rejects.toThrow(
865
+ new ErrorResourceNotFound('customer', customerId, {
866
+ privateFields: {
867
+ customerId: customerId,
868
+ paymentInterface: paymentInterfaceValue,
869
+ interfaceAccount: interfaceAccountValue,
870
+ },
871
+ }),
872
+ );
873
+ expect(spyer).toHaveBeenCalledWith({
874
+ queryString: `customer(id="${customerId}") and paymentInterface="${paymentInterfaceValue}" and interfaceAccount="${interfaceAccountValue}"`,
875
+ });
876
+ });
877
+ });
878
+ });