@jimrising/easymerchantsdk-react-native 2.4.8 → 2.4.9

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 (85) hide show
  1. package/README.md +1 -1
  2. package/android/.settings/org.eclipse.buildship.core.prefs +2 -2
  3. package/android/build.gradle +3 -4
  4. package/android/config.properties +5 -0
  5. package/android/config.properties.example +5 -0
  6. package/ios/ApiManager/APIRequest.swift +0 -3
  7. package/ios/ApiManager/APIService.swift +0 -2
  8. package/ios/Classes/EasyMerchantSdk.h +0 -1
  9. package/ios/Classes/EasyMerchantSdk.m +54 -5
  10. package/ios/Classes/EasyMerchantSdk.swift +1 -15
  11. package/ios/Classes/EasyPayViewController.swift +1 -1
  12. package/ios/EnvironmentConfig.swift +0 -1
  13. package/ios/Example/SceneDelegate.swift +23 -1
  14. package/ios/Example/ViewController.swift +0 -8
  15. package/ios/Extensions/UIFont.swift +0 -1
  16. package/ios/Extensions/UIViewController+Extension.swift +0 -1
  17. package/ios/Helper/GrailPayHelper.swift +146 -58
  18. package/ios/Helper/GrailPayWebViewController.swift +416 -0
  19. package/ios/Helper/JavaScriptBridge.swift +312 -0
  20. package/ios/Helper/WebViewConfig.swift +159 -0
  21. package/ios/Models/Request.swift +48 -204
  22. package/ios/easymerchantsdk.podspec +2 -2
  23. package/package.json +1 -1
  24. package/android/build/generated/source/buildConfig/debug/com/reactlibrary/BuildConfig.java +0 -10
  25. package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/AndroidManifest.xml +0 -7
  26. package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/output-metadata.json +0 -18
  27. package/android/build/intermediates/aar_metadata/debug/writeDebugAarMetadata/aar-metadata.properties +0 -6
  28. package/android/build/intermediates/annotation_processor_list/debug/javaPreCompileDebug/annotationProcessors.json +0 -1
  29. package/android/build/intermediates/compile_library_classes_jar/debug/bundleLibCompileToJarDebug/classes.jar +0 -0
  30. package/android/build/intermediates/compile_r_class_jar/debug/generateDebugRFile/R.jar +0 -0
  31. package/android/build/intermediates/compile_symbol_list/debug/generateDebugRFile/R.txt +0 -0
  32. package/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +0 -1
  33. package/android/build/intermediates/incremental/debug/packageDebugResources/merger.xml +0 -2
  34. package/android/build/intermediates/incremental/mergeDebugAssets/merger.xml +0 -2
  35. package/android/build/intermediates/incremental/mergeDebugJniLibFolders/merger.xml +0 -2
  36. package/android/build/intermediates/incremental/mergeDebugShaders/merger.xml +0 -2
  37. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/reactlibrary/BuildConfig.class +0 -0
  38. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/reactlibrary/RNEasymerchantsdkModule$1$1.class +0 -0
  39. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/reactlibrary/RNEasymerchantsdkModule$1.class +0 -0
  40. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/reactlibrary/RNEasymerchantsdkModule$2.class +0 -0
  41. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/reactlibrary/RNEasymerchantsdkModule$3.class +0 -0
  42. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/reactlibrary/RNEasymerchantsdkModule.class +0 -0
  43. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/reactlibrary/RNEasymerchantsdkPackage.class +0 -0
  44. package/android/build/intermediates/local_only_symbol_list/debug/parseDebugLocalResources/R-def.txt +0 -2
  45. package/android/build/intermediates/manifest_merge_blame_file/debug/processDebugManifest/manifest-merger-blame-debug-report.txt +0 -7
  46. package/android/build/intermediates/merged_manifest/debug/processDebugManifest/AndroidManifest.xml +0 -7
  47. package/android/build/intermediates/navigation_json/debug/extractDeepLinksDebug/navigation.json +0 -1
  48. package/android/build/intermediates/nested_resources_validation_report/debug/generateDebugResources/nestedResourcesValidationReport.txt +0 -1
  49. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/reactlibrary/BuildConfig.class +0 -0
  50. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/reactlibrary/RNEasymerchantsdkModule$1$1.class +0 -0
  51. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/reactlibrary/RNEasymerchantsdkModule$1.class +0 -0
  52. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/reactlibrary/RNEasymerchantsdkModule$2.class +0 -0
  53. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/reactlibrary/RNEasymerchantsdkModule$3.class +0 -0
  54. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/reactlibrary/RNEasymerchantsdkModule.class +0 -0
  55. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/reactlibrary/RNEasymerchantsdkPackage.class +0 -0
  56. package/android/build/intermediates/runtime_library_classes_jar/debug/bundleLibRuntimeToJarDebug/classes.jar +0 -0
  57. package/android/build/intermediates/symbol_list_with_package_name/debug/generateDebugRFile/package-aware-r.txt +0 -1
  58. package/android/build/outputs/logs/manifest-merger-debug-report.txt +0 -16
  59. package/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin +0 -0
  60. package/ios/Pods/UserDefaults/UserStoreSingleton.swift +0 -425
  61. package/ios/Pods/ViewControllers/AdditionalInfoVC.swift +0 -2996
  62. package/ios/Pods/ViewControllers/BaseVC.swift +0 -142
  63. package/ios/Pods/ViewControllers/BillingInfoVC/BillingInfoVC.swift +0 -3807
  64. package/ios/Pods/ViewControllers/BillingInfoVC/Cells/CityListTVC.swift +0 -46
  65. package/ios/Pods/ViewControllers/BillingInfoVC/Cells/CountryListTVC.swift +0 -47
  66. package/ios/Pods/ViewControllers/BillingInfoVC/Cells/StateListTVC.swift +0 -46
  67. package/ios/Pods/ViewControllers/Clean Runner_2025-07-23T14-58-05.txt +0 -13
  68. package/ios/Pods/ViewControllers/CountryListVC.swift +0 -435
  69. package/ios/Pods/ViewControllers/EmailVerificationVC.swift +0 -300
  70. package/ios/Pods/ViewControllers/GrailPayVC.swift +0 -492
  71. package/ios/Pods/ViewControllers/OTPVerificationVC.swift +0 -2278
  72. package/ios/Pods/ViewControllers/PaymentDoneVC.swift +0 -287
  73. package/ios/Pods/ViewControllers/PaymentErrorVC.swift +0 -85
  74. package/ios/Pods/ViewControllers/PaymentInformation/AccountTypeTVC.swift +0 -41
  75. package/ios/Pods/ViewControllers/PaymentInformation/PaymentInfoVC.swift +0 -13115
  76. package/ios/Pods/ViewControllers/PaymentInformation/PaymentInformationCVC.swift +0 -35
  77. package/ios/Pods/ViewControllers/PaymentInformation/RecurringTVC.swift +0 -40
  78. package/ios/Pods/ViewControllers/PaymentInformation/SavedAccountsTVC/SavedAccountTVC.swift +0 -80
  79. package/ios/Pods/ViewControllers/PaymentInformation/SavedAccountsTVC/SavedAccountTVC.xib +0 -163
  80. package/ios/Pods/ViewControllers/PaymentInformation/SavedCardsTVC/SavedCardsTVC.swift +0 -81
  81. package/ios/Pods/ViewControllers/PaymentInformation/SavedCardsTVC/SavedCardsTVC.xib +0 -188
  82. package/ios/Pods/ViewControllers/PaymentStatusWebViewVC.swift +0 -167
  83. package/ios/Pods/ViewControllers/TermAndConditionsVC.swift +0 -63
  84. package/ios/Pods/ViewControllers/ThreeDSecurePaymentDoneVC.swift +0 -1254
  85. package/ios/easymerchantsdk.storyboard +0 -9089
@@ -0,0 +1,312 @@
1
+ //
2
+ // JavaScriptBridge.swift
3
+ // EasyPay
4
+ //
5
+ // JavaScript bridge handler for GrailPay WebView communication
6
+ //
7
+
8
+ import Foundation
9
+ import WebKit
10
+
11
+ public class JavaScriptBridge: NSObject, WKScriptMessageHandler {
12
+ weak var webViewController: GrailPayWebViewController?
13
+
14
+ public init(webViewController: GrailPayWebViewController) {
15
+ self.webViewController = webViewController
16
+ super.init()
17
+ }
18
+
19
+ public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
20
+ if let bodyString = message.body as? String {
21
+ if let data = bodyString.data(using: .utf8),
22
+ let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
23
+ let type = json["type"] as? String {
24
+ handleMessage(type: type, data: json)
25
+ } else {
26
+ handleMessage(type: "message", data: ["message": bodyString])
27
+ }
28
+ return
29
+ }
30
+
31
+ guard let body = message.body as? [String: Any] else { return }
32
+
33
+ if let rawMessage = body["rawMessage"] as? String {
34
+ if let data = rawMessage.data(using: .utf8),
35
+ let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
36
+ let type = json["type"] as? String {
37
+ handleMessage(type: type, data: json)
38
+ } else {
39
+ handleMessage(type: "message", data: ["message": rawMessage])
40
+ }
41
+ return
42
+ }
43
+
44
+ if let type = body["type"] as? String {
45
+ handleMessage(type: type, data: body)
46
+ } else if let eventType = body["eventType"] as? String {
47
+ handleMessage(type: eventType, data: body)
48
+ } else if body["result"] != nil {
49
+ handleMessage(type: "onComplete", data: body)
50
+ } else if body["message"] != nil {
51
+ handleMessage(type: "showMessage", data: body)
52
+ }
53
+ }
54
+
55
+ private func handleMessage(type: String, data: [String: Any]) {
56
+ switch type {
57
+ case "postMessage", "showMessage":
58
+ if let message = data["message"] as? String {
59
+ showMessage(message)
60
+ }
61
+
62
+ case "onComplete":
63
+ if let resultJson = data["result"] as? String {
64
+ handleCompletion(resultJson)
65
+ } else if let resultDict = data["result"] as? [String: Any] {
66
+ handleCompletionDict(resultDict)
67
+ }
68
+
69
+ case "closeWebView":
70
+ webViewController?.closePopupFromBridge()
71
+ webViewController?.closeWebView()
72
+
73
+ case "opener_postMessage", "popup_postMessage":
74
+ handlePopupMessage(data)
75
+
76
+ case "closePopup":
77
+ DispatchQueue.main.async {
78
+ self.webViewController?.closePopupFromBridge()
79
+ }
80
+
81
+ case "consoleLog":
82
+ break
83
+
84
+ case "defaultAccountSelected", "linkedDefaultAccount":
85
+ DispatchQueue.main.async {
86
+ var resultData: NSDictionary = [:]
87
+ if let accountData = data["data"] {
88
+ if let accountArray = accountData as? [[String: Any]] {
89
+ resultData = ["data": accountArray]
90
+ } else if let accountDict = accountData as? [String: Any] {
91
+ resultData = ["data": [accountDict]]
92
+ } else {
93
+ resultData = ["data": [accountData]]
94
+ }
95
+ } else {
96
+ resultData = ["data": [data]]
97
+ }
98
+ let result = SDKResult(type: .success, data: resultData)
99
+ self.webViewController?.handleCompletion(result: result)
100
+ }
101
+
102
+ case "accountLinked", "linkSuccess", "success", "complete", "done":
103
+ DispatchQueue.main.async {
104
+ var resultData: NSDictionary = [:]
105
+ if let accountData = data["data"] {
106
+ if let accountArray = accountData as? [[String: Any]] {
107
+ resultData = ["data": accountArray]
108
+ } else if let accountDict = accountData as? [String: Any] {
109
+ resultData = ["data": [accountDict]]
110
+ } else {
111
+ resultData = ["data": [accountData]]
112
+ }
113
+ } else {
114
+ resultData = ["data": [data]]
115
+ }
116
+ let result = SDKResult(type: .success, data: resultData)
117
+ self.webViewController?.handleCompletion(result: result)
118
+ }
119
+
120
+ case "error", "linkError", "failure":
121
+ DispatchQueue.main.async {
122
+ self.webViewController?.closePopupFromBridge()
123
+ if let errorMessage = data["message"] as? String ?? data["error"] as? String {
124
+ self.webViewController?.showAlert(title: "Error", message: errorMessage)
125
+ }
126
+ }
127
+
128
+ case "cancelled", "closed", "dismissed":
129
+ DispatchQueue.main.async {
130
+ let cancelledData: NSDictionary = ["status": false, "message": "User cancelled"]
131
+ let result = SDKResult(type: .cancelled, data: cancelledData)
132
+ self.webViewController?.handleCompletion(result: result)
133
+ }
134
+
135
+ default:
136
+ break
137
+ }
138
+ }
139
+
140
+ private func handlePopupMessage(_ data: [String: Any]) {
141
+ guard let message = data["message"] else { return }
142
+
143
+ var oauthData: [String: String] = [:]
144
+
145
+ if let messageDict = message as? [String: Any] {
146
+ if let status = messageDict["status"] as? String { oauthData["status"] = status }
147
+ if let state = messageDict["state"] as? String { oauthData["state"] = state }
148
+ if let url = messageDict["url"] as? String { oauthData["url"] = url }
149
+ if let redirectUrl = messageDict["redirectUrl"] as? String { oauthData["url"] = redirectUrl }
150
+ if let publicToken = messageDict["public_token"] as? String { oauthData["public_token"] = publicToken }
151
+ if let type = messageDict["type"] as? String { oauthData["type"] = type }
152
+
153
+ if let eventData = messageDict["eventData"] as? [String: Any],
154
+ let redirectURL = eventData["redirectURL"] as? String {
155
+ oauthData["url"] = redirectURL
156
+ oauthData["redirectURL"] = redirectURL
157
+
158
+ if let urlComponents = URLComponents(string: redirectURL) {
159
+ urlComponents.queryItems?.forEach { item in
160
+ if let value = item.value {
161
+ oauthData[item.name] = value
162
+ }
163
+ }
164
+ }
165
+ }
166
+
167
+ if messageDict["moneykit"] != nil {
168
+ oauthData["moneykit"] = "true"
169
+ }
170
+ }
171
+
172
+ DispatchQueue.main.async {
173
+ self.webViewController?.forwardOAuthDataToMainWebView(oauthData)
174
+ }
175
+ }
176
+
177
+ func showMessage(_ message: String) {
178
+ DispatchQueue.main.async {
179
+ self.webViewController?.showAlert(title: "GrailPay", message: message)
180
+ }
181
+ }
182
+
183
+ private func handleCompletion(_ resultJson: String) {
184
+ DispatchQueue.main.async {
185
+ if let data = resultJson.data(using: .utf8),
186
+ let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
187
+ self.handleCompletionDict(json)
188
+ } else {
189
+ self.webViewController?.closePopupFromBridge()
190
+ self.webViewController?.closeWebView()
191
+ }
192
+ }
193
+ }
194
+
195
+ private func handleCompletionDict(_ resultDict: [String: Any]) {
196
+ DispatchQueue.main.async {
197
+ var resultData: NSDictionary = [:]
198
+
199
+ if let data = resultDict["data"] {
200
+ if let dataArray = data as? [[String: Any]] {
201
+ resultData = ["data": dataArray]
202
+ } else if let dataDict = data as? [String: Any] {
203
+ resultData = ["data": [dataDict]]
204
+ } else {
205
+ resultData = ["data": data]
206
+ }
207
+ } else {
208
+ resultData = resultDict as NSDictionary
209
+ }
210
+
211
+ let result = SDKResult(type: .success, data: resultData)
212
+ self.webViewController?.handleCompletion(result: result)
213
+ }
214
+ }
215
+ }
216
+
217
+ // MARK: - OAuth Callback Bridge
218
+ public class OAuthCallbackBridge: NSObject, WKScriptMessageHandler {
219
+ let oauthData: [String: String]
220
+
221
+ init(oauthData: [String: String]) {
222
+ self.oauthData = oauthData
223
+ super.init()
224
+ }
225
+
226
+ public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
227
+ }
228
+
229
+ func getOAuthDataJSON() -> String {
230
+ guard let jsonData = try? JSONSerialization.data(withJSONObject: oauthData, options: []),
231
+ let jsonString = String(data: jsonData, encoding: .utf8) else {
232
+ return "{}"
233
+ }
234
+ return jsonString
235
+ }
236
+ }
237
+
238
+ // MARK: - WKWebView Extensions
239
+ extension WKWebView {
240
+ public func injectJavaScript(_ script: String, completion: ((Any?, Error?) -> Void)? = nil) {
241
+ evaluateJavaScript(script) { result, error in
242
+ completion?(result, error)
243
+ }
244
+ }
245
+
246
+ public func injectMessageListener() {
247
+ let script = """
248
+ (function() {
249
+ window.addEventListener('message', function(event) {
250
+ try {
251
+ var data = event.data;
252
+ if (!data || typeof data !== 'object') return;
253
+
254
+ if (data.__bridgeProxy === true && data.message) {
255
+ if (window.webkit?.messageHandlers?.androidBridge) {
256
+ window.webkit.messageHandlers.androidBridge.postMessage(data.message);
257
+ }
258
+ return;
259
+ }
260
+
261
+ const grailpayEvent = new CustomEvent('grailpay-oauth-complete', { detail: data });
262
+ window.dispatchEvent(grailpayEvent);
263
+ } catch (error) {}
264
+ }, false);
265
+ })();
266
+ """
267
+ injectJavaScript(script)
268
+ }
269
+
270
+ public func injectOAuthData(_ oauthData: [String: String]) {
271
+ guard let jsonData = try? JSONSerialization.data(withJSONObject: oauthData, options: []),
272
+ let jsonString = String(data: jsonData, encoding: .utf8) else { return }
273
+
274
+ let script = """
275
+ (function() {
276
+ var oauthData = \(jsonString);
277
+ window.oauthCallbackData = oauthData;
278
+ var redirectURL = oauthData.url || oauthData.redirectURL || window.location.href;
279
+
280
+ window.postMessage({ type: 'link.resume', moneykit: 1, eventData: { redirectURL: redirectURL } }, '*');
281
+ window.postMessage({ type: 'oauth_complete', source: 'popup', url: redirectURL, status: oauthData.status, state: oauthData.state }, '*');
282
+ window.postMessage({ type: 'mk-oauth-callback', redirectUrl: redirectURL, status: oauthData.status, state: oauthData.state }, '*');
283
+ window.postMessage({ success: true, url: redirectURL, status: oauthData.status, state: oauthData.state }, '*');
284
+
285
+ document.querySelectorAll('iframe').forEach(function(iframe) {
286
+ try {
287
+ if (iframe.contentWindow) {
288
+ iframe.contentWindow.postMessage({ type: 'link.resume', moneykit: 1, eventData: { redirectURL: redirectURL } }, '*');
289
+ iframe.contentWindow.postMessage({ type: 'oauth_complete', source: 'popup', url: redirectURL, status: oauthData.status, state: oauthData.state }, '*');
290
+ iframe.contentWindow.postMessage({ type: 'mk-oauth-callback', redirectUrl: redirectURL, status: oauthData.status, state: oauthData.state }, '*');
291
+ iframe.contentWindow.postMessage({ success: true, url: redirectURL, status: oauthData.status, state: oauthData.state }, '*');
292
+ }
293
+ } catch (e) {}
294
+ });
295
+
296
+ window.dispatchEvent(new CustomEvent('grailpay-oauth-complete', {
297
+ detail: { type: 'oauth_complete', source: 'popup', url: redirectURL, status: oauthData.status, state: oauthData.state },
298
+ bubbles: true
299
+ }));
300
+
301
+ if (typeof window.receiveDeepLinkData !== 'function') {
302
+ window.receiveDeepLinkData = function(data) {
303
+ window.oauthCallbackData = data;
304
+ window.dispatchEvent(new CustomEvent('grailpay-oauth-complete', { detail: data }));
305
+ };
306
+ }
307
+ window.receiveDeepLinkData(oauthData);
308
+ })();
309
+ """
310
+ injectJavaScript(script)
311
+ }
312
+ }
@@ -0,0 +1,159 @@
1
+ //
2
+ // WebViewConfig.swift
3
+ // EasyPay
4
+ //
5
+ // WebView configuration utilities for GrailPay
6
+ //
7
+
8
+ import Foundation
9
+ import WebKit
10
+
11
+ public class WebViewConfig {
12
+ public static func createMainWebView(bridge: JavaScriptBridge) -> WKWebView {
13
+ let config = WKWebViewConfiguration()
14
+ config.preferences.javaScriptEnabled = true
15
+ config.preferences.javaScriptCanOpenWindowsAutomatically = true
16
+ config.websiteDataStore = .default()
17
+
18
+ let contentController = WKUserContentController()
19
+ contentController.add(bridge, name: "androidBridge")
20
+ contentController.add(bridge, name: "grailpay")
21
+ contentController.add(bridge, name: "nativeBridge")
22
+ contentController.add(bridge, name: "iosHandler")
23
+
24
+ let bridgeShimScript = WKUserScript(
25
+ source: WebViewConfig.getBridgeShimScript(),
26
+ injectionTime: .atDocumentStart,
27
+ forMainFrameOnly: false
28
+ )
29
+ contentController.addUserScript(bridgeShimScript)
30
+
31
+ config.userContentController = contentController
32
+ config.allowsInlineMediaPlayback = true
33
+ config.mediaTypesRequiringUserActionForPlayback = []
34
+
35
+ let webView = WKWebView(frame: .zero, configuration: config)
36
+ webView.allowsBackForwardNavigationGestures = true
37
+ webView.allowsLinkPreview = true
38
+ webView.customUserAgent = "Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36"
39
+
40
+ return webView
41
+ }
42
+
43
+ public static func createPopupWebView(with configuration: WKWebViewConfiguration) -> WKWebView {
44
+ let webView = WKWebView(frame: .zero, configuration: configuration)
45
+ webView.allowsBackForwardNavigationGestures = true
46
+ webView.customUserAgent = "Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36"
47
+ return webView
48
+ }
49
+
50
+ public static func createCallbackWebView(oauthBridge: OAuthCallbackBridge) -> WKWebView {
51
+ let config = WKWebViewConfiguration()
52
+ config.preferences.javaScriptEnabled = true
53
+
54
+ let contentController = WKUserContentController()
55
+ contentController.add(oauthBridge, name: "oauthBridge")
56
+ config.userContentController = contentController
57
+
58
+ let webView = WKWebView(frame: .zero, configuration: config)
59
+ webView.customUserAgent = "Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36"
60
+ return webView
61
+ }
62
+
63
+ public static func enableCookies() {
64
+ HTTPCookieStorage.shared.cookieAcceptPolicy = .always
65
+ }
66
+
67
+ public static func getBridgeShimScript() -> String {
68
+ """
69
+ (function() {
70
+ var isMainFrame = (window === window.top);
71
+
72
+ if (!window.AndroidBridge) {
73
+ window.AndroidBridge = {
74
+ postMessage: function(message) {
75
+ try {
76
+ var parsed = typeof message === 'string' ? JSON.parse(message) : message;
77
+ if (window.webkit?.messageHandlers?.androidBridge) {
78
+ window.webkit.messageHandlers.androidBridge.postMessage(parsed);
79
+ } else if (window !== window.top) {
80
+ window.parent.postMessage({ __bridgeProxy: true, message: parsed }, '*');
81
+ }
82
+ } catch (e) {
83
+ if (window.webkit?.messageHandlers?.androidBridge) {
84
+ window.webkit.messageHandlers.androidBridge.postMessage({ rawMessage: message });
85
+ }
86
+ }
87
+ },
88
+ showMessage: function(message) {
89
+ if (window.webkit?.messageHandlers?.androidBridge) {
90
+ window.webkit.messageHandlers.androidBridge.postMessage({ type: 'showMessage', message: message });
91
+ }
92
+ },
93
+ onComplete: function(result) {
94
+ try {
95
+ var parsed = typeof result === 'string' ? JSON.parse(result) : result;
96
+ if (window.webkit?.messageHandlers?.androidBridge) {
97
+ window.webkit.messageHandlers.androidBridge.postMessage({ type: 'onComplete', result: JSON.stringify(parsed) });
98
+ }
99
+ } catch (e) {
100
+ if (window.webkit?.messageHandlers?.androidBridge) {
101
+ window.webkit.messageHandlers.androidBridge.postMessage({ type: 'onComplete', result: result });
102
+ }
103
+ }
104
+ },
105
+ closeWebView: function() {
106
+ if (window.webkit?.messageHandlers?.androidBridge) {
107
+ window.webkit.messageHandlers.androidBridge.postMessage({ type: 'closeWebView' });
108
+ }
109
+ }
110
+ };
111
+ }
112
+
113
+ if (isMainFrame) {
114
+ window.addEventListener('message', function(event) {
115
+ try {
116
+ var data = event.data;
117
+ if (data && data.__bridgeProxy === true && data.message) {
118
+ if (window.webkit?.messageHandlers?.androidBridge) {
119
+ window.webkit.messageHandlers.androidBridge.postMessage(data.message);
120
+ }
121
+ }
122
+ } catch (e) {}
123
+ }, false);
124
+ } else {
125
+ if (!window.webkit) window.webkit = {};
126
+ if (!window.webkit.messageHandlers) window.webkit.messageHandlers = {};
127
+ if (!window.webkit.messageHandlers.androidBridge) {
128
+ window.webkit.messageHandlers.androidBridge = {
129
+ postMessage: function(message) {
130
+ window.parent.postMessage({ __bridgeProxy: true, message: message }, '*');
131
+ }
132
+ };
133
+ }
134
+ if (!window.AndroidBridge) {
135
+ window.AndroidBridge = {
136
+ postMessage: function(message) {
137
+ try {
138
+ var parsed = typeof message === 'string' ? JSON.parse(message) : message;
139
+ window.parent.postMessage({ __bridgeProxy: true, message: parsed }, '*');
140
+ } catch (e) {
141
+ window.parent.postMessage({ __bridgeProxy: true, message: { rawMessage: message } }, '*');
142
+ }
143
+ },
144
+ showMessage: function(msg) {
145
+ window.parent.postMessage({ __bridgeProxy: true, message: { type: 'showMessage', message: msg } }, '*');
146
+ },
147
+ onComplete: function(result) {
148
+ window.parent.postMessage({ __bridgeProxy: true, message: { type: 'onComplete', result: result } }, '*');
149
+ },
150
+ closeWebView: function() {
151
+ window.parent.postMessage({ __bridgeProxy: true, message: { type: 'closeWebView' } }, '*');
152
+ }
153
+ };
154
+ }
155
+ }
156
+ })();
157
+ """
158
+ }
159
+ }