tonder-web-sdk 1.12.3 → 1.15.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tonder-web-sdk",
3
- "version": "1.12.3",
3
+ "version": "1.15.2",
4
4
  "description": "tonder sdk for integrations",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -4,8 +4,8 @@ export class ThreeDSHandler {
4
4
  apiKey,
5
5
  baseUrl,
6
6
  }) {
7
- this.baseUrl = baseUrl,
8
- this.apiKey = apiKey,
7
+ this.baseUrl = baseUrl
8
+ this.apiKey = apiKey
9
9
  this.payload = payload
10
10
  }
11
11
 
@@ -60,12 +60,12 @@ export class ThreeDSHandler {
60
60
 
61
61
  loadIframe() {
62
62
  const iframe = this.payload?.next_action?.iframe_resources?.iframe
63
-
64
63
  if (iframe) {
65
64
  return new Promise((resolve, reject) => {
66
65
  const iframe = this.payload?.next_action?.iframe_resources?.iframe
67
66
 
68
67
  if (iframe) {
68
+ // TODO: This is not working for Azul
69
69
  this.saveVerifyTransactionUrl()
70
70
  const container = document.createElement('div')
71
71
  container.innerHTML = iframe
@@ -127,8 +127,6 @@ export class ThreeDSHandler {
127
127
  return response;
128
128
  }
129
129
 
130
- // TODO: the method below needs to be tested with a real 3DS challenge
131
- // since we couldn't get a test card that works with this feature
132
130
  async handle3dsChallenge(response_json) {
133
131
  // Create the form element:
134
132
  const form = document.createElement('form');
@@ -139,27 +137,26 @@ export class ThreeDSHandler {
139
137
  // Add hidden fields:
140
138
  const creqInput = document.createElement('input');
141
139
  creqInput.type = 'hidden';
142
- creqInput.name = response_json.creq;
140
+ creqInput.name = 'creq';
143
141
  creqInput.value = response_json.creq;
144
142
  form.appendChild(creqInput);
145
143
 
146
- const termUrlInput = document.createElement('input');
147
- termUrlInput.type = 'hidden';
148
- termUrlInput.name = response_json.term_url;
149
- termUrlInput.value = response_json.TermUrl;
150
- form.appendChild(termUrlInput);
144
+ if (response_json.term_url) {
145
+ const termUrlInput = document.createElement('input');
146
+ termUrlInput.type = 'hidden';
147
+ termUrlInput.name = 'TermUrl';
148
+ termUrlInput.value = response_json.term_url;
149
+ form.appendChild(termUrlInput);
150
+ }
151
151
 
152
152
  // Append the form to the body:
153
153
  document.body.appendChild(form);
154
154
  form.submit();
155
-
156
- await this.verifyTransactionStatus();
157
155
  }
158
156
 
159
- // TODO: This method could be removed
157
+ // TODO: This works for Azul
160
158
  async handleTransactionResponse(response) {
161
159
  const response_json = await response.json();
162
-
163
160
  // Azul property
164
161
  if (response_json.status === "Pending" && response_json.redirect_post_url) {
165
162
  return await this.handle3dsChallenge(response_json);
@@ -173,7 +170,6 @@ export class ThreeDSHandler {
173
170
 
174
171
  async verifyTransactionStatus() {
175
172
  const verifyUrl = this.getUrlWithExpiration();
176
-
177
173
  if (verifyUrl) {
178
174
  const url = `${this.baseUrl}${verifyUrl}`;
179
175
  try {
@@ -34,6 +34,7 @@ export class BaseInlineCheckout {
34
34
  */
35
35
  configureCheckout(data) {
36
36
  if ("customer" in data) this.#handleCustomer(data["customer"]);
37
+ if ("secureToken" in data) this.#handleSecureToken(data["secureToken"]);
37
38
  }
38
39
 
39
40
  /**
@@ -46,8 +47,9 @@ export class BaseInlineCheckout {
46
47
  const result3ds = await this.process3ds.verifyTransactionStatus();
47
48
  const resultCheckout = await this.#resumeCheckout(result3ds);
48
49
  this.process3ds.setPayload(resultCheckout);
50
+ const response = await this.#handle3dsRedirect(resultCheckout);
49
51
  globalLoader.remove();
50
- return this.#handle3dsRedirect(resultCheckout);
52
+ return response
51
53
  }
52
54
 
53
55
  /**
@@ -158,6 +160,7 @@ export class BaseInlineCheckout {
158
160
  amount: total,
159
161
  date: dateString,
160
162
  order_id: jsonResponseOrder.id,
163
+ customer_order_reference: this.order_reference ? this.order_reference : reference,
161
164
  };
162
165
  const jsonResponsePayment = await createPayment(
163
166
  this.baseUrl,
@@ -222,7 +225,7 @@ export class BaseInlineCheckout {
222
225
 
223
226
  async #resumeCheckout(response) {
224
227
  // Stop the routing process if the transaction is either hard declined or successful
225
- if (response?.decline?.error_type === "Hard") {
228
+ if (response?.decline?.error_type === "Hard" || !!response?.checkout?.is_route_finished) {
226
229
  return response;
227
230
  }
228
231
 
@@ -265,8 +268,13 @@ export class BaseInlineCheckout {
265
268
  this.customer = customer;
266
269
  }
267
270
 
271
+ #handleSecureToken(secureToken) {
272
+ this.secureToken = secureToken;
273
+ }
274
+
268
275
  #handleMetadata(data) {
269
276
  this.metadata = data?.metadata;
277
+ this.order_reference = data?.order_reference;
270
278
  }
271
279
 
272
280
  #handleCurrency(data) {
@@ -282,23 +290,22 @@ export class BaseInlineCheckout {
282
290
  }
283
291
 
284
292
  async #handle3dsRedirect(response) {
293
+ console.log('Handling 3DS redirect...');
285
294
  const iframe = response?.next_action?.iframe_resources?.iframe;
295
+ const threeDsChallenge = response?.next_action?.three_ds_challenge;
286
296
 
287
297
  if (iframe) {
288
- this.process3ds
289
- .loadIframe()
290
- .then(() => {
291
- //TODO: Check if this will be necessary on the frontend side
292
- // after some the tests in production, since the 3DS process
293
- // doesn't works properly on the sandbox environment
294
- // setTimeout(() => {
295
- // process3ds.verifyTransactionStatus();
296
- // }, 10000);
297
- this.process3ds.verifyTransactionStatus();
298
- })
299
- .catch((error) => {
300
- console.log("Error loading iframe:", error);
301
- });
298
+ try {
299
+ await this.process3ds.loadIframe();
300
+ const res = await this.process3ds.verifyTransactionStatus();
301
+ return res;
302
+ } catch (error) {
303
+ console.log("Error loading iframe:", error);
304
+ }
305
+ } else if (threeDsChallenge) {
306
+ await this.process3ds.handle3dsChallenge(threeDsChallenge);
307
+ const res = await this.process3ds.verifyTransactionStatus();
308
+ return res;
302
309
  } else {
303
310
  const redirectUrl = this.process3ds.getRedirectUrl();
304
311
  if (redirectUrl) {
@@ -63,14 +63,14 @@ export class InlineCheckout extends BaseInlineCheckout {
63
63
  this.customStyles = styles
64
64
  this.abortRefreshCardsController = new AbortController();
65
65
  // TODO: Wait until SaveCards is ready (server token).
66
- // this.customization = {
67
- // ...this.customization,
68
- // ...(customization || {}),
69
- // saveCards: {
70
- // ...this.customization.saveCards,
71
- // ...(customization?.saveCards || {}),
72
- // },
73
- // }
66
+ this.customization = {
67
+ ...this.customization,
68
+ ...(customization || {}),
69
+ saveCards: {
70
+ ...this.customization.saveCards,
71
+ ...(customization?.saveCards || {}),
72
+ },
73
+ }
74
74
  }
75
75
 
76
76
  #mountPayButton() {
@@ -295,9 +295,13 @@ export class InlineCheckout extends BaseInlineCheckout {
295
295
  const saveCard = document.getElementById("save-checkout-card");
296
296
  if ((saveCard && "checked" in saveCard && saveCard.checked) || !!this.customization.saveCards?.autoSave) {
297
297
  try {
298
- await saveCustomerCard(this.baseUrl, auth_token, businessId, {
299
- skyflow_id: cardTokens.skyflow_id,
300
- });
298
+ await saveCustomerCard(
299
+ this.baseUrl,
300
+ auth_token,
301
+ this.secureToken,
302
+ businessId,
303
+ { skyflow_id: cardTokens.skyflow_id, }
304
+ );
301
305
  showMessage(MESSAGES.cardSaved, this.collectorIds.msgNotification);
302
306
  } catch (error) {
303
307
  if (error?.message) {
@@ -314,6 +318,7 @@ export class InlineCheckout extends BaseInlineCheckout {
314
318
  const cardsResponse = await fetchCustomerCards(
315
319
  this.baseUrl,
316
320
  token,
321
+ this.secureToken,
317
322
  this.merchantData.business.pk,
318
323
  );
319
324
  let cards = []
@@ -400,7 +405,13 @@ export class InlineCheckout extends BaseInlineCheckout {
400
405
  this.abortRefreshCardsController = new AbortController();
401
406
  }
402
407
  const businessId = this.merchantData.business.pk
403
- await removeCustomerCard(this.baseUrl, customerToken, skyflow_id, businessId)
408
+ await removeCustomerCard(
409
+ this.baseUrl,
410
+ customerToken,
411
+ this.secureToken,
412
+ skyflow_id,
413
+ businessId
414
+ )
404
415
  } catch (error) { } finally {
405
416
  this.deletingCards = this.deletingCards.filter(id => id !== skyflow_id);
406
417
  this.#refreshCardOnDelete(customerToken)
@@ -11,6 +11,7 @@ import {MESSAGES} from "../shared/constants/messages";
11
11
  *
12
12
  * @param {string} baseUrl - The base URL of the API.
13
13
  * @param {string} customerToken - The customer's authentication token.
14
+ * @param {string} secureToken - Token generated by secure-token endpoint.
14
15
  * @param {string | number} businessId - The business ID.
15
16
  * @param {import("../../types").ISaveCardSkyflowRequest} data - The card information to be saved.
16
17
  * @returns {Promise<import("../../types").ISaveCardResponse>} The saved card data.
@@ -20,6 +21,7 @@ import {MESSAGES} from "../shared/constants/messages";
20
21
  export async function saveCustomerCard(
21
22
  baseUrl,
22
23
  customerToken,
24
+ secureToken,
23
25
  businessId,
24
26
  data,
25
27
  ) {
@@ -28,7 +30,8 @@ export async function saveCustomerCard(
28
30
  const response = await fetch(url, {
29
31
  method: "POST",
30
32
  headers: {
31
- Authorization: `Token ${customerToken}`,
33
+ Authorization: `Bearer ${secureToken}`,
34
+ "User-Token": customerToken,
32
35
  "Content-Type": "application/json",
33
36
  },
34
37
  body: JSON.stringify(data),
@@ -57,6 +60,7 @@ export async function saveCustomerCard(
57
60
  * Removes a customer's card.
58
61
  * @param {string} baseUrl - The base URL of the API.
59
62
  * @param {string} customerToken - The customer's authentication token.
63
+ * @param {string} secureToken - Token generated by secure-token endpoint.
60
64
  * @param {string} skyflowId - The Skyflow ID of the card to be removed.
61
65
  * @param {string} businessId - The business ID.
62
66
  * @returns {Promise<string>} The result of the card removal operation.
@@ -66,6 +70,7 @@ export async function saveCustomerCard(
66
70
  export async function removeCustomerCard(
67
71
  baseUrl,
68
72
  customerToken,
73
+ secureToken,
69
74
  skyflowId = "",
70
75
  businessId,
71
76
  ) {
@@ -75,8 +80,9 @@ export async function removeCustomerCard(
75
80
  const response = await fetch(url, {
76
81
  method: "DELETE",
77
82
  headers: {
78
- Authorization: `Token ${customerToken}`,
83
+ Authorization: `Bearer ${secureToken}`,
79
84
  "Content-Type": "application/json",
85
+ "User-Token": customerToken,
80
86
  },
81
87
  });
82
88
 
@@ -94,6 +100,7 @@ export async function removeCustomerCard(
94
100
  * Fetches a customer's saved cards.
95
101
  * @param {string} baseUrl - The base URL of the API.
96
102
  * @param {string} customerToken - The customer's authentication token.
103
+ * @param {string} secureToken - Token generated by secure-token endpoint.
97
104
  * @param {string} businessId - The business ID.
98
105
  * @param {AbortSignal} signal - The abort signal to cancel the request.
99
106
  * @returns {Promise<import("../../types").ICustomerCardsResponse>} The customer's saved cards.
@@ -103,6 +110,7 @@ export async function removeCustomerCard(
103
110
  export async function fetchCustomerCards(
104
111
  baseUrl,
105
112
  customerToken,
113
+ secureToken,
106
114
  businessId,
107
115
  signal= null,
108
116
  ) {
@@ -111,12 +119,21 @@ export async function fetchCustomerCards(
111
119
  const response = await fetch(url, {
112
120
  method: "GET",
113
121
  headers: {
114
- Authorization: `Token ${customerToken}`,
115
- "Content-Type": "application/json",
122
+ Authorization: `Bearer ${secureToken}`,
123
+ "User-Token": customerToken,
116
124
  },
117
125
  signal,
118
126
  });
119
127
  if (response.ok) return await response.json();
128
+ if (response.status === 401) {
129
+ return {
130
+ code: 401,
131
+ body: {},
132
+ name: "",
133
+ message: "Unauthorized",
134
+ };
135
+ }
136
+
120
137
  const res_json = await response.json();
121
138
 
122
139
  throw await buildErrorResponse(response, res_json, MESSAGES.getCardsError);
package/src/index-dev.js CHANGED
@@ -98,11 +98,13 @@ const checkoutData = {
98
98
  // metadata: {
99
99
  // order_id: 123456
100
100
  // }
101
+ // Reference from the merchant
102
+ order_reference: "ORD-123456"
101
103
  };
102
104
 
103
105
  // localhost
104
106
  const apiKey = "11e3d3c3e95e0eaabbcae61ebad34ee5f93c3d27";
105
- const returnUrl = "http://127.0.0.1:8080/";
107
+ const returnUrl = "http://localhost:8080/";
106
108
  // stage
107
109
  // const apiKey = "8365683bdc33dd6d50fe2397188d79f1a6765852";
108
110
 
@@ -113,7 +115,6 @@ const commonConfig = {
113
115
  styles: customStyles,
114
116
  };
115
117
 
116
- let checkout;
117
118
  let inlineCheckout;
118
119
  let liteInlineCheckout;
119
120
 
@@ -133,7 +134,10 @@ function setupInlineCheckout() {
133
134
  },
134
135
  },
135
136
  });
136
- inlineCheckout.configureCheckout({ customer: checkoutData.customer });
137
+ inlineCheckout.configureCheckout({
138
+ customer: checkoutData.customer,
139
+ secureToken: "eyJhbGc..."
140
+ });
137
141
  inlineCheckout.injectCheckout();
138
142
  // ['Declined', 'Cancelled', 'Failed', 'Success', 'Pending', 'Authorized']
139
143
  inlineCheckout.verify3dsTransaction().then((response) => {
@@ -159,7 +163,10 @@ function setupInlineCheckout() {
159
163
  function setupLiteInlineCheckout() {
160
164
  loadMaskitoMask();
161
165
  liteInlineCheckout = new LiteInlineCheckout(commonConfig);
162
- liteInlineCheckout.configureCheckout({ customer: checkoutData.customer });
166
+ liteInlineCheckout.configureCheckout({
167
+ customer: checkoutData.customer,
168
+ secureToken: "eyJhbGc..."
169
+ });
163
170
  liteInlineCheckout.injectCheckout().then(() => {});
164
171
  liteInlineCheckout.verify3dsTransaction().then((response) => {
165
172
  console.log("Verify 3ds response", response);
package/types/common.d.ts CHANGED
@@ -9,6 +9,7 @@ export interface IInlineCheckoutBaseOptions {
9
9
 
10
10
  export interface IConfigureCheckout {
11
11
  customer: ICustomer;
12
+ secureToken: string;
12
13
  }
13
14
 
14
15
  export interface IApiError {