@tonder.io/ionic-full-sdk 0.0.61 → 0.0.62-beta.DEV-2106.99e3729

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.io/ionic-full-sdk",
3
- "version": "0.0.61",
3
+ "version": "0.0.62-beta.DEV-2106.99e3729",
4
4
  "description": "Tonder ionic full SDK",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -10,13 +10,14 @@
10
10
  "author": "",
11
11
  "license": "ISC",
12
12
  "dependencies": {
13
- "@tonder.io/ionic-lite-sdk": "^0.0.67",
13
+ "@tonder.io/ionic-lite-sdk": "0.0.68-beta.DEV-2106.48d18dd",
14
14
  "accordion-js": "^3.4.0",
15
15
  "crypto-js": "^4.1.1",
16
16
  "lodash.get": "^4.4.2",
17
17
  "skyflow-js": "2.5.0"
18
18
  },
19
19
  "devDependencies": {
20
+ "@rollup/plugin-replace": "^6.0.3",
20
21
  "@rollup/plugin-terser": "^0.4.4",
21
22
  "@rollup/plugin-typescript": "^11.1.6",
22
23
  "@types/crypto-js": "^4.2.2",
package/rollup.config.js CHANGED
@@ -1,15 +1,25 @@
1
1
  const typescript = require('@rollup/plugin-typescript');
2
2
  const terser = require('@rollup/plugin-terser');
3
3
  const postcss = require('rollup-plugin-postcss');
4
+ const replace = require('@rollup/plugin-replace');
5
+ const packageJson = require('./package.json');
4
6
 
5
7
  module.exports = {
6
8
  input: './src/index.ts',
7
- output: {
9
+ output:
10
+ {
8
11
  dir: 'dist',
9
12
  format: 'es',
10
13
  plugins: [terser()]
11
14
  },
12
15
  plugins: [
16
+ replace({
17
+ preventAssignment: true,
18
+ values: {
19
+ '__SDK_NAME__': JSON.stringify(packageJson.name),
20
+ '__SDK_VERSION__': JSON.stringify(packageJson.version)
21
+ }
22
+ }),
13
23
  typescript({
14
24
  exclude: ["tests/**", "jest.config.ts"]
15
25
  }),
@@ -5,15 +5,18 @@ import {
5
5
  mapCards,
6
6
  } from '../helpers/utils';
7
7
  import {initSkyflow, initUpdateSkyflow} from '../helpers/skyflow'
8
- import { ErrorResponse } from '@tonder.io/ionic-lite-sdk/dist/classes/errorResponse';
9
8
  import { CustomerRegisterResponse } from '@tonder.io/ionic-lite-sdk/dist/types/responses';
10
- import {BaseInlineCheckout} from "@tonder.io/ionic-lite-sdk";
9
+ import { BaseInlineCheckout } from "@tonder.io/ionic-lite-sdk";
11
10
  import {IProcessPaymentRequest} from "@tonder.io/ionic-lite-sdk/dist/types/checkout";
12
11
  import {ISaveCardInternalResponse, ISaveCardSkyflowRequest} from "@tonder.io/ionic-lite-sdk/dist/types/card";
13
12
  import {ITonderPaymentMethod} from "@tonder.io/ionic-lite-sdk/dist/types/paymentMethod";
14
13
  import type { SkyflowCollectFields, SkyflowCollectResponse } from "@tonder.io/ionic-lite-sdk/dist/types/cardOnFile";
15
14
  import {IEvents, IInlineCheckoutOptions, IInlineCustomizationOptions, InCollectorContainer} from "../types/commons";
16
15
  import {IInlineCheckout} from "../types/inlineCheckout";
16
+ import { buildPublicAppError } from "../shared/utils/appError";
17
+ import { ErrorKeyEnum } from "../shared/enums/ErrorKeyEnum";
18
+ import { MESSAGES_EN, MESSAGES_ES } from "../shared/constants/messages";
19
+ import { SDK_INFO } from "../helpers/sdkInfo";
17
20
  import get from "lodash.get";
18
21
  // @ts-ignore
19
22
  import Accordion from "accordion-js";
@@ -63,15 +66,18 @@ export class InlineCheckout extends BaseInlineCheckout<IInlineCustomizationOptio
63
66
  customization,
64
67
  events
65
68
  }: IInlineCheckoutOptions) {
66
- super({
69
+ const baseOptions = {
67
70
  apiKey: apiKey,
68
71
  returnUrl: returnUrl,
69
72
  mode: mode,
70
73
  callBack: callBack,
71
74
  customization: customization,
72
75
  tdsIframeId: collectorIds && 'tdsIframe' in collectorIds ? collectorIds?.tdsIframe : "tdsIframe",
73
- tonderPayButtonId: collectorIds ? collectorIds?.tonderPayButton : "tonderPayButton"
74
- });
76
+ tonderPayButtonId: collectorIds ? collectorIds?.tonderPayButton : "tonderPayButton",
77
+ sdkInfo: SDK_INFO,
78
+ };
79
+ super(baseOptions);
80
+
75
81
  this.renderPaymentButton = renderPaymentButton || false;
76
82
  this.renderSaveCardButton = renderSaveCardButton;
77
83
  this.customStyles = styles
@@ -334,6 +340,14 @@ export class InlineCheckout extends BaseInlineCheckout<IInlineCustomizationOptio
334
340
  this.#mountPayButton(container_radio_id)
335
341
  }catch (e){
336
342
  console.error("Ha ocurrido un error", e);
343
+ this.reportSdkError(e, {
344
+ feature: "handle-open-card-accordion",
345
+ process_id: container_radio_id,
346
+ metadata: {
347
+ step: "#handleOpenCardAccordion",
348
+ type,
349
+ },
350
+ });
337
351
  }
338
352
  await this.#handleRadioButtonClick(radio_card, null, type);
339
353
  }
@@ -386,8 +400,8 @@ export class InlineCheckout extends BaseInlineCheckout<IInlineCustomizationOptio
386
400
  async #handlePaymentClick() {
387
401
  try {
388
402
  await this.payment({} as IProcessPaymentRequest);
389
- } catch (error) {
390
- console.error("Payment error:", error);
403
+ } catch (error: any) {
404
+ console.error("Payment error:", error?.message, error?.code, error?.details);
391
405
  }
392
406
  }
393
407
 
@@ -677,7 +691,7 @@ export class InlineCheckout extends BaseInlineCheckout<IInlineCustomizationOptio
677
691
  try{
678
692
  if(this.merchantData!) {
679
693
  const cardTokensSkyflowTonder: any = await this.#getCardTokens(this.collectorIds.tonderSaveCardButton!);
680
- const customerResponse : CustomerRegisterResponse | ErrorResponse = await this._getCustomer() as CustomerRegisterResponse;
694
+ const customerResponse : CustomerRegisterResponse = await this._getCustomer() as CustomerRegisterResponse;
681
695
 
682
696
  if("auth_token" in customerResponse && this.secureToken) {
683
697
  // @ts-ignore
@@ -702,23 +716,35 @@ export class InlineCheckout extends BaseInlineCheckout<IInlineCustomizationOptio
702
716
 
703
717
  // 2. Process card on file
704
718
  const cardOnFile = await this._initializeCardOnFile();
705
- const result = await cardOnFile.process({
706
- cardTokens: {
707
- name: cardTokensSkyflowTonder.cardholder_name,
708
- number: cardTokensSkyflowTonder.card_number,
709
- expiryMonth: cardTokensSkyflowTonder.expiration_month,
710
- expiryYear: cardTokensSkyflowTonder.expiration_year,
711
- cvv: cardTokensSkyflowTonder.cvv,
712
- },
713
- cardBin,
714
- contactDetails: {
715
- firstName: first_name || '',
716
- lastName: last_name || '',
717
- email: email || '',
718
- },
719
- customerId: authToken,
720
- currency: this.currency || 'MXN',
721
- });
719
+ let result: { subscriptionId: string };
720
+ try {
721
+ result = await cardOnFile.process({
722
+ cardTokens: {
723
+ name: cardTokensSkyflowTonder.cardholder_name,
724
+ number: cardTokensSkyflowTonder.card_number,
725
+ expiryMonth: cardTokensSkyflowTonder.expiration_month,
726
+ expiryYear: cardTokensSkyflowTonder.expiration_year,
727
+ cvv: cardTokensSkyflowTonder.cvv,
728
+ },
729
+ cardBin,
730
+ contactDetails: {
731
+ firstName: first_name || '',
732
+ lastName: last_name || '',
733
+ email: email || '',
734
+ },
735
+ customerId: authToken,
736
+ currency: this.currency || 'MXN',
737
+ });
738
+ } catch (processError) {
739
+ throw buildPublicAppError(
740
+ {
741
+ errorCode: ErrorKeyEnum.CARD_ON_FILE_DECLINED,
742
+ lockErrorCode: true,
743
+ message: MESSAGES_EN[ErrorKeyEnum.CARD_ON_FILE_DECLINED],
744
+ },
745
+ processError,
746
+ );
747
+ }
722
748
 
723
749
  // 3. Save card with subscription_id
724
750
  await this.#handleSaveCard(
@@ -749,8 +775,21 @@ export class InlineCheckout extends BaseInlineCheckout<IInlineCustomizationOptio
749
775
  if(cardId && auth_token){
750
776
  await this._removeCustomerCard(auth_token, this.merchantData!.business.pk, cardId)
751
777
  }
778
+ this.reportSdkError(error, {
779
+ feature: "save-card",
780
+ process_id: cardId,
781
+ metadata: {
782
+ step: "#validateAndSaveCard",
783
+ },
784
+ });
752
785
  const exist_msg_error = typeof error === "object" && error !== null && "message" in error;
753
- showError(exist_msg_error ? error?.message as string:"Ha ocurrido un error", this.radioChecked, this.collectorIds.msgError)
786
+ const visualMessage =
787
+ (error as any)?.code === ErrorKeyEnum.CARD_ON_FILE_DECLINED
788
+ ? MESSAGES_ES[ErrorKeyEnum.CARD_ON_FILE_DECLINED]
789
+ : exist_msg_error
790
+ ? (error?.message as string)
791
+ : "Ha ocurrido un error";
792
+ showError(visualMessage, this.radioChecked, this.collectorIds.msgError)
754
793
  throw error;
755
794
  } finally {}
756
795
  }
@@ -786,23 +825,46 @@ export class InlineCheckout extends BaseInlineCheckout<IInlineCustomizationOptio
786
825
  throw new Error("Card BIN not returned from save card");
787
826
  }
788
827
 
789
- const result = await cardOnFile.process({
790
- cardTokens: {
791
- name: cardFields.cardholder_name,
792
- number: cardFields.card_number,
793
- expiryMonth: cardFields.expiration_month,
794
- expiryYear: cardFields.expiration_year,
795
- cvv: cardFields.cvv,
796
- },
797
- cardBin,
798
- contactDetails: {
799
- firstName: first_name || '',
800
- lastName: last_name || '',
801
- email: email || '',
828
+ let result: { subscriptionId: string };
829
+ try {
830
+ result = await cardOnFile.process({
831
+ cardTokens: {
832
+ name: cardFields.cardholder_name,
833
+ number: cardFields.card_number,
834
+ expiryMonth: cardFields.expiration_month,
835
+ expiryYear: cardFields.expiration_year,
836
+ cvv: cardFields.cvv,
837
+ },
838
+ cardBin,
839
+ contactDetails: {
840
+ firstName: first_name || '',
841
+ lastName: last_name || '',
842
+ email: email || '',
843
+ },
844
+ customerId: auth_token,
845
+ currency: this.currency || 'MXN',
846
+ });
847
+ } catch (processError) {
848
+ console.error("Error processing card-on-file payment=====", processError);
849
+ this.reportSdkError(processError, {
850
+ feature: "payment",
851
+ process_id: cardId,
852
+ metadata: {
853
+ step: "#processCardOnFilePayment_cardOnFile.process",
802
854
  },
803
- customerId: auth_token,
804
- currency: this.currency || 'MXN',
805
- });
855
+ });
856
+ throw buildPublicAppError(
857
+ {
858
+ errorCode: ErrorKeyEnum.CARD_ON_FILE_DECLINED,
859
+ lockErrorCode: true,
860
+ message: MESSAGES_EN[ErrorKeyEnum.CARD_ON_FILE_DECLINED],
861
+ details: {
862
+ cardId,
863
+ },
864
+ },
865
+ processError,
866
+ );
867
+ }
806
868
 
807
869
  // Update saved card with subscription_id
808
870
  await this.#handleSaveCard(
@@ -817,7 +879,25 @@ export class InlineCheckout extends BaseInlineCheckout<IInlineCustomizationOptio
817
879
 
818
880
  return { subscriptionId: result.subscriptionId, cardId: saveCardResponse.skyflow_id };
819
881
  }catch (error) {
820
- return { subscriptionId: null, cardId };
882
+ this.reportSdkError(error, {
883
+ feature: "payment",
884
+ process_id: cardId,
885
+ metadata: {
886
+ step: "#processCardOnFilePayment",
887
+ error,
888
+ },
889
+ });
890
+ throw buildPublicAppError(
891
+ {
892
+ errorCode: ErrorKeyEnum.CARD_ON_FILE_DECLINED,
893
+ lockErrorCode: true,
894
+ message: MESSAGES_EN[ErrorKeyEnum.CARD_ON_FILE_DECLINED],
895
+ details: {
896
+ cardId,
897
+ },
898
+ },
899
+ error,
900
+ );
821
901
  }
822
902
  }
823
903
 
@@ -906,7 +986,18 @@ export class InlineCheckout extends BaseInlineCheckout<IInlineCustomizationOptio
906
986
  await this._removeCustomerCard(auth_token, this.merchantData!.business.pk, cardIdSaved)
907
987
  await this.#loadCardsList(auth_token)
908
988
  }
909
- showError("Ha ocurrido un error", this.radioChecked, this.collectorIds.msgError)
989
+ this.reportSdkError(error, {
990
+ feature: "payment",
991
+ process_id: cardIdSaved || this.radioChecked || this.getCustomerId(),
992
+ metadata: {
993
+ step: "_checkout",
994
+ },
995
+ });
996
+ const visualMessage =
997
+ (error as any)?.code === ErrorKeyEnum.CARD_ON_FILE_DECLINED
998
+ ? MESSAGES_ES[ErrorKeyEnum.CARD_ON_FILE_DECLINED]
999
+ : "Ha ocurrido un error";
1000
+ showError(visualMessage, this.radioChecked, this.collectorIds.msgError)
910
1001
  throw error;
911
1002
  } finally {
912
1003
  this.#updatePayButton({cardId: this.radioChecked, disabled: false});
@@ -916,7 +1007,7 @@ export class InlineCheckout extends BaseInlineCheckout<IInlineCustomizationOptio
916
1007
  async #handleSaveCard(
917
1008
  authToken: string,
918
1009
  businessId: string | number,
919
- cardTokens: ISaveCardSkyflowRequest & { subscription_id?: string },
1010
+ cardTokens: ISaveCardSkyflowRequest,
920
1011
  options: { forceSave?: boolean; showNotification?: boolean; appOrigin?: boolean; refreshCards?: boolean } = {}
921
1012
  ): Promise<ISaveCardInternalResponse | undefined> {
922
1013
  // Validate required data
@@ -944,6 +1035,13 @@ export class InlineCheckout extends BaseInlineCheckout<IInlineCustomizationOptio
944
1035
  showError("No se han configurado los datos de seguridad para guardar tarjeta", this.radioChecked, this.collectorIds.msgError);
945
1036
  }
946
1037
  } catch (error) {
1038
+ this.reportSdkError(error, {
1039
+ feature: "save-card",
1040
+ process_id: cardTokens.skyflow_id || this.getCustomerId(),
1041
+ metadata: {
1042
+ step: "#handleSaveCard",
1043
+ },
1044
+ });
947
1045
  const exist_msg_error = typeof error === "object" && error !== null && "message" in error;
948
1046
  if (exist_msg_error && error?.message !== 'Error') {
949
1047
  showError(error?.message as string, this.radioChecked, this.collectorIds.msgError);
@@ -0,0 +1,11 @@
1
+ /**
2
+ * SDK information injected from package.json at build time
3
+ * Values are replaced by Rollup's replace plugin
4
+ */
5
+ declare const __SDK_NAME__: string;
6
+ declare const __SDK_VERSION__: string;
7
+
8
+ export const SDK_INFO = Object.freeze({
9
+ name: __SDK_NAME__,
10
+ version: __SDK_VERSION__,
11
+ });
@@ -0,0 +1,13 @@
1
+ import { ErrorKeyEnum } from "../enums/ErrorKeyEnum";
2
+
3
+ export const MESSAGES_EN: Record<string, string> = {
4
+ [ErrorKeyEnum.CARD_ON_FILE_DECLINED]:
5
+ "Transaction declined. Please verify your card details.",
6
+ [ErrorKeyEnum.UNKNOWN_ERROR]: 'An unexpected error occurred.',
7
+ };
8
+
9
+ export const MESSAGES_ES: Record<string, string> = {
10
+ [ErrorKeyEnum.CARD_ON_FILE_DECLINED]:
11
+ "Transaccion declinada. Verique los datos de su tarjeta.",
12
+ [ErrorKeyEnum.UNKNOWN_ERROR]: 'Ocurrió un error inesperado.',
13
+ };
@@ -0,0 +1,6 @@
1
+ export enum ErrorKeyEnum {
2
+ SAVE_CARD_ERROR = "SAVE_CARD_ERROR",
3
+ PAYMENT_PROCESS_ERROR = "PAYMENT_PROCESS_ERROR",
4
+ CARD_ON_FILE_DECLINED = "CARD_ON_FILE_DECLINED",
5
+ UNKNOWN_ERROR = 'UNKNOWN_ERROR'
6
+ }
@@ -0,0 +1,304 @@
1
+ import { MESSAGES_EN } from '../constants/messages';
2
+ import { ErrorKeyEnum } from '../enums/ErrorKeyEnum';
3
+
4
+ const DEFAULT_SYSTEM_ERROR = 'APP_INTERNAL_001';
5
+ const LOCK_ERROR_CODE_KEY = '__tonderLockErrorCode__';
6
+
7
+ function isRecord(value: unknown): value is Record<string, unknown> {
8
+ return typeof value === 'object' && value !== null;
9
+ }
10
+
11
+ export interface IAppErrorInput {
12
+ errorCode: string;
13
+ message?: string;
14
+ statusCode?: number;
15
+ details?: Record<string, unknown>;
16
+ originalError?: unknown;
17
+ lockErrorCode?: boolean;
18
+ }
19
+
20
+ export class AppError extends Error {
21
+ public readonly status: 'error' = 'error';
22
+ public readonly code: string;
23
+ public readonly statusCode: number;
24
+ public readonly originalError?: unknown;
25
+ public details: Record<string, unknown>;
26
+
27
+ constructor(error: IAppErrorInput) {
28
+ const resolvedStatusCode = AppError.resolveStatusCode(
29
+ error.statusCode,
30
+ error.originalError,
31
+ );
32
+ const resolvedMessage = AppError.resolveMessage(
33
+ error.errorCode,
34
+ error.message,
35
+ );
36
+ const resolvedSystemError = AppError.resolveSystemError(
37
+ error.details?.systemError,
38
+ error.originalError,
39
+ );
40
+
41
+ super(resolvedMessage);
42
+
43
+ Object.setPrototypeOf(this, new.target.prototype);
44
+
45
+ this.name = 'TonderError';
46
+ this.code = error.errorCode;
47
+ this.statusCode = resolvedStatusCode;
48
+ this.originalError = error.originalError;
49
+ this.details = {
50
+ code: error.errorCode,
51
+ statusCode: resolvedStatusCode,
52
+ systemError: resolvedSystemError,
53
+ };
54
+
55
+ if (error.lockErrorCode) {
56
+ Object.defineProperty(this, LOCK_ERROR_CODE_KEY, {
57
+ value: true,
58
+ enumerable: false,
59
+ configurable: true,
60
+ });
61
+ }
62
+
63
+ Error.captureStackTrace?.(this, AppError);
64
+ }
65
+
66
+ private static isRecord(value: unknown): value is Record<string, unknown> {
67
+ return typeof value === 'object' && value !== null;
68
+ }
69
+
70
+ public static parseStatusCode(code: unknown): number {
71
+ const parsed = Number(code);
72
+ if (!Number.isFinite(parsed)) return 500;
73
+ if (parsed < 100 || parsed > 599) return 500;
74
+ return Math.trunc(parsed);
75
+ }
76
+
77
+ public static resolveStatusCode(
78
+ explicitStatusCode: unknown,
79
+ originalError?: unknown,
80
+ ): number {
81
+ const candidates: unknown[] = [explicitStatusCode];
82
+
83
+ if (AppError.isRecord(originalError)) {
84
+ candidates.push(
85
+ originalError.statusCode,
86
+ originalError.status,
87
+ );
88
+
89
+ const body = AppError.isRecord(originalError.body)
90
+ ? originalError.body
91
+ : null;
92
+ if (body) {
93
+ candidates.push(body.statusCode, body.status);
94
+ }
95
+ }
96
+
97
+ for (const candidate of candidates) {
98
+ if (AppError.isHttpStatusCode(candidate)) {
99
+ return Math.trunc(Number(candidate));
100
+ }
101
+ }
102
+
103
+ return 500;
104
+ }
105
+
106
+ public static resolveMessage(errorCode: string, message?: string): string {
107
+ if (message) return message;
108
+ return (
109
+ MESSAGES_EN[errorCode] ||
110
+ MESSAGES_EN[ErrorKeyEnum.UNKNOWN_ERROR] ||
111
+ 'An unexpected error occurred.'
112
+ );
113
+ }
114
+
115
+ public static isHttpStatusCode(value: unknown): boolean {
116
+ const parsed = Number(value);
117
+ return Number.isFinite(parsed) && parsed >= 100 && parsed <= 599;
118
+ }
119
+
120
+ public static normalizeSystemError(value: unknown): string | null {
121
+ if (typeof value !== 'string') return null;
122
+ const trimmed = value.trim();
123
+ return trimmed || null;
124
+ }
125
+
126
+ public static resolveSystemError(
127
+ explicitSystemError: unknown,
128
+ originalError?: unknown,
129
+ ): string {
130
+ const candidates: unknown[] = [explicitSystemError];
131
+
132
+ if (AppError.isRecord(originalError)) {
133
+ const details = AppError.isRecord(originalError.details)
134
+ ? originalError.details
135
+ : null;
136
+ const body = AppError.isRecord(originalError.body)
137
+ ? originalError.body
138
+ : null;
139
+ const bodyDetails = body && AppError.isRecord(body.details)
140
+ ? body.details
141
+ : null;
142
+
143
+ candidates.push(
144
+ originalError.systemError,
145
+ originalError.code,
146
+ details?.systemError,
147
+ details?.code,
148
+ body?.systemError,
149
+ body?.code,
150
+ bodyDetails?.systemError,
151
+ bodyDetails?.code,
152
+ );
153
+ }
154
+
155
+ for (const candidate of candidates) {
156
+ const resolved = AppError.normalizeSystemError(candidate);
157
+ if (resolved) return resolved;
158
+ }
159
+
160
+ return DEFAULT_SYSTEM_ERROR;
161
+ }
162
+ }
163
+
164
+ export interface IBuildPublicAppErrorInput {
165
+ errorCode: string;
166
+ message?: string;
167
+ details?: Record<string, unknown>;
168
+ statusCode?: number;
169
+ response?: Response;
170
+ lockErrorCode?: boolean;
171
+ }
172
+
173
+ function markErrorCodeLocked(error: Record<string, unknown>): void {
174
+ Object.defineProperty(error, LOCK_ERROR_CODE_KEY, {
175
+ value: true,
176
+ enumerable: false,
177
+ configurable: true,
178
+ });
179
+ }
180
+
181
+ function hasLockedErrorCode(error: unknown): boolean {
182
+ return isRecord(error) && error[LOCK_ERROR_CODE_KEY] === true;
183
+ }
184
+
185
+ function isTonderAppErrorLike(error: unknown): error is AppError {
186
+ return (
187
+ isRecord(error) &&
188
+ error.name === 'TonderError' &&
189
+ typeof error.code === 'string'
190
+ );
191
+ }
192
+
193
+ function getOriginalError(error: unknown): unknown {
194
+ if (error instanceof AppError) {
195
+ return error.originalError !== undefined ? error.originalError : error;
196
+ }
197
+
198
+ return error;
199
+ }
200
+
201
+ async function parseResponseBody(response: Response): Promise<unknown> {
202
+ try {
203
+ return await response.clone().json();
204
+ } catch (_) {
205
+ // Ignore JSON parsing errors and fallback to text.
206
+ }
207
+
208
+ try {
209
+ const text = await response.text();
210
+ return text || undefined;
211
+ } catch (_) {
212
+ return undefined;
213
+ }
214
+ }
215
+
216
+ function buildPublicAppErrorSync(
217
+ data: IBuildPublicAppErrorInput,
218
+ error?: unknown,
219
+ ): AppError {
220
+ if (!data?.errorCode) {
221
+ throw new Error('buildPublicAppError requires errorCode');
222
+ }
223
+
224
+ const explicitSystemError = data.details?.systemError;
225
+ const hasExplicitOverrides =
226
+ !!data.message ||
227
+ data.statusCode !== undefined ||
228
+ explicitSystemError !== undefined;
229
+
230
+ if (isTonderAppErrorLike(error)) {
231
+ if (data.lockErrorCode) {
232
+ markErrorCodeLocked(error as unknown as Record<string, unknown>);
233
+ }
234
+
235
+ if (!hasExplicitOverrides) {
236
+ if (data.errorCode === error.code || hasLockedErrorCode(error)) {
237
+ return error;
238
+ }
239
+ }
240
+ }
241
+
242
+ return new AppError({
243
+ errorCode: data.errorCode,
244
+ message: data.message,
245
+ statusCode: data.statusCode,
246
+ details:
247
+ explicitSystemError !== undefined
248
+ ? { systemError: explicitSystemError }
249
+ : undefined,
250
+ originalError: getOriginalError(error),
251
+ lockErrorCode: data.lockErrorCode || hasLockedErrorCode(error),
252
+ });
253
+ }
254
+
255
+ async function buildPublicAppErrorFromHttpResponse(
256
+ data: IBuildPublicAppErrorInput & { response: Response },
257
+ ): Promise<AppError> {
258
+ const body = await parseResponseBody(data.response);
259
+
260
+ const originalError: Record<string, unknown> = {
261
+ status: data.response.status,
262
+ };
263
+
264
+ if (data.response.statusText) {
265
+ originalError.statusText = data.response.statusText;
266
+ }
267
+
268
+ if (body !== undefined) {
269
+ originalError.body = body;
270
+ }
271
+
272
+ return new AppError({
273
+ errorCode: data.errorCode,
274
+ message: data.message,
275
+ statusCode: data.statusCode ?? data.response.status,
276
+ details:
277
+ data.details?.systemError !== undefined
278
+ ? { systemError: data.details.systemError }
279
+ : undefined,
280
+ originalError,
281
+ lockErrorCode: data.lockErrorCode,
282
+ });
283
+ }
284
+
285
+ export function buildPublicAppError(
286
+ data: IBuildPublicAppErrorInput & { response: Response },
287
+ error?: unknown,
288
+ ): Promise<AppError>;
289
+ export function buildPublicAppError(
290
+ data: IBuildPublicAppErrorInput,
291
+ error?: unknown,
292
+ ): AppError;
293
+ export function buildPublicAppError(
294
+ data: IBuildPublicAppErrorInput,
295
+ error?: unknown,
296
+ ): AppError | Promise<AppError> {
297
+ if (data?.response) {
298
+ return buildPublicAppErrorFromHttpResponse(
299
+ data as IBuildPublicAppErrorInput & { response: Response },
300
+ );
301
+ }
302
+
303
+ return buildPublicAppErrorSync(data, error);
304
+ }
@@ -1,7 +1,6 @@
1
1
  import {IConfigureCheckout} from "@tonder.io/ionic-lite-sdk/dist/types/commons";
2
2
  import {
3
3
  IProcessPaymentRequest,
4
- IStartCheckoutErrorResponse,
5
4
  IStartCheckoutResponse
6
5
  } from "@tonder.io/ionic-lite-sdk/dist/types/checkout";
7
6
  import {ITransaction} from "@tonder.io/ionic-lite-sdk/dist/types/transaction";
@@ -32,7 +31,6 @@ export interface IInlineCheckout {
32
31
  *
33
32
  * @throws {Error} Throws an error if the checkout process fails. The error object contains
34
33
  * additional `details` property with the response from the server if available.
35
- * @property {IStartCheckoutErrorResponse} error.details - The response body from the server when an error occurs.
36
34
  *
37
35
  * @public
38
36
  */