@henrylabs-interview/payment-processor 0.2.11 → 0.2.12
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.
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
type CheckoutCreateResponse = CheckoutCreateSuccessDeferred | CheckoutCreateSuccessImmediate | CheckoutCreateFailure;
|
|
2
2
|
interface CheckoutCreateGeneric {
|
|
3
|
+
_reqId: string;
|
|
3
4
|
status: 'success' | 'failure';
|
|
4
5
|
code: number;
|
|
5
6
|
message: string;
|
|
@@ -42,6 +43,7 @@ interface CheckoutConfirmParamsRawCard extends CheckoutConfirmParamsGeneric {
|
|
|
42
43
|
}
|
|
43
44
|
type CheckoutConfirmResponse = CheckoutConfirmSuccessDeferred | CheckoutConfirmSuccessImmediate | CheckoutConfirmFailure;
|
|
44
45
|
interface CheckoutConfirmGeneric {
|
|
46
|
+
_reqId: string;
|
|
45
47
|
status: 'success' | 'failure';
|
|
46
48
|
code: number;
|
|
47
49
|
message: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readHistory, writeHistory } from '../utils/store';
|
|
2
|
-
import { generateTimeBasedID, hashToString, signPayload } from '../utils/crypto';
|
|
2
|
+
import { generateTimeBasedID, genReqId, hashToString, signPayload } from '../utils/crypto';
|
|
3
3
|
import { INTERNAL_WEBHOOKS } from './webhooks';
|
|
4
4
|
import { sleep } from '../utils/async';
|
|
5
5
|
import { isValidCardNumber, isValidExpiry } from '../utils/card';
|
|
@@ -67,6 +67,7 @@ export class Checkout {
|
|
|
67
67
|
validateCreate(params) {
|
|
68
68
|
if (params.amount <= 0) {
|
|
69
69
|
return {
|
|
70
|
+
_reqId: genReqId(),
|
|
70
71
|
status: 'failure',
|
|
71
72
|
substatus: '500-error',
|
|
72
73
|
code: 500,
|
|
@@ -75,6 +76,7 @@ export class Checkout {
|
|
|
75
76
|
}
|
|
76
77
|
if (params.currency === 'JPY') {
|
|
77
78
|
return {
|
|
79
|
+
_reqId: genReqId(),
|
|
78
80
|
status: 'failure',
|
|
79
81
|
substatus: '501-not-supported',
|
|
80
82
|
code: 501,
|
|
@@ -84,6 +86,7 @@ export class Checkout {
|
|
|
84
86
|
// Simulated random internal failure
|
|
85
87
|
if (Math.random() > 0.85) {
|
|
86
88
|
return {
|
|
89
|
+
_reqId: genReqId(),
|
|
87
90
|
status: 'failure',
|
|
88
91
|
substatus: '500-error',
|
|
89
92
|
code: 500,
|
|
@@ -96,6 +99,7 @@ export class Checkout {
|
|
|
96
99
|
const resCase = this.determineResponseCase(params.amount, duplicateCount);
|
|
97
100
|
if (resCase === 'failure-retry') {
|
|
98
101
|
return {
|
|
102
|
+
_reqId: genReqId(),
|
|
99
103
|
status: 'failure',
|
|
100
104
|
substatus: '503-retry',
|
|
101
105
|
code: 503,
|
|
@@ -104,6 +108,7 @@ export class Checkout {
|
|
|
104
108
|
}
|
|
105
109
|
if (resCase === 'failure-fraud') {
|
|
106
110
|
return {
|
|
111
|
+
_reqId: genReqId(),
|
|
107
112
|
status: 'failure',
|
|
108
113
|
substatus: '502-fraud',
|
|
109
114
|
code: 502,
|
|
@@ -113,6 +118,7 @@ export class Checkout {
|
|
|
113
118
|
const checkoutId = await this.createCheckoutRecord(hashId);
|
|
114
119
|
if (resCase === 'success-deferred') {
|
|
115
120
|
return {
|
|
121
|
+
_reqId: genReqId(),
|
|
116
122
|
status: 'success',
|
|
117
123
|
substatus: '202-deferred',
|
|
118
124
|
code: 202,
|
|
@@ -120,6 +126,7 @@ export class Checkout {
|
|
|
120
126
|
};
|
|
121
127
|
}
|
|
122
128
|
return {
|
|
129
|
+
_reqId: genReqId(),
|
|
123
130
|
status: 'success',
|
|
124
131
|
substatus: '201-immediate',
|
|
125
132
|
code: 201,
|
|
@@ -145,6 +152,7 @@ export class Checkout {
|
|
|
145
152
|
if (Math.random() > 0.2) {
|
|
146
153
|
const checkoutId = await this.createCheckoutRecord(hashId);
|
|
147
154
|
this.sendWebhookResponse('checkout.create', {
|
|
155
|
+
_reqId: response._reqId,
|
|
148
156
|
status: 'success',
|
|
149
157
|
substatus: '201-immediate',
|
|
150
158
|
code: 201,
|
|
@@ -157,6 +165,7 @@ export class Checkout {
|
|
|
157
165
|
}
|
|
158
166
|
else if (Math.random() > 0.05) {
|
|
159
167
|
this.sendWebhookResponse('checkout.create', {
|
|
168
|
+
_reqId: response._reqId,
|
|
160
169
|
status: 'failure',
|
|
161
170
|
substatus: '503-retry',
|
|
162
171
|
code: 503,
|
|
@@ -165,6 +174,7 @@ export class Checkout {
|
|
|
165
174
|
}
|
|
166
175
|
else {
|
|
167
176
|
this.sendWebhookResponse('checkout.create', {
|
|
177
|
+
_reqId: response._reqId,
|
|
168
178
|
status: 'failure',
|
|
169
179
|
substatus: '502-fraud',
|
|
170
180
|
code: 502,
|
|
@@ -189,6 +199,7 @@ export class Checkout {
|
|
|
189
199
|
async validateConfirm(params) {
|
|
190
200
|
if (`${params.checkoutId}`.length !== 20 || !`${params.checkoutId}`.startsWith('cki_')) {
|
|
191
201
|
return {
|
|
202
|
+
_reqId: genReqId(),
|
|
192
203
|
status: 'failure',
|
|
193
204
|
substatus: '500-error',
|
|
194
205
|
code: 500,
|
|
@@ -197,6 +208,7 @@ export class Checkout {
|
|
|
197
208
|
}
|
|
198
209
|
if (!INTERNAL_CHECKOUTS[`${params.checkoutId}`]) {
|
|
199
210
|
return {
|
|
211
|
+
_reqId: genReqId(),
|
|
200
212
|
status: 'failure',
|
|
201
213
|
substatus: '503-retry',
|
|
202
214
|
code: 504,
|
|
@@ -206,6 +218,7 @@ export class Checkout {
|
|
|
206
218
|
if (params.type === 'embedded') {
|
|
207
219
|
if (params.data.paymentToken.length !== 20 || !params.data.paymentToken.startsWith('pmt_')) {
|
|
208
220
|
return {
|
|
221
|
+
_reqId: genReqId(),
|
|
209
222
|
status: 'failure',
|
|
210
223
|
substatus: '500-error',
|
|
211
224
|
code: 500,
|
|
@@ -217,6 +230,7 @@ export class Checkout {
|
|
|
217
230
|
const paymentToken = `pmt_${await generateTimeBasedID('payment-token')}`;
|
|
218
231
|
if (params.data.paymentToken !== paymentToken) {
|
|
219
232
|
return {
|
|
233
|
+
_reqId: genReqId(),
|
|
220
234
|
status: 'failure',
|
|
221
235
|
substatus: '503-retry',
|
|
222
236
|
code: 503,
|
|
@@ -228,6 +242,7 @@ export class Checkout {
|
|
|
228
242
|
const { number, expMonth, expYear, cvc } = params.data;
|
|
229
243
|
if (!isValidCardNumber(number)) {
|
|
230
244
|
return {
|
|
245
|
+
_reqId: genReqId(),
|
|
231
246
|
status: 'failure',
|
|
232
247
|
substatus: '500-error',
|
|
233
248
|
code: 500,
|
|
@@ -236,6 +251,7 @@ export class Checkout {
|
|
|
236
251
|
}
|
|
237
252
|
if (!isValidExpiry(expMonth, expYear)) {
|
|
238
253
|
return {
|
|
254
|
+
_reqId: genReqId(),
|
|
239
255
|
status: 'failure',
|
|
240
256
|
substatus: '500-error',
|
|
241
257
|
code: 500,
|
|
@@ -244,6 +260,7 @@ export class Checkout {
|
|
|
244
260
|
}
|
|
245
261
|
if (!/^\d{3,4}$/.test(cvc)) {
|
|
246
262
|
return {
|
|
263
|
+
_reqId: genReqId(),
|
|
247
264
|
status: 'failure',
|
|
248
265
|
substatus: '500-error',
|
|
249
266
|
code: 500,
|
|
@@ -257,6 +274,7 @@ export class Checkout {
|
|
|
257
274
|
const decision = Math.random();
|
|
258
275
|
if (decision > 0.95) {
|
|
259
276
|
return {
|
|
277
|
+
_reqId: genReqId(),
|
|
260
278
|
status: 'failure',
|
|
261
279
|
substatus: '502-fraud',
|
|
262
280
|
code: 502,
|
|
@@ -265,6 +283,7 @@ export class Checkout {
|
|
|
265
283
|
}
|
|
266
284
|
if (decision > 0.65) {
|
|
267
285
|
return {
|
|
286
|
+
_reqId: genReqId(),
|
|
268
287
|
status: 'failure',
|
|
269
288
|
substatus: '503-retry',
|
|
270
289
|
code: 503,
|
|
@@ -273,6 +292,7 @@ export class Checkout {
|
|
|
273
292
|
}
|
|
274
293
|
if (decision > 0.35) {
|
|
275
294
|
return {
|
|
295
|
+
_reqId: genReqId(),
|
|
276
296
|
status: 'success',
|
|
277
297
|
substatus: '202-deferred',
|
|
278
298
|
code: 202,
|
|
@@ -285,6 +305,7 @@ export class Checkout {
|
|
|
285
305
|
const { historyRecordId } = INTERNAL_CHECKOUTS[`${checkoutId}`] ?? {};
|
|
286
306
|
if (!historyRecordId) {
|
|
287
307
|
return {
|
|
308
|
+
_reqId: genReqId(),
|
|
288
309
|
status: 'failure',
|
|
289
310
|
substatus: '500-error',
|
|
290
311
|
code: 500,
|
|
@@ -295,6 +316,7 @@ export class Checkout {
|
|
|
295
316
|
const record = history.find((v) => v.id === historyRecordId);
|
|
296
317
|
if (!record) {
|
|
297
318
|
return {
|
|
319
|
+
_reqId: genReqId(),
|
|
298
320
|
status: 'failure',
|
|
299
321
|
substatus: '500-error',
|
|
300
322
|
code: 500,
|
|
@@ -309,6 +331,7 @@ export class Checkout {
|
|
|
309
331
|
customerId: record.customerId,
|
|
310
332
|
})));
|
|
311
333
|
return {
|
|
334
|
+
_reqId: genReqId(),
|
|
312
335
|
status: 'success',
|
|
313
336
|
substatus: '201-immediate',
|
|
314
337
|
code: 201,
|
|
@@ -332,6 +355,7 @@ export class Checkout {
|
|
|
332
355
|
}
|
|
333
356
|
else if (Math.random() > 0.05) {
|
|
334
357
|
this.sendWebhookResponse('checkout.confirm', {
|
|
358
|
+
_reqId: response._reqId,
|
|
335
359
|
status: 'failure',
|
|
336
360
|
substatus: '503-retry',
|
|
337
361
|
code: 503,
|
|
@@ -340,6 +364,7 @@ export class Checkout {
|
|
|
340
364
|
}
|
|
341
365
|
else {
|
|
342
366
|
this.sendWebhookResponse('checkout.confirm', {
|
|
367
|
+
_reqId: response._reqId,
|
|
343
368
|
status: 'failure',
|
|
344
369
|
substatus: '502-fraud',
|
|
345
370
|
code: 502,
|
|
@@ -402,7 +427,7 @@ export class Checkout {
|
|
|
402
427
|
const matchingTypes = ['checkout', baseType, eventType];
|
|
403
428
|
const hooks = INTERNAL_WEBHOOKS.filter((w) => matchingTypes.includes(w.event));
|
|
404
429
|
const event = {
|
|
405
|
-
|
|
430
|
+
uid: `_${genReqId()}_`,
|
|
406
431
|
type: eventType,
|
|
407
432
|
createdAt: Date.now(),
|
|
408
433
|
data: response,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export type EventType = 'checkout' | 'checkout.create' | 'checkout.create.success' | 'checkout.create.failure' | 'checkout.confirm' | 'checkout.confirm.success' | 'checkout.confirm.failure';
|
|
2
2
|
export interface WebhookEvent {
|
|
3
|
-
|
|
3
|
+
uid: string;
|
|
4
4
|
type: EventType;
|
|
5
5
|
createdAt: number;
|
|
6
6
|
data: Record<string, any>;
|
package/dist/utils/crypto.d.ts
CHANGED
|
@@ -2,3 +2,4 @@ export declare function generateID(length?: number): number;
|
|
|
2
2
|
export declare function generateTimeBasedID(extra?: string): Promise<string>;
|
|
3
3
|
export declare function hashToString(input: string): Promise<string>;
|
|
4
4
|
export declare function signPayload(payload: string, secret: string): string;
|
|
5
|
+
export declare function genReqId(): string;
|
package/dist/utils/crypto.js
CHANGED