@yosle/tropipayjs 0.1.19 → 0.1.21

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/.nvmrc CHANGED
@@ -1 +1 @@
1
- v18.16.1
1
+ v20.10.0
package/README.md CHANGED
@@ -47,8 +47,8 @@ or
47
47
 
48
48
  ## Setting up your app credentials
49
49
 
50
- ![alt](https://github.com/yosle/tropipayjs/blob/master/docs/images/app-credentials-menu.png?raw=true)
51
- ![alt](https://github.com/yosle/tropipayjs/blob/master/docs/images/confirmation-code-screen.png?raw=true)
50
+ ![alt](https://yosle.github.io/tropipayjs-docs/images/cred2.png)
51
+ ![alt](https://yosle.github.io/tropipayjs-docs/images/cred3.png)
52
52
 
53
53
  In order to use the Tropipay API you'll need a client ID and a client secret. Sign up with your [Tropipay](www.tropipay.com) account, go to the App Menu->Applications and credentials. You'll be asked to enter a confirmation code (use 123456 in the test enviroment). _Make sure you test everything in the [test environment](https://tropipay-dev.herokuapp.com) first before using your real account._ Create an app and configure the client id an client secret for the app.
54
54
 
@@ -5,7 +5,7 @@
5
5
  *
6
6
  */
7
7
  import { Axios } from "axios";
8
- import { TropipayConfig, AccountBalance, Country, PaymentLink, mediationPaymentCardConfig, LoginResponse, AccountDeposits } from "../interfaces";
8
+ import { TropipayConfig, AccountBalance, Country, PaymentLink, mediationPaymentCardConfig, LoginResponse } from "../interfaces";
9
9
  type ServerMode = "Development" | "Production";
10
10
  import { TropipayHooks } from "../hooks/TropipayHooks";
11
11
  import { PaymentCard } from "../paymentcard/PaymentCard";
@@ -13,21 +13,21 @@ import { DepositAccounts } from "../depositAccount/depositAccounts";
13
13
  export declare class Tropipay {
14
14
  readonly clientId: string;
15
15
  readonly clientSecret: string;
16
+ readonly scopes: String[];
16
17
  request: Axios;
17
- static accessToken: string | undefined;
18
- static refreshToken: string | undefined;
18
+ static accessToken: string | null;
19
+ static refreshToken: string | null;
19
20
  serverMode: ServerMode;
20
21
  hooks: TropipayHooks;
21
22
  paymentCards: PaymentCard;
22
23
  depositAccounts: DepositAccounts;
23
- constructor(config: TropipayConfig);
24
- login(): Promise<LoginResponse>;
25
24
  /**
26
- * Get all deposits in this account.
27
- * @returns A Promise of an Array of AccountDeposits or throws an Exception
28
- * @see https://tpp.stoplight.io/docs/tropipay-api-doc/b3A6OTgyOTQ1Mg-get-deposit-accounts-list
25
+ * Initializes a new instance of the Tropipay class.
26
+ *
27
+ * @param {TropipayConfig} config - The configuration object.
29
28
  */
30
- getDepositAccounts(): Promise<AccountDeposits[] | Error>;
29
+ constructor(config: TropipayConfig);
30
+ login(): Promise<LoginResponse>;
31
31
  /**
32
32
  * Get the list of all supported countries by Tropipay.
33
33
  * @returns Array of Countries Data
@@ -1 +1,2 @@
1
1
  export type ServerMode = "Development" | "Production";
2
+ export declare const MAX_IMAGE_SIZE_MB = 1;
@@ -25,7 +25,7 @@ export declare class TropipayHooks {
25
25
  value: string;
26
26
  }): Promise<any>;
27
27
  /**
28
- * Get the sucbcribed hook info by his event type.
28
+ * Get the subscribed hook info by his event type.
29
29
  * If no event type is passed, it will return
30
30
  * all subscribed hooks or empty Array if none exist.
31
31
  * @param eventType or no params for retrieving all hooks
package/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Axios } from 'axios';
2
2
 
3
3
  type ServerMode$1 = "Development" | "Production";
4
+ declare const MAX_IMAGE_SIZE_MB = 1;
4
5
 
5
6
  type AccountBalance = {
6
7
  balance: number;
@@ -10,6 +11,7 @@ type AccountBalance = {
10
11
  interface TropipayConfig {
11
12
  clientId: string;
12
13
  clientSecret: string;
14
+ scopes?: string[];
13
15
  serverMode?: ServerMode$1;
14
16
  }
15
17
  type TropipayCredentials = {
@@ -62,6 +64,7 @@ interface PaymentLinkPayload {
62
64
  };
63
65
  directPayment: boolean;
64
66
  paymentMethods?: string[];
67
+ imageBase?: string;
65
68
  }
66
69
  interface PaymentLink extends PaymentLinkPayload {
67
70
  expirationDate: string;
@@ -186,7 +189,7 @@ declare class TropipayHooks {
186
189
  value: string;
187
190
  }): Promise<any>;
188
191
  /**
189
- * Get the sucbcribed hook info by his event type.
192
+ * Get the subscribed hook info by his event type.
190
193
  * If no event type is passed, it will return
191
194
  * all subscribed hooks or empty Array if none exist.
192
195
  * @param eventType or no params for retrieving all hooks
@@ -292,21 +295,21 @@ type ServerMode = "Development" | "Production";
292
295
  declare class Tropipay {
293
296
  readonly clientId: string;
294
297
  readonly clientSecret: string;
298
+ readonly scopes: String[];
295
299
  request: Axios;
296
- static accessToken: string | undefined;
297
- static refreshToken: string | undefined;
300
+ static accessToken: string | null;
301
+ static refreshToken: string | null;
298
302
  serverMode: ServerMode;
299
303
  hooks: TropipayHooks;
300
304
  paymentCards: PaymentCard;
301
305
  depositAccounts: DepositAccounts;
302
- constructor(config: TropipayConfig);
303
- login(): Promise<LoginResponse>;
304
306
  /**
305
- * Get all deposits in this account.
306
- * @returns A Promise of an Array of AccountDeposits or throws an Exception
307
- * @see https://tpp.stoplight.io/docs/tropipay-api-doc/b3A6OTgyOTQ1Mg-get-deposit-accounts-list
307
+ * Initializes a new instance of the Tropipay class.
308
+ *
309
+ * @param {TropipayConfig} config - The configuration object.
308
310
  */
309
- getDepositAccounts(): Promise<AccountDeposits[] | Error>;
311
+ constructor(config: TropipayConfig);
312
+ login(): Promise<LoginResponse>;
310
313
  /**
311
314
  * Get the list of all supported countries by Tropipay.
312
315
  * @returns Array of Countries Data
@@ -382,8 +385,36 @@ declare class ServerSideUtils {
382
385
  clientId: string;
383
386
  clientSecret: string;
384
387
  } | Tropipay, originalCurrencyAmount: string, bankOrderCode: string, signature: string): boolean;
388
+ /**
389
+ * Checks if the provided base64 string represents a square image.
390
+ *
391
+ * @param {string} base64String - The base64 string of the image
392
+ * @return {Promise<boolean>} A Promise that resolves to a boolean indicating whether the image is square
393
+ */
394
+ static isBase64ImageSquare(base64String: string): Promise<boolean>;
395
+ /**
396
+ * Takes a local file path and returns a base64 representation of the file content.
397
+ *
398
+ * @param {string} filepath - the path of the file to be converted to base64
399
+ * @return {Promise<string>} a Promise that resolves to the base64 representation of the file content
400
+ */
401
+ static fileToBase64(filepath: string): Promise<string>;
402
+ /**
403
+ * Get the base64 representation of a remote file from the given URL.
404
+ *
405
+ * @param {string} url - the URL of the file
406
+ * @return {Promise<string>} the base64 representation of the file
407
+ */
408
+ static getBase64FromFileUrl(url: string): Promise<string>;
409
+ /**
410
+ * Check if the base64 string represents a valid image and has a valid size
411
+ *
412
+ * @param {string} base64Image - the base64 image to be validated
413
+ * @return {Promise<string>} the valid base64 image
414
+ */
415
+ static isValidImage(base64Image: string): boolean;
385
416
  }
386
417
 
387
418
  declare const SERVER_MODE: ServerMode$1;
388
419
 
389
- export { AccountBalance, AccountDeposits, ClientSideUtils, Country, Deposit, DepositAccounts, HookEventType, HookTargetType, LoginError, LoginResponse, PaymentCard, PaymentLink, PaymentLinkPayload, SERVER_MODE, ServerMode$1 as ServerMode, ServerSideUtils, Tropipay, TropipayConfig, TropipayCredentials, TropipayHooks, UserHook, UserHookSubscribed, mediationPaymentCardConfig };
420
+ export { AccountBalance, AccountDeposits, ClientSideUtils, Country, Deposit, DepositAccounts, HookEventType, HookTargetType, LoginError, LoginResponse, MAX_IMAGE_SIZE_MB, PaymentCard, PaymentLink, PaymentLinkPayload, SERVER_MODE, ServerMode$1 as ServerMode, ServerSideUtils, Tropipay, TropipayConfig, TropipayCredentials, TropipayHooks, UserHook, UserHookSubscribed, mediationPaymentCardConfig };
package/index.js CHANGED
@@ -4,6 +4,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var axios = require('axios');
6
6
  var crypto = require('crypto');
7
+ var fs = require('fs/promises');
7
8
 
8
9
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
9
10
 
@@ -27,6 +28,7 @@ function _interopNamespace(e) {
27
28
 
28
29
  var axios__default = /*#__PURE__*/_interopDefaultLegacy(axios);
29
30
  var crypto__namespace = /*#__PURE__*/_interopNamespace(crypto);
31
+ var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
30
32
 
31
33
  function handleExceptions(error) {
32
34
  if (error instanceof axios.AxiosError) {
@@ -40,8 +42,8 @@ function handleExceptions(error) {
40
42
  return new TropipayJSException(errorMessage, axiosResponse.status, axiosResponse.data.error);
41
43
  case 404:
42
44
  return new TropipayJSException(errorMessage, axiosResponse.status, axiosResponse.data.error);
43
- // case 429:
44
- // return new TooManyRequestsException(errorMessage);
45
+ case 429:
46
+ return new TropipayJSException(`${errorMessage} Too many request or rate limited`, axiosResponse.status, axiosResponse.data.error);
45
47
  default:
46
48
  return new TropipayJSException(errorMessage, axiosResponse.status, axiosResponse.data.error);
47
49
  }
@@ -56,7 +58,7 @@ function handleExceptions(error) {
56
58
  }
57
59
  }
58
60
  else {
59
- return new TropipayJSException(`jsbfvbsfvbf`, 500, null);
61
+ return new TropipayJSException("An Unknown error occurred", 500, null);
60
62
  }
61
63
  }
62
64
  class TropipayJSException extends Error {
@@ -118,7 +120,7 @@ class TropipayHooks {
118
120
  }
119
121
  }
120
122
  /**
121
- * Get the sucbcribed hook info by his event type.
123
+ * Get the subscribed hook info by his event type.
122
124
  * If no event type is passed, it will return
123
125
  * all subscribed hooks or empty Array if none exist.
124
126
  * @param eventType or no params for retrieving all hooks
@@ -406,6 +408,7 @@ class DepositAccounts {
406
408
  class Tropipay {
407
409
  clientId;
408
410
  clientSecret;
411
+ scopes;
409
412
  request;
410
413
  static accessToken;
411
414
  static refreshToken;
@@ -413,7 +416,32 @@ class Tropipay {
413
416
  hooks;
414
417
  paymentCards;
415
418
  depositAccounts;
419
+ /**
420
+ * Initializes a new instance of the Tropipay class.
421
+ *
422
+ * @param {TropipayConfig} config - The configuration object.
423
+ */
416
424
  constructor(config) {
425
+ // use all scopes if none is passed
426
+ if (!config?.scopes) {
427
+ this.scopes = [
428
+ "ALLOW_GET_PROFILE_DATA",
429
+ "ALLOW_PAYMENT_IN",
430
+ "ALLOW_EXTERNAL_CHARGE",
431
+ "KYC3_FULL_ALLOW",
432
+ "ALLOW_PAYMENT_OUT",
433
+ "ALLOW_MARKET_PURCHASES",
434
+ "ALLOW_GET_BALANCE",
435
+ "ALLOW_GET_MOVEMENT_LIST",
436
+ "ALLOW_GET_CREDENTIAL",
437
+ ];
438
+ }
439
+ else {
440
+ this.scopes = config.scopes;
441
+ }
442
+ if (!config.clientId || !config.clientSecret) {
443
+ throw new TropipayJSException(`You must pass clientId and clientSecret in Tropipay constructor`, 400, null);
444
+ }
417
445
  this.clientId = config.clientId;
418
446
  this.clientSecret = config.clientSecret;
419
447
  this.serverMode = config.serverMode || "Development";
@@ -433,11 +461,26 @@ class Tropipay {
433
461
  }
434
462
  async login() {
435
463
  try {
464
+ if (Tropipay.refreshToken) {
465
+ const { data } = await this.request.post("/api/v2/access/token", {
466
+ client_id: this.clientId,
467
+ client_secret: this.clientSecret,
468
+ grant_type: "refresh_token",
469
+ refresh_token: Tropipay.refreshToken,
470
+ }, {
471
+ headers: {
472
+ "Content-Type": "application/json",
473
+ Accept: "application/json",
474
+ },
475
+ });
476
+ return data;
477
+ }
478
+ // normal credetials login
436
479
  const { data } = await this.request.post("/api/v2/access/token", {
437
480
  client_id: this.clientId,
438
481
  client_secret: this.clientSecret,
439
482
  grant_type: "client_credentials",
440
- scope: "ALLOW_GET_PROFILE_DATA ALLOW_PAYMENT_IN ALLOW_EXTERNAL_CHARGE KYC3_FULL_ALLOW ALLOW_PAYMENT_OUT ALLOW_MARKET_PURCHASES ALLOW_GET_BALANCE ALLOW_GET_MOVEMENT_LIST ALLOW_GET_CREDENTIAL",
483
+ scope: this.scopes.join(" "), // "ALLOW_GET_PROFILE_DATA ALLOW_PAYMENT_IN ALLOW_EXTERNAL_CHARGE KYC3_FULL_ALLOW ALLOW_PAYMENT_OUT ALLOW_MARKET_PURCHASES ALLOW_GET_BALANCE ALLOW_GET_MOVEMENT_LIST ALLOW_GET_CREDENTIAL",
441
484
  }, {
442
485
  headers: {
443
486
  "Content-Type": "application/json",
@@ -449,29 +492,8 @@ class Tropipay {
449
492
  return data;
450
493
  }
451
494
  catch (error) {
452
- throw handleExceptions(error);
453
- }
454
- }
455
- /**
456
- * Get all deposits in this account.
457
- * @returns A Promise of an Array of AccountDeposits or throws an Exception
458
- * @see https://tpp.stoplight.io/docs/tropipay-api-doc/b3A6OTgyOTQ1Mg-get-deposit-accounts-list
459
- */
460
- async getDepositAccounts() {
461
- if (!Tropipay.accessToken) {
462
- await this.login();
463
- }
464
- try {
465
- const deposits = await this.request.get("/api/v2/deposit_accounts", {
466
- headers: {
467
- "Content-Type": "application/json",
468
- Authorization: `Bearer ${Tropipay.accessToken}`,
469
- Accept: "application/json",
470
- },
471
- });
472
- return deposits.data;
473
- }
474
- catch (error) {
495
+ Tropipay.accessToken = null;
496
+ Tropipay.refreshToken = null;
475
497
  throw handleExceptions(error);
476
498
  }
477
499
  }
@@ -650,6 +672,8 @@ class ClientSideUtils {
650
672
  }
651
673
  }
652
674
 
675
+ const MAX_IMAGE_SIZE_MB = 1;
676
+
653
677
  class ServerSideUtils {
654
678
  tropipay;
655
679
  constructor(tropipayInstance) {
@@ -673,6 +697,79 @@ class ServerSideUtils {
673
697
  .digest("hex");
674
698
  return localSignature === signature;
675
699
  }
700
+ /**
701
+ * Checks if the provided base64 string represents a square image.
702
+ *
703
+ * @param {string} base64String - The base64 string of the image
704
+ * @return {Promise<boolean>} A Promise that resolves to a boolean indicating whether the image is square
705
+ */
706
+ static async isBase64ImageSquare(base64String) {
707
+ return new Promise((resolve, reject) => {
708
+ const img = new Image();
709
+ img.onload = () => {
710
+ // Check if the image has a 1:1 aspect ratio
711
+ const isSquare = img.width === img.height;
712
+ resolve(isSquare);
713
+ };
714
+ img.onerror = (error) => {
715
+ reject(error);
716
+ };
717
+ img.src = base64String;
718
+ });
719
+ }
720
+ /**
721
+ * Takes a local file path and returns a base64 representation of the file content.
722
+ *
723
+ * @param {string} filepath - the path of the file to be converted to base64
724
+ * @return {Promise<string>} a Promise that resolves to the base64 representation of the file content
725
+ */
726
+ static async fileToBase64(filepath) {
727
+ const contents = await fs__default["default"].readFile(filepath);
728
+ let base64content = contents.toString("base64");
729
+ const ext = filepath.split(".").pop();
730
+ return `data:image/${ext};base64,` + base64content;
731
+ }
732
+ /**
733
+ * Get the base64 representation of a remote file from the given URL.
734
+ *
735
+ * @param {string} url - the URL of the file
736
+ * @return {Promise<string>} the base64 representation of the file
737
+ */
738
+ static async getBase64FromFileUrl(url) {
739
+ const response = await axios__default["default"].get(url, {
740
+ responseType: "arraybuffer",
741
+ maxRedirects: 5,
742
+ });
743
+ return Buffer.from(response.data, "binary").toString("base64");
744
+ }
745
+ /**
746
+ * Check if the base64 string represents a valid image and has a valid size
747
+ *
748
+ * @param {string} base64Image - the base64 image to be validated
749
+ * @return {Promise<string>} the valid base64 image
750
+ */
751
+ static isValidImage(base64Image) {
752
+ // Check if the base64 string represents a valid image and has a valid size
753
+ try {
754
+ const isBase64Image = base64Image.startsWith("data:image/");
755
+ if (!isBase64Image) {
756
+ console.error("Not a valid base64 image");
757
+ throw new Error("Not a valid base64 image");
758
+ }
759
+ // TODO: use constants intead of magic numbers here
760
+ const isSizeLessThan2MB = base64Image.length * 0.75 <= MAX_IMAGE_SIZE_MB * 1024 * 1024;
761
+ if (!isSizeLessThan2MB) {
762
+ console.error(`The image should be less than 2mb`);
763
+ throw new Error(`The image should be less than 2mb`);
764
+ }
765
+ // all is fine
766
+ return true;
767
+ }
768
+ catch (error) {
769
+ console.error("Error checking base64 image:", error);
770
+ throw new Error(`Error checking base64 image`);
771
+ }
772
+ }
676
773
  }
677
774
 
678
775
  const SERVER_MODE = "Development"; // Move the constant here
@@ -689,13 +786,14 @@ const SERVER_MODE = "Development"; // Move the constant here
689
786
  * @license MIT
690
787
  */
691
788
  if (typeof window !== "undefined") {
692
- console.warn(`⚠️ **Warning:** The Tropipay SDK should only be instantiated server-side for security reasons.
693
- Using it client-side may lead to unexpected behavior and security vulnerabilities. YOUR CREDENTIALS COULD BE EXPOSED. Check
694
- https://yosle.github.io/tropipayjs-docs/guides/installation/ for more details.`);
789
+ const err_msg = `⚠️ **Warning:** The Tropipay SDK should only be instantiated server-side for security reasons. Using it client-side may lead to unexpected behavior and security vulnerabilities. YOUR CREDENTIALS COULD BE EXPOSED. Check https://yosle.github.io/tropipayjs-docs/guides/installation/ for more details.`;
790
+ console.error(err_msg);
791
+ throw new Error(err_msg);
695
792
  }
696
793
 
697
794
  exports.ClientSideUtils = ClientSideUtils;
698
795
  exports.DepositAccounts = DepositAccounts;
796
+ exports.MAX_IMAGE_SIZE_MB = MAX_IMAGE_SIZE_MB;
699
797
  exports.PaymentCard = PaymentCard;
700
798
  exports.SERVER_MODE = SERVER_MODE;
701
799
  exports.ServerSideUtils = ServerSideUtils;
@@ -7,6 +7,7 @@ export type AccountBalance = {
7
7
  export interface TropipayConfig {
8
8
  clientId: string;
9
9
  clientSecret: string;
10
+ scopes?: string[];
10
11
  serverMode?: ServerMode;
11
12
  }
12
13
  export type TropipayCredentials = {
@@ -59,6 +60,7 @@ export interface PaymentLinkPayload {
59
60
  };
60
61
  directPayment: boolean;
61
62
  paymentMethods?: string[];
63
+ imageBase?: string;
62
64
  }
63
65
  export interface PaymentLink extends PaymentLinkPayload {
64
66
  expirationDate: string;
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@yosle/tropipayjs",
3
- "version": "0.1.19",
3
+ "version": "0.1.21",
4
4
  "description": "Javascript / Typescript SDK for the Tropipay API",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1",
7
+ "test": "vitest run",
8
8
  "_clear": "rimraf src/dist/*",
9
9
  "_tsc": "tsc",
10
10
  "_make-bundle": "rollup --config",
@@ -30,11 +30,14 @@
30
30
  },
31
31
  "devDependencies": {
32
32
  "@rollup/plugin-typescript": "^8.5.0",
33
+ "@types/tough-cookie": "^4.0.5",
33
34
  "rimraf": "^3.0.2",
34
35
  "rollup": "^2.79.1",
35
36
  "rollup-plugin-dts": "^4.2.2",
36
37
  "run-s": "^0.0.0",
37
- "typescript": "^5.0.4"
38
+ "typescript": "^5.0.4",
39
+ "vitest": "^1.2.2",
40
+ "@types/node": "^20.12.7"
38
41
  },
39
42
  "funding": {
40
43
  "type": "individual",
@@ -0,0 +1 @@
1
+ export {};
package/rollup.config.js CHANGED
@@ -9,7 +9,7 @@ const config = [
9
9
  format: "cjs",
10
10
  sourcemap: true,
11
11
  },
12
- external: ["axios", "crypto"],
12
+ external: ["axios", "crypto", "fs/promises"],
13
13
  plugins: [typescript()],
14
14
  },
15
15
  {
@@ -20,19 +20,51 @@ type ServerMode = "Development" | "Production";
20
20
  import { TropipayHooks } from "../hooks/TropipayHooks";
21
21
  import { PaymentCard } from "../paymentcard/PaymentCard";
22
22
  import { DepositAccounts } from "../depositAccount/depositAccounts";
23
- import { handleExceptions } from "../utils/errors";
23
+ import { TropipayJSException, handleExceptions } from "../utils/errors";
24
24
  export class Tropipay {
25
25
  readonly clientId: string;
26
26
  readonly clientSecret: string;
27
+ readonly scopes: String[];
27
28
  public request: Axios;
28
- public static accessToken: string | undefined;
29
- public static refreshToken: string | undefined;
29
+ public static accessToken: string | null;
30
+ public static refreshToken: string | null;
30
31
  public serverMode: ServerMode;
31
32
  public hooks: TropipayHooks;
32
33
  public paymentCards: PaymentCard;
33
34
  public depositAccounts: DepositAccounts;
34
35
 
36
+ /**
37
+ * Initializes a new instance of the Tropipay class.
38
+ *
39
+ * @param {TropipayConfig} config - The configuration object.
40
+ */
41
+
35
42
  constructor(config: TropipayConfig) {
43
+ // use all scopes if none is passed
44
+ if (!config?.scopes) {
45
+ this.scopes = [
46
+ "ALLOW_GET_PROFILE_DATA",
47
+ "ALLOW_PAYMENT_IN",
48
+ "ALLOW_EXTERNAL_CHARGE",
49
+ "KYC3_FULL_ALLOW",
50
+ "ALLOW_PAYMENT_OUT",
51
+ "ALLOW_MARKET_PURCHASES",
52
+ "ALLOW_GET_BALANCE",
53
+ "ALLOW_GET_MOVEMENT_LIST",
54
+ "ALLOW_GET_CREDENTIAL",
55
+ ];
56
+ } else {
57
+ this.scopes = config.scopes;
58
+ }
59
+
60
+ if (!config.clientId || !config.clientSecret) {
61
+ throw new TropipayJSException(
62
+ `You must pass clientId and clientSecret in Tropipay constructor`,
63
+ 400,
64
+ null
65
+ );
66
+ }
67
+
36
68
  this.clientId = config.clientId;
37
69
  this.clientSecret = config.clientSecret;
38
70
  this.serverMode = config.serverMode || "Development";
@@ -54,14 +86,33 @@ export class Tropipay {
54
86
 
55
87
  public async login() {
56
88
  try {
89
+ if (Tropipay.refreshToken) {
90
+ const { data } = await this.request.post<LoginResponse>(
91
+ "/api/v2/access/token",
92
+ {
93
+ client_id: this.clientId,
94
+ client_secret: this.clientSecret,
95
+ grant_type: "refresh_token",
96
+ refresh_token: Tropipay.refreshToken,
97
+ },
98
+ {
99
+ headers: {
100
+ "Content-Type": "application/json",
101
+ Accept: "application/json",
102
+ },
103
+ }
104
+ );
105
+ return data;
106
+ }
107
+
108
+ // normal credetials login
57
109
  const { data } = await this.request.post<LoginResponse>(
58
110
  "/api/v2/access/token",
59
111
  {
60
112
  client_id: this.clientId,
61
113
  client_secret: this.clientSecret,
62
114
  grant_type: "client_credentials",
63
- scope:
64
- "ALLOW_GET_PROFILE_DATA ALLOW_PAYMENT_IN ALLOW_EXTERNAL_CHARGE KYC3_FULL_ALLOW ALLOW_PAYMENT_OUT ALLOW_MARKET_PURCHASES ALLOW_GET_BALANCE ALLOW_GET_MOVEMENT_LIST ALLOW_GET_CREDENTIAL",
115
+ scope: this.scopes.join(" "), // "ALLOW_GET_PROFILE_DATA ALLOW_PAYMENT_IN ALLOW_EXTERNAL_CHARGE KYC3_FULL_ALLOW ALLOW_PAYMENT_OUT ALLOW_MARKET_PURCHASES ALLOW_GET_BALANCE ALLOW_GET_MOVEMENT_LIST ALLOW_GET_CREDENTIAL",
65
116
  },
66
117
  {
67
118
  headers: {
@@ -75,29 +126,8 @@ export class Tropipay {
75
126
  Tropipay.refreshToken = data.refresh_token;
76
127
  return data;
77
128
  } catch (error) {
78
- throw handleExceptions(error as any);
79
- }
80
- }
81
-
82
- /**
83
- * Get all deposits in this account.
84
- * @returns A Promise of an Array of AccountDeposits or throws an Exception
85
- * @see https://tpp.stoplight.io/docs/tropipay-api-doc/b3A6OTgyOTQ1Mg-get-deposit-accounts-list
86
- */
87
- async getDepositAccounts(): Promise<AccountDeposits[] | Error> {
88
- if (!Tropipay.accessToken) {
89
- await this.login();
90
- }
91
- try {
92
- const deposits = await this.request.get("/api/v2/deposit_accounts", {
93
- headers: {
94
- "Content-Type": "application/json",
95
- Authorization: `Bearer ${Tropipay.accessToken}`,
96
- Accept: "application/json",
97
- },
98
- });
99
- return deposits.data;
100
- } catch (error) {
129
+ Tropipay.accessToken = null;
130
+ Tropipay.refreshToken = null;
101
131
  throw handleExceptions(error as any);
102
132
  }
103
133
  }
@@ -1 +1,2 @@
1
1
  export type ServerMode = "Development" | "Production";
2
+ export const MAX_IMAGE_SIZE_MB = 1;
@@ -57,7 +57,7 @@ export class TropipayHooks {
57
57
  }
58
58
  }
59
59
  /**
60
- * Get the sucbcribed hook info by his event type.
60
+ * Get the subscribed hook info by his event type.
61
61
  * If no event type is passed, it will return
62
62
  * all subscribed hooks or empty Array if none exist.
63
63
  * @param eventType or no params for retrieving all hooks
package/src/index.ts CHANGED
@@ -10,14 +10,10 @@
10
10
  * @license MIT
11
11
  */
12
12
 
13
- import { DepositAccounts } from "./depositAccount/depositAccounts";
14
-
15
13
  if (typeof window !== "undefined") {
16
- console.warn(
17
- `⚠️ **Warning:** The Tropipay SDK should only be instantiated server-side for security reasons.
18
- Using it client-side may lead to unexpected behavior and security vulnerabilities. YOUR CREDENTIALS COULD BE EXPOSED. Check
19
- https://yosle.github.io/tropipayjs-docs/guides/installation/ for more details.`
20
- );
14
+ const err_msg = `⚠️ **Warning:** The Tropipay SDK should only be instantiated server-side for security reasons. Using it client-side may lead to unexpected behavior and security vulnerabilities. YOUR CREDENTIALS COULD BE EXPOSED. Check https://yosle.github.io/tropipayjs-docs/guides/installation/ for more details.`;
15
+ console.error(err_msg);
16
+ throw new Error(err_msg);
21
17
  }
22
18
 
23
19
  export { Tropipay } from "./api/TropipayAPI";
@@ -9,6 +9,7 @@ export type AccountBalance = {
9
9
  export interface TropipayConfig {
10
10
  clientId: string;
11
11
  clientSecret: string;
12
+ scopes?: string[];
12
13
  serverMode?: ServerMode;
13
14
  }
14
15
 
@@ -86,6 +87,7 @@ export interface PaymentLinkPayload {
86
87
  };
87
88
  directPayment: boolean;
88
89
  paymentMethods?: string[];
90
+ imageBase?: string;
89
91
  }
90
92
 
91
93
  export interface PaymentLink extends PaymentLinkPayload {
@@ -0,0 +1,10 @@
1
+ import { describe, expect, it } from "vitest";
2
+
3
+ function sum(a: number, b: number) {
4
+ return a + b;
5
+ }
6
+ describe("dummy test", () => {
7
+ it("dummy test", () => {
8
+ expect(sum(1, 2)).toBe(3);
9
+ });
10
+ });
@@ -1,7 +1,9 @@
1
1
  import * as crypto from "crypto";
2
2
  import { Tropipay } from "../api/TropipayAPI";
3
- import { AxiosError, AxiosResponse } from "axios";
4
3
 
4
+ import axios from "axios";
5
+ import fs from "fs/promises";
6
+ import { MAX_IMAGE_SIZE_MB } from "../constants/TropipayConstants";
5
7
  export class ServerSideUtils {
6
8
  private tropipay: Tropipay;
7
9
  constructor(tropipayInstance: Tropipay) {
@@ -38,4 +40,87 @@ export class ServerSideUtils {
38
40
  .digest("hex");
39
41
  return localSignature === signature;
40
42
  }
43
+ /**
44
+ * Checks if the provided base64 string represents a square image.
45
+ *
46
+ * @param {string} base64String - The base64 string of the image
47
+ * @return {Promise<boolean>} A Promise that resolves to a boolean indicating whether the image is square
48
+ */
49
+ public static async isBase64ImageSquare(
50
+ base64String: string
51
+ ): Promise<boolean> {
52
+ return new Promise<boolean>((resolve, reject) => {
53
+ const img = new Image();
54
+
55
+ img.onload = () => {
56
+ // Check if the image has a 1:1 aspect ratio
57
+ const isSquare = img.width === img.height;
58
+ resolve(isSquare);
59
+ };
60
+
61
+ img.onerror = (error) => {
62
+ reject(error);
63
+ };
64
+
65
+ img.src = base64String;
66
+ });
67
+ }
68
+
69
+ /**
70
+ * Takes a local file path and returns a base64 representation of the file content.
71
+ *
72
+ * @param {string} filepath - the path of the file to be converted to base64
73
+ * @return {Promise<string>} a Promise that resolves to the base64 representation of the file content
74
+ */
75
+ public static async fileToBase64(filepath: string): Promise<string> {
76
+ const contents = await fs.readFile(filepath);
77
+ let base64content = contents.toString("base64");
78
+ const ext = filepath.split(".").pop();
79
+ return `data:image/${ext};base64,` + base64content;
80
+ }
81
+
82
+ /**
83
+ * Get the base64 representation of a remote file from the given URL.
84
+ *
85
+ * @param {string} url - the URL of the file
86
+ * @return {Promise<string>} the base64 representation of the file
87
+ */
88
+ public static async getBase64FromFileUrl(url: string): Promise<string> {
89
+ const response = await axios.get(url, {
90
+ responseType: "arraybuffer",
91
+ maxRedirects: 5,
92
+ });
93
+ return Buffer.from(response.data, "binary").toString("base64");
94
+ }
95
+
96
+ /**
97
+ * Check if the base64 string represents a valid image and has a valid size
98
+ *
99
+ * @param {string} base64Image - the base64 image to be validated
100
+ * @return {Promise<string>} the valid base64 image
101
+ */
102
+ public static isValidImage(base64Image: string): boolean {
103
+ // Check if the base64 string represents a valid image and has a valid size
104
+ try {
105
+ const isBase64Image = base64Image.startsWith("data:image/");
106
+ if (!isBase64Image) {
107
+ console.error("Not a valid base64 image");
108
+ throw new Error("Not a valid base64 image");
109
+ }
110
+
111
+ // TODO: use constants intead of magic numbers here
112
+ const isSizeLessThan2MB =
113
+ base64Image.length * 0.75 <= MAX_IMAGE_SIZE_MB * 1024 * 1024;
114
+
115
+ if (!isSizeLessThan2MB) {
116
+ console.error(`The image should be less than 2mb`);
117
+ throw new Error(`The image should be less than 2mb`);
118
+ }
119
+ // all is fine
120
+ return true;
121
+ } catch (error) {
122
+ console.error("Error checking base64 image:", error);
123
+ throw new Error(`Error checking base64 image`);
124
+ }
125
+ }
41
126
  }
@@ -57,8 +57,12 @@ export function handleExceptions(
57
57
  axiosResponse.status,
58
58
  axiosResponse.data.error
59
59
  );
60
- // case 429:
61
- // return new TooManyRequestsException(errorMessage);
60
+ case 429:
61
+ return new TropipayJSException(
62
+ `${errorMessage} Too many request or rate limited`,
63
+ axiosResponse.status,
64
+ axiosResponse.data.error
65
+ );
62
66
  default:
63
67
  return new TropipayJSException(
64
68
  errorMessage,
@@ -78,11 +82,11 @@ export function handleExceptions(
78
82
  return new TropipayJSException("An error occurred", 500, null);
79
83
  }
80
84
  } else {
81
- return new TropipayJSException(`jsbfvbsfvbf`, 500, null);
85
+ return new TropipayJSException("An Unknown error occurred", 500, null);
82
86
  }
83
87
  }
84
88
 
85
- class TropipayJSException extends Error {
89
+ export class TropipayJSException extends Error {
86
90
  code: number; // Status code
87
91
  error: unknown;
88
92
  message: string;
@@ -14,4 +14,32 @@ export declare class ServerSideUtils {
14
14
  clientId: string;
15
15
  clientSecret: string;
16
16
  } | Tropipay, originalCurrencyAmount: string, bankOrderCode: string, signature: string): boolean;
17
+ /**
18
+ * Checks if the provided base64 string represents a square image.
19
+ *
20
+ * @param {string} base64String - The base64 string of the image
21
+ * @return {Promise<boolean>} A Promise that resolves to a boolean indicating whether the image is square
22
+ */
23
+ static isBase64ImageSquare(base64String: string): Promise<boolean>;
24
+ /**
25
+ * Takes a local file path and returns a base64 representation of the file content.
26
+ *
27
+ * @param {string} filepath - the path of the file to be converted to base64
28
+ * @return {Promise<string>} a Promise that resolves to the base64 representation of the file content
29
+ */
30
+ static fileToBase64(filepath: string): Promise<string>;
31
+ /**
32
+ * Get the base64 representation of a remote file from the given URL.
33
+ *
34
+ * @param {string} url - the URL of the file
35
+ * @return {Promise<string>} the base64 representation of the file
36
+ */
37
+ static getBase64FromFileUrl(url: string): Promise<string>;
38
+ /**
39
+ * Check if the base64 string represents a valid image and has a valid size
40
+ *
41
+ * @param {string} base64Image - the base64 image to be validated
42
+ * @return {Promise<string>} the valid base64 image
43
+ */
44
+ static isValidImage(base64Image: string): boolean;
17
45
  }
package/utils/errors.d.ts CHANGED
@@ -8,10 +8,9 @@ export interface ErrorDetail {
8
8
  meta: unknown;
9
9
  }
10
10
  export declare function handleExceptions(error: AxiosError | Error): TropipayJSException;
11
- declare class TropipayJSException extends Error {
11
+ export declare class TropipayJSException extends Error {
12
12
  code: number;
13
13
  error: unknown;
14
14
  message: string;
15
15
  constructor(message: string, code: number, data: unknown);
16
16
  }
17
- export {};