@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,1616 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import type {
3
+ Cart,
4
+ CartPagedQueryResponse,
5
+ Order,
6
+ Payment,
7
+ } from '@commercetools/platform-sdk';
8
+ import { HttpResponse, http } from 'msw';
9
+ import { setupServer } from 'msw/node';
10
+ import { RequestContextProvider } from '@/api/index.js';
11
+ import { DefaultCommercetoolsAPI } from '@/commercetools/api/root-api.js';
12
+ import { DefaultCartService } from '@/commercetools/services/ct-cart.service.js';
13
+ import { CommercetoolsLogger } from '@/logger/commercetools-logger.js';
14
+ import { authToken } from '@/mocks/auth.mock.js';
15
+ import type { SessionPrincipal } from '@/security/authn/types/authn.type.js';
16
+ import { SessionAuthentication } from '@/security/index.js';
17
+
18
+ const mockServer = setupServer();
19
+ const projectKey = 'test';
20
+ const contextProvider = new RequestContextProvider({
21
+ getContextFn: () => ({
22
+ correlationId: 'correlation-id',
23
+ requestId: 'request-id',
24
+ }),
25
+ updateContextFn: () => {},
26
+ });
27
+
28
+ const logger = new CommercetoolsLogger({ contextProvider, projectKey });
29
+
30
+ const ctAPI = new DefaultCommercetoolsAPI({
31
+ apiUrl: 'http://api.test.com',
32
+ authUrl: 'http://auth.test.com',
33
+ clientId: 'clientId',
34
+ clientSecret: 'clientSecret',
35
+ projectKey,
36
+ contextProvider,
37
+ logger,
38
+ httpClient: (input: string | URL | Request, init?: RequestInit) => {
39
+ return fetch(input, init);
40
+ },
41
+ });
42
+ // TODO finalise the test
43
+ const ctCartService = new DefaultCartService({
44
+ ctAPI,
45
+ logger,
46
+ contextProvider,
47
+ });
48
+
49
+ describe('commercetools cart service', () => {
50
+ beforeAll(() =>
51
+ mockServer.listen({
52
+ onUnhandledRequest: 'error',
53
+ }),
54
+ );
55
+ afterAll(() => mockServer.close());
56
+
57
+ describe('getPaymentAmount', () => {
58
+ test('should return the total net amount if no taxed price and does not exist any payment approved', async () => {
59
+ //Given
60
+ const cart = {
61
+ id: randomUUID(),
62
+ version: 1,
63
+ totalPrice: {
64
+ currencyCode: 'EUR',
65
+ centAmount: 100,
66
+ },
67
+ } as Cart;
68
+
69
+ //When
70
+ const result = await ctCartService.getPaymentAmount({ cart });
71
+
72
+ //Then
73
+ expect(result).toEqual({
74
+ currencyCode: 'EUR',
75
+ centAmount: 100,
76
+ });
77
+ });
78
+
79
+ test('should return the total gross amount if taxed price and does not exist any payment approved', async () => {
80
+ //Given
81
+ const cart = {
82
+ id: randomUUID(),
83
+ version: 1,
84
+ taxedPrice: {
85
+ totalGross: {
86
+ currencyCode: 'EUR',
87
+ centAmount: 120,
88
+ },
89
+ },
90
+ totalPrice: {
91
+ currencyCode: 'EUR',
92
+ centAmount: 100,
93
+ },
94
+ } as Cart;
95
+
96
+ //When
97
+ const result = await ctCartService.getPaymentAmount({ cart });
98
+
99
+ //Then
100
+ expect(result).toEqual({
101
+ currencyCode: 'EUR',
102
+ centAmount: 120,
103
+ });
104
+ });
105
+
106
+ test('should discount the existent payment approved from the cart gross price', async () => {
107
+ //Given
108
+ const cart = {
109
+ id: randomUUID(),
110
+ version: 1,
111
+ taxedPrice: {
112
+ totalGross: {
113
+ currencyCode: 'EUR',
114
+ centAmount: 120,
115
+ },
116
+ },
117
+ totalPrice: {
118
+ currencyCode: 'EUR',
119
+ centAmount: 100,
120
+ },
121
+ paymentInfo: {
122
+ payments: [
123
+ {
124
+ typeId: 'payment',
125
+ id: randomUUID(),
126
+ },
127
+ {
128
+ typeId: 'payment',
129
+ id: randomUUID(),
130
+ },
131
+ ],
132
+ },
133
+ } as Cart;
134
+
135
+ const approvedPayment = {
136
+ id: cart.paymentInfo?.payments[0].id,
137
+ version: 1,
138
+ amountPlanned: {
139
+ currencyCode: 'EUR',
140
+ centAmount: 100,
141
+ },
142
+ transactions: [
143
+ {
144
+ type: 'Authorization',
145
+ state: 'Success',
146
+ amount: {
147
+ currencyCode: 'EUR',
148
+ centAmount: 100,
149
+ },
150
+ },
151
+ ],
152
+ } as Payment;
153
+
154
+ const rejectedPayment = {
155
+ id: cart.paymentInfo?.payments[1].id,
156
+ version: 1,
157
+ amountPlanned: {
158
+ currencyCode: 'EUR',
159
+ centAmount: 100,
160
+ },
161
+ transactions: [
162
+ {
163
+ type: 'Authorization',
164
+ state: 'Failure',
165
+ amount: {
166
+ currencyCode: 'EUR',
167
+ centAmount: 100,
168
+ },
169
+ },
170
+ ],
171
+ } as Payment;
172
+
173
+ mockServer.use(
174
+ mockGetAuthToken(200, authToken),
175
+ mockGetPaymentById(approvedPayment.id, 200, approvedPayment),
176
+ mockGetPaymentById(rejectedPayment.id, 200, rejectedPayment),
177
+ );
178
+
179
+ //When
180
+ const result = await ctCartService.getPaymentAmount({ cart });
181
+
182
+ //Then
183
+ expect(result).toEqual({
184
+ currencyCode: 'EUR',
185
+ centAmount: 20,
186
+ });
187
+ });
188
+
189
+ test('should not discount the existent payment reverted from the cart gross price', async () => {
190
+ //Given
191
+ const cart = {
192
+ id: randomUUID(),
193
+ version: 1,
194
+ taxedPrice: {
195
+ totalGross: {
196
+ currencyCode: 'EUR',
197
+ centAmount: 120,
198
+ },
199
+ },
200
+ totalPrice: {
201
+ currencyCode: 'EUR',
202
+ centAmount: 100,
203
+ },
204
+ paymentInfo: {
205
+ payments: [
206
+ {
207
+ typeId: 'payment',
208
+ id: randomUUID(),
209
+ },
210
+ ],
211
+ },
212
+ } as Cart;
213
+
214
+ const revertedPayment = {
215
+ id: cart.paymentInfo?.payments[0].id,
216
+ version: 1,
217
+ amountPlanned: {
218
+ currencyCode: 'EUR',
219
+ centAmount: 100,
220
+ },
221
+ transactions: [
222
+ {
223
+ type: 'Authorization',
224
+ state: 'Success',
225
+ amount: {
226
+ currencyCode: 'EUR',
227
+ centAmount: 100,
228
+ },
229
+ },
230
+ {
231
+ type: 'CancelAuthorization',
232
+ state: 'Success',
233
+ amount: {
234
+ currencyCode: 'EUR',
235
+ centAmount: 100,
236
+ },
237
+ },
238
+ ],
239
+ } as Payment;
240
+
241
+ mockServer.use(
242
+ mockGetAuthToken(200, authToken),
243
+ mockGetPaymentById(revertedPayment.id, 200, revertedPayment),
244
+ );
245
+
246
+ //When
247
+ const result = await ctCartService.getPaymentAmount({ cart });
248
+
249
+ //Then
250
+ expect(result).toEqual({
251
+ currencyCode: 'EUR',
252
+ centAmount: 120,
253
+ });
254
+ });
255
+
256
+ test('should return an error when the cart is full paid', async () => {
257
+ //Given
258
+ const cart = {
259
+ id: randomUUID(),
260
+ version: 1,
261
+ taxedPrice: {
262
+ totalGross: {
263
+ currencyCode: 'EUR',
264
+ centAmount: 120,
265
+ },
266
+ },
267
+ totalPrice: {
268
+ currencyCode: 'EUR',
269
+ centAmount: 100,
270
+ },
271
+ paymentInfo: {
272
+ payments: [
273
+ {
274
+ typeId: 'payment',
275
+ id: randomUUID(),
276
+ },
277
+ {
278
+ typeId: 'payment',
279
+ id: randomUUID(),
280
+ },
281
+ ],
282
+ },
283
+ } as Cart;
284
+
285
+ const approvedPayment = {
286
+ id: cart.paymentInfo?.payments[0].id,
287
+ version: 1,
288
+ amountPlanned: {
289
+ currencyCode: 'EUR',
290
+ centAmount: 100,
291
+ },
292
+ transactions: [
293
+ {
294
+ type: 'Authorization',
295
+ state: 'Success',
296
+ amount: {
297
+ currencyCode: 'EUR',
298
+ centAmount: 100,
299
+ },
300
+ },
301
+ ],
302
+ } as Payment;
303
+
304
+ const anotherApprovedPayment = {
305
+ id: cart.paymentInfo?.payments[1].id,
306
+ version: 1,
307
+ amountPlanned: {
308
+ currencyCode: 'EUR',
309
+ centAmount: 20,
310
+ },
311
+ transactions: [
312
+ {
313
+ type: 'Authorization',
314
+ state: 'Success',
315
+ amount: {
316
+ currencyCode: 'EUR',
317
+ centAmount: 20,
318
+ },
319
+ },
320
+ ],
321
+ } as Payment;
322
+
323
+ mockServer.use(
324
+ mockGetAuthToken(200, authToken),
325
+ mockGetPaymentById(approvedPayment.id, 200, approvedPayment),
326
+ mockGetPaymentById(
327
+ anotherApprovedPayment.id,
328
+ 200,
329
+ anotherApprovedPayment,
330
+ ),
331
+ );
332
+
333
+ //When
334
+ const resultPromise = ctCartService.getPaymentAmount({ cart });
335
+
336
+ //Then
337
+ await expect(resultPromise).rejects.toThrow(
338
+ 'The cart has already been paid in full',
339
+ );
340
+ });
341
+ });
342
+
343
+ describe('getPlannedPaymentAmount', () => {
344
+ test('should return the total net amount if no taxed price and does not exist any payment approved', async () => {
345
+ //Given
346
+ const cart = {
347
+ id: randomUUID(),
348
+ version: 1,
349
+ totalPrice: {
350
+ currencyCode: 'EUR',
351
+ centAmount: 100,
352
+ },
353
+ } as Cart;
354
+
355
+ //When
356
+ const result = await ctCartService.getPlannedPaymentAmount({ cart });
357
+
358
+ //Then
359
+ expect(result).toEqual({
360
+ currencyCode: 'EUR',
361
+ centAmount: 100,
362
+ });
363
+ });
364
+
365
+ test('should return the total gross amount if taxed price and does not exist any payment approved', async () => {
366
+ //Given
367
+ const cart = {
368
+ id: randomUUID(),
369
+ version: 1,
370
+ taxedPrice: {
371
+ totalGross: {
372
+ currencyCode: 'EUR',
373
+ centAmount: 120,
374
+ },
375
+ },
376
+ totalPrice: {
377
+ currencyCode: 'EUR',
378
+ centAmount: 100,
379
+ },
380
+ } as Cart;
381
+
382
+ //When
383
+ const result = await ctCartService.getPlannedPaymentAmount({ cart });
384
+
385
+ //Then
386
+ expect(result).toEqual({
387
+ currencyCode: 'EUR',
388
+ centAmount: 120,
389
+ });
390
+ });
391
+
392
+ test('should discount the existent payment approved from the cart gross price', async () => {
393
+ //Given
394
+ const cart = {
395
+ id: randomUUID(),
396
+ version: 1,
397
+ taxedPrice: {
398
+ totalGross: {
399
+ currencyCode: 'EUR',
400
+ centAmount: 120,
401
+ },
402
+ },
403
+ totalPrice: {
404
+ currencyCode: 'EUR',
405
+ centAmount: 100,
406
+ },
407
+ paymentInfo: {
408
+ payments: [
409
+ {
410
+ typeId: 'payment',
411
+ id: randomUUID(),
412
+ },
413
+ {
414
+ typeId: 'payment',
415
+ id: randomUUID(),
416
+ },
417
+ ],
418
+ },
419
+ } as Cart;
420
+
421
+ const approvedPayment = {
422
+ id: cart.paymentInfo?.payments[0].id,
423
+ version: 1,
424
+ amountPlanned: {
425
+ currencyCode: 'EUR',
426
+ centAmount: 100,
427
+ },
428
+ transactions: [
429
+ {
430
+ type: 'Authorization',
431
+ state: 'Success',
432
+ amount: {
433
+ currencyCode: 'EUR',
434
+ centAmount: 100,
435
+ },
436
+ },
437
+ ],
438
+ } as Payment;
439
+
440
+ const rejectedPayment = {
441
+ id: cart.paymentInfo?.payments[1].id,
442
+ version: 1,
443
+ amountPlanned: {
444
+ currencyCode: 'EUR',
445
+ centAmount: 100,
446
+ },
447
+ transactions: [
448
+ {
449
+ type: 'Authorization',
450
+ state: 'Failure',
451
+ amount: {
452
+ currencyCode: 'EUR',
453
+ centAmount: 100,
454
+ },
455
+ },
456
+ ],
457
+ } as Payment;
458
+
459
+ mockServer.use(
460
+ mockGetAuthToken(200, authToken),
461
+ mockGetPaymentById(approvedPayment.id, 200, approvedPayment),
462
+ mockGetPaymentById(rejectedPayment.id, 200, rejectedPayment),
463
+ );
464
+
465
+ //When
466
+ const result = await ctCartService.getPlannedPaymentAmount({ cart });
467
+
468
+ //Then
469
+ expect(result).toEqual({
470
+ currencyCode: 'EUR',
471
+ centAmount: 20,
472
+ });
473
+ });
474
+
475
+ test('should not discount the existent payment reverted from the cart gross price', async () => {
476
+ //Given
477
+ const cart = {
478
+ id: randomUUID(),
479
+ version: 1,
480
+ taxedPrice: {
481
+ totalGross: {
482
+ currencyCode: 'EUR',
483
+ centAmount: 120,
484
+ },
485
+ },
486
+ totalPrice: {
487
+ currencyCode: 'EUR',
488
+ centAmount: 100,
489
+ },
490
+ paymentInfo: {
491
+ payments: [
492
+ {
493
+ typeId: 'payment',
494
+ id: randomUUID(),
495
+ },
496
+ ],
497
+ },
498
+ } as Cart;
499
+
500
+ const revertedPayment = {
501
+ id: cart.paymentInfo?.payments[0].id,
502
+ version: 1,
503
+ amountPlanned: {
504
+ currencyCode: 'EUR',
505
+ centAmount: 100,
506
+ },
507
+ transactions: [
508
+ {
509
+ type: 'Authorization',
510
+ state: 'Success',
511
+ amount: {
512
+ currencyCode: 'EUR',
513
+ centAmount: 100,
514
+ },
515
+ },
516
+ {
517
+ type: 'CancelAuthorization',
518
+ state: 'Success',
519
+ amount: {
520
+ currencyCode: 'EUR',
521
+ centAmount: 100,
522
+ },
523
+ },
524
+ ],
525
+ } as Payment;
526
+
527
+ mockServer.use(
528
+ mockGetAuthToken(200, authToken),
529
+ mockGetPaymentById(revertedPayment.id, 200, revertedPayment),
530
+ );
531
+
532
+ //When
533
+ const result = await ctCartService.getPlannedPaymentAmount({ cart });
534
+
535
+ //Then
536
+ expect(result).toEqual({
537
+ currencyCode: 'EUR',
538
+ centAmount: 120,
539
+ });
540
+ });
541
+
542
+ test('should return an error when the cart is full paid', async () => {
543
+ //Given
544
+ const cart = {
545
+ id: randomUUID(),
546
+ version: 1,
547
+ taxedPrice: {
548
+ totalGross: {
549
+ currencyCode: 'EUR',
550
+ centAmount: 120,
551
+ },
552
+ },
553
+ totalPrice: {
554
+ currencyCode: 'EUR',
555
+ centAmount: 100,
556
+ },
557
+ paymentInfo: {
558
+ payments: [
559
+ {
560
+ typeId: 'payment',
561
+ id: randomUUID(),
562
+ },
563
+ {
564
+ typeId: 'payment',
565
+ id: randomUUID(),
566
+ },
567
+ ],
568
+ },
569
+ } as Cart;
570
+
571
+ const approvedPayment = {
572
+ id: cart.paymentInfo?.payments[0].id,
573
+ version: 1,
574
+ amountPlanned: {
575
+ currencyCode: 'EUR',
576
+ centAmount: 100,
577
+ },
578
+ transactions: [
579
+ {
580
+ type: 'Authorization',
581
+ state: 'Success',
582
+ amount: {
583
+ currencyCode: 'EUR',
584
+ centAmount: 100,
585
+ },
586
+ },
587
+ ],
588
+ } as Payment;
589
+
590
+ const anotherApprovedPayment = {
591
+ id: cart.paymentInfo?.payments[1].id,
592
+ version: 1,
593
+ amountPlanned: {
594
+ currencyCode: 'EUR',
595
+ centAmount: 20,
596
+ },
597
+ transactions: [
598
+ {
599
+ type: 'Authorization',
600
+ state: 'Success',
601
+ amount: {
602
+ currencyCode: 'EUR',
603
+ centAmount: 20,
604
+ },
605
+ },
606
+ ],
607
+ } as Payment;
608
+
609
+ mockServer.use(
610
+ mockGetAuthToken(200, authToken),
611
+ mockGetPaymentById(approvedPayment.id, 200, approvedPayment),
612
+ mockGetPaymentById(
613
+ anotherApprovedPayment.id,
614
+ 200,
615
+ anotherApprovedPayment,
616
+ ),
617
+ );
618
+
619
+ //When
620
+ const resultPromise = ctCartService.getPlannedPaymentAmount({ cart });
621
+
622
+ //Then
623
+ await expect(resultPromise).rejects.toThrow(
624
+ 'The cart has already been paid in full',
625
+ );
626
+ });
627
+ });
628
+
629
+ describe('getPlannedPaymentAmount with giftCardPlannedAmount set in context', () => {
630
+ const contextProvider = new RequestContextProvider({
631
+ getContextFn: () => ({
632
+ correlationId: 'correlation-id',
633
+ requestId: 'request-id',
634
+ authentication: new SessionAuthentication('some-id', {
635
+ giftCardPlannedAmount: {
636
+ centAmount: 10,
637
+ currencyCode: 'EUR',
638
+ },
639
+ } as SessionPrincipal),
640
+ }),
641
+ updateContextFn: () => {},
642
+ });
643
+
644
+ const ctCartService = new DefaultCartService({
645
+ ctAPI,
646
+ logger,
647
+ contextProvider,
648
+ });
649
+
650
+ test('should return the total net amount if no taxed price and does not exist any payment approved', async () => {
651
+ //Given
652
+ const cart = {
653
+ id: randomUUID(),
654
+ version: 1,
655
+ totalPrice: {
656
+ currencyCode: 'EUR',
657
+ centAmount: 100,
658
+ },
659
+ } as Cart;
660
+
661
+ //When
662
+ const result = await ctCartService.getPlannedPaymentAmount({ cart });
663
+
664
+ //Then
665
+ expect(result).toEqual({
666
+ currencyCode: 'EUR',
667
+ centAmount: 90,
668
+ });
669
+ });
670
+
671
+ test('should return the total gross amount if taxed price and does not exist any payment approved', async () => {
672
+ //Given
673
+ const cart = {
674
+ id: randomUUID(),
675
+ version: 1,
676
+ taxedPrice: {
677
+ totalGross: {
678
+ currencyCode: 'EUR',
679
+ centAmount: 120,
680
+ },
681
+ },
682
+ totalPrice: {
683
+ currencyCode: 'EUR',
684
+ centAmount: 100,
685
+ },
686
+ } as Cart;
687
+
688
+ //When
689
+ const result = await ctCartService.getPlannedPaymentAmount({ cart });
690
+
691
+ //Then
692
+ expect(result).toEqual({
693
+ currencyCode: 'EUR',
694
+ centAmount: 110,
695
+ });
696
+ });
697
+
698
+ test('should discount the existent payment approved from the cart gross price', async () => {
699
+ //Given
700
+ const cart = {
701
+ id: randomUUID(),
702
+ version: 1,
703
+ taxedPrice: {
704
+ totalGross: {
705
+ currencyCode: 'EUR',
706
+ centAmount: 120,
707
+ },
708
+ },
709
+ totalPrice: {
710
+ currencyCode: 'EUR',
711
+ centAmount: 100,
712
+ },
713
+ paymentInfo: {
714
+ payments: [
715
+ {
716
+ typeId: 'payment',
717
+ id: randomUUID(),
718
+ },
719
+ {
720
+ typeId: 'payment',
721
+ id: randomUUID(),
722
+ },
723
+ ],
724
+ },
725
+ } as Cart;
726
+
727
+ const approvedPayment = {
728
+ id: cart.paymentInfo?.payments[0].id,
729
+ version: 1,
730
+ amountPlanned: {
731
+ currencyCode: 'EUR',
732
+ centAmount: 100,
733
+ },
734
+ transactions: [
735
+ {
736
+ type: 'Authorization',
737
+ state: 'Success',
738
+ amount: {
739
+ currencyCode: 'EUR',
740
+ centAmount: 100,
741
+ },
742
+ },
743
+ ],
744
+ } as Payment;
745
+
746
+ const rejectedPayment = {
747
+ id: cart.paymentInfo?.payments[1].id,
748
+ version: 1,
749
+ amountPlanned: {
750
+ currencyCode: 'EUR',
751
+ centAmount: 100,
752
+ },
753
+ transactions: [
754
+ {
755
+ type: 'Authorization',
756
+ state: 'Failure',
757
+ amount: {
758
+ currencyCode: 'EUR',
759
+ centAmount: 100,
760
+ },
761
+ },
762
+ ],
763
+ } as Payment;
764
+
765
+ mockServer.use(
766
+ mockGetAuthToken(200, authToken),
767
+ mockGetPaymentById(approvedPayment.id, 200, approvedPayment),
768
+ mockGetPaymentById(rejectedPayment.id, 200, rejectedPayment),
769
+ );
770
+
771
+ //When
772
+ const result = await ctCartService.getPlannedPaymentAmount({ cart });
773
+
774
+ //Then
775
+ expect(result).toEqual({
776
+ currencyCode: 'EUR',
777
+ centAmount: 10,
778
+ });
779
+ });
780
+
781
+ test('should not discount the existent payment reverted from the cart gross price', async () => {
782
+ //Given
783
+ const cart = {
784
+ id: randomUUID(),
785
+ version: 1,
786
+ taxedPrice: {
787
+ totalGross: {
788
+ currencyCode: 'EUR',
789
+ centAmount: 120,
790
+ },
791
+ },
792
+ totalPrice: {
793
+ currencyCode: 'EUR',
794
+ centAmount: 100,
795
+ },
796
+ paymentInfo: {
797
+ payments: [
798
+ {
799
+ typeId: 'payment',
800
+ id: randomUUID(),
801
+ },
802
+ ],
803
+ },
804
+ } as Cart;
805
+
806
+ const revertedPayment = {
807
+ id: cart.paymentInfo?.payments[0].id,
808
+ version: 1,
809
+ amountPlanned: {
810
+ currencyCode: 'EUR',
811
+ centAmount: 100,
812
+ },
813
+ transactions: [
814
+ {
815
+ type: 'Authorization',
816
+ state: 'Success',
817
+ amount: {
818
+ currencyCode: 'EUR',
819
+ centAmount: 100,
820
+ },
821
+ },
822
+ {
823
+ type: 'CancelAuthorization',
824
+ state: 'Success',
825
+ amount: {
826
+ currencyCode: 'EUR',
827
+ centAmount: 100,
828
+ },
829
+ },
830
+ ],
831
+ } as Payment;
832
+
833
+ mockServer.use(
834
+ mockGetAuthToken(200, authToken),
835
+ mockGetPaymentById(revertedPayment.id, 200, revertedPayment),
836
+ );
837
+
838
+ //When
839
+ const result = await ctCartService.getPlannedPaymentAmount({ cart });
840
+
841
+ //Then
842
+ expect(result).toEqual({
843
+ currencyCode: 'EUR',
844
+ centAmount: 110,
845
+ });
846
+ });
847
+
848
+ test('should return an error when the cart is full paid', async () => {
849
+ //Given
850
+ const cart = {
851
+ id: randomUUID(),
852
+ version: 1,
853
+ taxedPrice: {
854
+ totalGross: {
855
+ currencyCode: 'EUR',
856
+ centAmount: 120,
857
+ },
858
+ },
859
+ totalPrice: {
860
+ currencyCode: 'EUR',
861
+ centAmount: 100,
862
+ },
863
+ paymentInfo: {
864
+ payments: [
865
+ {
866
+ typeId: 'payment',
867
+ id: randomUUID(),
868
+ },
869
+ {
870
+ typeId: 'payment',
871
+ id: randomUUID(),
872
+ },
873
+ ],
874
+ },
875
+ } as Cart;
876
+
877
+ const approvedPayment = {
878
+ id: cart.paymentInfo?.payments[0].id,
879
+ version: 1,
880
+ amountPlanned: {
881
+ currencyCode: 'EUR',
882
+ centAmount: 100,
883
+ },
884
+ transactions: [
885
+ {
886
+ type: 'Authorization',
887
+ state: 'Success',
888
+ amount: {
889
+ currencyCode: 'EUR',
890
+ centAmount: 100,
891
+ },
892
+ },
893
+ ],
894
+ } as Payment;
895
+
896
+ const anotherApprovedPayment = {
897
+ id: cart.paymentInfo?.payments[1].id,
898
+ version: 1,
899
+ amountPlanned: {
900
+ currencyCode: 'EUR',
901
+ centAmount: 20,
902
+ },
903
+ transactions: [
904
+ {
905
+ type: 'Authorization',
906
+ state: 'Success',
907
+ amount: {
908
+ currencyCode: 'EUR',
909
+ centAmount: 20,
910
+ },
911
+ },
912
+ ],
913
+ } as Payment;
914
+
915
+ mockServer.use(
916
+ mockGetAuthToken(200, authToken),
917
+ mockGetPaymentById(approvedPayment.id, 200, approvedPayment),
918
+ mockGetPaymentById(
919
+ anotherApprovedPayment.id,
920
+ 200,
921
+ anotherApprovedPayment,
922
+ ),
923
+ );
924
+
925
+ //When
926
+ const resultPromise = ctCartService.getPlannedPaymentAmount({ cart });
927
+
928
+ //Then
929
+ await expect(resultPromise).rejects.toThrow(
930
+ 'The cart has already been paid in full',
931
+ );
932
+ });
933
+
934
+ test('should return cart amount when giftCardPlannedAmount currency does not match with cart currency', async () => {
935
+ const contextProvider = new RequestContextProvider({
936
+ getContextFn: () => ({
937
+ correlationId: 'correlation-id',
938
+ requestId: 'request-id',
939
+ authentication: new SessionAuthentication('some-id', {
940
+ giftCardPlannedAmount: {
941
+ centAmount: 10,
942
+ currencyCode: 'USD',
943
+ },
944
+ } as SessionPrincipal),
945
+ }),
946
+ updateContextFn: () => {},
947
+ });
948
+
949
+ const ctCartService = new DefaultCartService({
950
+ ctAPI,
951
+ logger,
952
+ contextProvider,
953
+ });
954
+
955
+ //Given
956
+ const cart = {
957
+ id: randomUUID(),
958
+ version: 1,
959
+ totalPrice: {
960
+ currencyCode: 'EUR',
961
+ centAmount: 100,
962
+ },
963
+ } as Cart;
964
+
965
+ //When
966
+ const result = await ctCartService.getPlannedPaymentAmount({ cart });
967
+
968
+ //Then
969
+ expect(result).toEqual({
970
+ currencyCode: 'EUR',
971
+ centAmount: 100,
972
+ });
973
+ });
974
+ });
975
+
976
+ describe('getOneShippingAddress', () => {
977
+ test('should return undefined if Single and no shippingAddress', async () => {
978
+ //Given
979
+ const cart = {
980
+ id: randomUUID(),
981
+ version: 1,
982
+ shippingMode: 'Single',
983
+ } as Cart;
984
+
985
+ //When
986
+ const result = ctCartService.getOneShippingAddress({ cart });
987
+
988
+ //Then
989
+ expect(result).toBeUndefined();
990
+ });
991
+
992
+ test('should return undefined if Multiple and shipping empty', async () => {
993
+ //Given
994
+ const cart = {
995
+ id: randomUUID(),
996
+ version: 1,
997
+ shippingMode: 'Multiple',
998
+ shipping: [],
999
+ } as unknown as Cart;
1000
+
1001
+ //When
1002
+ const result = ctCartService.getOneShippingAddress({ cart });
1003
+
1004
+ //Then
1005
+ expect(result).toBeUndefined();
1006
+ });
1007
+
1008
+ test('should return undefined if Multiple and no shipping', async () => {
1009
+ //Given
1010
+ const cart = {
1011
+ id: randomUUID(),
1012
+ version: 1,
1013
+ shippingMode: 'Multiple',
1014
+ } as unknown as Cart;
1015
+
1016
+ //When
1017
+ const result = ctCartService.getOneShippingAddress({ cart });
1018
+
1019
+ //Then
1020
+ expect(result).toBeUndefined();
1021
+ });
1022
+
1023
+ test('should return the shipping address in case of single shipping address', async () => {
1024
+ //Given
1025
+ const cart = {
1026
+ id: randomUUID(),
1027
+ version: 1,
1028
+ shippingAddress: {
1029
+ id: randomUUID(),
1030
+ country: 'US',
1031
+ state: 'NY',
1032
+ city: 'New York',
1033
+ streetName: '5th Avenue',
1034
+ postalCode: '10001',
1035
+ },
1036
+ shippingMode: 'Single',
1037
+ } as Cart;
1038
+
1039
+ //When
1040
+ const result = ctCartService.getOneShippingAddress({ cart });
1041
+
1042
+ //Then
1043
+ expect(result).toEqual(cart.shippingAddress);
1044
+ });
1045
+
1046
+ test('should return the first shipping address in case of multiple shipping addresses', async () => {
1047
+ //Given
1048
+ const cart = {
1049
+ id: randomUUID(),
1050
+ version: 1,
1051
+ shippingMode: 'Multiple',
1052
+ shipping: [
1053
+ {
1054
+ shippingKey: 'id0',
1055
+ shippingAddress: {
1056
+ id: randomUUID(),
1057
+ country: 'US',
1058
+ state: 'NY',
1059
+ city: 'New York',
1060
+ streetName: '5th Avenue',
1061
+ postalCode: '10001',
1062
+ },
1063
+ },
1064
+ {
1065
+ shippingKey: 'id1',
1066
+ shippingAddress: {
1067
+ id: randomUUID(),
1068
+ country: 'US',
1069
+ state: 'CA',
1070
+ city: 'Los Angeles',
1071
+ streetName: 'Hollywood Blvd',
1072
+ postalCode: '90028',
1073
+ },
1074
+ },
1075
+ ],
1076
+ } as Cart;
1077
+
1078
+ //When
1079
+ const result = ctCartService.getOneShippingAddress({ cart });
1080
+
1081
+ //Then
1082
+ expect(result).toEqual(cart.shipping[0].shippingAddress);
1083
+ });
1084
+ });
1085
+
1086
+ describe('getNormalizedShipping', () => {
1087
+ test('should return the empty array info in case of mode Single with no shippingInfo/shippingAddress', async () => {
1088
+ //Given
1089
+ const cart = {
1090
+ id: randomUUID(),
1091
+ shippingMode: 'Single',
1092
+ version: 1,
1093
+ } as Cart;
1094
+
1095
+ //When
1096
+ const result = ctCartService.getNormalizedShipping({ cart });
1097
+
1098
+ //Then
1099
+ expect(result).toEqual([]);
1100
+ });
1101
+
1102
+ test('should return the empty array info in case of mode Multiple with no shipping', async () => {
1103
+ //Given
1104
+ const cart = {
1105
+ id: randomUUID(),
1106
+ shippingMode: 'Multiple',
1107
+ version: 1,
1108
+ shippingAddress: {
1109
+ id: randomUUID(),
1110
+ country: 'US',
1111
+ state: 'NY',
1112
+ city: 'New York',
1113
+ streetName: '5th Avenue',
1114
+ postalCode: '10001',
1115
+ },
1116
+ } as Cart;
1117
+
1118
+ //When
1119
+ const result = ctCartService.getNormalizedShipping({ cart });
1120
+
1121
+ //Then
1122
+ expect(result).toEqual([]);
1123
+ });
1124
+
1125
+ test('should return the empty array info in case of mode Multiple with empty shipping', async () => {
1126
+ //Given
1127
+ const cart = {
1128
+ id: randomUUID(),
1129
+ shippingMode: 'Multiple',
1130
+ version: 1,
1131
+ shipping: [],
1132
+ } as unknown as Cart;
1133
+
1134
+ //When
1135
+ const result = ctCartService.getNormalizedShipping({ cart });
1136
+
1137
+ //Then
1138
+ expect(result).toEqual([]);
1139
+ });
1140
+
1141
+ test('should return the shipping info in case of single shipping info', async () => {
1142
+ //Given
1143
+ const cart = {
1144
+ id: randomUUID(),
1145
+ shippingMode: 'Single',
1146
+ version: 1,
1147
+ shippingAddress: {
1148
+ id: randomUUID(),
1149
+ country: 'US',
1150
+ state: 'NY',
1151
+ city: 'New York',
1152
+ streetName: '5th Avenue',
1153
+ postalCode: '10001',
1154
+ },
1155
+ shippingInfo: {
1156
+ shippingMethodName: 'Standard Delivery',
1157
+ price: {
1158
+ currencyCode: 'EUR',
1159
+ centAmount: 0,
1160
+ },
1161
+ shippingRate: {
1162
+ price: {
1163
+ currencyCode: 'EUR',
1164
+ centAmount: 0,
1165
+ },
1166
+ },
1167
+ shippingMethod: {
1168
+ typeId: 'shipping-method',
1169
+ id: '23ac9884-6d83-498a-86b8-62d778ca494b',
1170
+ },
1171
+ taxedPrice: {
1172
+ totalNet: {
1173
+ type: 'centPrecision',
1174
+ currencyCode: 'EUR',
1175
+ centAmount: 0,
1176
+ fractionDigits: 2,
1177
+ },
1178
+ totalGross: {
1179
+ type: 'centPrecision',
1180
+ currencyCode: 'EUR',
1181
+ centAmount: 0,
1182
+ fractionDigits: 2,
1183
+ },
1184
+ totalTax: {
1185
+ type: 'centPrecision',
1186
+ currencyCode: 'EUR',
1187
+ centAmount: 0,
1188
+ fractionDigits: 2,
1189
+ },
1190
+ },
1191
+ shippingMethodState: 'MatchesCart',
1192
+ },
1193
+ } as Cart;
1194
+
1195
+ //When
1196
+ const result = ctCartService.getNormalizedShipping({ cart });
1197
+
1198
+ //Then
1199
+ expect(result).toEqual([
1200
+ {
1201
+ shippingAddress: cart.shippingAddress,
1202
+ shippingInfo: cart.shippingInfo,
1203
+ },
1204
+ ]);
1205
+ });
1206
+
1207
+ test('should return the shipping info in case of multiple shipping infos', async () => {
1208
+ //Given
1209
+ const cart = {
1210
+ id: randomUUID(),
1211
+ shippingMode: 'Multiple',
1212
+ version: 1,
1213
+ shipping: [
1214
+ {
1215
+ shippingKey: 'id0',
1216
+ shippingAddress: {
1217
+ id: randomUUID(),
1218
+ country: 'US',
1219
+ state: 'NY',
1220
+ city: 'New York',
1221
+ streetName: '5th Avenue',
1222
+ postalCode: '10001',
1223
+ },
1224
+ shippingInfo: {
1225
+ shippingMethodName: 'Standard Delivery',
1226
+ price: {
1227
+ currencyCode: 'EUR',
1228
+ centAmount: 0,
1229
+ },
1230
+ shippingRate: {
1231
+ price: {
1232
+ currencyCode: 'EUR',
1233
+ centAmount: 0,
1234
+ },
1235
+ },
1236
+ shippingMethod: {
1237
+ typeId: 'shipping-method',
1238
+ id: '23ac9884-6d83-498a-86b8-62d778ca494b',
1239
+ },
1240
+ taxedPrice: {
1241
+ totalNet: {
1242
+ type: 'centPrecision',
1243
+ currencyCode: 'EUR',
1244
+ centAmount: 0,
1245
+ fractionDigits: 2,
1246
+ },
1247
+ totalGross: {
1248
+ type: 'centPrecision',
1249
+ currencyCode: 'EUR',
1250
+ centAmount: 0,
1251
+ fractionDigits: 2,
1252
+ },
1253
+ totalTax: {
1254
+ type: 'centPrecision',
1255
+ currencyCode: 'EUR',
1256
+ centAmount: 0,
1257
+ fractionDigits: 2,
1258
+ },
1259
+ },
1260
+ shippingMethodState: 'MatchesCart',
1261
+ },
1262
+ },
1263
+ {
1264
+ shippingKey: 'id1',
1265
+ shippingAddress: {
1266
+ id: randomUUID(),
1267
+ country: 'US',
1268
+ state: 'CA',
1269
+ city: 'Los Angeles',
1270
+ streetName: 'Hollywood Blvd',
1271
+ postalCode: '90028',
1272
+ },
1273
+ shippingInfo: {
1274
+ shippingMethodName: 'Standard Delivery',
1275
+ price: {
1276
+ currencyCode: 'EUR',
1277
+ centAmount: 0,
1278
+ },
1279
+ shippingRate: {
1280
+ price: {
1281
+ currencyCode: 'EUR',
1282
+ centAmount: 0,
1283
+ },
1284
+ },
1285
+ shippingMethod: {
1286
+ typeId: 'shipping-method',
1287
+ id: '23ac9884-6d83-498a-86b8-62d778ca494b',
1288
+ },
1289
+ taxedPrice: {
1290
+ totalNet: {
1291
+ type: 'centPrecision',
1292
+ currencyCode: 'EUR',
1293
+ centAmount: 0,
1294
+ fractionDigits: 2,
1295
+ },
1296
+ totalGross: {
1297
+ type: 'centPrecision',
1298
+ currencyCode: 'EUR',
1299
+ centAmount: 0,
1300
+ fractionDigits: 2,
1301
+ },
1302
+ totalTax: {
1303
+ type: 'centPrecision',
1304
+ currencyCode: 'EUR',
1305
+ centAmount: 0,
1306
+ fractionDigits: 2,
1307
+ },
1308
+ },
1309
+ shippingMethodState: 'MatchesCart',
1310
+ },
1311
+ },
1312
+ ],
1313
+ } as Cart;
1314
+
1315
+ //When
1316
+ const result = ctCartService.getNormalizedShipping({ cart });
1317
+
1318
+ //Then
1319
+ expect(result).toEqual([
1320
+ {
1321
+ shippingAddress: cart.shipping[0].shippingAddress,
1322
+ shippingInfo: cart.shipping[0].shippingInfo,
1323
+ },
1324
+ {
1325
+ shippingAddress: cart.shipping[1].shippingAddress,
1326
+ shippingInfo: cart.shipping[1].shippingInfo,
1327
+ },
1328
+ ]);
1329
+ });
1330
+ });
1331
+
1332
+ describe('getCartByPaymentId', () => {
1333
+ test('should return a cart', async () => {
1334
+ //Given
1335
+ const paymentId = randomUUID();
1336
+ const cart: CartPagedQueryResponse = {
1337
+ count: 1,
1338
+ limit: 100,
1339
+ offset: 0,
1340
+ results: [
1341
+ {
1342
+ id: 'order-id',
1343
+ version: 1,
1344
+ paymentInfo: {
1345
+ payments: [
1346
+ {
1347
+ id: paymentId,
1348
+ typeId: 'payment',
1349
+ },
1350
+ ],
1351
+ },
1352
+ } as Cart,
1353
+ ],
1354
+ };
1355
+
1356
+ mockServer.use(mockGetAuthToken(200, authToken), mockFindCart(200, cart));
1357
+
1358
+ // Actual
1359
+ const actual = await ctCartService.getCartByPaymentId({ paymentId });
1360
+
1361
+ // Expected
1362
+ const expected: Order = {
1363
+ id: 'order-id',
1364
+ version: 1,
1365
+ paymentInfo: {
1366
+ payments: [
1367
+ {
1368
+ id: paymentId,
1369
+ typeId: 'payment',
1370
+ },
1371
+ ],
1372
+ },
1373
+ } as Order;
1374
+
1375
+ expect(actual).toEqual(expected);
1376
+ });
1377
+
1378
+ test('should return ErrorResourceNotFound if no cart by the given paymentId could be found', async () => {
1379
+ //Given
1380
+ const paymentId = randomUUID();
1381
+ const order: CartPagedQueryResponse = {
1382
+ count: 0,
1383
+ limit: 100,
1384
+ offset: 0,
1385
+ results: [],
1386
+ };
1387
+
1388
+ mockServer.use(
1389
+ mockGetAuthToken(200, authToken),
1390
+ mockFindCart(200, order),
1391
+ );
1392
+
1393
+ // Actual
1394
+ const actual = ctCartService.getCartByPaymentId({ paymentId });
1395
+
1396
+ // Expected
1397
+ await expect(actual).rejects.toThrow(
1398
+ `The referenced object of type cart paymentInfo(payments(id = "${paymentId}")) was not found. It either doesn't exist, or it can't be accessed from this endpoint (e.g., if the endpoint filters by store or customer account).`,
1399
+ );
1400
+ });
1401
+ });
1402
+
1403
+ describe('isRecurringCart', () => {
1404
+ test('should return false when cart has no customerId', () => {
1405
+ // Given
1406
+ const cart = {
1407
+ id: randomUUID(),
1408
+ version: 1,
1409
+ origin: 'Customer',
1410
+ lineItems: [],
1411
+ customLineItems: [],
1412
+ } as unknown as Cart;
1413
+
1414
+ // When
1415
+ const result = ctCartService.isRecurringCart(cart);
1416
+
1417
+ // Then
1418
+ expect(result).toBe(false);
1419
+ });
1420
+
1421
+ test('should return true when cart origin is RecurringOrder', () => {
1422
+ // Given
1423
+ const cart = {
1424
+ id: randomUUID(),
1425
+ version: 1,
1426
+ customerId: randomUUID(),
1427
+ origin: 'RecurringOrder',
1428
+ lineItems: [],
1429
+ customLineItems: [],
1430
+ } as unknown as Cart;
1431
+
1432
+ // When
1433
+ const result = ctCartService.isRecurringCart(cart);
1434
+
1435
+ // Then
1436
+ expect(result).toBe(true);
1437
+ });
1438
+
1439
+ test('should return true when at least one line item has recurrenceInfo', () => {
1440
+ // Given
1441
+ const cart = {
1442
+ id: randomUUID(),
1443
+ version: 1,
1444
+ customerId: randomUUID(),
1445
+ origin: 'Customer',
1446
+ lineItems: [
1447
+ {
1448
+ id: randomUUID(),
1449
+ recurrenceInfo: {
1450
+ interval: 'month',
1451
+ count: 1,
1452
+ },
1453
+ },
1454
+ ],
1455
+ customLineItems: [],
1456
+ } as unknown as Cart;
1457
+
1458
+ // When
1459
+ const result = ctCartService.isRecurringCart(cart);
1460
+
1461
+ // Then
1462
+ expect(result).toBe(true);
1463
+ });
1464
+
1465
+ test('should return true when at least one custom line item has recurrenceInfo', () => {
1466
+ // Given
1467
+ const cart = {
1468
+ id: randomUUID(),
1469
+ version: 1,
1470
+ customerId: randomUUID(),
1471
+ origin: 'Customer',
1472
+ lineItems: [],
1473
+ customLineItems: [
1474
+ {
1475
+ id: randomUUID(),
1476
+ recurrenceInfo: {
1477
+ interval: 'week',
1478
+ count: 2,
1479
+ },
1480
+ },
1481
+ ],
1482
+ } as unknown as Cart;
1483
+
1484
+ // When
1485
+ const result = ctCartService.isRecurringCart(cart);
1486
+
1487
+ // Then
1488
+ expect(result).toBe(true);
1489
+ });
1490
+
1491
+ test('should return false when cart has customerId but no recurring indicators', () => {
1492
+ // Given
1493
+ const cart = {
1494
+ id: randomUUID(),
1495
+ version: 1,
1496
+ customerId: randomUUID(),
1497
+ origin: 'Customer',
1498
+ lineItems: [
1499
+ {
1500
+ id: randomUUID(),
1501
+ },
1502
+ ],
1503
+ customLineItems: [],
1504
+ } as unknown as Cart;
1505
+
1506
+ // When
1507
+ const result = ctCartService.isRecurringCart(cart);
1508
+
1509
+ // Then
1510
+ expect(result).toBe(false);
1511
+ });
1512
+
1513
+ test('should return true when cart has multiple recurring conditions', () => {
1514
+ // Given
1515
+ const cart = {
1516
+ id: randomUUID(),
1517
+ version: 1,
1518
+ customerId: randomUUID(),
1519
+ origin: 'RecurringOrder',
1520
+ lineItems: [
1521
+ {
1522
+ id: randomUUID(),
1523
+ recurrenceInfo: {
1524
+ interval: 'month',
1525
+ count: 1,
1526
+ },
1527
+ },
1528
+ ],
1529
+ customLineItems: [
1530
+ {
1531
+ id: randomUUID(),
1532
+ recurrenceInfo: {
1533
+ interval: 'week',
1534
+ count: 1,
1535
+ },
1536
+ },
1537
+ ],
1538
+ } as unknown as Cart;
1539
+
1540
+ // When
1541
+ const result = ctCartService.isRecurringCart(cart);
1542
+
1543
+ // Then
1544
+ expect(result).toBe(true);
1545
+ });
1546
+
1547
+ test('should return true when at least one line item has recurrenceInfo among multiple items', () => {
1548
+ // Given
1549
+ const cart = {
1550
+ id: randomUUID(),
1551
+ version: 1,
1552
+ customerId: randomUUID(),
1553
+ origin: 'Customer',
1554
+ lineItems: [
1555
+ {
1556
+ id: randomUUID(),
1557
+ },
1558
+ {
1559
+ id: randomUUID(),
1560
+ recurrenceInfo: {
1561
+ interval: 'day',
1562
+ count: 7,
1563
+ },
1564
+ },
1565
+ {
1566
+ id: randomUUID(),
1567
+ },
1568
+ ],
1569
+ customLineItems: [],
1570
+ } as unknown as Cart;
1571
+
1572
+ // When
1573
+ const result = ctCartService.isRecurringCart(cart);
1574
+
1575
+ // Then
1576
+ expect(result).toBe(true);
1577
+ });
1578
+
1579
+ test('should return false when lineItems and customLineItems are undefined', () => {
1580
+ // Given
1581
+ const cart = {
1582
+ id: randomUUID(),
1583
+ version: 1,
1584
+ customerId: randomUUID(),
1585
+ origin: 'Customer',
1586
+ } as unknown as Cart;
1587
+
1588
+ // When
1589
+ const result = ctCartService.isRecurringCart(cart);
1590
+
1591
+ // Then
1592
+ expect(result).toBe(false);
1593
+ });
1594
+ });
1595
+ });
1596
+
1597
+ const mockGetAuthToken = (respCode: number, data?: object) =>
1598
+ http.post('http://auth.test.com/oauth/token', async () => {
1599
+ return HttpResponse.json(data, {
1600
+ status: respCode,
1601
+ });
1602
+ });
1603
+
1604
+ const mockGetPaymentById = (id: string, respCode: number, data?: object) =>
1605
+ http.get(`http://api.test.com/test/payments/${id}`, async () => {
1606
+ return HttpResponse.json(data, {
1607
+ status: respCode,
1608
+ });
1609
+ });
1610
+
1611
+ const mockFindCart = (respCode: number, data?: object) =>
1612
+ http.get(`http://api.test.com/test/carts`, async () => {
1613
+ return HttpResponse.json(data, {
1614
+ status: respCode,
1615
+ });
1616
+ });