@deuna/react-native-sdk 1.0.2 → 1.0.4

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.
Files changed (115) hide show
  1. package/README.md +21 -0
  2. package/lib/module/DeunaSDK.js +96 -75
  3. package/lib/module/DeunaSDK.js.map +1 -1
  4. package/lib/module/components/DeunaWidget.js +54 -26
  5. package/lib/module/components/DeunaWidget.js.map +1 -1
  6. package/lib/module/components/ExternalUrlWebView.js +56 -0
  7. package/lib/module/components/ExternalUrlWebView.js.map +1 -0
  8. package/lib/module/controllers/BaseWebViewController.js +21 -20
  9. package/lib/module/controllers/BaseWebViewController.js.map +1 -1
  10. package/lib/module/controllers/ElementsWidgetController.js +5 -5
  11. package/lib/module/controllers/ElementsWidgetController.js.map +1 -1
  12. package/lib/module/controllers/{OpenInNewTabController.js → ExternalUrlController.js} +4 -4
  13. package/lib/module/controllers/ExternalUrlController.js.map +1 -0
  14. package/lib/module/controllers/PaymentWidgetController.js +5 -4
  15. package/lib/module/controllers/PaymentWidgetController.js.map +1 -1
  16. package/lib/module/helpers/Completer.js +2 -2
  17. package/lib/module/helpers/Completer.js.map +1 -1
  18. package/lib/module/helpers/CrossPlatformBrowser.js +51 -0
  19. package/lib/module/helpers/CrossPlatformBrowser.js.map +1 -0
  20. package/lib/module/helpers/ExternalUrlHelper.js +96 -0
  21. package/lib/module/helpers/ExternalUrlHelper.js.map +1 -0
  22. package/lib/module/helpers/SubmitStrategy.js +44 -0
  23. package/lib/module/helpers/SubmitStrategy.js.map +1 -0
  24. package/lib/module/helpers/ViewManager.js +32 -0
  25. package/lib/module/helpers/ViewManager.js.map +1 -0
  26. package/lib/module/helpers/{getController.js → buildDeunaWidgetController.js} +12 -4
  27. package/lib/module/helpers/buildDeunaWidgetController.js.map +1 -0
  28. package/lib/module/helpers/getSubmitStrategy.js +32 -0
  29. package/lib/module/helpers/getSubmitStrategy.js.map +1 -0
  30. package/lib/module/interfaces/constants.js +2 -0
  31. package/lib/module/interfaces/constants.js.map +1 -1
  32. package/lib/module/interfaces/types.js.map +1 -1
  33. package/lib/module/types/helpers/buildElementsLink.js +3 -1
  34. package/lib/module/types/helpers/buildElementsLink.js.map +1 -1
  35. package/lib/module/types/helpers/buildPaymentLink.js +3 -1
  36. package/lib/module/types/helpers/buildPaymentLink.js.map +1 -1
  37. package/lib/module/types/helpers/buildVoucherLink.js +3 -1
  38. package/lib/module/types/helpers/buildVoucherLink.js.map +1 -1
  39. package/lib/module/types/helpers/constants.js +4 -0
  40. package/lib/module/types/helpers/constants.js.map +1 -0
  41. package/lib/module/types/helpers/urlConfig.js +1 -1
  42. package/lib/module/types/helpers/urlConfig.js.map +1 -1
  43. package/lib/typescript/deuna-sdk-react-native/src/DeunaSDK.d.ts +14 -13
  44. package/lib/typescript/deuna-sdk-react-native/src/DeunaSDK.d.ts.map +1 -1
  45. package/lib/typescript/deuna-sdk-react-native/src/components/DeunaWidget.d.ts.map +1 -1
  46. package/lib/typescript/deuna-sdk-react-native/src/components/ExternalUrlWebView.d.ts +7 -0
  47. package/lib/typescript/deuna-sdk-react-native/src/components/ExternalUrlWebView.d.ts.map +1 -0
  48. package/lib/typescript/deuna-sdk-react-native/src/controllers/BaseWebViewController.d.ts +7 -6
  49. package/lib/typescript/deuna-sdk-react-native/src/controllers/BaseWebViewController.d.ts.map +1 -1
  50. package/lib/typescript/deuna-sdk-react-native/src/controllers/ElementsWidgetController.d.ts +3 -1
  51. package/lib/typescript/deuna-sdk-react-native/src/controllers/ElementsWidgetController.d.ts.map +1 -1
  52. package/lib/typescript/deuna-sdk-react-native/src/controllers/{OpenInNewTabController.d.ts → ExternalUrlController.d.ts} +2 -2
  53. package/lib/typescript/deuna-sdk-react-native/src/controllers/ExternalUrlController.d.ts.map +1 -0
  54. package/lib/typescript/deuna-sdk-react-native/src/controllers/PaymentWidgetController.d.ts +3 -2
  55. package/lib/typescript/deuna-sdk-react-native/src/controllers/PaymentWidgetController.d.ts.map +1 -1
  56. package/lib/typescript/deuna-sdk-react-native/src/helpers/CrossPlatformBrowser.d.ts +25 -0
  57. package/lib/typescript/deuna-sdk-react-native/src/helpers/CrossPlatformBrowser.d.ts.map +1 -0
  58. package/lib/typescript/deuna-sdk-react-native/src/helpers/ExternalUrlHelper.d.ts +48 -0
  59. package/lib/typescript/deuna-sdk-react-native/src/helpers/ExternalUrlHelper.d.ts.map +1 -0
  60. package/lib/typescript/deuna-sdk-react-native/src/helpers/SubmitStrategy.d.ts +23 -0
  61. package/lib/typescript/deuna-sdk-react-native/src/helpers/SubmitStrategy.d.ts.map +1 -0
  62. package/lib/typescript/deuna-sdk-react-native/src/helpers/ViewManager.d.ts +19 -0
  63. package/lib/typescript/deuna-sdk-react-native/src/helpers/ViewManager.d.ts.map +1 -0
  64. package/lib/typescript/deuna-sdk-react-native/src/helpers/{getController.d.ts → buildDeunaWidgetController.d.ts} +2 -2
  65. package/lib/typescript/deuna-sdk-react-native/src/helpers/buildDeunaWidgetController.d.ts.map +1 -0
  66. package/lib/typescript/deuna-sdk-react-native/src/helpers/getSubmitStrategy.d.ts +4 -0
  67. package/lib/typescript/deuna-sdk-react-native/src/helpers/getSubmitStrategy.d.ts.map +1 -0
  68. package/lib/typescript/deuna-sdk-react-native/src/interfaces/constants.d.ts +2 -0
  69. package/lib/typescript/deuna-sdk-react-native/src/interfaces/constants.d.ts.map +1 -1
  70. package/lib/typescript/deuna-sdk-react-native/src/interfaces/types.d.ts +6 -0
  71. package/lib/typescript/deuna-sdk-react-native/src/interfaces/types.d.ts.map +1 -1
  72. package/lib/typescript/deuna-sdk-react-native/src/types/helpers/buildElementsLink.d.ts.map +1 -1
  73. package/lib/typescript/deuna-sdk-react-native/src/types/helpers/buildPaymentLink.d.ts.map +1 -1
  74. package/lib/typescript/deuna-sdk-react-native/src/types/helpers/buildVoucherLink.d.ts.map +1 -1
  75. package/lib/typescript/deuna-sdk-react-native/src/types/helpers/constants.d.ts +2 -0
  76. package/lib/typescript/deuna-sdk-react-native/src/types/helpers/constants.d.ts.map +1 -0
  77. package/lib/typescript/deuna-sdk-react-native/src/types/helpers/urlConfig.d.ts +1 -0
  78. package/lib/typescript/deuna-sdk-react-native/src/types/helpers/urlConfig.d.ts.map +1 -1
  79. package/lib/typescript/deuna-sdk-react-native/src/types/interfaces/callbacks.d.ts +8 -0
  80. package/lib/typescript/deuna-sdk-react-native/src/types/interfaces/callbacks.d.ts.map +1 -1
  81. package/lib/typescript/deuna-sdk-react-native/src/types/interfaces/initWidgetBase.d.ts +1 -0
  82. package/lib/typescript/deuna-sdk-react-native/src/types/interfaces/initWidgetBase.d.ts.map +1 -1
  83. package/package.json +7 -5
  84. package/src/DeunaSDK.ts +115 -83
  85. package/src/components/DeunaWidget.tsx +75 -35
  86. package/src/components/ExternalUrlWebView.tsx +65 -0
  87. package/src/controllers/BaseWebViewController.ts +28 -27
  88. package/src/controllers/ElementsWidgetController.ts +8 -6
  89. package/src/controllers/{OpenInNewTabController.ts → ExternalUrlController.ts} +3 -3
  90. package/src/controllers/PaymentWidgetController.ts +12 -5
  91. package/src/helpers/Completer.ts +2 -2
  92. package/src/helpers/CrossPlatformBrowser.ts +49 -0
  93. package/src/helpers/ExternalUrlHelper.ts +118 -0
  94. package/src/helpers/SubmitStrategy.ts +67 -0
  95. package/src/helpers/ViewManager.ts +45 -0
  96. package/src/helpers/{getController.ts → buildDeunaWidgetController.ts} +11 -4
  97. package/src/helpers/getSubmitStrategy.ts +42 -0
  98. package/src/interfaces/constants.ts +5 -1
  99. package/src/interfaces/types.ts +8 -0
  100. package/src/types/helpers/buildElementsLink.ts +4 -1
  101. package/src/types/helpers/buildPaymentLink.ts +2 -0
  102. package/src/types/helpers/buildVoucherLink.ts +2 -0
  103. package/src/types/helpers/constants.ts +1 -0
  104. package/src/types/helpers/urlConfig.ts +3 -1
  105. package/src/types/interfaces/callbacks.ts +10 -1
  106. package/src/types/interfaces/initWidgetBase.ts +1 -0
  107. package/lib/module/components/NewTabWebView.js +0 -56
  108. package/lib/module/components/NewTabWebView.js.map +0 -1
  109. package/lib/module/controllers/OpenInNewTabController.js.map +0 -1
  110. package/lib/module/helpers/getController.js.map +0 -1
  111. package/lib/typescript/deuna-sdk-react-native/src/components/NewTabWebView.d.ts +0 -7
  112. package/lib/typescript/deuna-sdk-react-native/src/components/NewTabWebView.d.ts.map +0 -1
  113. package/lib/typescript/deuna-sdk-react-native/src/controllers/OpenInNewTabController.d.ts.map +0 -1
  114. package/lib/typescript/deuna-sdk-react-native/src/helpers/getController.d.ts.map +0 -1
  115. package/src/components/NewTabWebView.tsx +0 -64
@@ -8,25 +8,24 @@ import {
8
8
  State,
9
9
  SubmitResult,
10
10
  } from '../types';
11
- import { submitError } from '../interfaces';
11
+ import { submitError, WidgetConfig } from '../interfaces';
12
12
  import { Platform } from 'react-native';
13
13
  import { ShouldStartLoadRequest } from 'react-native-webview/lib/WebViewTypes';
14
14
 
15
15
  export interface WebViewDelegate {
16
- onOpenInNewTab?: (url: string) => void;
16
+ onOpenExternalUrl?: (url: string) => void;
17
17
  onCloseButtonPressed?: () => void;
18
- onCloseSubWebView?: () => void;
19
18
  onFileDownload?: (url: string) => void;
20
- onNewTabWindowClose?: () => void;
19
+ onCloseExternalUrl?: () => void;
21
20
  }
22
21
 
23
22
  export enum WebViewEventType {
24
23
  consoleLog = 'consoleLog',
25
24
  eventDispatch = 'eventDispatch',
26
25
  jsExecutor = 'jsExecutor',
27
- openInNewTab = 'openInNewTab',
26
+ openExternalUrl = 'openExternalUrl',
28
27
  redirect = 'redirect',
29
- newTabWindowClose = 'newTabWindowClose',
28
+ closeExternalUrl = 'closeExternalUrl',
30
29
  }
31
30
 
32
31
  const DOWNLOAD_FILE_REGEX =
@@ -34,7 +33,6 @@ const DOWNLOAD_FILE_REGEX =
34
33
 
35
34
  export abstract class BaseWebViewController {
36
35
  initialized = false;
37
- redirectUrl: string | null = null;
38
36
  webView: WebView | null = null;
39
37
  url: string | null = null;
40
38
 
@@ -78,26 +76,22 @@ export abstract class BaseWebViewController {
78
76
  * @returns boolean indicating if the URL should be loaded in current WebView
79
77
  */
80
78
  onShouldStartLoadWithRequest = (request: ShouldStartLoadRequest) => {
81
- // Prevent loading if it's the redirect URL
82
- if (this.redirectUrl === request.url) {
83
- return false;
79
+ if (this.url === request.url) {
80
+ return true;
84
81
  }
85
82
 
86
- const isNewNavigation = request.isTopFrame && request.url !== this.url;
87
- const isClickNavigation = request.navigationType === 'click';
88
- const shouldHandleInNewTab = !this.urlMustBeLoadedInTheSameWebView(
83
+ const isAndroid = Platform.OS === 'android';
84
+
85
+ const isNewNavigation = isAndroid
86
+ ? request.url.startsWith('https://')
87
+ : request.navigationType === 'click';
88
+
89
+ const shouldOpenExternally = !this.urlMustBeLoadedInTheSameWebView(
89
90
  request.url
90
91
  );
91
92
 
92
- // For iOS, check both click navigation and new navigation
93
- // For Android, only check new navigation
94
- const shouldOpenInNewTab =
95
- Platform.OS === 'ios'
96
- ? (isClickNavigation || isNewNavigation) && shouldHandleInNewTab
97
- : isNewNavigation && shouldHandleInNewTab;
98
-
99
- if (shouldOpenInNewTab) {
100
- this.delegate?.onOpenInNewTab?.(request.url);
93
+ if (isNewNavigation && shouldOpenExternally) {
94
+ this.delegate?.onOpenExternalUrl?.(request.url);
101
95
  return false;
102
96
  }
103
97
 
@@ -118,6 +112,10 @@ export abstract class DeunaWebViewController extends BaseWebViewController {
118
112
  redirectUrl: string | null = null;
119
113
  closedAction: ClosedAction = 'systemAction';
120
114
 
115
+ constructor(readonly widgetConfig: WidgetConfig) {
116
+ super();
117
+ }
118
+
121
119
  abstract onEventDispatch: (event: Record<string, any>) => void;
122
120
 
123
121
  onLoad = () => {
@@ -154,18 +152,18 @@ export abstract class DeunaWebViewController extends BaseWebViewController {
154
152
  this.jsExecutor.requests.get(requestId)?.(data);
155
153
  this.jsExecutor.requests.delete(requestId);
156
154
  },
157
- [WebViewEventType.openInNewTab]: () => {
158
- this.delegate?.onOpenInNewTab?.(eventData.url);
155
+ [WebViewEventType.openExternalUrl]: () => {
156
+ this.delegate?.onOpenExternalUrl?.(eventData.url);
159
157
  },
160
158
  [WebViewEventType.redirect]: () => {
161
159
  if (this.redirectUrl) {
162
160
  return;
163
161
  }
164
162
  this.redirectUrl = eventData.url;
165
- this.delegate?.onOpenInNewTab?.(eventData.url);
163
+ this.delegate?.onOpenExternalUrl?.(eventData.url);
166
164
  },
167
- [WebViewEventType.newTabWindowClose]: () => {
168
- this.delegate?.onNewTabWindowClose?.();
165
+ [WebViewEventType.closeExternalUrl]: () => {
166
+ this.delegate?.onCloseExternalUrl?.();
169
167
  },
170
168
  };
171
169
 
@@ -175,6 +173,9 @@ export abstract class DeunaWebViewController extends BaseWebViewController {
175
173
  setXprops = () => {
176
174
  this.webView?.injectJavaScript(
177
175
  `
176
+ window.open = function(url, target, features) {
177
+ window.ReactNativeWebView.postMessage(JSON.stringify({ type: '${WebViewEventType.openExternalUrl}', url }));
178
+ };
178
179
  console.log = function(message) {
179
180
  window.ReactNativeWebView.postMessage(JSON.stringify({ type: '${WebViewEventType.consoleLog}', message }));
180
181
  };
@@ -4,11 +4,14 @@ import {
4
4
  ElementsEventType,
5
5
  } from '../interfaces/events/elements';
6
6
  import { ElementsWidgetCallbacks } from '../types';
7
- import { ElementsErrorType } from '../interfaces';
7
+ import { ElementsErrorType, WidgetConfig } from '../interfaces';
8
8
 
9
9
  export class ElementsWidgetController extends DeunaWebViewController {
10
- constructor(readonly callbacks: ElementsWidgetCallbacks) {
11
- super();
10
+ constructor(
11
+ readonly callbacks: ElementsWidgetCallbacks,
12
+ readonly widgetConfig: WidgetConfig
13
+ ) {
14
+ super(widgetConfig);
12
15
  }
13
16
 
14
17
  onError = (event: any) => {
@@ -34,12 +37,11 @@ export class ElementsWidgetController extends DeunaWebViewController {
34
37
  this.delegate?.onCloseButtonPressed?.();
35
38
  },
36
39
  [ElementsEventType.vaultSaveSuccess]: () => {
37
- this.delegate?.onCloseSubWebView?.();
40
+ this.delegate?.onCloseExternalUrl?.();
38
41
  this.callbacks.onSuccess?.(event.data);
39
42
  },
40
43
  [ElementsEventType.vaultSaveError]: () => {
41
- this.delegate?.onCloseSubWebView?.();
42
- this.delegate?.onCloseSubWebView?.();
44
+ this.delegate?.onCloseExternalUrl?.();
43
45
  const { metadata } = elementsEvent.data;
44
46
 
45
47
  if (metadata) {
@@ -5,7 +5,7 @@ import {
5
5
  } from './BaseWebViewController';
6
6
  import { DeunaLogs } from '../DeunaLogs';
7
7
 
8
- export class OpenInNewTabController extends BaseWebViewController {
8
+ export class ExternalUrlController extends BaseWebViewController {
9
9
  constructor(readonly url: string) {
10
10
  super();
11
11
  this.url = url;
@@ -16,7 +16,7 @@ export class OpenInNewTabController extends BaseWebViewController {
16
16
  };
17
17
 
18
18
  onError = (event: any) => {
19
- console.warn(event);
19
+ DeunaLogs.error(`EXTERNAL URL ERROR`, event);
20
20
  };
21
21
 
22
22
  urlMustBeLoadedInTheSameWebView = (url: string) => {
@@ -41,7 +41,7 @@ export class OpenInNewTabController extends BaseWebViewController {
41
41
  window.ReactNativeWebView.postMessage(JSON.stringify({ type: '${WebViewEventType.consoleLog}', message }));
42
42
  };
43
43
  window.close = function() {
44
- window.ReactNativeWebView.postMessage(JSON.stringify({ type: '${WebViewEventType.newTabWindowClose}', data: '' }));
44
+ window.ReactNativeWebView.postMessage(JSON.stringify({ type: '${WebViewEventType.closeExternalUrl}', data: '' }));
45
45
  };
46
46
  (function() {
47
47
  setTimeout(function() {
@@ -4,7 +4,11 @@ import {
4
4
  PaymentErrorType,
5
5
  } from '../interfaces';
6
6
  import { constants } from '../interfaces/constants';
7
- import { DownloadType, OnDownloadFile } from '../interfaces/types';
7
+ import {
8
+ DownloadType,
9
+ OnDownloadFile,
10
+ WidgetConfig,
11
+ } from '../interfaces/types';
8
12
  import {
9
13
  PaymentWidgetCallbacks,
10
14
  NextActionWidgetCallbacks,
@@ -18,8 +22,11 @@ type Callbacks = PaymentWidgetCallbacks &
18
22
  OnDownloadFile;
19
23
 
20
24
  export class PaymentWidgetController extends DeunaWebViewController {
21
- constructor(readonly callbacks: Callbacks) {
22
- super();
25
+ constructor(
26
+ readonly callbacks: Callbacks,
27
+ readonly widgetConfig: WidgetConfig
28
+ ) {
29
+ super(widgetConfig);
23
30
  }
24
31
 
25
32
  onError = (event: any) => {
@@ -78,11 +85,11 @@ export class PaymentWidgetController extends DeunaWebViewController {
78
85
  this.callbacks.onPaymentProcessing?.();
79
86
  },
80
87
  [CheckoutEventType.purchase]: () => {
81
- this.delegate?.onCloseSubWebView?.();
88
+ this.delegate?.onCloseExternalUrl?.();
82
89
  this.callbacks.onSuccess?.(checkoutEvent.data.order);
83
90
  },
84
91
  [CheckoutEventType.purchaseError]: () => {
85
- this.delegate?.onCloseSubWebView?.();
92
+ this.delegate?.onCloseExternalUrl?.();
86
93
  const { metadata } = checkoutEvent.data;
87
94
 
88
95
  if (metadata) {
@@ -17,14 +17,14 @@ export class Completer<T> {
17
17
 
18
18
  complete(value: T): void {
19
19
  if (this.completed) return;
20
- this.completed = true;
21
20
  this.resolvePromise?.(value);
21
+ this.completed = true;
22
22
  }
23
23
 
24
24
  completeError(error: any): void {
25
25
  if (this.completed) return;
26
- this.completed = true;
27
26
  this.rejectPromise?.(error);
27
+ this.completed = true;
28
28
  }
29
29
 
30
30
  isCompleted(): boolean {
@@ -0,0 +1,49 @@
1
+ import * as WebBrowser from 'expo-web-browser';
2
+ import { Platform } from 'react-native';
3
+
4
+ /**
5
+ * This class is used to open a browser in a SafariView Controller (iOS) or
6
+ * a Chrome Custom Tab (Android) depending on the platform.
7
+ *
8
+ * It is used to open the external URLs in the DEUNA widget.
9
+ */
10
+ class CrossPlatformBrowser {
11
+ private initialized = false;
12
+ /**
13
+ * Initialize the browser
14
+ */
15
+ async initialize() {
16
+ if (this.initialized) return;
17
+ await WebBrowser.maybeCompleteAuthSession();
18
+ this.initialized = true;
19
+ }
20
+
21
+ /**
22
+ * Open an URL in a SafariView Controller (iOS) or a Chrome Custom Tab (Android) depending on the platform
23
+ * @param url - The URL to open
24
+ */
25
+ async openBrowser(url: string) {
26
+ try {
27
+ await this.initialize();
28
+ await WebBrowser.openBrowserAsync(url);
29
+ } catch (error) {
30
+ console.error('Error opening browser', error);
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Close the browser
36
+ */
37
+ async closeBrowser() {
38
+ if (Platform.OS !== 'ios') {
39
+ return;
40
+ }
41
+ try {
42
+ await WebBrowser.dismissBrowser();
43
+ } catch (error) {
44
+ console.error('Error closing browser', error);
45
+ }
46
+ }
47
+ }
48
+
49
+ export const crossPlatformBrowser = new CrossPlatformBrowser();
@@ -0,0 +1,118 @@
1
+ import { AppState, Platform } from 'react-native';
2
+ import { WebViewDelegate } from '../controllers/BaseWebViewController';
3
+ import { ExternalUrlController } from '../controllers/ExternalUrlController';
4
+ import { Completer } from './Completer';
5
+ import { crossPlatformBrowser } from './CrossPlatformBrowser';
6
+
7
+ export enum ExternalUrlBrowser {
8
+ WEB_VIEW = 'web_view',
9
+ CROSS_PLATFORM_BROWSER = 'cross_platform_browser',
10
+ }
11
+
12
+ interface OpenUrlParams {
13
+ url: string;
14
+ browser: ExternalUrlBrowser;
15
+ delegate: WebViewDelegate;
16
+ }
17
+
18
+ interface ExternalUrlManager<T extends ExternalUrlBrowser> {
19
+ type: T;
20
+ }
21
+
22
+ export interface WebViewManager
23
+ extends ExternalUrlManager<ExternalUrlBrowser.WEB_VIEW> {
24
+ controller: ExternalUrlController;
25
+ }
26
+
27
+ export interface CrossPlatformBrowserManager
28
+ extends ExternalUrlManager<ExternalUrlBrowser.CROSS_PLATFORM_BROWSER> {
29
+ type: ExternalUrlBrowser.CROSS_PLATFORM_BROWSER;
30
+ }
31
+
32
+ export class ExternalUrlHelper {
33
+ private manager: ExternalUrlManager<ExternalUrlBrowser> | null = null;
34
+ private completer: Completer<void> | null = null;
35
+
36
+ constructor() {
37
+ this.startCloseChecker();
38
+ }
39
+
40
+ get externalUrlWebViewController() {
41
+ if (this.manager?.type === ExternalUrlBrowser.WEB_VIEW) {
42
+ return (this.manager as WebViewManager).controller;
43
+ }
44
+ return null;
45
+ }
46
+
47
+ /**
48
+ * Wait until the webview or cross platform browser is closed
49
+ */
50
+ async waitForClose() {
51
+ if (this.completer) {
52
+ await this.completer.wait;
53
+ this.completer = null;
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Listen when the app is brought back to the foreground
59
+ */
60
+ private startCloseChecker() {
61
+ AppState.addEventListener('change', (state) => {
62
+ if (
63
+ state === 'active' &&
64
+ this.manager?.type === ExternalUrlBrowser.CROSS_PLATFORM_BROWSER
65
+ ) {
66
+ this.manager = null;
67
+ this.completeClose();
68
+ }
69
+ });
70
+ }
71
+
72
+ /**
73
+ * Opens the webview or cross platform browser
74
+ */
75
+ async openUrl(params: OpenUrlParams) {
76
+ const { url, browser, delegate } = params;
77
+
78
+ const mapper = {
79
+ [ExternalUrlBrowser.WEB_VIEW]: async () => {
80
+ const controller = new ExternalUrlController(url);
81
+ controller.delegate = delegate;
82
+ this.manager = {
83
+ type: ExternalUrlBrowser.WEB_VIEW,
84
+ controller,
85
+ } as WebViewManager;
86
+ },
87
+ [ExternalUrlBrowser.CROSS_PLATFORM_BROWSER]: async () => {
88
+ this.completer = new Completer<void>();
89
+ this.manager = {
90
+ type: ExternalUrlBrowser.CROSS_PLATFORM_BROWSER,
91
+ };
92
+
93
+ await crossPlatformBrowser.openBrowser(url);
94
+ if (Platform.OS === 'ios') {
95
+ this.completer?.complete();
96
+ }
97
+ },
98
+ };
99
+ await mapper[browser]();
100
+ }
101
+
102
+ /**
103
+ * Notify that the browser has been closed
104
+ */
105
+ private completeClose() {
106
+ this.completer?.complete();
107
+ }
108
+
109
+ /**
110
+ * Closes the web view
111
+ */
112
+ async closeWebView() {
113
+ if (this.manager?.type === ExternalUrlBrowser.WEB_VIEW) {
114
+ (this.manager as WebViewManager).controller?.dispose();
115
+ this.manager = null;
116
+ }
117
+ }
118
+ }
@@ -0,0 +1,67 @@
1
+ import { DeunaSDK } from '../DeunaSDK';
2
+ import { Mode } from '../interfaces';
3
+ import { Environment, SubmitResult } from '../types';
4
+
5
+ export type SubmitStrategyType = 'PayPal';
6
+
7
+ export type SubmitStrategyConfig = {
8
+ publicApiKey: string;
9
+ environment: Environment;
10
+ orderToken?: string;
11
+ userToken?: string;
12
+ };
13
+
14
+ export abstract class SubmitStrategy {
15
+ abstract deunaSDK: DeunaSDK;
16
+ constructor(
17
+ readonly config: SubmitStrategyConfig,
18
+ readonly type: SubmitStrategyType
19
+ ) {}
20
+
21
+ abstract submit(): Promise<SubmitResult>;
22
+ }
23
+
24
+ export class PayPalSubmitStrategy extends SubmitStrategy {
25
+ deunaSDK: DeunaSDK;
26
+
27
+ constructor(
28
+ config: SubmitStrategyConfig,
29
+ readonly onDestroyed: () => void
30
+ ) {
31
+ super(config, 'PayPal');
32
+ this.deunaSDK = new DeunaSDK(
33
+ {
34
+ publicApiKey: this.config.publicApiKey,
35
+ environment: this.config.environment,
36
+ },
37
+ this.onDestroyed
38
+ );
39
+ }
40
+
41
+ async submit(): Promise<SubmitResult> {
42
+ await this.deunaSDK.initPaymentWidget({
43
+ mode: Mode.MODAL,
44
+ orderToken: this.config.orderToken ?? '',
45
+ userToken: this.config.userToken,
46
+ paymentMethods: [
47
+ {
48
+ paymentMethod: 'wallet',
49
+ processors: ['paypal_wallet'],
50
+ configuration: {
51
+ express: true,
52
+ flowType: {
53
+ type: 'twoStep',
54
+ },
55
+ },
56
+ },
57
+ ],
58
+ callbacks: {},
59
+ });
60
+
61
+ return {
62
+ status: 'success',
63
+ code: 'success',
64
+ message: 'Payment submitted successfully',
65
+ };
66
+ }
67
+ }
@@ -0,0 +1,45 @@
1
+ import {
2
+ BaseWebViewController,
3
+ WebViewDelegate,
4
+ } from '../controllers/BaseWebViewController';
5
+ import { Mode } from '../interfaces';
6
+
7
+ export class WebViewManager<
8
+ T extends BaseWebViewController = BaseWebViewController,
9
+ > {
10
+ private _controller: T | null = null;
11
+ private _mode: Mode | null = null;
12
+
13
+ initialize = (params: {
14
+ controller: T;
15
+ delegate: WebViewDelegate;
16
+ mode: Mode;
17
+ }): void => {
18
+ this._controller = params.controller;
19
+ this._controller.delegate = params.delegate;
20
+ this._mode = params.mode;
21
+ };
22
+
23
+ get isInitialized(): boolean {
24
+ return !!this._controller;
25
+ }
26
+
27
+ get mode(): Mode | null {
28
+ return this._mode;
29
+ }
30
+
31
+ get controller(): T {
32
+ if (!this._controller) {
33
+ throw new Error('WebView not initialized');
34
+ }
35
+ return this._controller;
36
+ }
37
+
38
+ /**
39
+ * Destroy the webview and the modal (if it exists)
40
+ */
41
+ destroy = () => {
42
+ this._controller?.dispose();
43
+ this._controller = null;
44
+ };
45
+ }
@@ -34,7 +34,7 @@ type ControllerProps =
34
34
  | VoucherWidgetControllerProps
35
35
  | ElementsWidgetControllerProps;
36
36
 
37
- export const getWidgetController = (
37
+ export const buildDeunaWidgetController = (
38
38
  config: InitializeParams,
39
39
  props: ControllerProps & { mode?: Mode; sessionId?: string }
40
40
  ): DeunaWebViewController => {
@@ -47,7 +47,8 @@ export const getWidgetController = (
47
47
  userToken: rest.userToken ?? '',
48
48
  language: rest.language ?? 'es',
49
49
  sessionId: rest.sessionId ?? '',
50
- mode: mode ?? Mode.MODAL,
50
+ mode: mode === Mode.MODAL ? 'modal' : 'target',
51
+ ...(rest.domain && { domain: rest.domain }),
51
52
  };
52
53
 
53
54
  const widgetMappers = {
@@ -69,10 +70,16 @@ export const getWidgetController = (
69
70
  voucher: () => baseParams,
70
71
  };
71
72
 
73
+ const widgetConfig = {
74
+ orderToken: rest.orderToken,
75
+ behavior: rest.behavior,
76
+ userToken: rest.userToken,
77
+ };
78
+
72
79
  const controller =
73
80
  widget === 'elements'
74
- ? new ElementsWidgetController(callbacks)
75
- : new PaymentWidgetController(callbacks);
81
+ ? new ElementsWidgetController(callbacks, widgetConfig)
82
+ : new PaymentWidgetController(callbacks, widgetConfig);
76
83
 
77
84
  controller.url = linkBuilders[widget](widgetMappers[widget]());
78
85
  controller.hidePayButton = rest.hidePayButton ?? false;
@@ -0,0 +1,42 @@
1
+ import { DeunaSDK } from '../DeunaSDK';
2
+ import { TWO_STEP_FLOW } from '../interfaces/constants';
3
+ import { PayPalSubmitStrategy, SubmitStrategy } from './SubmitStrategy';
4
+
5
+ export const getSubmitStrategy = async (
6
+ deunaSDK: DeunaSDK
7
+ ): Promise<SubmitStrategy | null> => {
8
+ const selectedPaymentMethod = await deunaSDK.getSelectedPaymentMethod();
9
+ if (!selectedPaymentMethod) {
10
+ return null;
11
+ }
12
+
13
+ const { widgetConfig } = deunaSDK.deunaWidgetManager.controller;
14
+
15
+ const processorName = selectedPaymentMethod.processor_name as string;
16
+
17
+ const configFlowType = selectedPaymentMethod.configuration?.flowType?.type;
18
+ const behaviorFlowType =
19
+ widgetConfig.behavior?.paymentMethods?.flowType?.type;
20
+
21
+ const isTwoStepFlow =
22
+ configFlowType === TWO_STEP_FLOW ||
23
+ (!configFlowType && behaviorFlowType === TWO_STEP_FLOW);
24
+
25
+ if (isTwoStepFlow && processorName === 'paypal_wallet') {
26
+ return new PayPalSubmitStrategy(
27
+ {
28
+ publicApiKey: deunaSDK.config.publicApiKey,
29
+ environment: deunaSDK.config.environment ?? 'production',
30
+ orderToken: widgetConfig.orderToken,
31
+ userToken: widgetConfig.userToken,
32
+ },
33
+ () => {
34
+ if (deunaSDK.submitStrategy) {
35
+ deunaSDK.submitStrategy = null;
36
+ deunaSDK.notifyListeners();
37
+ }
38
+ }
39
+ );
40
+ }
41
+ return null;
42
+ };
@@ -4,5 +4,9 @@ export const constants = {
4
4
  voucherPdfDownloadUrl: 'voucherPdfDownloadUrl',
5
5
  };
6
6
 
7
+ export const DEVICE_FINGERPRINT_URL =
8
+ 'https://cdn.stg.deuna.io/mobile-sdks/get_fraud_id.html';
7
9
 
8
- export const DEVICE_FINGERPRINT_URL = 'https://cdn.stg.deuna.io/mobile-sdks/get_fraud_id.html';
10
+ export const TWO_STEP_FLOW = 'twoStep';
11
+
12
+ export const DOMAINS_MUST_BE_USE_CROSS_PLATFORM_BROWSER = ['mercadopago.com'];
@@ -1,3 +1,11 @@
1
+ import { BehaviorWidget } from '../types';
2
+
3
+ export interface WidgetConfig {
4
+ behavior?: BehaviorWidget;
5
+ orderToken?: string;
6
+ userToken?: string;
7
+ }
8
+
1
9
  export enum Mode {
2
10
  MODAL = 'modal',
3
11
  EMBEDDED = 'embedded',
@@ -1,9 +1,10 @@
1
1
  import { Environment } from "../base";
2
2
  import { hasKey } from "../utils/hasKey";
3
3
  import { getIntegrationType, UrlConfig } from "./urlConfig";
4
+ import { PLATFORM_DEFAULT } from "./constants";
4
5
 
5
6
  const ELEMENTS_URLS: Record<Environment, string> = {
6
- production: "https://elements.deuna.io",
7
+ production: "https://elements.deuna.com",
7
8
  sandbox: "https://elements.sandbox.deuna.io",
8
9
  staging: "https://elements.stg.deuna.io",
9
10
  develop: "https://elements.dev.deuna.io",
@@ -28,6 +29,7 @@ interface SearchParams {
28
29
  userToken?: string;
29
30
  showSavedCardsFlow?: string;
30
31
  showDefaultCardFlow?: string;
32
+ platform?: string;
31
33
  }
32
34
 
33
35
  /**
@@ -50,6 +52,7 @@ const buildSearchParams = (config: UrlConfig): URLSearchParams => {
50
52
  const searchParams: SearchParams = {
51
53
  publicApiKey: config.publicApiKey,
52
54
  orderToken: config.orderToken,
55
+ platform: config.platform || PLATFORM_DEFAULT,
53
56
  email: userInfo?.email || "",
54
57
  firstName: userInfo?.firstName || "",
55
58
  lastName: userInfo?.lastName || "",
@@ -10,6 +10,7 @@ import {
10
10
  getIntegrationType,
11
11
  UrlConfig,
12
12
  } from "./urlConfig";
13
+ import { PLATFORM_DEFAULT } from "./constants";
13
14
 
14
15
  const urls: Record<Environment, string> = {
15
16
  production: "https://pay.deuna.io",
@@ -31,6 +32,7 @@ export const buildPaymentLink = (config: UrlConfig): string => {
31
32
  mode: "widget",
32
33
  int: getIntegrationType(config.mode),
33
34
  language: config.language,
35
+ platform: config.platform || PLATFORM_DEFAULT,
34
36
  });
35
37
 
36
38
  // config.
@@ -5,6 +5,7 @@
5
5
  */
6
6
 
7
7
  import { Environment } from "../base";
8
+ import { PLATFORM_DEFAULT } from "./constants";
8
9
  import { getIntegrationType, UrlConfig } from "./urlConfig";
9
10
 
10
11
  const urls: Record<Environment, string> = {
@@ -28,6 +29,7 @@ export const buildVoucherLink = (config: UrlConfig): string => {
28
29
  int: getIntegrationType(config.mode),
29
30
  language: config.language,
30
31
  orderToken,
32
+ platform: config.platform || PLATFORM_DEFAULT,
31
33
  });
32
34
 
33
35
  const xpropsB64 = {