Skip to main content

Cash Atlas Webview Integration

Nova Credit supports integrating Cash Atlas® into native web applications through the use of a WebView in both iOS and Android.

Minimum iOS version supported: 15.0

Minimum Android version supported: 12.0

Settings

  • Javascript enabled must be set to true
  • javaScriptCanOpenWindowsAutomatically must be set to true
  • setSupportMultipleWindows must be set to true (Android)

React Native

import { WebView } from 'react-native-webview';
<WebView javaScriptEnabled javaScriptCanOpenWindowsAutomatically setSupportMultipleWindows />;

Swift

let preferences = WKWebpagePreferences()
preferences.allowsContentJavaScript = true
webView.configuration.defaultWebpagePreferences = preferences
webView.configuration.preferences.javaScriptCanOpenWindowsAutomatically = true

Kotlin

val webView = WebView(context)
webView.settings.javaScriptEnabled = true
webView.settings.javaScriptCanOpenWindowsAutomatically = true
webView.settings.setSupportMultipleWindows(true)

Navigation Handling

External links need to open in an external browser i.e. bank OAuth flows. This requires some explicit handling depending on the platform.

React Native

import { Linking } from 'react-native';
import { WebView } from 'react-native-webview';

<WebView
onOpenWindow={({ nativeEvent }) => {
Linking.openURL(nativeEvent.targetUrl);
}}
/>;

Swift

    // Set the ui delegate
webView.uiDelegate = self

// This method is called when a new window/tab is requested (e.g., target="_blank")
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
if navigationAction.request.url != nil {
if UIApplication.shared.canOpenURL(navigationAction.request.url!) {
UIApplication.shared.open(navigationAction.request.url!, options: [:], completionHandler: nil)
}
}

return nil
}

Kotlin

    // This method is called when a new window/tab is requested (e.g., target="_blank")
webView.webChromeClient = object : WebChromeClient() {
override fun onCreateWindow(
view: WebView?,
isDialog: Boolean,
isUserGesture: Boolean,
resultMsg: android.os.Message?
): Boolean {
// Create a temporary, invisible WebView to intercept the URL
val tempWebView = WebView(this@MainActivity)
tempWebView.settings.javaScriptEnabled = true // Essential for some redirects

tempWebView.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(tempView: WebView?, request: WebResourceRequest?): Boolean {
val url = request?.url.toString()
if (url.isNotEmpty()) {
try {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
startActivity(intent)
} catch (e: Exception) {
e.printStackTrace()
}
}
return true
}
}

// Attach the temporary WebView to the message so it receives the new window's load
val transport = resultMsg?.obj as WebView.WebViewTransport
transport.webView = tempWebView
resultMsg.sendToTarget()

return true
}
}

Permissions

Internet is required for Android webview but no explicit permissions are needed for file system uploads.

iOS: Info.plist

  • None required

Android: AndroidManifest.xml

  • android.permission.INTERNET

React Native Expo: app.json

{
"android": {
"permissions": ["android.permission.INTERNET"]
},
"ios": {
"infoPlist": {}
}
}

Additionally, if you are using bank statement uploads then handling the file picker inside of Android Webview requires some additional logic. React Native and iOS should work without any modifications.

Kotlin

class MainActivity : AppCompatActivity() {
private var mFilePathCallback: ValueCallback<Array<Uri>>? = null // To send file URIs back to WebView
private val FILE_CHOOSER_REQUEST_CODE = 123 // Request code for startActivityForResult
private val PERMISSION_REQUEST_CODE = 456 // Request code for ActivityCompat.requestPermissions

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

val webView: WebView = findViewById(R.id.webView)
webView.settings.javaScriptEnabled = true
webView.settings.javaScriptCanOpenWindowsAutomatically = true
webView.settings.setSupportMultipleWindows(true)

webView.webChromeClient = object : WebChromeClient() {
// This function is called when file picker is opened
override fun onShowFileChooser(
webView: WebView?,
filePathCallback: ValueCallback<Array<Uri>>?,
fileChooserParams: FileChooserParams?
): Boolean {
mFilePathCallback = filePathCallback
openFileChooser(fileChooserParams)
return true
}
}

webView.loadUrl("webviewUrl")
}

// Helper function to launch the system file picker
private fun openFileChooser(fileChooserParams: WebChromeClient.FileChooserParams?) {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "application/pdf"
}

try {
startActivityForResult(intent, FILE_CHOOSER_REQUEST_CODE)
} catch (e: Exception) {
e.printStackTrace()

// Cancel the WebView request if picker fails to open
mFilePathCallback?.onReceiveValue(null)
mFilePathCallback = null
}
}

// Handle the result from the file picker Intent
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == FILE_CHOOSER_REQUEST_CODE) {
if (mFilePathCallback == null) {
return
}

// Get selected Uri(s)
val result = if (resultCode == Activity.RESULT_OK) {
data?.dataString?.let { arrayOf(Uri.parse(it)) } ?: data?.clipData?.let { clipData ->
Array(clipData.itemCount) { i -> clipData.getItemAt(i).uri }
}
} else {
// User cancelled the picker
null
}

// Send the result back to the WebView
mFilePathCallback?.onReceiveValue(result)
mFilePathCallback = null // Clear the callback
}
}
}

Callback handling

You can handle client side callbacks onSuccess, onError, and onExit by posting a message to the WebView. More details about client side hooks are available in our Quickstart Guide.

JS registration

// When registering Nova Credit handle the callbacks
window.Nova.register({
env: 'sandbox',
productId: 'productId',
publicId: 'publicId',
onSuccess: function(publicToken, status){
const msg = JSON.stringify({ message:'NOVA_SUCCESS', publicToken, status });
// React Native
window.ReactNativeWebView.postMessage(msg, '*');
// iOS
window.webkit.messageHandlers.native.postMessage(msg, '*');
// Android
window.parent.postMessage(msg, '*');
}
onError: function(publicToken, error){
const msg = JSON.stringify({ message:'NOVA_ERROR', publicToken, error });
// postMessage(msg, '*');
}
onExit: function(){
const msg = JSON.stringify({ message:'NOVA_EXIT'});
// postMessage(msg, '*');
},
});

React Native

<Webview onMessage={event => console.log(event.nativeEvent.data)} />

Swift

let configuration = WKWebViewConfiguration()
configuration.userContentController.add(self, name: "NOVA_MESSAGE")

func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if message.name == "NOVA_MESSAGE" {
// Parse message
}
}

Kotlin

val novaListener = object : WebMessageListener() {
override fun onPostMessage(
view: WebView,
message: WebMessageCompat,
sourceOrigin: Uri,
isMainFrame: Boolean,
replyProxy: JavaScriptReplyProxy
) {
// Parse message
}
}

WebViewCompat.addWebMessageListener(webView, "myObject", rules, novaListener)