@fat-zebra/sdk 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fat-zebra-sdk-1.0.2.tgz +0 -0
- package/package.json +1 -1
- package/src/index.ts +0 -9
- package/src/react/VerifyCard.tsx +0 -36
- package/src/react/index.ts +0 -3
- package/src/react/url.ts +0 -97
- package/src/react/useFatZebra.ts +0 -154
- package/src/sca/__tests__/eci-mappings.test.ts +0 -35
- package/src/sca/__tests__/index.test.ts +0 -514
- package/src/sca/cardinal.ts +0 -136
- package/src/sca/eci-mappings.ts +0 -63
- package/src/sca/index.ts +0 -329
- package/src/sca/scenarios/enrollment.ts +0 -164
- package/src/sca/scenarios/index.ts +0 -4
- package/src/sca/scenarios/validation.ts +0 -142
- package/src/sca/types.ts +0 -209
- package/src/shared/api-gateway-client.ts +0 -71
- package/src/shared/bridge-client.ts +0 -26
- package/src/shared/constants.ts +0 -19
- package/src/shared/event-manager.ts +0 -45
- package/src/shared/post-message-client.test.ts +0 -73
- package/src/shared/post-message-client.ts +0 -127
- package/src/shared/types.test.ts +0 -65
- package/src/shared/types.ts +0 -182
- package/src/shared/util.test.ts +0 -164
- package/src/shared/util.ts +0 -98
- package/tests/helpers/api-gateway-mock.ts +0 -46
|
@@ -1,514 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @jest-environment jsdom
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import Sca, { ScaConfig, ScaRunProps } from '..'
|
|
6
|
-
import {
|
|
7
|
-
Customer,
|
|
8
|
-
PaymentIntent,
|
|
9
|
-
PublicEvent,
|
|
10
|
-
} from '../../shared/types'
|
|
11
|
-
import { LocalStorageAccessTokenKey } from '../../shared/constants'
|
|
12
|
-
import { mockRequest } from '../../../tests/helpers/api-gateway-mock'
|
|
13
|
-
import * as nock from 'nock'
|
|
14
|
-
import { CardinalManager } from '../cardinal'
|
|
15
|
-
import { ChallengeWindowSize, EnrollSCARequest, ThreedsData } from '../types'
|
|
16
|
-
import {
|
|
17
|
-
enrollmentScenarios,
|
|
18
|
-
EnrollmentScenario,
|
|
19
|
-
} from '../scenarios'
|
|
20
|
-
import GatewayClient from '../../shared/api-gateway-client'
|
|
21
|
-
|
|
22
|
-
jest.mock('../cardinal')
|
|
23
|
-
// mock module function
|
|
24
|
-
const eventManager = require('../../shared/event-manager')
|
|
25
|
-
eventManager.emit = jest.fn()
|
|
26
|
-
|
|
27
|
-
interface ResponseOverride {
|
|
28
|
-
status: number
|
|
29
|
-
body: unknown
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const mockCardinalManager = () => {
|
|
33
|
-
// @ts-ignore
|
|
34
|
-
CardinalManager.mockImplementation(() => {
|
|
35
|
-
return {
|
|
36
|
-
processBin: () => ['success'],
|
|
37
|
-
setup: () => true,
|
|
38
|
-
onPaymentSetupComplete: () => {
|
|
39
|
-
return {
|
|
40
|
-
sessionId: 'session_123'
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
|
-
onPaymentValidated: () => true,
|
|
44
|
-
continue: () => jest.fn()
|
|
45
|
-
}
|
|
46
|
-
})
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const gatewayClient = new GatewayClient({
|
|
50
|
-
accessToken: "abc1234",
|
|
51
|
-
username: "TEST"
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
const scaTests = (config: ScaRunProps) => {
|
|
55
|
-
const { paymentIntent, cardToken, customer } = config
|
|
56
|
-
|
|
57
|
-
const requestCustomer = (customer: ScaRunProps['customer']): EnrollSCARequest['customer'] => {
|
|
58
|
-
let properties: EnrollSCARequest['customer'] = {};
|
|
59
|
-
if (!customer) return properties;
|
|
60
|
-
if (customer.firstName) properties.first_name = customer.firstName
|
|
61
|
-
if (customer.lastName) properties.last_name = customer.lastName
|
|
62
|
-
if (customer.email) properties.email = customer.email
|
|
63
|
-
if (customer.address) properties.address = customer.address
|
|
64
|
-
if (customer.city) properties.city = customer.city
|
|
65
|
-
if (customer.state) properties.state = customer.state
|
|
66
|
-
if (customer.postcode) properties.postcode = customer.postcode
|
|
67
|
-
if (customer.country) properties.country = customer.country
|
|
68
|
-
return properties
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const enrollmentRequestBody: EnrollSCARequest = {
|
|
72
|
-
amount: paymentIntent.payment.amount,
|
|
73
|
-
card_token: cardToken,
|
|
74
|
-
currency: paymentIntent.payment.currency,
|
|
75
|
-
reference: paymentIntent.payment.reference,
|
|
76
|
-
hide_card_holder: paymentIntent.payment.hide_card_holder,
|
|
77
|
-
verification: paymentIntent.verification,
|
|
78
|
-
session_id: 'session_123',
|
|
79
|
-
challenge_window_size: ChallengeWindowSize.SIZE_FULL_PAGE,
|
|
80
|
-
customer: requestCustomer(customer),
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const mockCreateSCASessionSuccess = () => {
|
|
84
|
-
mockRequest({
|
|
85
|
-
request: {
|
|
86
|
-
action: 'POST',
|
|
87
|
-
path: '/sca/session',
|
|
88
|
-
body: {
|
|
89
|
-
amount: paymentIntent.payment.amount,
|
|
90
|
-
currency: paymentIntent.payment.currency,
|
|
91
|
-
hide_card_holder: paymentIntent.payment.hide_card_holder,
|
|
92
|
-
}
|
|
93
|
-
},
|
|
94
|
-
response: {
|
|
95
|
-
status: 200,
|
|
96
|
-
body: {
|
|
97
|
-
data: {
|
|
98
|
-
jwt: 'thisisajwt'
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
})
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const mockSCASession = (override?: ResponseOverride) => {
|
|
106
|
-
const responseBody = {
|
|
107
|
-
data: {
|
|
108
|
-
jwt: 'thisisajwt'
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
mockRequest({
|
|
113
|
-
request: {
|
|
114
|
-
action: 'POST',
|
|
115
|
-
path: '/sca/session',
|
|
116
|
-
body: {
|
|
117
|
-
amount: paymentIntent.payment.amount,
|
|
118
|
-
currency: paymentIntent.payment.currency,
|
|
119
|
-
hide_card_holder: paymentIntent.payment.hide_card_holder,
|
|
120
|
-
}
|
|
121
|
-
},
|
|
122
|
-
response: {
|
|
123
|
-
status: (override ? override.status : 200),
|
|
124
|
-
body: (override ? override.body : responseBody)
|
|
125
|
-
}
|
|
126
|
-
})
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const mockSCAEnrollment = (override?: ResponseOverride) => {
|
|
130
|
-
const responseBody = {
|
|
131
|
-
data: {
|
|
132
|
-
action: {
|
|
133
|
-
proceed: true
|
|
134
|
-
},
|
|
135
|
-
version: '1.0',
|
|
136
|
-
enrolled: 'Y',
|
|
137
|
-
acs_url: '',
|
|
138
|
-
pareq: 'asdfasdf',
|
|
139
|
-
pares: '',
|
|
140
|
-
decision: 'ACCEPT',
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
mockRequest({
|
|
145
|
-
request: {
|
|
146
|
-
action: 'POST',
|
|
147
|
-
path: '/sca/enrollment',
|
|
148
|
-
body: enrollmentRequestBody
|
|
149
|
-
},
|
|
150
|
-
response: {
|
|
151
|
-
status: (override ? override.status : 200),
|
|
152
|
-
body: (override ? override.body : responseBody)
|
|
153
|
-
}
|
|
154
|
-
})
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
describe('SCA session failed', () => {
|
|
158
|
-
it('Emits fz.validation.error event with detail', async () => {
|
|
159
|
-
|
|
160
|
-
mockRequest({
|
|
161
|
-
request: {
|
|
162
|
-
action: 'POST',
|
|
163
|
-
path: '/sca/session',
|
|
164
|
-
body: {
|
|
165
|
-
amount: paymentIntent.payment.amount,
|
|
166
|
-
currency: paymentIntent.payment.currency,
|
|
167
|
-
hide_card_holder: paymentIntent.payment.hide_card_holder,
|
|
168
|
-
}
|
|
169
|
-
},
|
|
170
|
-
response: {
|
|
171
|
-
status: 422,
|
|
172
|
-
body: {
|
|
173
|
-
errors: ['Create SCA session error.']
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
mockSCAEnrollment()
|
|
179
|
-
|
|
180
|
-
const sca = new Sca({ gatewayClient })
|
|
181
|
-
|
|
182
|
-
await sca.run(config)
|
|
183
|
-
|
|
184
|
-
const eventDetail: any = {
|
|
185
|
-
detail: {
|
|
186
|
-
errors: ['FatZebra.3DS: JWT creation failed.'],
|
|
187
|
-
data: null
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
expect(eventManager.emit).toHaveBeenCalledTimes(1)
|
|
191
|
-
expect(eventManager.emit.mock.calls[0][0]).toBe(PublicEvent.SCA_ERROR)
|
|
192
|
-
expect(eventManager.emit.mock.calls[0][1]).toMatchObject(eventDetail.detail)
|
|
193
|
-
})
|
|
194
|
-
})
|
|
195
|
-
|
|
196
|
-
describe('Bin processing failed', () => {
|
|
197
|
-
it('Emits fz.validation.error event without detail', async () => {
|
|
198
|
-
//@ts-ignore
|
|
199
|
-
CardinalManager.mockImplementation(() => {
|
|
200
|
-
return {
|
|
201
|
-
processBin: () => {
|
|
202
|
-
throw new Error('GG')
|
|
203
|
-
},
|
|
204
|
-
setup: () => true,
|
|
205
|
-
onPaymentSetupComplete: () => {
|
|
206
|
-
return {
|
|
207
|
-
sessionId: '123'
|
|
208
|
-
}
|
|
209
|
-
},
|
|
210
|
-
onPaymentValidated: () => true
|
|
211
|
-
}
|
|
212
|
-
})
|
|
213
|
-
|
|
214
|
-
mockCreateSCASessionSuccess()
|
|
215
|
-
mockSCAEnrollment()
|
|
216
|
-
|
|
217
|
-
const sca = new Sca({ gatewayClient })
|
|
218
|
-
sca.cardinal = new CardinalManager()
|
|
219
|
-
await sca.run(config)
|
|
220
|
-
|
|
221
|
-
const eventDetail: any = {
|
|
222
|
-
detail: {
|
|
223
|
-
errors: ['FatZebra.3DS: BIN verification failed.'],
|
|
224
|
-
data: null
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
expect(eventManager.emit).toHaveBeenCalledTimes(1)
|
|
229
|
-
expect(eventManager.emit.mock.calls[0][0]).toBe(PublicEvent.SCA_ERROR)
|
|
230
|
-
expect(eventManager.emit.mock.calls[0][1]).toMatchObject(eventDetail.detail)
|
|
231
|
-
})
|
|
232
|
-
})
|
|
233
|
-
|
|
234
|
-
describe('3DS version 2', () => {
|
|
235
|
-
const scenarios = enrollmentScenarios.filter((item) => item.threedsVersion[0] == '2')
|
|
236
|
-
|
|
237
|
-
scenarios.forEach((scenario: EnrollmentScenario) => {
|
|
238
|
-
describe(`Enrollment: ${scenario.description}`, () => {
|
|
239
|
-
beforeEach(() => {
|
|
240
|
-
mockCardinalManager()
|
|
241
|
-
mockSCASession()
|
|
242
|
-
mockSCAEnrollment({
|
|
243
|
-
status: 200,
|
|
244
|
-
body: {
|
|
245
|
-
data: {
|
|
246
|
-
version: '2.1.0',
|
|
247
|
-
cavv: 'cavv',
|
|
248
|
-
eci: '05',
|
|
249
|
-
xid: 'xid',
|
|
250
|
-
pareq: 'pareq',
|
|
251
|
-
pares: scenario.pares,
|
|
252
|
-
enrolled: scenario.veresEnrolled,
|
|
253
|
-
reason_code: scenario.reasonCode,
|
|
254
|
-
directory_server_txn_id: 'txn123',
|
|
255
|
-
ucaf_collection_indicator: 'ucaf123',
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
})
|
|
259
|
-
})
|
|
260
|
-
|
|
261
|
-
it('SCA', async () => {
|
|
262
|
-
const sca = new Sca({ gatewayClient})
|
|
263
|
-
sca.cardinal = new CardinalManager()
|
|
264
|
-
const spy = jest.spyOn(sca.cardinal, 'continue')
|
|
265
|
-
|
|
266
|
-
await sca.run(config)
|
|
267
|
-
|
|
268
|
-
if (scenario.outcome.authenticationType === 'challenge') {
|
|
269
|
-
expect(spy).toHaveBeenCalledTimes(1)
|
|
270
|
-
expect(eventManager.emit).toHaveBeenCalledTimes(0)
|
|
271
|
-
return
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
if (scenario.outcome.success) {
|
|
275
|
-
const eventDetail = {
|
|
276
|
-
detail: {
|
|
277
|
-
message: `FatZebra.3DS: 3DS success - ${scenario.description}.`,
|
|
278
|
-
data: {
|
|
279
|
-
cavv: 'cavv',
|
|
280
|
-
par: scenario.pares,
|
|
281
|
-
sli: '05',
|
|
282
|
-
xid: 'xid',
|
|
283
|
-
ver: 'Y',
|
|
284
|
-
threedsVersion: '2.1.0',
|
|
285
|
-
directoryServerTxnId: 'txn123',
|
|
286
|
-
} as ThreedsData,
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
expect(eventManager.emit).toHaveBeenCalledTimes(1)
|
|
290
|
-
expect(eventManager.emit.mock.calls[0][0]).toBe(PublicEvent.SCA_SUCCESS)
|
|
291
|
-
expect(eventManager.emit.mock.calls[0][1]).toMatchObject(eventDetail.detail)
|
|
292
|
-
} else {
|
|
293
|
-
const eventDetail = {
|
|
294
|
-
detail: {
|
|
295
|
-
errors: [`FatZebra.3DS: 3DS error - ${scenario.description}`],
|
|
296
|
-
data: {
|
|
297
|
-
errorCode: scenario.outcome.errorCode
|
|
298
|
-
},
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
expect(eventManager.emit).toHaveBeenCalledTimes(1)
|
|
302
|
-
expect(eventManager.emit.mock.calls[0][0]).toBe(PublicEvent.SCA_ERROR)
|
|
303
|
-
expect(eventManager.emit.mock.calls[0][1]).toMatchObject(eventDetail.detail)
|
|
304
|
-
}
|
|
305
|
-
})
|
|
306
|
-
})
|
|
307
|
-
})
|
|
308
|
-
})
|
|
309
|
-
|
|
310
|
-
describe('3DS version 1', () => {
|
|
311
|
-
const scenarios = enrollmentScenarios.filter((item) => item.threedsVersion[0] == '1')
|
|
312
|
-
|
|
313
|
-
scenarios.forEach((scenario: EnrollmentScenario) => {
|
|
314
|
-
describe(`Enrollment: ${scenario.description}`, () => {
|
|
315
|
-
beforeEach(() => {
|
|
316
|
-
mockCardinalManager()
|
|
317
|
-
mockSCASession()
|
|
318
|
-
mockSCAEnrollment({
|
|
319
|
-
status: 200,
|
|
320
|
-
body: {
|
|
321
|
-
data: {
|
|
322
|
-
version: '1.0.2',
|
|
323
|
-
cavv: 'cavv',
|
|
324
|
-
eci: '05',
|
|
325
|
-
xid: 'xid',
|
|
326
|
-
pareq: 'pareq',
|
|
327
|
-
pares: scenario.pares,
|
|
328
|
-
enrolled: scenario.veresEnrolled,
|
|
329
|
-
reason_code: scenario.reasonCode,
|
|
330
|
-
directory_server_txn_id: 'txn123',
|
|
331
|
-
ucaf_collection_indicator: 'ucaf123',
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
})
|
|
335
|
-
})
|
|
336
|
-
|
|
337
|
-
it('SCA', async () => {
|
|
338
|
-
const sca = new Sca({ gatewayClient })
|
|
339
|
-
sca.cardinal = new CardinalManager()
|
|
340
|
-
const spy = jest.spyOn(sca.cardinal, 'continue')
|
|
341
|
-
|
|
342
|
-
await sca.run(config)
|
|
343
|
-
|
|
344
|
-
if (scenario.outcome.authenticationType === 'challenge') {
|
|
345
|
-
expect(spy).toHaveBeenCalledTimes(1)
|
|
346
|
-
expect(eventManager.emit).toHaveBeenCalledTimes(0)
|
|
347
|
-
return
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
if (scenario.outcome.success) {
|
|
351
|
-
const eventDetail = {
|
|
352
|
-
detail: {
|
|
353
|
-
message: `FatZebra.3DS: 3DS success - ${scenario.description}.`,
|
|
354
|
-
data: {
|
|
355
|
-
cavv: 'cavv',
|
|
356
|
-
par: scenario.pares,
|
|
357
|
-
sli: '05',
|
|
358
|
-
xid: 'xid',
|
|
359
|
-
ver: scenario.veresEnrolled,
|
|
360
|
-
threedsVersion: '1.0.2',
|
|
361
|
-
directoryServerTxnId: 'txn123',
|
|
362
|
-
} as ThreedsData,
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
expect(eventManager.emit).toHaveBeenCalledTimes(1)
|
|
366
|
-
expect(eventManager.emit.mock.calls[0][0]).toBe(PublicEvent.SCA_SUCCESS)
|
|
367
|
-
expect(eventManager.emit.mock.calls[0][1]).toMatchObject(eventDetail.detail)
|
|
368
|
-
} else {
|
|
369
|
-
const eventDetail = {
|
|
370
|
-
detail: {
|
|
371
|
-
errors: [`FatZebra.3DS: 3DS error - ${scenario.description}`],
|
|
372
|
-
data: {
|
|
373
|
-
errorCode: scenario.outcome.errorCode
|
|
374
|
-
},
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
expect(eventManager.emit).toHaveBeenCalledTimes(1)
|
|
378
|
-
expect(eventManager.emit.mock.calls[0][0]).toBe(PublicEvent.SCA_ERROR)
|
|
379
|
-
expect(eventManager.emit.mock.calls[0][1]).toMatchObject(eventDetail.detail)
|
|
380
|
-
}
|
|
381
|
-
})
|
|
382
|
-
})
|
|
383
|
-
})
|
|
384
|
-
})
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
describe('Sca', () => {
|
|
388
|
-
beforeEach(() => {
|
|
389
|
-
jest.clearAllMocks()
|
|
390
|
-
nock.disableNetConnect()
|
|
391
|
-
nock.cleanAll()
|
|
392
|
-
|
|
393
|
-
mockCardinalManager()
|
|
394
|
-
|
|
395
|
-
localStorage.setItem(LocalStorageAccessTokenKey, 'TOKEN')
|
|
396
|
-
})
|
|
397
|
-
|
|
398
|
-
const bin = '400000'
|
|
399
|
-
const cardToken = 'token_12345'
|
|
400
|
-
const paymentIntent: PaymentIntent = {
|
|
401
|
-
payment: {
|
|
402
|
-
amount: 1000,
|
|
403
|
-
currency: 'AUD',
|
|
404
|
-
reference: 'ref_12345',
|
|
405
|
-
hide_card_holder: false,
|
|
406
|
-
},
|
|
407
|
-
verification: 'ver_12345'
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
describe('#run', () => {
|
|
411
|
-
describe('customer details: full', () => {
|
|
412
|
-
const customer: Customer = {
|
|
413
|
-
firstName: 'Ziggy',
|
|
414
|
-
lastName: 'Zebra',
|
|
415
|
-
email: 'ziggy.zebra@fatzebra.com',
|
|
416
|
-
address: '2/58-62 Kippax Street',
|
|
417
|
-
city: 'Surry Hills',
|
|
418
|
-
postcode: '2010',
|
|
419
|
-
state: 'NSW',
|
|
420
|
-
country: 'Australia'
|
|
421
|
-
}
|
|
422
|
-
const config = {
|
|
423
|
-
bin,
|
|
424
|
-
cardToken,
|
|
425
|
-
customer,
|
|
426
|
-
paymentIntent,
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
scaTests(config)
|
|
430
|
-
})
|
|
431
|
-
|
|
432
|
-
describe('customer details: partial', () => {
|
|
433
|
-
const customer: Customer = {
|
|
434
|
-
firstName: 'Ziggy',
|
|
435
|
-
lastName: '',
|
|
436
|
-
email: 'ziggy.zebra@fatzebra.com',
|
|
437
|
-
address: null,
|
|
438
|
-
city: undefined,
|
|
439
|
-
postcode: '',
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
const config = {
|
|
443
|
-
bin,
|
|
444
|
-
cardToken,
|
|
445
|
-
customer,
|
|
446
|
-
paymentIntent,
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
scaTests(config)
|
|
450
|
-
})
|
|
451
|
-
|
|
452
|
-
describe('customer details: empty', () => {
|
|
453
|
-
const customer: Customer = {}
|
|
454
|
-
|
|
455
|
-
const config = {
|
|
456
|
-
bin,
|
|
457
|
-
cardToken,
|
|
458
|
-
customer,
|
|
459
|
-
paymentIntent,
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
scaTests(config)
|
|
463
|
-
})
|
|
464
|
-
|
|
465
|
-
describe('no customer', () => {
|
|
466
|
-
const config = {
|
|
467
|
-
bin,
|
|
468
|
-
cardToken,
|
|
469
|
-
paymentIntent,
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
scaTests(config)
|
|
473
|
-
})
|
|
474
|
-
})
|
|
475
|
-
|
|
476
|
-
describe('#customerProperties', () => {
|
|
477
|
-
const sca = new Sca({ gatewayClient })
|
|
478
|
-
|
|
479
|
-
it('formats the property keys into snake_case', () => {
|
|
480
|
-
const customer: Customer = {
|
|
481
|
-
firstName: 'Ziggy',
|
|
482
|
-
lastName: 'Zebra',
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
const expectedProperties = {
|
|
486
|
-
first_name: 'Ziggy',
|
|
487
|
-
last_name: 'Zebra',
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
expect(sca.customerProperties(customer)).toEqual(expectedProperties)
|
|
491
|
-
})
|
|
492
|
-
|
|
493
|
-
it('only retains non-empty customer properties', () => {
|
|
494
|
-
const customer: Customer = {
|
|
495
|
-
firstName: 'Ziggy',
|
|
496
|
-
lastName: 'Zebra',
|
|
497
|
-
email: 'ziggy.zebra@fatzebra.com',
|
|
498
|
-
address: null,
|
|
499
|
-
city: null,
|
|
500
|
-
postcode: undefined,
|
|
501
|
-
state: undefined,
|
|
502
|
-
country: ''
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
const expectedProperties = {
|
|
506
|
-
first_name: 'Ziggy',
|
|
507
|
-
last_name: 'Zebra',
|
|
508
|
-
email: 'ziggy.zebra@fatzebra.com',
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
expect(sca.customerProperties(customer)).toEqual(expectedProperties)
|
|
512
|
-
})
|
|
513
|
-
})
|
|
514
|
-
})
|
package/src/sca/cardinal.ts
DELETED
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
// https://cardinaldocs.atlassian.net/wiki/spaces/CC/pages/1409568/Configurations
|
|
2
|
-
interface CardinalConfig {
|
|
3
|
-
timeout?: number
|
|
4
|
-
maxRequestRetries?: number
|
|
5
|
-
logging?: {
|
|
6
|
-
level: 'on' | 'off' | 'verbose'
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
interface CardinalContinueObject {
|
|
11
|
-
AcsUrl: string
|
|
12
|
-
Payload: string
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
interface CardinalOrderObject {
|
|
16
|
-
OrderDetails: {
|
|
17
|
-
TransactionId: string
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
interface CardinalSetupCompleteResponseData {
|
|
22
|
-
sessionId: string
|
|
23
|
-
modules: { module: string, loaded: boolean }[]
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// https://cardinaldocs.atlassian.net/wiki/spaces/CC/pages/98315/Response+Objects
|
|
27
|
-
interface PaymentValidatedResponseData {
|
|
28
|
-
Validated: boolean
|
|
29
|
-
ErrorNumber: number
|
|
30
|
-
ErrorDescription: string
|
|
31
|
-
ActionCode: 'SUCCESS' | 'NOACTION' | 'FAILURE' | 'ERROR'
|
|
32
|
-
Payment: {
|
|
33
|
-
OrderId: string
|
|
34
|
-
OrderNumber: string
|
|
35
|
-
ProcessorTransactionId: string
|
|
36
|
-
ReasonDescription: string
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
interface PaymentValidatedDTO {
|
|
41
|
-
processorTransactionId: string
|
|
42
|
-
jwt: string
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
enum CardinalWorkflow {
|
|
46
|
-
CCA = 'cca',
|
|
47
|
-
INIT = 'init',
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
enum CardinalPaymentEvent {
|
|
51
|
-
SETUP_COMPLETE = 'payments.setupComplete',
|
|
52
|
-
VALIDATED = 'payments.validated',
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
interface Cardinal {
|
|
56
|
-
configure(config: CardinalConfig): void
|
|
57
|
-
continue(
|
|
58
|
-
workflow: CardinalWorkflow,
|
|
59
|
-
data: CardinalContinueObject,
|
|
60
|
-
order: CardinalOrderObject
|
|
61
|
-
): void
|
|
62
|
-
on(event: string, callback: (...data: any[]) => void): void
|
|
63
|
-
setup(workflow: CardinalWorkflow, data: any): void
|
|
64
|
-
trigger(event: string, cardBin: string): Promise<any>
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
interface ScaResult {
|
|
68
|
-
success: boolean
|
|
69
|
-
type: 'frictionless' | 'challenge' | 'none'
|
|
70
|
-
error: string
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const defaultConfig = {
|
|
74
|
-
timeout: 8000,
|
|
75
|
-
maxRequestRetries: 3,
|
|
76
|
-
logging: {
|
|
77
|
-
level: 'on'
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export default class CardinalManager {
|
|
82
|
-
private cardinal: Cardinal
|
|
83
|
-
|
|
84
|
-
constructor(config?: CardinalConfig) {
|
|
85
|
-
window.Cardinal.configure((config ? config : defaultConfig as CardinalConfig))
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
setup(jwt: any): void {
|
|
89
|
-
window.Cardinal.setup(CardinalWorkflow.INIT, { jwt })
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
onPaymentValidated(handler: (data: PaymentValidatedDTO, error: string) => void): void {
|
|
93
|
-
window.Cardinal.on('payments.validated', (data: PaymentValidatedResponseData, jwt: string) => {
|
|
94
|
-
if (data.ErrorDescription.toLowerCase() === 'success') {
|
|
95
|
-
handler({
|
|
96
|
-
processorTransactionId: data.Payment.ProcessorTransactionId,
|
|
97
|
-
jwt
|
|
98
|
-
}, null)
|
|
99
|
-
} else {
|
|
100
|
-
handler(null, data.ErrorDescription)
|
|
101
|
-
}
|
|
102
|
-
})
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
onPaymentSetupComplete(): Promise<CardinalSetupCompleteResponseData> {
|
|
106
|
-
return new Promise((resolve, reject) => {
|
|
107
|
-
window.Cardinal.on(CardinalPaymentEvent.SETUP_COMPLETE, (data: CardinalSetupCompleteResponseData) => {
|
|
108
|
-
resolve(data)
|
|
109
|
-
})
|
|
110
|
-
})
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
async processBin(bin: string): Promise<any> {
|
|
114
|
-
await window.Cardinal.trigger('bin.process', bin)
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
continue(acsUrl: string, pareq: string, transactionId: string) {
|
|
118
|
-
window.Cardinal.continue(
|
|
119
|
-
CardinalWorkflow.CCA,
|
|
120
|
-
{
|
|
121
|
-
AcsUrl: acsUrl,
|
|
122
|
-
Payload: pareq
|
|
123
|
-
},
|
|
124
|
-
{
|
|
125
|
-
OrderDetails: {
|
|
126
|
-
TransactionId: transactionId
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
)
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
export {
|
|
134
|
-
CardinalManager
|
|
135
|
-
}
|
|
136
|
-
export type { PaymentValidatedDTO }
|
package/src/sca/eci-mappings.ts
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
// FatZebra only supports SLI 05, 06, 07 & 09.
|
|
2
|
-
// https://docs.fatzebra.com/docs/3d-secure
|
|
3
|
-
// Cardinal returns ECI(or SLI for MC) in the range of 01~07
|
|
4
|
-
// https://cardinaldocs.atlassian.net/wiki/spaces/CCen/pages/903577725/EMV+3DS+2.0+Test+Cases
|
|
5
|
-
const eciMappings: { vendorEci: string, fatzebraSli: string }[] = [
|
|
6
|
-
{
|
|
7
|
-
// Cards: Mastercard, CB Mastercard
|
|
8
|
-
// Test Case 2: Failed Frictionless Authentication
|
|
9
|
-
// Test Case 4: Unavailable Frictionless Authentication from the Issuer
|
|
10
|
-
// Test Case 5: Rejected Frictionless Authentication by the Issuer
|
|
11
|
-
// Test Case 6: Authentication Not Available on Lookup
|
|
12
|
-
// Test Case 7: Error on Lookup
|
|
13
|
-
// Test Case 9: Bypassed Authentication
|
|
14
|
-
// Test Case 11: Failed Step Up Authentication
|
|
15
|
-
// Test Case 12: Step Up Authentication is Unavailable
|
|
16
|
-
// Test Case 13: Error on Authentication
|
|
17
|
-
vendorEci: '00',
|
|
18
|
-
// Non-authenticated transaction - issuer not support. CAVV not present.
|
|
19
|
-
fatzebraSli: '07',
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
// Cards: Mastercard, CB Mastercard
|
|
23
|
-
// Test Case 3: Attempts Stand-In Frictionless Authentication
|
|
24
|
-
vendorEci: '01',
|
|
25
|
-
// Cards: Visa, American Express, Discover (Diners Club), CB Visa, ELO
|
|
26
|
-
// Secure, Non-authenticated transaction (XID present, CAVV not present)
|
|
27
|
-
fatzebraSli: '06',
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
// Cards: Mastercard, CB Mastercard
|
|
31
|
-
// Test Case 1: Successful Frictionless Authentication
|
|
32
|
-
// Test Case 10: Successful Step Up Authentication
|
|
33
|
-
vendorEci: '02',
|
|
34
|
-
// Cards: Visa, American Express, Discover (Diners Club), CB Visa, ELO
|
|
35
|
-
// Secure, Authenticated transaction with XID and CAVV present
|
|
36
|
-
fatzebraSli: '05',
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
vendorEci: '05',
|
|
40
|
-
// Secure, Authenticated transaction with XID and CAVV present
|
|
41
|
-
fatzebraSli: '05',
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
vendorEci: '06',
|
|
45
|
-
// Secure, Non-authenticated transaction (XID present, CAVV not present)
|
|
46
|
-
fatzebraSli: '06',
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
vendorEci: '07',
|
|
50
|
-
// Secure, Non-authenticated transaction (XID and CAVV not present). This is the default value.
|
|
51
|
-
fatzebraSli: '07',
|
|
52
|
-
},
|
|
53
|
-
]
|
|
54
|
-
|
|
55
|
-
const toFzSli = (vendorEci: string): string => {
|
|
56
|
-
const matched = eciMappings.find((item) => item.vendorEci == vendorEci)
|
|
57
|
-
return matched ? matched.fatzebraSli : vendorEci
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export {
|
|
61
|
-
eciMappings,
|
|
62
|
-
toFzSli,
|
|
63
|
-
}
|