@yosle/tropipayjs 0.2.1 → 0.2.2

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/README.md CHANGED
@@ -133,6 +133,33 @@ console.log(paylink.shortUrl);
133
133
 
134
134
  For more examples, please refer to the [Documentation](https://github.com/yosle/tropipayjs/blob/master/docs/)
135
135
 
136
+ ### Refunding a Transaction
137
+
138
+ The library provides two methods for handling refunds with 2FA confirmation:
139
+
140
+ #### Request a Security Code (Production only)
141
+
142
+ In **Production** mode, you must first request a 2FA code via SMS:
143
+
144
+ ```javascript
145
+ await tpp.requestSecurityCode();
146
+ // An SMS with the code will be sent to your phone
147
+ ```
148
+
149
+ #### Refund a Movement
150
+
151
+ Once you have the security code (or in Development mode), you can refund a transaction:
152
+
153
+ ```javascript
154
+ // Development mode — use the default test code
155
+ const result = await tpp.refundMovement("ORD-123456", 5000, "123456");
156
+
157
+ // Production mode — pass the code received via SMS
158
+ const result = await tpp.refundMovement("ORD-123456", 5000, "847291");
159
+ ```
160
+
161
+ **Note:** The refund endpoint requires the `ALLOW_REFUND` scope and 2FA to be enabled on your Tropipay account. In Development mode the default 2FA code is `123456` unless you have configured the Authenticator app.
162
+
136
163
  # Contributing
137
164
 
138
165
  If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". Don't forget to give the project a star! Thanks again!
@@ -5,19 +5,17 @@
5
5
  *
6
6
  */
7
7
  import { Axios } from "axios";
8
- import { TropipayConfig, AccountBalance, Country, PaymentLink, MediationPaymentCardConfig, LoginResponse } from "../interfaces";
8
+ import { TropipayConfig, AccountBalance, Country, PaymentLink, MediationPaymentCardConfig, LoginResponse, RefundResponse } from "../interfaces";
9
9
  type ServerMode = "Development" | "Production";
10
10
  import TropipayHooks from "../hooks/TropipayHooks";
11
11
  import PaymentCard from "../paymentcard/PaymentCard";
12
12
  import MediationPaymentCard from "../mediationPaymentCard/MediationPaymentCard";
13
13
  import DepositAccounts from "../depositAccount/depositAccounts";
14
- import Accounts from "../accounts/accounts";
15
14
  export declare class Tropipay {
16
15
  readonly clientId: string;
17
16
  readonly clientSecret: string;
18
17
  readonly scopes: String[];
19
18
  request: Axios;
20
- loginRequest: Axios;
21
19
  static accessToken: string | null;
22
20
  static refreshToken: string | null;
23
21
  static expiresIn: number | null;
@@ -26,7 +24,6 @@ export declare class Tropipay {
26
24
  paymentCards: PaymentCard;
27
25
  depositAccounts: DepositAccounts;
28
26
  mediationPaymentCard: MediationPaymentCard;
29
- accounts: Accounts;
30
27
  /**
31
28
  * Initializes a new instance of the Tropipay class.
32
29
  *
@@ -85,10 +82,39 @@ export declare class Tropipay {
85
82
  * belonging or not to the TropiPay platform with the particularity
86
83
  * that the payment will be held in custody or retained until it is
87
84
  * released with the approval of the payer.
85
+ * @deprecated This method is no longer supported and may be removed in a future release.
88
86
  * @see https://tpp.stoplight.io/docs/tropipay-api-doc/12a128ff971e4-creating-a-mediation-payment-card
89
87
  * @param config Payload with the payment details
90
88
  */
91
89
  createMediationPaymentCard(config: MediationPaymentCardConfig): Promise<PaymentLink>;
90
+ /**
91
+ * Refund a completed transaction.
92
+ * Requires 2FA enabled and ALLOW_REFUND permission on the Tropipay account.
93
+ *
94
+ * In Development mode, `securityCode` defaults to `"123456"` when omitted.
95
+ * In Production mode, you must first call `requestSecurityCode()` to receive
96
+ * the 2FA code via SMS, then pass it as the `securityCode` parameter.
97
+ *
98
+ * @see https://doc.tropipay.com/docs/api-reference/movements#refund-a-transaction
99
+ * @param orderCode Code of the order/transaction to refund (e.g. "ORD-123456")
100
+ * @param amount Amount to refund in cents (e.g. 5000 = 50.00 USD/EUR)
101
+ * @param securityCode 2FA confirmation code. Use "123456" in Development or
102
+ * the code received via SMS in Production (obtained via requestSecurityCode()).
103
+ */
104
+ refundMovement(orderCode: string, amount: number, securityCode: string): Promise<RefundResponse>;
105
+ /**
106
+ * Request a 2FA security code to be sent via SMS for confirming sensitive
107
+ * operations like refunds.
108
+ *
109
+ * In Production mode, you must call this method first to trigger an SMS with
110
+ * the code, then use the received code when calling `refundMovement()`.
111
+ * In Development mode this is not required as the default code "123456"
112
+ * is used automatically.
113
+ *
114
+ * @see https://doc.tropipay.com/docs/api-reference/movements#refund-a-transaction
115
+ * @returns The API response confirming the code was sent
116
+ */
117
+ requestSecurityCode(type?: "sms" | "email"): Promise<any>;
92
118
  }
93
119
  export declare class ClientSideUtils {
94
120
  constructor(tropipayInstance: Tropipay);
@@ -102,5 +128,6 @@ export declare const Scopes: {
102
128
  ALLOW_GET_BALANCE: string;
103
129
  ALLOW_GET_MOVEMENT_LIST: string;
104
130
  ALLOW_GET_CREDENTIAL: string;
131
+ ALLOW_REFUND: string;
105
132
  };
106
133
  export {};
package/index.d.ts CHANGED
@@ -39,71 +39,36 @@ interface UserHookSubscribed extends UserHook {
39
39
  createdAt: string;
40
40
  updatedAt: string;
41
41
  }
42
- /**
43
- * Interface for creating payment links/cards
44
- * @see https://tpp.stoplight.io/docs/tropipay-api-doc/b3A6ODgyNTM3OQ-create-a-new-pay-link-charge
45
- */
46
42
  interface PaymentLinkPayload {
47
- /** Account ID where the funds will be received if compatible */
48
- accountId?: number;
49
- /** Reference code for the payment, preferred to be unique */
50
43
  reference: string;
51
- /** Short description of the payment */
52
44
  concept: string;
53
- /** Whether to mark this payment link as favorite */
54
45
  favorite: boolean;
55
- /** Amount to charge in the specified currency */
56
46
  amount: number;
57
- /** Currency code (EUR, USD, etc.) */
58
47
  currency: string;
59
- /** Detailed description of the payment */
60
48
  description: string;
61
- /** If true, the payment link can only be used once */
62
49
  singleUse: boolean;
63
- /** Reason ID for the payment */
64
50
  reasonId: number;
65
- /** Number of days until the payment link expires */
66
51
  expirationDays: number;
67
- /** Language code for the payment page (en, es, etc.) */
68
52
  lang: string;
69
- /** URL to redirect after successful payment */
70
53
  urlSuccess: string;
71
- /** URL to redirect after failed payment */
72
54
  urlFailed: string;
73
- /** Webhook URL for payment notifications */
74
55
  urlNotification: string;
75
- /** Date when the service will be provided (ISO format) */
76
56
  serviceDate: string;
77
- /** Client information for the payment */
78
57
  client?: {
79
- /** Client's first name */
80
58
  name?: string;
81
- /** Client's last name */
82
59
  lastName?: string;
83
- /** Client's address */
84
60
  address?: string;
85
- /** Client's phone number */
86
61
  phone?: string;
87
- /** Client's email address */
88
62
  email?: string;
89
- /** Client's country ID */
90
63
  countryId?: number;
91
- /** Client's country ISO code */
92
64
  countryIso?: string;
93
- /** Client's city */
94
65
  city: string;
95
- /** Client's postal code */
96
66
  postCode: string;
97
- /** Whether client accepted terms and conditions */
98
67
  termsAndConditions: boolean;
99
68
  } | null;
100
- /** @deprecated If true, redirects directly to payment page */
101
69
  directPayment: boolean;
102
- /** List of allowed payment methods */
103
70
  paymentMethods?: string[];
104
- /** Base64 encoded image for the payment page */
105
71
  imageBase?: string;
106
- /** Whether to save the payment token for future use */
107
72
  saveToken?: boolean;
108
73
  }
109
74
  interface PaymentLink extends PaymentLinkPayload {
@@ -203,6 +168,16 @@ type AccountDeposits = {
203
168
  count: number;
204
169
  rows: Deposit[];
205
170
  };
171
+ type RefundResponse = {
172
+ id: number;
173
+ orderCode: string;
174
+ amount: number;
175
+ currency: string;
176
+ state: string;
177
+ type: string;
178
+ createdAt: string;
179
+ completedAt: string;
180
+ };
206
181
 
207
182
  declare class TropipayHooks {
208
183
  private tropipay;
@@ -354,58 +329,6 @@ declare class DepositAccounts {
354
329
  delete(id: number): Promise<any>;
355
330
  }
356
331
 
357
- interface PaymentMethod {
358
- slug: string;
359
- name: string;
360
- enabled: boolean;
361
- allowedCurrencies: string[];
362
- }
363
- interface ServiceLimit {
364
- value: number | null;
365
- currency: string;
366
- }
367
- interface ServiceLimits {
368
- min: ServiceLimit;
369
- max: ServiceLimit;
370
- }
371
- interface Service {
372
- slug: string;
373
- enabled: boolean;
374
- allowed_currencies: string[];
375
- limits: ServiceLimits;
376
- }
377
- interface TropiCard {
378
- }
379
- interface Account {
380
- id: number;
381
- accountNumber: string;
382
- userId: string;
383
- alias: string;
384
- balance: number;
385
- pendingIn: number;
386
- pendingOut: number;
387
- state: number;
388
- paymentEntityId: number;
389
- currency: string;
390
- type: number;
391
- createdAt: string;
392
- updatedAt: string;
393
- isDefault: boolean;
394
- groupId: number | null;
395
- TropiCards: TropiCard[];
396
- services: Service[];
397
- paymentMethods: PaymentMethod[];
398
- }
399
- declare class Accounts {
400
- private tropipay;
401
- constructor(tropipayInstance: Tropipay);
402
- /**
403
- * List all accounts of the authenticated user
404
- * @returns Array of Account objects
405
- */
406
- list(): Promise<Account[]>;
407
- }
408
-
409
332
  /**
410
333
  * Tropipayjs is a Typescript/Javascript library for the Tropipay API.
411
334
  *
@@ -420,7 +343,6 @@ declare class Tropipay {
420
343
  readonly clientSecret: string;
421
344
  readonly scopes: String[];
422
345
  request: Axios;
423
- loginRequest: Axios;
424
346
  static accessToken: string | null;
425
347
  static refreshToken: string | null;
426
348
  static expiresIn: number | null;
@@ -429,7 +351,6 @@ declare class Tropipay {
429
351
  paymentCards: PaymentCard;
430
352
  depositAccounts: DepositAccounts;
431
353
  mediationPaymentCard: MediationPaymentCard;
432
- accounts: Accounts;
433
354
  /**
434
355
  * Initializes a new instance of the Tropipay class.
435
356
  *
@@ -488,10 +409,39 @@ declare class Tropipay {
488
409
  * belonging or not to the TropiPay platform with the particularity
489
410
  * that the payment will be held in custody or retained until it is
490
411
  * released with the approval of the payer.
412
+ * @deprecated This method is no longer supported and may be removed in a future release.
491
413
  * @see https://tpp.stoplight.io/docs/tropipay-api-doc/12a128ff971e4-creating-a-mediation-payment-card
492
414
  * @param config Payload with the payment details
493
415
  */
494
416
  createMediationPaymentCard(config: MediationPaymentCardConfig): Promise<PaymentLink>;
417
+ /**
418
+ * Refund a completed transaction.
419
+ * Requires 2FA enabled and ALLOW_REFUND permission on the Tropipay account.
420
+ *
421
+ * In Development mode, `securityCode` defaults to `"123456"` when omitted.
422
+ * In Production mode, you must first call `requestSecurityCode()` to receive
423
+ * the 2FA code via SMS, then pass it as the `securityCode` parameter.
424
+ *
425
+ * @see https://doc.tropipay.com/docs/api-reference/movements#refund-a-transaction
426
+ * @param orderCode Code of the order/transaction to refund (e.g. "ORD-123456")
427
+ * @param amount Amount to refund in cents (e.g. 5000 = 50.00 USD/EUR)
428
+ * @param securityCode 2FA confirmation code. Use "123456" in Development or
429
+ * the code received via SMS in Production (obtained via requestSecurityCode()).
430
+ */
431
+ refundMovement(orderCode: string, amount: number, securityCode: string): Promise<RefundResponse>;
432
+ /**
433
+ * Request a 2FA security code to be sent via SMS for confirming sensitive
434
+ * operations like refunds.
435
+ *
436
+ * In Production mode, you must call this method first to trigger an SMS with
437
+ * the code, then use the received code when calling `refundMovement()`.
438
+ * In Development mode this is not required as the default code "123456"
439
+ * is used automatically.
440
+ *
441
+ * @see https://doc.tropipay.com/docs/api-reference/movements#refund-a-transaction
442
+ * @returns The API response confirming the code was sent
443
+ */
444
+ requestSecurityCode(type?: "sms" | "email"): Promise<any>;
495
445
  }
496
446
  declare class ClientSideUtils {
497
447
  constructor(tropipayInstance: Tropipay);
@@ -501,9 +451,7 @@ declare class ServerSideUtils {
501
451
  private tropipay;
502
452
  constructor(tropipayInstance: Tropipay);
503
453
  /**
504
- * DEPRECATED Verify Topipay's signature on API V2 webhooks.
505
- * @deprecated This method is deprecated and will be removed in future versions.
506
- * Use the `verifySignatureV3` method from the `ServerSideUtils` class instead.
454
+ * Verify Topipay's signature on webhooks.
507
455
  * @param credentials Credential object or Tropipay instance
508
456
  * @param {String} originalCurrencyAmount
509
457
  * @param bankOrderCode
@@ -542,19 +490,8 @@ declare class ServerSideUtils {
542
490
  * @return {Promise<string>} the valid base64 image
543
491
  */
544
492
  static isValidImage(base64Image: string): boolean;
545
- /**
546
- * Verify Topipay's signature on API V3 webhooks.
547
- * @param {string} originalCurrencyAmount
548
- * @param {string} bankOrderCode
549
- * @param {string} signature signaturev3 field to be verified
550
- * @returns {boolean}
551
- */
552
- static verifySignatureV3(credentials: {
553
- clientId: string;
554
- clientSecret: string;
555
- } | Tropipay, originalCurrencyAmount: string, bankOrderCode: string, signature: string): boolean;
556
493
  }
557
494
 
558
495
  declare const SERVER_MODE: ServerMode$1;
559
496
 
560
- export { AccountBalance, AccountDeposits, ClientSideUtils, Country, Deposit, HookEventType, HookTargetType, LoginError, LoginResponse, MAX_IMAGE_SIZE_MB, MediationPaymentCardConfig, PaymentLink, PaymentLinkPayload, SERVER_MODE, ServerMode$1 as ServerMode, ServerSideUtils, Tropipay, TropipayConfig, TropipayCredentials, UserHook, UserHookSubscribed };
497
+ export { AccountBalance, AccountDeposits, ClientSideUtils, Country, Deposit, HookEventType, HookTargetType, LoginError, LoginResponse, MAX_IMAGE_SIZE_MB, MediationPaymentCardConfig, PaymentLink, PaymentLinkPayload, RefundResponse, SERVER_MODE, ServerMode$1 as ServerMode, ServerSideUtils, Tropipay, TropipayConfig, TropipayCredentials, UserHook, UserHookSubscribed };
package/index.js CHANGED
@@ -220,7 +220,7 @@ class PaymentCard {
220
220
  await this.tropipay.login();
221
221
  }
222
222
  try {
223
- const paylink = await this.tropipay.request.post("/api/v3/paymentcards", payload, {
223
+ const paylink = await this.tropipay.request.post("/api/v2/paymentcards", payload, {
224
224
  headers: {
225
225
  "Content-Type": "application/json",
226
226
  Authorization: `Bearer ${Tropipay.accessToken}`,
@@ -243,7 +243,7 @@ class PaymentCard {
243
243
  await this.tropipay.login();
244
244
  }
245
245
  try {
246
- const paymentcards = await this.tropipay.request.get(`/api/v3/paymentcards`, {
246
+ const paymentcards = await this.tropipay.request.get(`/api/v2/paymentcards`, {
247
247
  headers: {
248
248
  "Content-Type": "application/json",
249
249
  Authorization: `Bearer ${Tropipay.accessToken}`,
@@ -268,7 +268,7 @@ class PaymentCard {
268
268
  await this.tropipay.login();
269
269
  }
270
270
  try {
271
- const paymentcard = await this.tropipay.request.get(`/api/v3/paymentcards/${id}`, {
271
+ const paymentcard = await this.tropipay.request.get(`/api/v2/paymentcards/${id}`, {
272
272
  headers: {
273
273
  "Content-Type": "application/json",
274
274
  Authorization: `Bearer ${Tropipay.accessToken}`,
@@ -294,7 +294,7 @@ class PaymentCard {
294
294
  await this.tropipay.login();
295
295
  }
296
296
  try {
297
- const paymentcard = await this.tropipay.request.delete(`/api/v3/paymentcards/`, {
297
+ const paymentcard = await this.tropipay.request.delete(`/api/v2/paymentcards/`, {
298
298
  headers: {
299
299
  "Content-Type": "application/json",
300
300
  Authorization: `Bearer ${Tropipay.accessToken}`,
@@ -360,7 +360,7 @@ class DepositAccounts {
360
360
  await this.tropipay.login();
361
361
  }
362
362
  try {
363
- const deposit = await this.tropipay.request.get(`/api/v3/deposit_accounts`, {
363
+ const deposit = await this.tropipay.request.get(`/api/v2/deposit_accounts`, {
364
364
  headers: {
365
365
  "Content-Type": "application/json",
366
366
  Authorization: `Bearer ${Tropipay.accessToken}`,
@@ -383,7 +383,7 @@ class DepositAccounts {
383
383
  await this.tropipay.login();
384
384
  }
385
385
  try {
386
- const deposit = await this.tropipay.request.post("/api/v3/deposit_accounts", depositAccountObj, {
386
+ const deposit = await this.tropipay.request.post("/api/v2/deposit_accounts", depositAccountObj, {
387
387
  headers: {
388
388
  "Content-Type": "application/json",
389
389
  Authorization: `Bearer ${Tropipay.accessToken}`,
@@ -407,7 +407,7 @@ class DepositAccounts {
407
407
  await this.tropipay.login();
408
408
  }
409
409
  try {
410
- const deposit = await this.tropipay.request.get(`/api/v3/deposit_accounts/${id}`, {
410
+ const deposit = await this.tropipay.request.get(`/api/v2/deposit_accounts/${id}`, {
411
411
  headers: {
412
412
  "Content-Type": "application/json",
413
413
  Authorization: `Bearer ${Tropipay.accessToken}`,
@@ -430,7 +430,7 @@ class DepositAccounts {
430
430
  await this.tropipay.login();
431
431
  }
432
432
  try {
433
- const deposit = await this.tropipay.request.put(`/api/v3/deposit_accounts/`, {
433
+ const deposit = await this.tropipay.request.put(`/api/v2/deposit_accounts/`, {
434
434
  headers: {
435
435
  "Content-Type": "application/json",
436
436
  Authorization: `Bearer ${Tropipay.accessToken}`,
@@ -453,7 +453,7 @@ class DepositAccounts {
453
453
  await this.tropipay.login();
454
454
  }
455
455
  try {
456
- const deposit = await this.tropipay.request.delete(`/api/v3/deposit_accounts/${id}`, {
456
+ const deposit = await this.tropipay.request.delete(`/api/v2/deposit_accounts/${id}`, {
457
457
  headers: {
458
458
  "Content-Type": "application/json",
459
459
  Authorization: `Bearer ${Tropipay.accessToken}`,
@@ -468,35 +468,6 @@ class DepositAccounts {
468
468
  }
469
469
  }
470
470
 
471
- class Accounts {
472
- tropipay;
473
- constructor(tropipayInstance) {
474
- this.tropipay = tropipayInstance;
475
- }
476
- /**
477
- * List all accounts of the authenticated user
478
- * @returns Array of Account objects
479
- */
480
- async list() {
481
- if (!Tropipay.accessToken) {
482
- await this.tropipay.login();
483
- }
484
- try {
485
- const accounts = await this.tropipay.request.get(`/api/v3/accounts/`, {
486
- headers: {
487
- "Content-Type": "application/json",
488
- Authorization: `Bearer ${Tropipay.accessToken}`,
489
- Accept: "application/json",
490
- },
491
- });
492
- return accounts.data;
493
- }
494
- catch (error) {
495
- throw handleExceptions(error);
496
- }
497
- }
498
- }
499
-
500
471
  /**
501
472
  * Tropipayjs is a Typescript/Javascript library for the Tropipay API.
502
473
  *
@@ -508,7 +479,6 @@ class Tropipay {
508
479
  clientSecret;
509
480
  scopes;
510
481
  request;
511
- loginRequest;
512
482
  static accessToken;
513
483
  static refreshToken;
514
484
  static expiresIn;
@@ -517,7 +487,6 @@ class Tropipay {
517
487
  paymentCards;
518
488
  depositAccounts;
519
489
  mediationPaymentCard;
520
- accounts;
521
490
  /**
522
491
  * Initializes a new instance of the Tropipay class.
523
492
  *
@@ -536,6 +505,7 @@ class Tropipay {
536
505
  "ALLOW_GET_BALANCE",
537
506
  "ALLOW_GET_MOVEMENT_LIST",
538
507
  "ALLOW_GET_CREDENTIAL",
508
+ "ALLOW_REFUND",
539
509
  ];
540
510
  }
541
511
  else {
@@ -549,7 +519,7 @@ class Tropipay {
549
519
  this.serverMode = config.serverMode || "Development";
550
520
  const tpp_env = this.serverMode === "Production"
551
521
  ? "https://www.tropipay.com"
552
- : "https://sandbox.tropipay.me";
522
+ : "https://tropipay-dev.herokuapp.com";
553
523
  this.request = axios__default["default"].create({
554
524
  baseURL: config.customTropipayUrl || tpp_env,
555
525
  headers: {
@@ -558,28 +528,21 @@ class Tropipay {
558
528
  Authorization: `Bearer ${Tropipay.accessToken}`,
559
529
  },
560
530
  });
561
- // Create a separate instance for login requests
562
- this.loginRequest = axios__default["default"].create({
563
- baseURL: config.customTropipayUrl || tpp_env,
564
- headers: {
565
- "Content-Type": "application/json",
566
- Accept: "application/json",
567
- },
568
- });
569
531
  // Add request interceptor for Token expired
570
532
  this.request.interceptors.request.use(async (config) => {
571
- const currentTimestamp = Math.floor(Date.now() / 1000);
533
+ const currentTimestamp = Math.floor(Date.now() / 1000); // Current time in seconds
572
534
  if (Tropipay.expiresIn && Tropipay.expiresIn < currentTimestamp) {
573
- console.debug("Token expired, attempting to log in");
535
+ // Token has expired, attempt to refresh it
574
536
  try {
575
537
  await this.login();
576
538
  }
577
539
  catch (error) {
540
+ // Handle token refresh error
541
+ Tropipay.accessToken = null;
542
+ Tropipay.refreshToken = null;
578
543
  throw handleExceptions(error);
579
544
  }
580
545
  }
581
- // Update the Authorization header in the config
582
- config.headers.Authorization = `Bearer ${Tropipay.accessToken}`;
583
546
  return config;
584
547
  }, (error) => {
585
548
  return Promise.reject(error);
@@ -588,12 +551,25 @@ class Tropipay {
588
551
  this.paymentCards = new PaymentCard(this);
589
552
  this.mediationPaymentCard = new MediationPaymentCard(this);
590
553
  this.depositAccounts = new DepositAccounts(this);
591
- this.accounts = new Accounts(this);
592
554
  }
593
555
  async login() {
594
556
  try {
557
+ if (Tropipay.refreshToken) {
558
+ const { data } = await this.request.post("/api/v2/access/token", {
559
+ client_id: this.clientId,
560
+ client_secret: this.clientSecret,
561
+ grant_type: "refresh_token",
562
+ refresh_token: Tropipay.refreshToken,
563
+ }, {
564
+ headers: {
565
+ "Content-Type": "application/json",
566
+ Accept: "application/json",
567
+ },
568
+ });
569
+ return data;
570
+ }
595
571
  // normal credetials login
596
- const { data } = await this.loginRequest.post("/api/v3/access/token", {
572
+ const { data } = await this.request.post("/api/v2/access/token", {
597
573
  client_id: this.clientId,
598
574
  client_secret: this.clientSecret,
599
575
  grant_type: "client_credentials",
@@ -605,6 +581,7 @@ class Tropipay {
605
581
  },
606
582
  });
607
583
  Tropipay.accessToken = data.access_token;
584
+ Tropipay.refreshToken = data.refresh_token;
608
585
  Tropipay.expiresIn = data.expires_in;
609
586
  return data;
610
587
  }
@@ -612,7 +589,6 @@ class Tropipay {
612
589
  Tropipay.accessToken = null;
613
590
  Tropipay.refreshToken = null;
614
591
  Tropipay.expiresIn = null;
615
- Tropipay.expiresIn = null;
616
592
  throw handleExceptions(error);
617
593
  }
618
594
  }
@@ -764,6 +740,7 @@ class Tropipay {
764
740
  * belonging or not to the TropiPay platform with the particularity
765
741
  * that the payment will be held in custody or retained until it is
766
742
  * released with the approval of the payer.
743
+ * @deprecated This method is no longer supported and may be removed in a future release.
767
744
  * @see https://tpp.stoplight.io/docs/tropipay-api-doc/12a128ff971e4-creating-a-mediation-payment-card
768
745
  * @param config Payload with the payment details
769
746
  */
@@ -784,6 +761,71 @@ class Tropipay {
784
761
  throw handleExceptions(error);
785
762
  }
786
763
  }
764
+ /**
765
+ * Refund a completed transaction.
766
+ * Requires 2FA enabled and ALLOW_REFUND permission on the Tropipay account.
767
+ *
768
+ * In Development mode, `securityCode` defaults to `"123456"` when omitted.
769
+ * In Production mode, you must first call `requestSecurityCode()` to receive
770
+ * the 2FA code via SMS, then pass it as the `securityCode` parameter.
771
+ *
772
+ * @see https://doc.tropipay.com/docs/api-reference/movements#refund-a-transaction
773
+ * @param orderCode Code of the order/transaction to refund (e.g. "ORD-123456")
774
+ * @param amount Amount to refund in cents (e.g. 5000 = 50.00 USD/EUR)
775
+ * @param securityCode 2FA confirmation code. Use "123456" in Development or
776
+ * the code received via SMS in Production (obtained via requestSecurityCode()).
777
+ */
778
+ async refundMovement(orderCode, amount, securityCode) {
779
+ if (!Tropipay.accessToken) {
780
+ await this.login();
781
+ }
782
+ if (!this.scopes.includes("ALLOW_REFUND")) {
783
+ throw new TropipayJSException("The credential does not have the ALLOW_REFUND scope required to perform refunds. If you believe your account should have this permission, please contact Tropipay support.", 403, null);
784
+ }
785
+ try {
786
+ const response = await this.request.post("/api/v3/movements/in/refund", { orderCode, amount, securityCode }, {
787
+ headers: {
788
+ "Content-Type": "application/json",
789
+ Accept: "application/json",
790
+ Authorization: `Bearer ${Tropipay.accessToken}`,
791
+ },
792
+ });
793
+ return response.data;
794
+ }
795
+ catch (error) {
796
+ throw handleExceptions(error);
797
+ }
798
+ }
799
+ /**
800
+ * Request a 2FA security code to be sent via SMS for confirming sensitive
801
+ * operations like refunds.
802
+ *
803
+ * In Production mode, you must call this method first to trigger an SMS with
804
+ * the code, then use the received code when calling `refundMovement()`.
805
+ * In Development mode this is not required as the default code "123456"
806
+ * is used automatically.
807
+ *
808
+ * @see https://doc.tropipay.com/docs/api-reference/movements#refund-a-transaction
809
+ * @returns The API response confirming the code was sent
810
+ */
811
+ async requestSecurityCode(type = "sms") {
812
+ if (!Tropipay.accessToken) {
813
+ await this.login();
814
+ }
815
+ try {
816
+ const response = await this.request.post("/api/v3/users/sendSecurityCode", { type }, {
817
+ headers: {
818
+ "Content-Type": "application/json",
819
+ Accept: "application/json",
820
+ Authorization: `Bearer ${Tropipay.accessToken}`,
821
+ },
822
+ });
823
+ return response.data;
824
+ }
825
+ catch (error) {
826
+ throw handleExceptions(error);
827
+ }
828
+ }
787
829
  }
788
830
  class ClientSideUtils {
789
831
  constructor(tropipayInstance) {
@@ -799,9 +841,7 @@ class ServerSideUtils {
799
841
  this.tropipay = tropipayInstance;
800
842
  }
801
843
  /**
802
- * DEPRECATED Verify Topipay's signature on API V2 webhooks.
803
- * @deprecated This method is deprecated and will be removed in future versions.
804
- * Use the `verifySignatureV3` method from the `ServerSideUtils` class instead.
844
+ * Verify Topipay's signature on webhooks.
805
845
  * @param credentials Credential object or Tropipay instance
806
846
  * @param {String} originalCurrencyAmount
807
847
  * @param bankOrderCode
@@ -891,28 +931,6 @@ class ServerSideUtils {
891
931
  throw new Error(`Error checking base64 image`);
892
932
  }
893
933
  }
894
- /**
895
- * Verify Topipay's signature on API V3 webhooks.
896
- * @param {string} originalCurrencyAmount
897
- * @param {string} bankOrderCode
898
- * @param {string} signature signaturev3 field to be verified
899
- * @returns {boolean}
900
- */
901
- static verifySignatureV3(credentials, originalCurrencyAmount, bankOrderCode, signature) {
902
- const sha1Secret = crypto__namespace
903
- .createHash("sha1")
904
- .update(credentials.clientSecret)
905
- .digest("hex");
906
- const dataToHash = bankOrderCode +
907
- credentials.clientId +
908
- sha1Secret +
909
- originalCurrencyAmount;
910
- const localSignature = crypto__namespace
911
- .createHash("sha256")
912
- .update(dataToHash)
913
- .digest("hex");
914
- return localSignature === signature;
915
- }
916
934
  }
917
935
 
918
936
  const SERVER_MODE = "Development"; // Move the constant here