Skip to content
Last updated

Apple iOS

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()
}

Google Android

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()
        }
    }
}

React Native

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}
    />
  );
};