@easyflow/javascript-sdk 2.1.7

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 (93) hide show
  1. package/.babelrc +5 -0
  2. package/.github/workflows/deploy-sdk-cf.yml +49 -0
  3. package/.github/workflows/release-sdk-cdn.yml +144 -0
  4. package/.github/workflows/release-sdk.yml +112 -0
  5. package/.prettierrc +6 -0
  6. package/CDN-DEPLOYMENT.md +175 -0
  7. package/DEMO.md +258 -0
  8. package/DEPLOYMENT.md +224 -0
  9. package/INTEGRATION-GUIDE.md +521 -0
  10. package/README.md +1013 -0
  11. package/coverage/base.css +224 -0
  12. package/coverage/block-navigation.js +87 -0
  13. package/coverage/easyflow-javascript-sdk/index.html +116 -0
  14. package/coverage/easyflow-javascript-sdk/libs/constants.mjs.html +268 -0
  15. package/coverage/easyflow-javascript-sdk/libs/errors.mjs.html +271 -0
  16. package/coverage/easyflow-javascript-sdk/libs/exception-handler.mjs.html +148 -0
  17. package/coverage/easyflow-javascript-sdk/libs/fingerprint.mjs.html +895 -0
  18. package/coverage/easyflow-javascript-sdk/libs/http.mjs.html +502 -0
  19. package/coverage/easyflow-javascript-sdk/libs/index.html +266 -0
  20. package/coverage/easyflow-javascript-sdk/libs/logger.mjs.html +568 -0
  21. package/coverage/easyflow-javascript-sdk/libs/sanitizer.mjs.html +1099 -0
  22. package/coverage/easyflow-javascript-sdk/libs/security.mjs.html +733 -0
  23. package/coverage/easyflow-javascript-sdk/libs/types.mjs.html +508 -0
  24. package/coverage/easyflow-javascript-sdk/libs/utils.mjs.html +379 -0
  25. package/coverage/easyflow-javascript-sdk/libs/validator.mjs.html +2623 -0
  26. package/coverage/easyflow-javascript-sdk/sdk.mjs.html +2434 -0
  27. package/coverage/favicon.png +0 -0
  28. package/coverage/index.html +131 -0
  29. package/coverage/lcov-report/base.css +224 -0
  30. package/coverage/lcov-report/block-navigation.js +87 -0
  31. package/coverage/lcov-report/easyflow-javascript-sdk/index.html +116 -0
  32. package/coverage/lcov-report/easyflow-javascript-sdk/libs/constants.mjs.html +268 -0
  33. package/coverage/lcov-report/easyflow-javascript-sdk/libs/errors.mjs.html +271 -0
  34. package/coverage/lcov-report/easyflow-javascript-sdk/libs/exception-handler.mjs.html +148 -0
  35. package/coverage/lcov-report/easyflow-javascript-sdk/libs/fingerprint.mjs.html +895 -0
  36. package/coverage/lcov-report/easyflow-javascript-sdk/libs/http.mjs.html +502 -0
  37. package/coverage/lcov-report/easyflow-javascript-sdk/libs/index.html +266 -0
  38. package/coverage/lcov-report/easyflow-javascript-sdk/libs/logger.mjs.html +568 -0
  39. package/coverage/lcov-report/easyflow-javascript-sdk/libs/sanitizer.mjs.html +1099 -0
  40. package/coverage/lcov-report/easyflow-javascript-sdk/libs/security.mjs.html +733 -0
  41. package/coverage/lcov-report/easyflow-javascript-sdk/libs/types.mjs.html +508 -0
  42. package/coverage/lcov-report/easyflow-javascript-sdk/libs/utils.mjs.html +379 -0
  43. package/coverage/lcov-report/easyflow-javascript-sdk/libs/validator.mjs.html +2623 -0
  44. package/coverage/lcov-report/easyflow-javascript-sdk/sdk.mjs.html +2434 -0
  45. package/coverage/lcov-report/favicon.png +0 -0
  46. package/coverage/lcov-report/index.html +131 -0
  47. package/coverage/lcov-report/prettify.css +1 -0
  48. package/coverage/lcov-report/prettify.js +2 -0
  49. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  50. package/coverage/lcov-report/sorter.js +196 -0
  51. package/coverage/lcov.info +1429 -0
  52. package/coverage/prettify.css +1 -0
  53. package/coverage/prettify.js +2 -0
  54. package/coverage/sort-arrow-sprite.png +0 -0
  55. package/coverage/sorter.js +196 -0
  56. package/dist/435.easyflow-sdk.min.js +1 -0
  57. package/dist/easyflow-sdk.min.js +1 -0
  58. package/dist/easyflow-sdk.min.js.LICENSE.txt +1 -0
  59. package/dist/index.html +756 -0
  60. package/docs/index.html +775 -0
  61. package/examples/lovable-integration.html +410 -0
  62. package/index.html +981 -0
  63. package/jest.config.js +37 -0
  64. package/jsdoc.json +42 -0
  65. package/libs/auto-integration.mjs +333 -0
  66. package/libs/constants.mjs +61 -0
  67. package/libs/constants.spec.js +198 -0
  68. package/libs/errors.mjs +62 -0
  69. package/libs/errors.spec.js +178 -0
  70. package/libs/exception-handler.mjs +21 -0
  71. package/libs/exception-handler.spec.js +237 -0
  72. package/libs/fingerprint.mjs +270 -0
  73. package/libs/http.mjs +163 -0
  74. package/libs/http.spec.js +427 -0
  75. package/libs/integration-wrapper.mjs +285 -0
  76. package/libs/logger.mjs +161 -0
  77. package/libs/logger.spec.js +389 -0
  78. package/libs/sanitizer.mjs +340 -0
  79. package/libs/sanitizer.spec.js +583 -0
  80. package/libs/security.mjs +217 -0
  81. package/libs/types.mjs +141 -0
  82. package/libs/utils.mjs +368 -0
  83. package/libs/utils.spec.js +231 -0
  84. package/libs/validator.mjs +952 -0
  85. package/libs/validator.spec.js +615 -0
  86. package/mocks/offer.mock.js +77 -0
  87. package/package.json +72 -0
  88. package/scripts/publish-npm.sh +82 -0
  89. package/sdk.mjs +945 -0
  90. package/sdk.spec.js +796 -0
  91. package/test-setup.cjs +211 -0
  92. package/test.html +154 -0
  93. package/webpack.config.cjs +41 -0
package/sdk.spec.js ADDED
@@ -0,0 +1,796 @@
1
+ import { beforeEach, describe, expect, jest, test } from '@jest/globals'
2
+
3
+ await jest.unstable_mockModule('./libs/security.mjs', () => ({
4
+ RateLimiter: jest.fn().mockImplementation(() => ({
5
+ checkLimit: jest.fn().mockResolvedValue(undefined),
6
+ })),
7
+ SECURITY_CONFIG: {
8
+ PRODUCTION_MODE: false,
9
+ },
10
+ }))
11
+
12
+ await jest.unstable_mockModule('./libs/logger.mjs', () => ({
13
+ SecureLogger: jest.fn().mockImplementation(() => ({
14
+ info: jest.fn(),
15
+ error: jest.fn(),
16
+ })),
17
+ }))
18
+
19
+ await jest.unstable_mockModule('./libs/http.mjs', () => ({
20
+ callSecureApi: jest.fn(),
21
+ }))
22
+
23
+ await jest.unstable_mockModule('./libs/sanitizer.mjs', () => ({
24
+ Sanitizer: {
25
+ sanitizeInput: jest.fn((input) => {
26
+ if (input === '' || input === null || input === undefined) {
27
+ return ''
28
+ }
29
+ return input
30
+ }),
31
+ sanitizeCreditCard: jest.fn((card) => card),
32
+ },
33
+ sanitizeOrderData: jest.fn((data) => data),
34
+ }))
35
+
36
+ await jest.unstable_mockModule('./libs/utils.mjs', () => ({
37
+ findPaymentByMethod: jest.fn(),
38
+ isValidBankBilletPayment: jest.fn(),
39
+ isValidPixPayment: jest.fn(),
40
+ mapCreditCardIfNeeded: jest.fn((data) => data),
41
+ }))
42
+
43
+ await jest.unstable_mockModule('./libs/validator.mjs', () => ({
44
+ Validator: {
45
+ validateOrderData: jest.fn(),
46
+ validateChargeItemsData: jest.fn(),
47
+ validateCreditCardData: jest.fn(),
48
+ validateBusinessId: jest.fn(),
49
+ validateOfferId: jest.fn(),
50
+ validateOrderId: jest.fn(),
51
+ validateCreditCardToken: jest.fn(),
52
+ validateCustomer: jest.fn(),
53
+ validatePagination: jest.fn(),
54
+ validateEmail: jest.fn(),
55
+ validateCPF: jest.fn(),
56
+ validateCNPJ: jest.fn(),
57
+ validateLegalDocument: jest.fn(),
58
+ validatePhone: jest.fn(),
59
+ validateAddress: jest.fn(),
60
+ validateCustomerId: jest.fn(),
61
+ validateCreditCardId: jest.fn(),
62
+ },
63
+ }))
64
+
65
+ const { EasyflowSDK, PAYMENT_METHODS } = await import('./sdk.mjs')
66
+ const { SecurityError, ValidationError, NetworkError } = await import(
67
+ './libs/errors.mjs'
68
+ )
69
+ const { callSecureApi } = await import('./libs/http.mjs')
70
+ const { Sanitizer } = await import('./libs/sanitizer.mjs')
71
+ const { findPaymentByMethod, isValidBankBilletPayment, isValidPixPayment } =
72
+ await import('./libs/utils.mjs')
73
+
74
+ describe('EasyflowSDK', () => {
75
+ let sdk
76
+ let mockCallSecureApi
77
+
78
+ beforeEach(() => {
79
+ jest.clearAllMocks()
80
+ mockCallSecureApi = callSecureApi
81
+ })
82
+
83
+ describe('constructor', () => {
84
+ test('should create SDK instance with string businessId', () => {
85
+ sdk = new EasyflowSDK('test-business-id')
86
+ expect(sdk.config.businessId).toBe('test-business-id')
87
+ })
88
+
89
+ test('should create SDK instance with object config', () => {
90
+ sdk = new EasyflowSDK({ businessId: 'test-business-id' })
91
+ expect(sdk.config.businessId).toBe('test-business-id')
92
+ })
93
+
94
+ test('should throw SecurityError when businessId is missing', () => {
95
+ expect(() => new EasyflowSDK()).toThrow(SecurityError)
96
+ expect(() => new EasyflowSDK({})).toThrow(SecurityError)
97
+ })
98
+
99
+ test('should have correct static version', () => {
100
+ expect(EasyflowSDK.version).toBeDefined()
101
+ expect(EasyflowSDK.version).toMatch(/^\d+\.\d+\.\d+$/)
102
+ })
103
+ })
104
+
105
+ describe('getOffer', () => {
106
+ beforeEach(() => {
107
+ sdk = new EasyflowSDK('test-business-id')
108
+ Sanitizer.sanitizeInput.mockImplementation(
109
+ (input) => input || 'offer-123'
110
+ )
111
+ })
112
+
113
+ test('should successfully get offer', async () => {
114
+ const mockOffer = { id: 'offer-123', items: [{ id: 'item-1' }] }
115
+ mockCallSecureApi.mockResolvedValue({ data: mockOffer })
116
+
117
+ const result = await sdk.getOffer('offer-123')
118
+
119
+ expect(result).toEqual(mockOffer)
120
+ expect(mockCallSecureApi).toHaveBeenCalledWith(
121
+ 'get-offer',
122
+ { offerId: 'offer-123' },
123
+ {}
124
+ )
125
+ })
126
+
127
+ test('should throw ValidationError for invalid offerId', async () => {
128
+ Sanitizer.sanitizeInput.mockReturnValue('')
129
+
130
+ await expect(sdk.getOffer('')).rejects.toThrow(ValidationError)
131
+ })
132
+
133
+ test('should throw NetworkError when API returns error', async () => {
134
+ mockCallSecureApi.mockResolvedValue({ error: 'API Error' })
135
+
136
+ await expect(sdk.getOffer('offer-123')).rejects.toThrow(
137
+ NetworkError
138
+ )
139
+ })
140
+
141
+ test('should throw ValidationError when offer not found', async () => {
142
+ mockCallSecureApi.mockResolvedValue({ data: null })
143
+
144
+ await expect(sdk.getOffer('offer-123')).rejects.toThrow(
145
+ ValidationError
146
+ )
147
+ })
148
+ })
149
+
150
+ describe('placeOrder', () => {
151
+ beforeEach(() => {
152
+ sdk = new EasyflowSDK('test-business-id')
153
+ Sanitizer.sanitizeInput.mockImplementation((input) => input)
154
+ })
155
+
156
+ test('should successfully place order', async () => {
157
+ const mockOffer = { id: 'offer-123', items: [{ id: 'item-1' }] }
158
+ const orderData = {
159
+ payments: [{ method: 'credit-card', numberInstallments: 1 }],
160
+ }
161
+ mockCallSecureApi
162
+ .mockResolvedValueOnce({ data: mockOffer })
163
+ .mockResolvedValueOnce({ data: { orderId: 'order-123' } })
164
+ const result = await sdk.placeOrder('offer-123', orderData)
165
+ expect(result).toBe('order-123')
166
+ expect(mockCallSecureApi).toHaveBeenCalledTimes(2)
167
+ })
168
+
169
+ test('should throw ValidationError for invalid offerId', async () => {
170
+ Sanitizer.sanitizeInput.mockReturnValue('')
171
+
172
+ await expect(sdk.placeOrder('', { payments: [] })).rejects.toThrow(
173
+ ValidationError
174
+ )
175
+ })
176
+
177
+ test('should throw ValidationError when offer has no items', async () => {
178
+ mockCallSecureApi.mockResolvedValue({ data: { items: [] } })
179
+
180
+ await expect(
181
+ sdk.placeOrder('offer-123', { payments: [] })
182
+ ).rejects.toThrow(ValidationError)
183
+ })
184
+ })
185
+
186
+ describe('getOrder', () => {
187
+ beforeEach(() => {
188
+ sdk = new EasyflowSDK('test-business-id')
189
+ Sanitizer.sanitizeInput.mockImplementation((input) => input)
190
+ })
191
+
192
+ test('should successfully get order', async () => {
193
+ const mockOrder = { id: 'order-123', status: 'pending' }
194
+ mockCallSecureApi.mockResolvedValue({ data: mockOrder })
195
+ const result = await sdk.getOrder('order-123', {
196
+ 'x-fingerprint': 'test-fingerprint',
197
+ })
198
+ expect(result).toEqual(mockOrder)
199
+ expect(mockCallSecureApi).toHaveBeenCalledWith(
200
+ 'get-order',
201
+ {
202
+ orderId: 'order-123',
203
+ },
204
+ { 'x-fingerprint': 'test-fingerprint' }
205
+ )
206
+ })
207
+ })
208
+
209
+ describe('getBankBillet', () => {
210
+ beforeEach(() => {
211
+ sdk = new EasyflowSDK('test-business-id')
212
+ Sanitizer.sanitizeInput.mockImplementation((input) => input)
213
+ })
214
+
215
+ test('should successfully get bank billet', async () => {
216
+ const mockOrder = {
217
+ payments: [
218
+ {
219
+ method: 'bank-billet',
220
+ bankBillet: { link: 'test-link' },
221
+ },
222
+ ],
223
+ }
224
+
225
+ mockCallSecureApi.mockResolvedValue({ data: mockOrder })
226
+ findPaymentByMethod.mockReturnValue({
227
+ bankBillet: { link: 'test-link' },
228
+ })
229
+ isValidBankBilletPayment.mockReturnValue(true)
230
+
231
+ const result = await sdk.getBankBillet('order-123')
232
+
233
+ expect(result).toEqual({ link: 'test-link' })
234
+ })
235
+
236
+ test('should return null when bank billet not found', async () => {
237
+ const mockOrder = { payments: [] }
238
+ mockCallSecureApi.mockResolvedValue({ data: mockOrder })
239
+ findPaymentByMethod.mockReturnValue(null)
240
+
241
+ const result = await sdk.getBankBillet('order-123')
242
+
243
+ expect(result).toBeNull()
244
+ })
245
+
246
+ test('should throw ValidationError for invalid orderId', async () => {
247
+ Sanitizer.sanitizeInput.mockReturnValue('')
248
+
249
+ await expect(sdk.getBankBillet('')).rejects.toThrow(ValidationError)
250
+ })
251
+ })
252
+
253
+ describe('charge', () => {
254
+ beforeEach(() => {
255
+ sdk = new EasyflowSDK('test-business-id')
256
+ Sanitizer.sanitizeInput.mockImplementation(
257
+ (input) => input || 'test-business-id'
258
+ )
259
+ })
260
+
261
+ test('should successfully process charge', async () => {
262
+ const chargeData = {
263
+ items: [{ name: 'Product', price: 1000 }],
264
+ payments: [{ method: 'pix', numberInstallments: 1 }],
265
+ }
266
+ mockCallSecureApi.mockImplementation((target, payload) => {
267
+ if (target === 'charge') {
268
+ if (!payload.businessId)
269
+ payload.businessId = 'test-business-id'
270
+ return Promise.resolve({ data: { orderId: 'order-123' } })
271
+ }
272
+ return Promise.resolve({})
273
+ })
274
+ const result = await sdk.charge(chargeData)
275
+ expect(result).toBe('order-123')
276
+ expect(mockCallSecureApi).toHaveBeenCalledWith(
277
+ 'charge',
278
+ expect.objectContaining({
279
+ businessId: 'test-business-id',
280
+ items: chargeData.items,
281
+ payments: chargeData.payments,
282
+ }),
283
+ {}
284
+ )
285
+ })
286
+
287
+ test('should throw NetworkError when API returns error', async () => {
288
+ const chargeData = {
289
+ items: [{ name: 'Product', price: 1000 }],
290
+ payments: [{ method: 'pix', numberInstallments: 1 }],
291
+ }
292
+
293
+ mockCallSecureApi.mockResolvedValue({ error: 'Charge failed' })
294
+
295
+ await expect(sdk.charge(chargeData)).rejects.toThrow(NetworkError)
296
+ })
297
+ })
298
+
299
+ describe('encrypt', () => {
300
+ beforeEach(() => {
301
+ sdk = new EasyflowSDK('test-business-id')
302
+ })
303
+
304
+ test('should successfully encrypt credit card', async () => {
305
+ const creditCard = {
306
+ cardNumber: '4111111111111111',
307
+ holderName: 'John Doe',
308
+ month: '12',
309
+ year: '2025',
310
+ cvv: '123',
311
+ }
312
+
313
+ mockCallSecureApi.mockResolvedValue({
314
+ data: { token: 'encrypted-token' },
315
+ })
316
+
317
+ const result = await sdk.encrypt(creditCard)
318
+
319
+ expect(result).toBe('encrypted-token')
320
+ expect(mockCallSecureApi).toHaveBeenCalledWith(
321
+ 'encrypt',
322
+ creditCard,
323
+ {}
324
+ )
325
+ })
326
+
327
+ test('should throw NetworkError when API returns error', async () => {
328
+ const creditCard = {
329
+ cardNumber: '4111111111111111',
330
+ holderName: 'John Doe',
331
+ month: '12',
332
+ year: '2025',
333
+ cvv: '123',
334
+ }
335
+
336
+ mockCallSecureApi.mockResolvedValue({ error: 'Encryption failed' })
337
+
338
+ await expect(sdk.encrypt(creditCard)).rejects.toThrow(NetworkError)
339
+ })
340
+ })
341
+
342
+ describe('getPix', () => {
343
+ beforeEach(() => {
344
+ sdk = new EasyflowSDK('test-business-id')
345
+ Sanitizer.sanitizeInput.mockImplementation((input) => input)
346
+ })
347
+
348
+ it('should get PIX data for order with PIX payment', async () => {
349
+ const mockOrder = {
350
+ payments: [
351
+ {
352
+ method: 'pix',
353
+ pix: {
354
+ qrCode: 'base64-qr-code',
355
+ copyAndPasteCode: 'pix-code',
356
+ },
357
+ },
358
+ ],
359
+ }
360
+
361
+ mockCallSecureApi.mockResolvedValue({ data: mockOrder })
362
+ findPaymentByMethod.mockReturnValue({
363
+ pix: {
364
+ qrCode: 'base64-qr-code',
365
+ copyAndPasteCode: 'pix-code',
366
+ },
367
+ })
368
+ isValidPixPayment.mockReturnValue(true)
369
+
370
+ const result = await sdk.getPix('order-123')
371
+
372
+ expect(result).toEqual({
373
+ qrCode: 'base64-qr-code',
374
+ copyAndPasteCode: 'pix-code',
375
+ })
376
+ expect(mockCallSecureApi).toHaveBeenCalledWith(
377
+ 'get-order',
378
+ { orderId: 'order-123' },
379
+ {}
380
+ )
381
+ })
382
+
383
+ it('should return null for order without PIX payment', async () => {
384
+ const mockOrder = {
385
+ payments: [
386
+ {
387
+ method: 'credit-card',
388
+ creditCard: { number: '4111111111111111' },
389
+ },
390
+ ],
391
+ }
392
+
393
+ mockCallSecureApi.mockResolvedValue({ data: mockOrder })
394
+ findPaymentByMethod.mockReturnValue(null)
395
+
396
+ const result = await sdk.getPix('order-123')
397
+
398
+ expect(result).toBeNull()
399
+ })
400
+
401
+ it('should throw error for invalid order ID', async () => {
402
+ Sanitizer.sanitizeInput.mockReturnValue('')
403
+
404
+ await expect(sdk.getPix('')).rejects.toThrow('Invalid order ID')
405
+ })
406
+ })
407
+
408
+ describe('Customer Management', () => {
409
+ beforeEach(() => {
410
+ Sanitizer.sanitizeInput.mockImplementation((input) => {
411
+ if (input === 'test-business-id') {
412
+ return 'test-business-id'
413
+ }
414
+ if (input === '' || input === null || input === undefined) {
415
+ return ''
416
+ }
417
+ return input
418
+ })
419
+ sdk = new EasyflowSDK('test-business-id')
420
+ })
421
+
422
+ describe('createCustomer', () => {
423
+ it('should create a new customer successfully', async () => {
424
+ const customerData = {
425
+ name: 'John Doe',
426
+ email: 'john@example.com',
427
+ document: { type: 'CPF', number: '12345678901' },
428
+ phone: {
429
+ areaCode: '+55',
430
+ ddd: '11',
431
+ number: '999999999',
432
+ isMobile: true,
433
+ },
434
+ }
435
+
436
+ const mockResponse = {
437
+ data: {
438
+ customer: {
439
+ id: '550e8400-e29b-41d4-a716-446655440000',
440
+ ...customerData,
441
+ business: {
442
+ id: 'business-123',
443
+ name: 'Test Business',
444
+ },
445
+ },
446
+ },
447
+ }
448
+
449
+ mockCallSecureApi.mockResolvedValue(mockResponse)
450
+
451
+ const result = await sdk.createCustomer(customerData)
452
+
453
+ expect(result).toEqual(mockResponse.data.customer)
454
+ expect(mockCallSecureApi).toHaveBeenCalledWith(
455
+ 'create-customer',
456
+ {
457
+ ...customerData,
458
+ businessId: 'test-business-id',
459
+ },
460
+ {}
461
+ )
462
+ })
463
+
464
+ it('should throw error when customer data is missing', async () => {
465
+ await expect(sdk.createCustomer(null)).rejects.toThrow(
466
+ 'Customer data is required'
467
+ )
468
+ await expect(sdk.createCustomer('')).rejects.toThrow(
469
+ 'Customer data is required'
470
+ )
471
+ })
472
+
473
+ it('should throw error when API returns error', async () => {
474
+ mockCallSecureApi.mockResolvedValue({ error: 'API Error' })
475
+
476
+ await expect(
477
+ sdk.createCustomer({ name: 'John' })
478
+ ).rejects.toThrow('API Error')
479
+ })
480
+ })
481
+
482
+ describe('getCustomer', () => {
483
+ it('should get customer by ID successfully', async () => {
484
+ const mockCustomer = {
485
+ id: '550e8400-e29b-41d4-a716-446655440000',
486
+ name: 'John Doe',
487
+ email: 'john@example.com',
488
+ document: {
489
+ type: 'CPF',
490
+ number: '12345678901',
491
+ },
492
+ }
493
+
494
+ mockCallSecureApi.mockResolvedValue({
495
+ data: { customer: mockCustomer },
496
+ })
497
+
498
+ const result = await sdk.getCustomer(
499
+ '550e8400-e29b-41d4-a716-446655440000'
500
+ )
501
+
502
+ expect(result).toEqual(mockCustomer)
503
+ expect(mockCallSecureApi).toHaveBeenCalledWith(
504
+ 'get-customer',
505
+ {
506
+ customerId: '550e8400-e29b-41d4-a716-446655440000',
507
+ businessId: 'test-business-id',
508
+ },
509
+ {}
510
+ )
511
+ })
512
+
513
+ it('should throw error for invalid customer ID', async () => {
514
+ Sanitizer.sanitizeInput.mockReturnValue('')
515
+
516
+ await expect(sdk.getCustomer('')).rejects.toThrow(
517
+ 'Invalid customer ID'
518
+ )
519
+ })
520
+
521
+ it('should throw error when customer not found', async () => {
522
+ mockCallSecureApi.mockResolvedValue({ data: null })
523
+
524
+ await expect(
525
+ sdk.getCustomer('550e8400-e29b-41d4-a716-446655440000')
526
+ ).rejects.toThrow('Customer not found')
527
+ })
528
+ })
529
+
530
+ describe('updateCustomer', () => {
531
+ it('should update customer successfully', async () => {
532
+ const updateData = {
533
+ name: 'John Updated',
534
+ email: 'john.updated@example.com',
535
+ }
536
+
537
+ const mockResponse = { data: { updated: true } }
538
+ mockCallSecureApi.mockResolvedValue(mockResponse)
539
+
540
+ const result = await sdk.updateCustomer(
541
+ '550e8400-e29b-41d4-a716-446655440000',
542
+ updateData
543
+ )
544
+
545
+ expect(result).toEqual({ updated: true })
546
+ expect(mockCallSecureApi).toHaveBeenCalledWith(
547
+ 'update-customer',
548
+ {
549
+ customerId: '550e8400-e29b-41d4-a716-446655440000',
550
+ businessId: 'test-business-id',
551
+ ...updateData,
552
+ },
553
+ {}
554
+ )
555
+ })
556
+
557
+ it('should throw error for invalid customer ID', async () => {
558
+ Sanitizer.sanitizeInput.mockReturnValue('')
559
+
560
+ await expect(
561
+ sdk.updateCustomer('', { name: 'John' })
562
+ ).rejects.toThrow('Invalid customer ID')
563
+ })
564
+
565
+ it('should throw error when update data is missing', async () => {
566
+ await expect(
567
+ sdk.updateCustomer(
568
+ '550e8400-e29b-41d4-a716-446655440000',
569
+ null
570
+ )
571
+ ).rejects.toThrow('Customer update data is required')
572
+ })
573
+ })
574
+ })
575
+
576
+ describe('Credit Card Management', () => {
577
+ beforeEach(() => {
578
+ Sanitizer.sanitizeInput.mockImplementation((input) => {
579
+ if (input === 'test-business-id') {
580
+ return 'test-business-id'
581
+ }
582
+ if (input === '' || input === null || input === undefined) {
583
+ return ''
584
+ }
585
+ return input
586
+ })
587
+ sdk = new EasyflowSDK('test-business-id')
588
+ })
589
+
590
+ describe('addCreditCard', () => {
591
+ it('should add credit card successfully', async () => {
592
+ const mockResponse = {
593
+ data: {
594
+ creditCardId: 'card-123',
595
+ creditCard: { creditCardId: 'card-123' },
596
+ },
597
+ }
598
+ mockCallSecureApi.mockResolvedValue(mockResponse)
599
+
600
+ const result = await sdk.addCreditCard(
601
+ 'customer-1234567890123456',
602
+ 'encrypted-token-456'
603
+ )
604
+
605
+ expect(result).toEqual({ creditCardId: 'card-123' })
606
+ expect(mockCallSecureApi).toHaveBeenCalledWith(
607
+ 'add-credit-card',
608
+ {
609
+ customerId: 'customer-1234567890123456',
610
+ businessId: 'test-business-id',
611
+ creditCardToken: 'encrypted-token-456',
612
+ },
613
+ {}
614
+ )
615
+ })
616
+
617
+ it('should throw error for invalid customer ID', async () => {
618
+ Sanitizer.sanitizeInput.mockImplementation((input) => {
619
+ if (input === 'test-business-id') return 'test-business-id'
620
+ if (input === '') return ''
621
+ return input
622
+ })
623
+
624
+ await expect(sdk.addCreditCard('', 'token')).rejects.toThrow(
625
+ 'Invalid customer ID'
626
+ )
627
+ })
628
+
629
+ it('should throw error for invalid credit card token', async () => {
630
+ Sanitizer.sanitizeInput.mockImplementation((input) => {
631
+ if (input === 'test-business-id') return 'test-business-id'
632
+ if (input === '550e8400-e29b-41d4-a716-446655440000')
633
+ return '550e8400-e29b-41d4-a716-446655440000'
634
+ if (input === '') return ''
635
+ return input
636
+ })
637
+
638
+ await expect(
639
+ sdk.addCreditCard(
640
+ '550e8400-e29b-41d4-a716-446655440000',
641
+ ''
642
+ )
643
+ ).rejects.toThrow('Invalid credit card token')
644
+ })
645
+
646
+ it('should throw error when API returns error', async () => {
647
+ mockCallSecureApi.mockResolvedValue({ error: 'API Error' })
648
+
649
+ await expect(
650
+ sdk.addCreditCard(
651
+ '550e8400-e29b-41d4-a716-446655440000',
652
+ 'token'
653
+ )
654
+ ).rejects.toThrow('API Error')
655
+ })
656
+ })
657
+
658
+ describe('removeCreditCard', () => {
659
+ it('should remove credit card successfully', async () => {
660
+ const mockResponse = { data: { removed: true } }
661
+ mockCallSecureApi.mockResolvedValue(mockResponse)
662
+
663
+ const result = await sdk.removeCreditCard(
664
+ '550e8400-e29b-41d4-a716-446655440000',
665
+ 'card-456'
666
+ )
667
+
668
+ expect(result).toEqual({ removed: true })
669
+ expect(mockCallSecureApi).toHaveBeenCalledWith(
670
+ 'delete-credit-card',
671
+ {
672
+ customerId: '550e8400-e29b-41d4-a716-446655440000',
673
+ businessId: 'test-business-id',
674
+ creditCardId: 'card-456',
675
+ },
676
+ {}
677
+ )
678
+ })
679
+
680
+ it('should throw error for invalid customer ID', async () => {
681
+ Sanitizer.sanitizeInput.mockImplementation((input) => {
682
+ if (input === 'test-business-id') return 'test-business-id'
683
+ if (input === '') return ''
684
+ return input
685
+ })
686
+
687
+ await expect(
688
+ sdk.removeCreditCard('', 'card-456')
689
+ ).rejects.toThrow('Invalid customer ID')
690
+ })
691
+
692
+ it('should throw error for invalid credit card ID', async () => {
693
+ Sanitizer.sanitizeInput.mockImplementation((input) => {
694
+ if (input === 'test-business-id') return 'test-business-id'
695
+ if (input === '550e8400-e29b-41d4-a716-446655440000')
696
+ return '550e8400-e29b-41d4-a716-446655440000'
697
+ if (input === '') return ''
698
+ return input
699
+ })
700
+
701
+ await expect(
702
+ sdk.removeCreditCard(
703
+ '550e8400-e29b-41d4-a716-446655440000',
704
+ ''
705
+ )
706
+ ).rejects.toThrow('Invalid credit card ID')
707
+ })
708
+ })
709
+
710
+ describe('getCreditCard', () => {
711
+ it('should get credit card successfully', async () => {
712
+ const mockResponse = {
713
+ data: {
714
+ creditCard: {
715
+ creditCardId: 'card-456',
716
+ brand: 'Visa',
717
+ last4Numbers: '1111',
718
+ expiresAtMonth: '12',
719
+ expiresAtYear: '2025',
720
+ },
721
+ },
722
+ }
723
+ mockCallSecureApi.mockResolvedValue(mockResponse)
724
+
725
+ const result = await sdk.getCreditCard(
726
+ '550e8400-e29b-41d4-a716-446655440000',
727
+ 'card-456'
728
+ )
729
+
730
+ expect(result).toEqual({
731
+ creditCardId: 'card-456',
732
+ brand: 'Visa',
733
+ last4Numbers: '1111',
734
+ expiresAtMonth: '12',
735
+ expiresAtYear: '2025',
736
+ })
737
+ expect(mockCallSecureApi).toHaveBeenCalledWith(
738
+ 'get-credit-card',
739
+ {
740
+ customerId: '550e8400-e29b-41d4-a716-446655440000',
741
+ businessId: 'test-business-id',
742
+ creditCardId: 'card-456',
743
+ },
744
+ {}
745
+ )
746
+ })
747
+
748
+ it('should throw error for invalid customer ID', async () => {
749
+ Sanitizer.sanitizeInput.mockImplementation((input) => {
750
+ if (input === 'test-business-id') return 'test-business-id'
751
+ if (input === '') return ''
752
+ return input
753
+ })
754
+
755
+ await expect(sdk.getCreditCard('', 'card-456')).rejects.toThrow(
756
+ 'Invalid customer ID'
757
+ )
758
+ })
759
+
760
+ it('should throw error for invalid credit card ID', async () => {
761
+ Sanitizer.sanitizeInput.mockImplementation((input) => {
762
+ if (input === 'test-business-id') return 'test-business-id'
763
+ if (input === '550e8400-e29b-41d4-a716-446655440000')
764
+ return '550e8400-e29b-41d4-a716-446655440000'
765
+ if (input === '') return ''
766
+ return input
767
+ })
768
+
769
+ await expect(
770
+ sdk.getCreditCard(
771
+ '550e8400-e29b-41d4-a716-446655440000',
772
+ ''
773
+ )
774
+ ).rejects.toThrow('Invalid credit card ID')
775
+ })
776
+
777
+ it('should throw error when credit card not found', async () => {
778
+ const mockResponse = { data: null }
779
+ mockCallSecureApi.mockResolvedValue(mockResponse)
780
+
781
+ await expect(
782
+ sdk.getCreditCard('customer-1234567890123456', 'card-456')
783
+ ).rejects.toThrow('Credit card not found')
784
+ })
785
+ })
786
+ })
787
+
788
+ describe('PAYMENT_METHODS', () => {
789
+ test('should export payment methods constants', () => {
790
+ expect(PAYMENT_METHODS).toBeDefined()
791
+ expect(PAYMENT_METHODS.CREDIT_CARD).toBe('credit-card')
792
+ expect(PAYMENT_METHODS.PIX).toBe('pix')
793
+ expect(PAYMENT_METHODS.BANK_BILLET).toBe('bank-billet')
794
+ })
795
+ })
796
+ })