import SwiftUI
import WebKit
struct WebView: UIViewRepresentable {
let url: URL
let handlerName = "iosListener"
var onTokenReceived: ((String) -> Void)?
var onFullResponseReceived: ((DailypayCardTokenizeResponse) -> Void)?
class Coordinator: NSObject, WKScriptMessageHandler {
var onIssuerReceived: ((String) -> Void)?
var onTokenReceived: ((String) -> Void)?
var onFullResponseReceived: ((DailypayCardTokenizeResponse) -> Void)?
init(onTokenReceived: ((String) -> Void)?, onFullResponseReceived: ((DailypayCardTokenizeResponse) -> Void)?) {
self.onTokenReceived = onTokenReceived
self.onFullResponseReceived = onFullResponseReceived
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
print("Received postMessage: \(message.body)")
var data: Data? = nil
if let dict = message.body as? [String: Any] {
data = try? JSONSerialization.data(withJSONObject: dict)
} else if let str = message.body as? String {
data = str.data(using: .utf8)
}
if let data, let msg = try? JSONDecoder().decode(DailypayCardTokenizeMessage.self, from: data),
msg.type == "DAILYPAY_CARD_TOKENIZE_RESPONSE" {
onIssuerReceived?(msg.response.issuer)
onTokenReceived?(msg.response.token)
onFullResponseReceived?(msg.response)
}
}
}
func makeCoordinator() -> Coordinator {
Coordinator(onTokenReceived: onTokenReceived, onFullResponseReceived: onFullResponseReceived)
}
func makeUIView(context: Context) -> WKWebView {
let contentController = WKUserContentController()
contentController.add(context.coordinator, name: handlerName)
let config = WKWebViewConfiguration()
config.userContentController = contentController
// Forward window.postMessage to iOS
let js = """
window.postMessage = function(data) {
window.webkit.messageHandlers.\(handlerName).postMessage(data);
};
"""
let userScript = WKUserScript(source: js, injectionTime: .atDocumentStart, forMainFrameOnly: false)
contentController.addUserScript(userScript)
let webView = WKWebView(frame: .zero, configuration: config)
let request = URLRequest(url: url)
webView.load(request)
return webView
}
func updateUIView(_ uiView: WKWebView, context: Context) {
// No-op
}
}
struct DailypayCardTokenizeResponse: Decodable {
let firstName: String
let lastName: String
let addressLineOne: String
let addressLineTwo: String
let addressCity: String
let addressState: String
let addressZipCode: String
let expirationMonth: String
let expirationYear: String
let addressCountry: String
let issuer: String
let token: String
enum CodingKeys: String, CodingKey {
case firstName = "first_name"
case lastName = "last_name"
case addressLineOne = "address_line_one"
case addressLineTwo = "address_line_two"
case addressCity = "address_city"
case addressState = "address_state"
case addressZipCode = "address_zip_code"
case expirationMonth = "expiration_month"
case expirationYear = "expiration_year"
case addressCountry = "address_country"
case issuer
case token
}
}
struct DailypayCardTokenizeMessage: Decodable {
let type: String
let response: DailypayCardTokenizeResponse
let success: Bool
}
struct ContentView: View {
// Replace client_id and implementation_id with values supplied by DailyPay
// that are unique to you and your application
@State private var urlString: String = "https://companion.workloads.production.dailypay.com/v1/widgets/debitcard/tokenizer?client_id=123&implementation_id=3213"
@State private var showAlert = false
@State private var issuer: String? = nil
@State private var showTokenAlert = false
@State private var token: String? = nil
func handleCardTokenizeResponse(_ response: DailypayCardTokenizeResponse) {
// Add card to DailyPay using POST /accounts
// See https://developer.dailypay.com/tag/Accounts#operation/createAccount
print("Ready to send to the DailyPay REST API: \(response)")
}
var body: some View {
VStack {
TextField("Enter URL", text: $urlString)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
if let url = URL(string: urlString) {
WebView(
url: url,
onTokenReceived: { token in
self.token = token
self.showTokenAlert = true
},
onFullResponseReceived: { response in
handleCardTokenizeResponse(response)
}
)
.edgesIgnoringSafeArea(.all)
} else {
Text("Invalid URL")
}
}
.alert(isPresented: $showTokenAlert) {
Alert(
title: Text("Token Received"),
message: Text(token ?? "No token"),
dismissButton: .default(Text("OK"))
)
}
}
}
#Preview {
ContentView()
}
package com.example.webviewrunner
import android.os.Bundle
import android.webkit.JavascriptInterface
import android.webkit.WebSettings
import android.webkit.WebView
import android.webkit.WebViewClient
import android.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
private lateinit var webView: WebView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
webView = findViewById(R.id.webView)
// Configure WebView settings
val webSettings: WebSettings = webView.settings
webSettings.javaScriptEnabled = true // Enable JavaScript if your page uses it
webSettings.setSupportZoom(true) // Enable zoom controls
webSettings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW // Allow mixed content (HTTP/HTTPS)
webSettings.domStorageEnabled = true // Enable DOM storage
// Set a WebViewClient to handle page navigation within the WebView itself
webView.webViewClient = object : WebViewClient() {
// This method is deprecated in API 24 and later, but still useful for older devices
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
url?.let {
view?.loadUrl(it)
}
return true // Return true to indicate that the app handled the URL
}
// For API 24 and later
override fun shouldOverrideUrlLoading(view: WebView?, request: android.webkit.WebResourceRequest?): Boolean {
request?.url?.let { uri ->
view?.loadUrl(uri.toString())
}
return true // Return true to indicate that the app handled the URL
}
override fun onReceivedError(view: WebView?, request: android.webkit.WebResourceRequest?, error: android.webkit.WebResourceError?) {
super.onReceivedError(view, request, error)
android.util.Log.e("WebView", "Error: ${error?.description}")
}
override fun onReceivedHttpError(view: WebView?, request: android.webkit.WebResourceRequest?, errorResponse: android.webkit.WebResourceResponse?) {
super.onReceivedHttpError(view, request, errorResponse)
android.util.Log.e("WebView", "HTTP error: ${errorResponse?.statusCode}")
}
}
// Add JavaScript interface for postMessage
webView.addJavascriptInterface(object {
@JavascriptInterface
fun postMessage(message: String) {
// Interpret response and send to DailyPay using POST /accounts
// See https://developer.dailypay.com/tag/Accounts#operation/createAccount
// For demo - alert the response payload here.
runOnUiThread {
AlertDialog.Builder(this@MainActivity)
.setTitle("Message from WebView")
.setMessage(message)
.setPositiveButton("OK", null)
.show()
}
}
}, "AndroidInterface")
// Replace client_id and implementation_id with values supplied by DailyPay
// that are unique to you and your application
webView.loadUrl("https://companion.workloads.production.dailypay.com/v1/widgets/debitcard/tokenizer?client_id=123&implementation_id=3213")
}
// Handle back button presses to navigate within the WebView's history
override fun onBackPressed() {
if (webView.canGoBack()) {
webView.goBack()
} else {
super.onBackPressed()
}
}
}
Install dependencies: npm install react-native-webview
or yarn add react-native-webview
import React, { useRef, useState } from "react";
import { View } from "react-native";
import { WebView } from "react-native-webview";
const TokenizerComponent = () => {
const webViewRef = useRef(null);
const [tokenData, setTokenData] = useState(null);
const handleWebViewMessage = (event) => {
try {
const messageData = JSON.parse(event.nativeEvent.data);
if (messageData.type === "DAILYPAY_CARD_TOKENIZE_RESPONSE") {
if (messageData.success && messageData.response) {
setTokenData(messageData.response);
}
}
} catch (error) {
console.error("Failed to parse message:", error);
}
};
return (
<WebView
ref={webViewRef}
source={{
// Replace client_id and implementation_id with values supplied by DailyPay
// that are unique to you and your application
uri: "https://companion.workloads.production.dailypay.com/v1/widgets/debitcard/tokenizer?client_id=123&implementation_id=3213",
}}
onMessage={handleWebViewMessage}
javaScriptEnabled={true}
domStorageEnabled={true}
/>
);
};