Skip to content
Last updated

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