How To - Using iOS to listen for events
How to capture our events from the Card and ACH Token (bloqs) and the Embedded Forms
The following code will help you read the events that our embedded forms and app bloqs send back to the parent window (webView) on iOS:
Using UIKit
(for iOS <= 13)
import UIKit
import WebKit
//Class that handles iFrame display, response retrieval and decoding of the JSON response
class ViewController: UIViewController, WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler {
var webView: WKWebView!
//Global variables that are changed based on which merchant is creating a transaction
var appid: String = "T6554252567241061980"
var clientid: String = "01dffeb784c64d098c8c691ea589eb82"
//Codable object that represents first level of response JSON object
struct response: Codable {
let event: String
let data: Body
}
//Codable object that represents second level of response JSON object (is dependent on actual contents and will throw an error if field does not exist when decoding JSON)
struct Body: Codable {
let token: String
}
//View object that handles what is being displayed to screen
override func loadView() {
//Initial WebKit config and content controller creation
let webConfiguration = WKWebViewConfiguration()
let contentController = WKUserContentController()
//JS script creation and setup for when in process of loading iFrame to inject script
let js: String = "window.addEventListener('message', function(e){ window.webkit.messageHandlers.callbackHandler.postMessage(JSON.stringify(e.data)); });"
let userScript = WKUserScript(source: js, injectionTime: WKUserScriptInjectionTime.atDocumentEnd, forMainFrameOnly: false)
contentController.removeAllUserScripts()
contentController.addUserScript(userScript)
//Setup to allow for ContentController class to find and receive response as a result of JS script injection
contentController.add(
self,
name: "callbackHandler"
)
webConfiguration.userContentController = contentController
//Required calls to allow iFrame to be displayed on screen
webView = WKWebView(frame: .zero, configuration: webConfiguration)
webView.uiDelegate = self
webView.navigationDelegate = self
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
//URL to iFrame initialization
let myURL = URL(string: "https://secure.forms.qorcommerce.io/vault/card/token/?app_id=\(appid)&client_id=\(clientid)&profile_id=10203047&request_id=12345909&intent=create&cvv_field=true&button_text=Save%20Payment%20Method&textfield_type=filled")
let myRequest = URLRequest(url: myURL!)
//URL to iFrame loaded
webView.load(myRequest)
}
//Function to handle the content being returned from iFrame JS script injection (in this case it is the JSON responses)
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
//Checking to see if message.body is not empty, and if so then setting a new variable as a String version of it
if let bodyString = message.body as? String {
//Converting new string as a Data object encoded as utf-8 which will allow for it to be decoded as JSON
let body = Data(bodyString.utf8)
do {
//JSON decoder code using the Codable objects initialized at beginning of file
let jsonResponse: response = try JSONDecoder().decode(response.self, from: body)
//Checks the "event" field which coincides with the JSON response type received
if (jsonResponse.event == "success_response") {
//Calls PopUpWindow class created in another swift file and displays the token that is created and returned from iFrame
//Note: Extra swift file is simply the popup window initializer that contains all the configurations desired such as size, colors and what information it contains for example
var popUpWindow: PopUpWindow!
popUpWindow = PopUpWindow(title: "Payment Token", text: "\(jsonResponse.data.token)", buttontext: "OK")
self.present(popUpWindow, animated: true, completion: nil)
}
//Used to catch errors thrown that include JSON encode/decode problems
} catch {
//Will print error only to the console of application in Xcode and not to the actual screen
print(error)
}
}
}
}
Using SwiftUI:
(for iOS > 13)
import SwiftUI
import WebKit
//Global variable to hold response token id
var token = ""
//Global variables that are changed based on which merchant is creating a transaction
var appid: String = "T6554252567241061980"
var clientid: String = "01dffeb784c64d098c8c691ea589eb82"
//Codable object that represents first level of response JSON object
struct response: Codable {
let event: String
let data: Body
}
//Codable object that represents second level of response JSON object (is dependent on actual contents and will throw an error if field does not exist when decoding JSON)
struct Body: Codable {
let token: String
}
//View object that handles what is being displayed to screen
struct ContentView: View {
//State variable that allows for Alert to toggle on and off
@State private var showingAlert = false
var body: some View {
//Class that handles all JS iFrame needs
WebView()
//Example of a UI popup to show the resulting token from tokenization response
.alert(isPresented: $showingAlert) {
Alert(title: Text("Response Token"), message: Text("\(token)"), dismissButton: .default(Text("OK")))
}
.onReceive(NotificationCenter.default.publisher(for: WebView.showAlertMsg)) { msg in
self.showingAlert = true // simply change the state of the View
}
}
}
//Class that handles calling JS iFrame and injecting JS script to get response back from tokenization process
struct WebView: UIViewRepresentable {
//Static variable that is used when sending a notification to listener in the ContentView to create our example popup
static let showAlertMsg = Notification.Name("ALERT_MSG")
//Function that sets up WebKit config to handle JS script injection and content controller
func makeUIView(context: Context) -> WKWebView {
//Initial WebKit config and content controller creation
let webConfiguration = WKWebViewConfiguration()
let wkcontentController = WKUserContentController()
//JS script creation and setup for when in process of loading iFrame to inject script
let js: String = "window.addEventListener('message', function(e){ window.webkit.messageHandlers.callbackHandler.postMessage(JSON.stringify(e.data)); });"
let userScript = WKUserScript(source: js, injectionTime: WKUserScriptInjectionTime.atDocumentEnd, forMainFrameOnly: false)
wkcontentController.addUserScript(userScript)
//Setup to allow for ContentController class to find and receive response as a result of JS script injection
wkcontentController.add(context.coordinator, name: "callbackHandler")
webConfiguration.userContentController = wkcontentController
//Required calls to allow iFrame to be displayed on screen
let webView = WKWebView(frame: .zero, configuration: webConfiguration)
context.coordinator.parent = webView
return webView
}
func updateUIView(_ uiView: WKWebView, context: Context) {
//URL to iFrame initialization
let request = URLRequest(url: URL(string: "https://secure.forms.qorcommerce.io/vault/card/token/?app_id=\(appid)&client_id=\(clientid)&profile_id=10203047&request_id=12345909&intent=create&cvv_field=true&button_text=Save%20Payment%20Method&textfield_type=filled")!)
//URL to iFrame loaded
uiView.load(request)
}
//Function that allows ContentController to be handled by an iOS handler
func makeCoordinator() -> ContentController {
ContentController()
}
//Class that contains ContentController functionality
class ContentController: NSObject, WKScriptMessageHandler {
weak var parent: WKWebView?
//Function to handle the content being returned from iFrame JS script injection (in this case it is the JSON responses)
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
//Checking to see if message.body is not empty, and if so then setting a new variable as a String version of it
if let bodyString = message.body as? String {
//Converting new string as a Data object encoded as utf8 which will allow for it to be decoded as JSON
let body = Data(bodyString.utf8)
do {
//JSON decoder code using the Codable objects initialized at beginning of file
let jsonResponse: response = try JSONDecoder().decode(response.self, from: body)
//Checks the "event" field which coincides with the JSON response type received
if (jsonResponse.event == "success_response") {
//Set global "token" variable as the token found in JSON response
token = jsonResponse.data.token
//Send out notification so that the Alert created in ContentView knows to display itself on screen
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
NotificationCenter.default.post(name: WebView.showAlertMsg, object: nil)
}
}
//Used to catch errors thrown that include JSON encode/decode problems
} catch {
//Will print error only to the console of application in Xcode and not to the actual screen
print(error)
}
}
}
}
}
Updated about 2 years ago