@savers_app/react-native-sandbox-sdk 1.2.6 → 1.2.8

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 (36) hide show
  1. package/README.md +22 -151
  2. package/lib/module/core/runtime.js +14 -0
  3. package/lib/module/core/runtime.js.map +1 -1
  4. package/lib/module/index.js +2 -0
  5. package/lib/module/index.js.map +1 -1
  6. package/lib/module/services/url/urlGenerator.js +20 -6
  7. package/lib/module/services/url/urlGenerator.js.map +1 -1
  8. package/lib/module/services/webview/DualWebViewBridgeController.js +267 -0
  9. package/lib/module/services/webview/DualWebViewBridgeController.js.map +1 -0
  10. package/lib/module/services/webview/dualWebViewBridge.types.js +19 -0
  11. package/lib/module/services/webview/dualWebViewBridge.types.js.map +1 -0
  12. package/lib/module/utils/config.js +2 -0
  13. package/lib/module/utils/config.js.map +1 -1
  14. package/lib/typescript/src/core/runtime.d.ts +4 -0
  15. package/lib/typescript/src/core/runtime.d.ts.map +1 -1
  16. package/lib/typescript/src/index.d.ts +2 -0
  17. package/lib/typescript/src/index.d.ts.map +1 -1
  18. package/lib/typescript/src/services/url/urlGenerator.d.ts +0 -1
  19. package/lib/typescript/src/services/url/urlGenerator.d.ts.map +1 -1
  20. package/lib/typescript/src/services/webview/DualWebViewBridgeController.d.ts +15 -0
  21. package/lib/typescript/src/services/webview/DualWebViewBridgeController.d.ts.map +1 -0
  22. package/lib/typescript/src/services/webview/dualWebViewBridge.types.d.ts +20 -0
  23. package/lib/typescript/src/services/webview/dualWebViewBridge.types.d.ts.map +1 -0
  24. package/lib/typescript/src/utils/config.d.ts.map +1 -1
  25. package/package.json +17 -9
  26. package/src/core/runtime.ts +21 -0
  27. package/src/index.tsx +2 -0
  28. package/src/services/url/urlGenerator.ts +18 -7
  29. package/src/services/webview/DualWebViewBridgeController.tsx +330 -0
  30. package/src/services/webview/dualWebViewBridge.types.ts +32 -0
  31. package/src/utils/config.ts +1 -0
  32. package/lib/module/services/device/location.js +0 -47
  33. package/lib/module/services/device/location.js.map +0 -1
  34. package/lib/typescript/src/services/device/location.d.ts +0 -8
  35. package/lib/typescript/src/services/device/location.d.ts.map +0 -1
  36. package/src/services/device/location.ts +0 -51
@@ -0,0 +1,267 @@
1
+ "use strict";
2
+
3
+ import React, { useCallback, useMemo, useRef, useState } from 'react';
4
+ import { Image, StyleSheet, TouchableOpacity, View } from 'react-native';
5
+ import WebView from 'react-native-webview';
6
+ import { parseDualWebViewEnvelope } from "./dualWebViewBridge.types.js";
7
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
8
+ const DEFAULT_TRAVEL_PORTAL_URL = 'https://sandbox.travelercashback.com';
9
+
10
+ /** Must match `applicationNameForUserAgent` on clo-app WebViews (DualWebViewBridgeController). */
11
+ const WEBVIEW_UA_MARKER = 'CloAppWebView';
12
+
13
+ /** Legacy / Hub: open travel from first WebView without JSON envelope. */
14
+ const TRAVEL_DEEP_LINK = 'saversapp://travel';
15
+ const LEGACY_OPEN_TRAVEL_ACTIONS = ['OPEN_TRAVEL_PORTAL', 'OPEN_TRAVEL'];
16
+ function buildInjectNativeBridgeScript(detail) {
17
+ return `
18
+ (function(){
19
+ var detail = ${JSON.stringify(detail)};
20
+ window.dispatchEvent(new CustomEvent('nativeBridge', { detail: detail }));
21
+ true;
22
+ })();
23
+ `;
24
+ }
25
+ export const DualWebViewBridgeController = ({
26
+ saversAppUrl,
27
+ travelPortalUrl = DEFAULT_TRAVEL_PORTAL_URL,
28
+ partnerLogo,
29
+ renderTravelBackIcon,
30
+ initialSurface = 'savers',
31
+ onSaversSdkMessage,
32
+ onLoadEndSavers,
33
+ onLoadEndTravel
34
+ }) => {
35
+ const saversRef = useRef(null);
36
+ const travelRef = useRef(null);
37
+ const [surface, setSurface] = useState(initialSurface);
38
+ const [saversKey, setSaversKey] = useState(0);
39
+ const [travelKey, setTravelKey] = useState(0);
40
+ const openTravel = useCallback(() => {
41
+ setSurface('travel');
42
+ setTravelKey(k => k + 1);
43
+ }, []);
44
+ const backToSavers = useCallback(() => {
45
+ setSurface('savers');
46
+ setSaversKey(k => k + 1);
47
+ }, []);
48
+ const relayTo = useCallback((target, payload, from) => {
49
+ const detail = {
50
+ bridge: 'dual-webview',
51
+ from,
52
+ action: 'relay',
53
+ payload
54
+ };
55
+ const ref = target === 'savers' ? saversRef : travelRef;
56
+ ref.current?.injectJavaScript(buildInjectNativeBridgeScript(detail));
57
+ }, []);
58
+ const handleBridgeEnvelope = useCallback(env => {
59
+ switch (env.action) {
60
+ case 'open_travel':
61
+ openTravel();
62
+ return true;
63
+ case 'close_travel':
64
+ backToSavers();
65
+ return true;
66
+ case 'relay':
67
+ if (env.to === 'savers') {
68
+ relayTo('savers', env.payload, env.from);
69
+ return true;
70
+ }
71
+ if (env.to === 'travel') {
72
+ relayTo('travel', env.payload, env.from);
73
+ return true;
74
+ }
75
+ return false;
76
+ default:
77
+ return false;
78
+ }
79
+ }, [backToSavers, openTravel, relayTo]);
80
+ const tryLegacyOpenTravel = useCallback(raw => {
81
+ if (!raw || typeof raw !== 'string') {
82
+ return false;
83
+ }
84
+ try {
85
+ const data = JSON.parse(raw);
86
+ if (data?.action === 'OPEN_TRAVEL_PORTAL' || data?.type === 'OPEN_TRAVEL' || data?.openTravel === true) {
87
+ openTravel();
88
+ return true;
89
+ }
90
+ } catch {
91
+ /* ignore */
92
+ }
93
+ if (LEGACY_OPEN_TRAVEL_ACTIONS.some(s => raw.includes(s))) {
94
+ openTravel();
95
+ return true;
96
+ }
97
+ return false;
98
+ }, [openTravel]);
99
+ const onMessageSavers = useCallback(e => {
100
+ const raw = e?.nativeEvent?.data ?? '';
101
+ const env = parseDualWebViewEnvelope(raw);
102
+ if (env && env.from === 'savers' && handleBridgeEnvelope(env)) {
103
+ return;
104
+ }
105
+ if (tryLegacyOpenTravel(raw)) {
106
+ return;
107
+ }
108
+ const postBack = data => {
109
+ const detail = {
110
+ bridge: 'dual-webview',
111
+ from: 'native',
112
+ action: 'relay',
113
+ payload: data
114
+ };
115
+ saversRef.current?.injectJavaScript(buildInjectNativeBridgeScript(detail));
116
+ };
117
+ onSaversSdkMessage?.(raw, postBack);
118
+ }, [handleBridgeEnvelope, onSaversSdkMessage, tryLegacyOpenTravel]);
119
+ const onMessageTravel = useCallback(e => {
120
+ const raw = e?.nativeEvent?.data ?? '';
121
+ const env = parseDualWebViewEnvelope(raw);
122
+ if (env && env.from === 'travel' && handleBridgeEnvelope(env)) {
123
+ return;
124
+ }
125
+ if (tryLegacyOpenTravel(raw)) {
126
+ return;
127
+ }
128
+ }, [handleBridgeEnvelope, tryLegacyOpenTravel]);
129
+ const onShouldStartLoadSavers = useCallback(request => {
130
+ const {
131
+ url
132
+ } = request;
133
+ if (url.startsWith(TRAVEL_DEEP_LINK) || url === 'saversapp://travel/') {
134
+ openTravel();
135
+ return false;
136
+ }
137
+ return true;
138
+ }, [openTravel]);
139
+ const travelSource = useMemo(() => ({
140
+ uri: travelPortalUrl
141
+ }), [travelPortalUrl]);
142
+ const saversSource = useMemo(() => ({
143
+ uri: saversAppUrl
144
+ }), [saversAppUrl]);
145
+ return /*#__PURE__*/_jsxs(View, {
146
+ style: styles.root,
147
+ children: [surface === 'savers' ? /*#__PURE__*/_jsx(WebView, {
148
+ ref: saversRef,
149
+ source: saversSource,
150
+ originWhitelist: ['*'],
151
+ applicationNameForUserAgent: WEBVIEW_UA_MARKER,
152
+ onMessage: onMessageSavers,
153
+ onShouldStartLoadWithRequest: onShouldStartLoadSavers,
154
+ onLoadEnd: onLoadEndSavers,
155
+ style: styles.webview,
156
+ sharedCookiesEnabled: true,
157
+ incognito: false,
158
+ webviewDebuggingEnabled: __DEV__
159
+ }, saversKey) : null, surface === 'travel' ? /*#__PURE__*/_jsxs(View, {
160
+ style: styles.travelColumn,
161
+ children: [/*#__PURE__*/_jsxs(View, {
162
+ style: styles.header,
163
+ children: [/*#__PURE__*/_jsx(TouchableOpacity, {
164
+ onPress: backToSavers,
165
+ hitSlop: 16,
166
+ accessibilityRole: "button",
167
+ accessibilityLabel: "Back",
168
+ style: styles.backButton,
169
+ children: renderTravelBackIcon ? renderTravelBackIcon() : /*#__PURE__*/_jsxs(View, {
170
+ style: styles.backIconWrapper,
171
+ children: [/*#__PURE__*/_jsx(View, {
172
+ style: styles.backIconShaft
173
+ }), /*#__PURE__*/_jsx(View, {
174
+ style: styles.backIconHead
175
+ })]
176
+ })
177
+ }), /*#__PURE__*/_jsx(View, {
178
+ style: styles.headerSpacer
179
+ }), partnerLogo ? /*#__PURE__*/_jsx(Image, {
180
+ source: {
181
+ uri: partnerLogo
182
+ },
183
+ resizeMode: "contain",
184
+ style: styles.logo
185
+ }) : /*#__PURE__*/_jsx(View, {
186
+ style: styles.logoPlaceholder
187
+ })]
188
+ }), /*#__PURE__*/_jsx(WebView, {
189
+ ref: travelRef,
190
+ source: travelSource,
191
+ originWhitelist: ['*'],
192
+ applicationNameForUserAgent: WEBVIEW_UA_MARKER,
193
+ onMessage: onMessageTravel,
194
+ onLoadEnd: onLoadEndTravel,
195
+ style: styles.webview,
196
+ sharedCookiesEnabled: true,
197
+ incognito: false,
198
+ webviewDebuggingEnabled: __DEV__
199
+ }, travelKey)]
200
+ }) : null]
201
+ });
202
+ };
203
+ const styles = StyleSheet.create({
204
+ root: {
205
+ flex: 1
206
+ },
207
+ travelColumn: {
208
+ flex: 1
209
+ },
210
+ webview: {
211
+ flex: 1
212
+ },
213
+ header: {
214
+ flexDirection: 'row',
215
+ alignItems: 'center',
216
+ backgroundColor: '#fff',
217
+ paddingHorizontal: 16,
218
+ paddingTop: 12,
219
+ paddingBottom: 12,
220
+ borderBottomWidth: StyleSheet.hairlineWidth,
221
+ borderBottomColor: '#E5E5E5'
222
+ },
223
+ backButton: {
224
+ width: 32,
225
+ height: 32,
226
+ justifyContent: 'center',
227
+ alignItems: 'center'
228
+ },
229
+ backIconWrapper: {
230
+ width: 20,
231
+ height: 16,
232
+ justifyContent: 'center',
233
+ position: 'relative'
234
+ },
235
+ backIconShaft: {
236
+ position: 'absolute',
237
+ left: 6,
238
+ right: 0,
239
+ height: 2,
240
+ backgroundColor: '#111827',
241
+ borderRadius: 1
242
+ },
243
+ backIconHead: {
244
+ width: 10,
245
+ height: 10,
246
+ borderLeftWidth: 2,
247
+ borderBottomWidth: 2,
248
+ borderColor: '#111827',
249
+ transform: [{
250
+ rotate: '45deg'
251
+ }],
252
+ marginLeft: 1
253
+ },
254
+ headerSpacer: {
255
+ flex: 1
256
+ },
257
+ logo: {
258
+ width: 120,
259
+ height: 32
260
+ },
261
+ logoPlaceholder: {
262
+ width: 120,
263
+ height: 32
264
+ }
265
+ });
266
+ export default DualWebViewBridgeController;
267
+ //# sourceMappingURL=DualWebViewBridgeController.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["React","useCallback","useMemo","useRef","useState","Image","StyleSheet","TouchableOpacity","View","WebView","parseDualWebViewEnvelope","jsx","_jsx","jsxs","_jsxs","DEFAULT_TRAVEL_PORTAL_URL","WEBVIEW_UA_MARKER","TRAVEL_DEEP_LINK","LEGACY_OPEN_TRAVEL_ACTIONS","buildInjectNativeBridgeScript","detail","JSON","stringify","DualWebViewBridgeController","saversAppUrl","travelPortalUrl","partnerLogo","renderTravelBackIcon","initialSurface","onSaversSdkMessage","onLoadEndSavers","onLoadEndTravel","saversRef","travelRef","surface","setSurface","saversKey","setSaversKey","travelKey","setTravelKey","openTravel","k","backToSavers","relayTo","target","payload","from","bridge","action","ref","current","injectJavaScript","handleBridgeEnvelope","env","to","tryLegacyOpenTravel","raw","data","parse","type","some","s","includes","onMessageSavers","e","nativeEvent","postBack","onMessageTravel","onShouldStartLoadSavers","request","url","startsWith","travelSource","uri","saversSource","style","styles","root","children","source","originWhitelist","applicationNameForUserAgent","onMessage","onShouldStartLoadWithRequest","onLoadEnd","webview","sharedCookiesEnabled","incognito","webviewDebuggingEnabled","__DEV__","travelColumn","header","onPress","hitSlop","accessibilityRole","accessibilityLabel","backButton","backIconWrapper","backIconShaft","backIconHead","headerSpacer","resizeMode","logo","logoPlaceholder","create","flex","flexDirection","alignItems","backgroundColor","paddingHorizontal","paddingTop","paddingBottom","borderBottomWidth","hairlineWidth","borderBottomColor","width","height","justifyContent","position","left","right","borderRadius","borderLeftWidth","borderColor","transform","rotate","marginLeft"],"sourceRoot":"../../../../src","sources":["services/webview/DualWebViewBridgeController.tsx"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Messages posted from either WebView via ReactNativeWebView.postMessage(string).
5
+ * Pages should JSON.stringify envelopes before posting.
6
+ */
7
+
8
+ export function parseDualWebViewEnvelope(raw) {
9
+ try {
10
+ const o = JSON.parse(raw);
11
+ if (o?.bridge === 'dual-webview' && o?.from && o?.action) {
12
+ return o;
13
+ }
14
+ } catch {
15
+ /* not JSON */
16
+ }
17
+ return null;
18
+ }
19
+ //# sourceMappingURL=dualWebViewBridge.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["parseDualWebViewEnvelope","raw","o","JSON","parse","bridge","from","action"],"sourceRoot":"../../../../src","sources":["services/webview/dualWebViewBridge.types.ts"],"mappings":";;AAAA;AACA;AACA;AACA;;AAgBA,OAAO,SAASA,wBAAwBA,CACtCC,GAAW,EACuB;EAClC,IAAI;IACF,MAAMC,CAAC,GAAGC,IAAI,CAACC,KAAK,CAACH,GAAG,CAA8B;IACtD,IAAIC,CAAC,EAAEG,MAAM,KAAK,cAAc,IAAIH,CAAC,EAAEI,IAAI,IAAIJ,CAAC,EAAEK,MAAM,EAAE;MACxD,OAAOL,CAAC;IACV;EACF,CAAC,CAAC,MAAM;IACN;EAAA;EAEF,OAAO,IAAI;AACb","ignoreList":[]}
@@ -5,6 +5,8 @@
5
5
  // Default environment (build-time injected)
6
6
  const injectedEnv = 'sandbox';
7
7
  const DEFAULT_ENVIRONMENT = injectedEnv === 'production' ? 'production' : 'sandbox';
8
+
9
+ /** Hosted merchant web origins; aligned with `SaversSdkHostedEnvironment` via `setEnvironment` in SDK init. */
8
10
  const ENV_URLS = {
9
11
  sandbox: 'https://testm.saversapp.com/',
10
12
  production: 'https://m.saversapp.com/'
@@ -1 +1 @@
1
- {"version":3,"names":["injectedEnv","DEFAULT_ENVIRONMENT","ENV_URLS","sandbox","production","currentEnvironment","BASE_URL","setEnvironment","env","getEnvironment","getBaseUrl"],"sourceRoot":"../../../src","sources":["utils/config.ts"],"mappings":";;AAAA;;AAIA;AACA,MAAMA,WAAW,GAAG,SAAmB;AACvC,MAAMC,mBAAgC,GACpCD,WAAW,KAAK,YAAY,GAAG,YAAY,GAAG,SAAS;AAEzD,MAAME,QAAqC,GAAG;EAC5CC,OAAO,EAAE,8BAA8B;EACvCC,UAAU,EAAE;AACd,CAAC;;AAED;AACA,IAAIC,kBAA+B,GAAGJ,mBAAmB;;AAEzD;AACA,IAAIK,QAAgB,GAAGJ,QAAQ,CAACG,kBAAkB,CAAC;;AAEnD;AACA,OAAO,MAAME,cAAc,GAAIC,GAAgB,IAAK;EAClDH,kBAAkB,GAAGG,GAAG;EACxBF,QAAQ,GAAGJ,QAAQ,CAACM,GAAG,CAAC;AAC1B,CAAC;;AAED;AACA,OAAO,MAAMC,cAAc,GAAGA,CAAA,KAAmB;EAC/C,OAAOJ,kBAAkB;AAC3B,CAAC;;AAED;AACA,OAAO,MAAMK,UAAU,GAAGA,CAAA,KAAc;EACtC,OAAOJ,QAAQ;AACjB,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["injectedEnv","DEFAULT_ENVIRONMENT","ENV_URLS","sandbox","production","currentEnvironment","BASE_URL","setEnvironment","env","getEnvironment","getBaseUrl"],"sourceRoot":"../../../src","sources":["utils/config.ts"],"mappings":";;AAAA;;AAIA;AACA,MAAMA,WAAW,GAAG,SAAmB;AACvC,MAAMC,mBAAgC,GACpCD,WAAW,KAAK,YAAY,GAAG,YAAY,GAAG,SAAS;;AAEzD;AACA,MAAME,QAAqC,GAAG;EAC5CC,OAAO,EAAE,8BAA8B;EACvCC,UAAU,EAAE;AACd,CAAC;;AAED;AACA,IAAIC,kBAA+B,GAAGJ,mBAAmB;;AAEzD;AACA,IAAIK,QAAgB,GAAGJ,QAAQ,CAACG,kBAAkB,CAAC;;AAEnD;AACA,OAAO,MAAME,cAAc,GAAIC,GAAgB,IAAK;EAClDH,kBAAkB,GAAGG,GAAG;EACxBF,QAAQ,GAAGJ,QAAQ,CAACM,GAAG,CAAC;AAC1B,CAAC;;AAED;AACA,OAAO,MAAMC,cAAc,GAAGA,CAAA,KAAmB;EAC/C,OAAOJ,kBAAkB;AAC3B,CAAC;;AAED;AACA,OAAO,MAAMK,UAAU,GAAGA,CAAA,KAAc;EACtC,OAAOJ,QAAQ;AACjB,CAAC","ignoreList":[]}
@@ -1,9 +1,13 @@
1
+ /** Controls which hosted merchant web origin `generateUrl` uses. */
2
+ export type SaversSdkHostedEnvironment = 'SANDBOX' | 'PROD';
1
3
  export declare function initializeSDK(providedKeys: {
2
4
  apiKey: string;
3
5
  encryptionKey: string;
4
6
  pRefCode: string;
5
7
  authMode: string;
6
8
  navigationRef: any;
9
+ /** When set, `generateUrl` uses testm (SANDBOX) or m (PROD). When omitted, build-time default from config applies. */
10
+ environment?: SaversSdkHostedEnvironment;
7
11
  }): void;
8
12
  export declare const SaversAppSDK: {
9
13
  initialized: typeof initializeSDK;
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../../src/core/runtime.ts"],"names":[],"mappings":"AAiBA,wBAAgB,aAAa,CAAC,YAAY,EAAE;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,GAAG,CAAC;CACpB,QAwDA;AAED,eAAO,MAAM,YAAY;;CAExB,CAAC"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../../src/core/runtime.ts"],"names":[],"mappings":"AAoBA,oEAAoE;AACpE,MAAM,MAAM,0BAA0B,GAAG,SAAS,GAAG,MAAM,CAAC;AAE5D,wBAAgB,aAAa,CAAC,YAAY,EAAE;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,GAAG,CAAC;IACnB,sHAAsH;IACtH,WAAW,CAAC,EAAE,0BAA0B,CAAC;CAC1C,QAqEA;AAED,eAAO,MAAM,YAAY;;CAExB,CAAC"}
@@ -6,6 +6,8 @@ export * from './utils/errors';
6
6
  export * from './utils/logger';
7
7
  export * from './utils/validator';
8
8
  export * from './services/webview/messageHandler';
9
+ export * from './services/webview/DualWebViewBridgeController';
10
+ export * from './services/webview/dualWebViewBridge.types';
9
11
  export * from './data/storage/deviceIdManager';
10
12
  export * from './services/navigation/dialPad';
11
13
  export * from './services/navigation/openBrowser';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,cAAc,+BAA+B,CAAC;AAC9C,cAAc,wCAAwC,CAAC;AACvD,cAAc,0CAA0C,CAAC;AACzD,cAAc,yBAAyB,CAAC;AACxC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,mCAAmC,CAAC;AAClD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,mCAAmC,CAAC;AAClD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,gBAAgB,CAAC;AAC/B,cAAc,4BAA4B,CAAC;AAC3C,cAAc,gCAAgC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,cAAc,+BAA+B,CAAC;AAC9C,cAAc,wCAAwC,CAAC;AACvD,cAAc,0CAA0C,CAAC;AACzD,cAAc,yBAAyB,CAAC;AACxC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,mCAAmC,CAAC;AAClD,cAAc,gDAAgD,CAAC;AAC/D,cAAc,4CAA4C,CAAC;AAC3D,cAAc,gCAAgC,CAAC;AAC/C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,mCAAmC,CAAC;AAClD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,gBAAgB,CAAC;AAC/B,cAAc,4BAA4B,CAAC;AAC3C,cAAc,gCAAgC,CAAC"}
@@ -34,7 +34,6 @@ export type UrlInput = {
34
34
  authType?: AuthType;
35
35
  deviceInfo?: DeviceInfo;
36
36
  sessionId?: string;
37
- nonce: string;
38
37
  };
39
38
  export declare function validateInput(input: UrlInput): {
40
39
  ok: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"urlGenerator.d.ts","sourceRoot":"","sources":["../../../../../src/services/url/urlGenerator.ts"],"names":[],"mappings":"AAYA,MAAM,MAAM,SAAS,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AACvD,MAAM,MAAM,MAAM,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,SAAS,EAAE,CAAA;CAAE,CAAC;AACjE,MAAM,MAAM,OAAO,GAAG;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,GAAG,GAAG,GAAG,CAAC;IACf,EAAE,CAAC,EAAE,GAAG,GAAG,GAAG,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AACF,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,UAAU,CAAC;AACtD,MAAM,MAAM,UAAU,GAAG;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;CACzC,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AACF,wBAAgB,aAAa,CAAC,KAAK,EAAE,QAAQ,GAAG;IAC9C,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAoCA;AAED,wBAAsB,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAwDlE"}
1
+ {"version":3,"file":"urlGenerator.d.ts","sourceRoot":"","sources":["../../../../../src/services/url/urlGenerator.ts"],"names":[],"mappings":"AAaA,MAAM,MAAM,SAAS,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AACvD,MAAM,MAAM,MAAM,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,SAAS,EAAE,CAAA;CAAE,CAAC;AACjE,MAAM,MAAM,OAAO,GAAG;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,GAAG,GAAG,GAAG,CAAC;IACf,EAAE,CAAC,EAAE,GAAG,GAAG,GAAG,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AACF,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,UAAU,CAAC;AACtD,MAAM,MAAM,UAAU,GAAG;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;CACzC,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AACF,wBAAgB,aAAa,CAAC,KAAK,EAAE,QAAQ,GAAG;IAC9C,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAmCA;AAED,wBAAsB,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAoElE"}
@@ -0,0 +1,15 @@
1
+ import React from 'react';
2
+ export type Surface = 'savers' | 'travel';
3
+ export type DualWebViewBridgeControllerProps = {
4
+ saversAppUrl: string;
5
+ travelPortalUrl?: string;
6
+ partnerLogo?: string;
7
+ renderTravelBackIcon?: () => React.ReactNode;
8
+ initialSurface?: Surface;
9
+ onSaversSdkMessage?: (raw: string, postBack: (data: unknown) => void) => void;
10
+ onLoadEndSavers?: () => void;
11
+ onLoadEndTravel?: () => void;
12
+ };
13
+ export declare const DualWebViewBridgeController: React.FC<DualWebViewBridgeControllerProps>;
14
+ export default DualWebViewBridgeController;
15
+ //# sourceMappingURL=DualWebViewBridgeController.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DualWebViewBridgeController.d.ts","sourceRoot":"","sources":["../../../../../src/services/webview/DualWebViewBridgeController.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAiD,MAAM,OAAO,CAAC;AAiBtE,MAAM,MAAM,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE1C,MAAM,MAAM,gCAAgC,GAAG;IAC7C,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB,CAAC,EAAE,MAAM,KAAK,CAAC,SAAS,CAAC;IAC7C,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,kBAAkB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,KAAK,IAAI,CAAC;IAC9E,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;CAC9B,CAAC;AAcF,eAAO,MAAM,2BAA2B,EAAE,KAAK,CAAC,EAAE,CAChD,gCAAgC,CAoOjC,CAAC;AA0DF,eAAe,2BAA2B,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Messages posted from either WebView via ReactNativeWebView.postMessage(string).
3
+ * Pages should JSON.stringify envelopes before posting.
4
+ */
5
+ export type DualWebViewBridgeEnvelope = {
6
+ bridge: 'dual-webview';
7
+ /** Who sent this message (set by the page). */
8
+ from: 'savers' | 'travel';
9
+ /**
10
+ * relay — forward payload to the other WebView via injectJavaScript + CustomEvent.
11
+ * open_travel — show travel surface (reloads travel WebView).
12
+ * close_travel — same as native back (reloads Savers WebView).
13
+ */
14
+ action: 'relay' | 'open_travel' | 'close_travel';
15
+ /** For relay: which surface should receive the event. */
16
+ to?: 'savers' | 'travel';
17
+ payload?: unknown;
18
+ };
19
+ export declare function parseDualWebViewEnvelope(raw: string): DualWebViewBridgeEnvelope | null;
20
+ //# sourceMappingURL=dualWebViewBridge.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dualWebViewBridge.types.d.ts","sourceRoot":"","sources":["../../../../../src/services/webview/dualWebViewBridge.types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC,MAAM,EAAE,cAAc,CAAC;IACvB,+CAA+C;IAC/C,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC1B;;;;OAIG;IACH,MAAM,EAAE,OAAO,GAAG,aAAa,GAAG,cAAc,CAAC;IACjD,yDAAyD;IACzD,EAAE,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,MAAM,GACV,yBAAyB,GAAG,IAAI,CAUlC"}
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../../src/utils/config.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,YAAY,CAAC;AAmBnD,eAAO,MAAM,cAAc,GAAI,KAAK,WAAW,SAG9C,CAAC;AAGF,eAAO,MAAM,cAAc,QAAO,WAEjC,CAAC;AAGF,eAAO,MAAM,UAAU,QAAO,MAE7B,CAAC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../../src/utils/config.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,YAAY,CAAC;AAoBnD,eAAO,MAAM,cAAc,GAAI,KAAK,WAAW,SAG9C,CAAC;AAGF,eAAO,MAAM,cAAc,QAAO,WAEjC,CAAC;AAGF,eAAO,MAAM,UAAU,QAAO,MAE7B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@savers_app/react-native-sandbox-sdk",
3
- "version": "1.2.6",
3
+ "version": "1.2.8",
4
4
  "description": "Cross-platform React Native SDK exposing native features (maps, dial pad, browser), device ID/location and session utilities, a URL generator, and a WebView message bridge to trigger actions from web content.",
5
5
  "main": "./lib/module/index.js",
6
6
  "react-native": "./src/index.tsx",
@@ -33,14 +33,18 @@
33
33
  "!**/.*"
34
34
  ],
35
35
  "scripts": {
36
- "run:android": "yarn workspace @saversapp/react-native-sdk-example android",
37
- "run:ios": "yarn workspace @saversapp/react-native-sdk-example ios",
36
+ "example:android:yarn": "yarn workspace @saversapp/react-native-sdk-example android",
37
+ "example:ios:yarn": "yarn workspace @saversapp/react-native-sdk-example ios",
38
+ "example:android:npm": "npm run -w @saversapp/react-native-sdk-example android",
39
+ "example:ios:npm": "npm run -w @saversapp/react-native-sdk-example ios",
38
40
  "clean": "del-cli lib",
39
41
  "prepare": "bob build",
40
42
  "set:sandbox": "cross-env SDK_ENV=sandbox SDK_PACKAGE=@savers_app/react-native-sandbox-sdk node scripts/replace-config.js",
41
43
  "set:production": "cross-env SDK_ENV=production SDK_PACKAGE=@savers_app/react-native-sdk node scripts/replace-config.js",
42
- "prepare:sandbox": "yarn set:sandbox && yarn install && yarn prepare",
43
- "prepare:production": "yarn set:production && yarn install && yarn prepare",
44
+ "prepare:sandbox:yarn": "del-cli package-lock.json && cross-env PKG_MGR=yarn yarn set:sandbox && yarn install && yarn prepare",
45
+ "prepare:production:yarn": "del-cli package-lock.json && cross-env PKG_MGR=yarn yarn set:production && yarn install && yarn prepare",
46
+ "prepare:sandbox:npm": "cross-env PKG_MGR=npm npm run set:sandbox && npm install && npm run prepare && del-cli package-lock.json && cross-env PKG_MGR=yarn npm run set:sandbox",
47
+ "prepare:production:npm": "cross-env PKG_MGR=npm npm run set:production && npm install && npm run prepare && del-cli package-lock.json && cross-env PKG_MGR=yarn npm run set:production",
44
48
  "typecheck": "tsc",
45
49
  "lint": "eslint \"**/*.{js,ts,tsx}\"",
46
50
  "test": "jest",
@@ -62,8 +66,8 @@
62
66
  },
63
67
  "homepage": "https://github.com/pm-code/savers-react-native-sdk#readme",
64
68
  "publishConfig": {
65
- "access": "public",
66
- "registry": "https://registry.npmjs.org/"
69
+ "access": "public",
70
+ "registry": "https://registry.npmjs.org/"
67
71
  },
68
72
  "devDependencies": {
69
73
  "@commitlint/config-conventional": "^19.8.1",
@@ -84,11 +88,13 @@
84
88
  "eslint-config-prettier": "^10.1.8",
85
89
  "eslint-plugin-prettier": "^5.5.4",
86
90
  "jest": "^29.7.0",
87
- "lefthook-darwin-arm64": "^2.1.3",
91
+ "lefthook": "^2.1.4",
88
92
  "prettier": "^3.8.1",
89
93
  "react": "19.1.0",
90
94
  "react-native": "0.81.5",
91
95
  "react-native-builder-bob": "^0.40.13",
96
+ "react-native-safe-area-context": "^5.5.2",
97
+ "react-native-webview": "^13.16.0",
92
98
  "release-it": "^19.0.4",
93
99
  "typescript": "^5.9.2"
94
100
  },
@@ -100,7 +106,9 @@
100
106
  "react": "*",
101
107
  "react-native": "*",
102
108
  "react-native-aes-gcm-crypto": "*",
103
- "react-native-device-info": "*"
109
+ "react-native-device-info": "*",
110
+ "react-native-safe-area-context": "*",
111
+ "react-native-webview": "*"
104
112
  },
105
113
  "workspaces": [
106
114
  "example",
@@ -12,15 +12,23 @@ import {
12
12
  import { getDeviceId } from '../data/storage/deviceIdManager';
13
13
  import { setReactRef } from '../data/storage/reactRefManager';
14
14
 
15
+ // Hosted app base URL (test vs production merchant web)
16
+ import { setEnvironment } from '../utils/config';
17
+
15
18
  // Services
16
19
  // import { getDeviceLocation } from '../services/device/location';
17
20
 
21
+ /** Controls which hosted merchant web origin `generateUrl` uses. */
22
+ export type SaversSdkHostedEnvironment = 'SANDBOX' | 'PROD';
23
+
18
24
  export function initializeSDK(providedKeys: {
19
25
  apiKey: string;
20
26
  encryptionKey: string;
21
27
  pRefCode: string;
22
28
  authMode: string;
23
29
  navigationRef: any;
30
+ /** When set, `generateUrl` uses testm (SANDBOX) or m (PROD). When omitted, build-time default from config applies. */
31
+ environment?: SaversSdkHostedEnvironment;
24
32
  }) {
25
33
  const keys = providedKeys;
26
34
  const missing: string[] = [];
@@ -35,6 +43,19 @@ export function initializeSDK(providedKeys: {
35
43
  logger.warn(msg);
36
44
  throw new Error(msg);
37
45
  }
46
+
47
+ if (keys.environment !== undefined) {
48
+ if (keys.environment !== 'SANDBOX' && keys.environment !== 'PROD') {
49
+ throw new Error(
50
+ `[SDK] Invalid environment: ${String(keys.environment)}. Use 'SANDBOX' or 'PROD'.`
51
+ );
52
+ }
53
+ setEnvironment(keys.environment === 'SANDBOX' ? 'sandbox' : 'production');
54
+ logger.info('[SDK] Hosted app environment', {
55
+ environment: keys.environment,
56
+ });
57
+ }
58
+
38
59
  setApiKey(keys.apiKey);
39
60
  setEncryptionKey(keys.encryptionKey);
40
61
  setPRefCode(keys.pRefCode);
package/src/index.tsx CHANGED
@@ -6,6 +6,8 @@ export * from './utils/errors';
6
6
  export * from './utils/logger';
7
7
  export * from './utils/validator';
8
8
  export * from './services/webview/messageHandler';
9
+ export * from './services/webview/DualWebViewBridgeController';
10
+ export * from './services/webview/dualWebViewBridge.types';
9
11
  export * from './data/storage/deviceIdManager';
10
12
  export * from './services/navigation/dialPad';
11
13
  export * from './services/navigation/openBrowser';
@@ -9,6 +9,7 @@ import { getLocationCoordinates } from '../../data/storage/locationManager';
9
9
  import { getEncryptionKey, getPRefCode } from '../../data/storage/keysManager';
10
10
 
11
11
  import { getBaseUrl } from '../../utils/config';
12
+ import { ApiService } from '../../data/network/apiService';
12
13
 
13
14
  export type Attribute = { key: string; value: string };
14
15
  export type Screen = { name?: string; attributes?: Attribute[] };
@@ -38,7 +39,6 @@ export type UrlInput = {
38
39
  authType?: AuthType;
39
40
  deviceInfo?: DeviceInfo;
40
41
  sessionId?: string;
41
- nonce: string;
42
42
  };
43
43
  export function validateInput(input: UrlInput): {
44
44
  ok: boolean;
@@ -49,7 +49,6 @@ export function validateInput(input: UrlInput): {
49
49
  if (!input.profile) errors.push('profile is mandatory');
50
50
  if (!input.profile?.userId) errors.push('userId is mandatory');
51
51
  if (!input.profile?.email) errors.push('email is mandatory');
52
- if (!input.nonce) errors.push('nonce is mandatory');
53
52
 
54
53
  const pv = input.profile?.pv;
55
54
  const ev = input.profile?.ev;
@@ -86,6 +85,22 @@ export async function generateUrl(input: UrlInput): Promise<string> {
86
85
  const storedLocation = await getLocationCoordinates();
87
86
  const sessionId = await getSessionId();
88
87
 
88
+ const programCode = await getPRefCode();
89
+ if (!programCode || programCode === '-') {
90
+ throw new SDKError('pRefCode is mandatory. Initialize SDK with pRefCode.');
91
+ }
92
+
93
+ // get nonce -- start
94
+ const generator = new ApiService().getNonce({
95
+ extUserId: input.profile?.userId ?? '',
96
+ partnerCode: programCode,
97
+ });
98
+ const { value } = await generator.next();
99
+ if (value?.error) throw new SDKError(String(value.error));
100
+ if (!value?.token) throw new SDKError('Nonce not received');
101
+ const resolvedNonce = value.token;
102
+ // get nonce -- end
103
+
89
104
  const deviceInfo: DeviceInfo =
90
105
  storedLocation != null
91
106
  ? { dId: deviceId, location: storedLocation }
@@ -101,7 +116,7 @@ export async function generateUrl(input: UrlInput): Promise<string> {
101
116
  authType: input.authType ?? 'PHONE',
102
117
  sessionId: sessionId ?? '',
103
118
  deviceInfo,
104
- nonce: input.nonce,
119
+ nonce: resolvedNonce,
105
120
  };
106
121
 
107
122
  const validation = validateInput({
@@ -130,10 +145,6 @@ export async function generateUrl(input: UrlInput): Promise<string> {
130
145
  }
131
146
  const encoded = await aesEncrypt(json, key);
132
147
 
133
- const programCode = await getPRefCode();
134
- if (!programCode || programCode === '-') {
135
- throw new SDKError('pRefCode is mandatory. Initialize SDK with pRefCode.');
136
- }
137
148
  return `${getBaseUrl()}?pRefCode=${encodeURIComponent(
138
149
  programCode
139
150
  )}&qP=${encodeURIComponent(encoded)}`;