@henrylabs-interview/payment-processor 0.1.12 → 0.1.13
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/dist/index.d.ts +1 -1
- package/dist/resources/checkout.d.ts +0 -9
- package/dist/resources/checkout.js +25 -26
- package/dist/resources/embedded.d.ts +1 -1
- package/dist/resources/embedded.js +13 -16
- package/dist/utils/crypto.d.ts +1 -0
- package/dist/utils/crypto.js +6 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -64,15 +64,6 @@ interface CheckoutConfirmFailure extends CheckoutConfirmGeneric {
|
|
|
64
64
|
status: 'failure';
|
|
65
65
|
substatus: '500-error' | '502-fraud' | '503-retry';
|
|
66
66
|
}
|
|
67
|
-
export declare const INTERNAL_CHECKOUTS: Record<string, {
|
|
68
|
-
historyRecordId: number;
|
|
69
|
-
}>;
|
|
70
|
-
export declare const INTERNAL_CARDS: Record<string, {
|
|
71
|
-
number: string;
|
|
72
|
-
expMonth: number;
|
|
73
|
-
expYear: number;
|
|
74
|
-
cvc: string;
|
|
75
|
-
}>;
|
|
76
67
|
export declare class Checkout {
|
|
77
68
|
/**
|
|
78
69
|
* Create a new checkout session
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { readHistory, writeHistory } from '../utils/store';
|
|
2
|
-
import { generateID, hashToNumber, signPayload } from '../utils/crypto';
|
|
2
|
+
import { generateID, generateTimeBasedID, hashToNumber, signPayload } from '../utils/crypto';
|
|
3
3
|
import { INTERNAL_WEBHOOKS } from './webhooks';
|
|
4
4
|
import { sleep } from '../utils/async';
|
|
5
5
|
// checkoutId -> { historyRecordId }
|
|
6
|
-
|
|
7
|
-
// paymentToken -> card details
|
|
8
|
-
export const INTERNAL_CARDS = {};
|
|
6
|
+
const INTERNAL_CHECKOUTS = {};
|
|
9
7
|
export class Checkout {
|
|
10
8
|
/**
|
|
11
9
|
* Create a new checkout session
|
|
@@ -132,7 +130,7 @@ export class Checkout {
|
|
|
132
130
|
};
|
|
133
131
|
}
|
|
134
132
|
createCheckoutRecord(hashId) {
|
|
135
|
-
const checkoutId =
|
|
133
|
+
const checkoutId = generateTimeBasedID('checkout');
|
|
136
134
|
INTERNAL_CHECKOUTS[`${checkoutId}`] = {
|
|
137
135
|
historyRecordId: hashId,
|
|
138
136
|
};
|
|
@@ -181,7 +179,7 @@ export class Checkout {
|
|
|
181
179
|
}
|
|
182
180
|
///
|
|
183
181
|
async validateConfirm(params) {
|
|
184
|
-
if (
|
|
182
|
+
if (`${params.checkoutId}`.length !== 12) {
|
|
185
183
|
return {
|
|
186
184
|
status: 'failure',
|
|
187
185
|
substatus: '500-error',
|
|
@@ -189,14 +187,30 @@ export class Checkout {
|
|
|
189
187
|
message: 'Invalid checkout ID',
|
|
190
188
|
};
|
|
191
189
|
}
|
|
190
|
+
if (!INTERNAL_CHECKOUTS[`${params.checkoutId}`]) {
|
|
191
|
+
return {
|
|
192
|
+
status: 'failure',
|
|
193
|
+
substatus: '503-retry',
|
|
194
|
+
code: 504,
|
|
195
|
+
message: 'Expired checkout ID',
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
if (params.type === 'embedded' && params.data.paymentToken.length !== 12) {
|
|
199
|
+
return {
|
|
200
|
+
status: 'failure',
|
|
201
|
+
substatus: '500-error',
|
|
202
|
+
code: 500,
|
|
203
|
+
message: 'Invalid payment token',
|
|
204
|
+
};
|
|
205
|
+
}
|
|
192
206
|
if (params.type === 'embedded') {
|
|
193
|
-
const
|
|
194
|
-
if (
|
|
207
|
+
const paymentToken = `pmt_${generateTimeBasedID('payment-token')}`;
|
|
208
|
+
if (params.data.paymentToken !== paymentToken) {
|
|
195
209
|
return {
|
|
196
210
|
status: 'failure',
|
|
197
|
-
substatus: '
|
|
198
|
-
code:
|
|
199
|
-
message: '
|
|
211
|
+
substatus: '503-retry',
|
|
212
|
+
code: 503,
|
|
213
|
+
message: 'Expired payment token',
|
|
200
214
|
};
|
|
201
215
|
}
|
|
202
216
|
}
|
|
@@ -230,21 +244,6 @@ export class Checkout {
|
|
|
230
244
|
return null;
|
|
231
245
|
}
|
|
232
246
|
async processConfirmDecision(params) {
|
|
233
|
-
if (params.type === 'raw-card') {
|
|
234
|
-
// Add new card to internal storage
|
|
235
|
-
const cardDetails = {
|
|
236
|
-
number: params.data.number,
|
|
237
|
-
expMonth: params.data.expMonth,
|
|
238
|
-
expYear: params.data.expYear > 2000 ? params.data.expYear : params.data.expYear + 2000,
|
|
239
|
-
cvc: params.data.cvc,
|
|
240
|
-
};
|
|
241
|
-
const paymentToken = 'pmt_' +
|
|
242
|
-
hashToNumber(JSON.stringify({
|
|
243
|
-
type: 'PAYMENT_TOKEN',
|
|
244
|
-
...cardDetails,
|
|
245
|
-
}));
|
|
246
|
-
INTERNAL_CARDS[`${paymentToken}`] = cardDetails;
|
|
247
|
-
}
|
|
248
247
|
const decision = Math.random();
|
|
249
248
|
if (decision > 0.85) {
|
|
250
249
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function renderEmbeddedCheckout(containerElementId: string, checkoutId:
|
|
1
|
+
export declare function renderEmbeddedCheckout(containerElementId: string, checkoutId: number, callbackFn: (paymentToken: string) => void): boolean;
|
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { INTERNAL_CARDS, INTERNAL_CHECKOUTS } from './checkout';
|
|
1
|
+
import { generateTimeBasedID } from '../utils/crypto';
|
|
3
2
|
export function renderEmbeddedCheckout(containerElementId, checkoutId, callbackFn) {
|
|
4
3
|
// Ensure browser environment
|
|
5
4
|
if (typeof window === 'undefined' || typeof document === 'undefined') {
|
|
6
5
|
console.warn('EmbeddedCheckoutUI can only be used in a browser environment.');
|
|
7
6
|
return false;
|
|
8
7
|
}
|
|
9
|
-
|
|
10
|
-
if (!historyRecordId) {
|
|
8
|
+
if (checkoutId.toString().length !== 12) {
|
|
11
9
|
console.warn(`Invalid checkout ID: "${checkoutId}".`);
|
|
12
10
|
return false;
|
|
13
11
|
}
|
|
12
|
+
if (`${checkoutId}` === `${generateTimeBasedID('checkout')}`) {
|
|
13
|
+
console.warn(`Expired checkout ID: "${checkoutId}".`);
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
14
16
|
const container = document.getElementById(containerElementId);
|
|
15
17
|
if (!container) {
|
|
16
18
|
console.warn(`Container element "${containerElementId}" not found.`);
|
|
@@ -147,18 +149,13 @@ export function renderEmbeddedCheckout(containerElementId, checkoutId, callbackF
|
|
|
147
149
|
const expYear = Number(container.querySelector('#exp-year')?.value);
|
|
148
150
|
const cvc = container.querySelector('#cvc')?.value ?? '';
|
|
149
151
|
// Add new card to internal storage
|
|
150
|
-
const cardDetails = {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
};
|
|
156
|
-
const paymentToken =
|
|
157
|
-
hashToNumber(JSON.stringify({
|
|
158
|
-
type: 'PAYMENT_TOKEN',
|
|
159
|
-
...cardDetails,
|
|
160
|
-
}));
|
|
161
|
-
INTERNAL_CARDS[`${paymentToken}`] = cardDetails;
|
|
152
|
+
// const cardDetails = {
|
|
153
|
+
// number: number,
|
|
154
|
+
// expMonth: expMonth,
|
|
155
|
+
// expYear: expYear > 2000 ? expYear : expYear + 2000,
|
|
156
|
+
// cvc: cvc,
|
|
157
|
+
// };
|
|
158
|
+
const paymentToken = `pmt_${generateTimeBasedID('payment-token')}`;
|
|
162
159
|
callbackFn(paymentToken);
|
|
163
160
|
});
|
|
164
161
|
return true;
|
package/dist/utils/crypto.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export declare function generateID(length?: number): number;
|
|
2
|
+
export declare function generateTimeBasedID(extra?: string): number;
|
|
2
3
|
export declare function hashToNumber(input: string, length?: number): number;
|
|
3
4
|
export declare function signPayload(payload: string, secret: string): string;
|
package/dist/utils/crypto.js
CHANGED
|
@@ -7,6 +7,12 @@ export function generateID(length = 12) {
|
|
|
7
7
|
}
|
|
8
8
|
return parseInt(digits.join(''));
|
|
9
9
|
}
|
|
10
|
+
export function generateTimeBasedID(extra = '') {
|
|
11
|
+
const now = Date.now(); // current time in ms
|
|
12
|
+
const oneMinute = 60 * 1000;
|
|
13
|
+
const ms = Math.floor(now / oneMinute) * oneMinute;
|
|
14
|
+
return hashToNumber(`${ms}---${extra}`);
|
|
15
|
+
}
|
|
10
16
|
export function hashToNumber(input, length = 12) {
|
|
11
17
|
if (length > 16) {
|
|
12
18
|
throw new Error('Length is greater than max length of 16!');
|