To support UPI Intent on web view of non-SDK based integration, merchant needs to do the following steps.
Note: This solution is for the merchants who have integrated JS Checkout. To know more about JS Checkout integration, please refer to the JS Checkout document.
1. Add the schema of the apps into Info.plist LSApplicationQueriesSchemes(Array).
2. Add the following lines where your web view loads.
webConfiguration.userContentController = WKUserContentController( )
webConfiguration.userContentController.add(self, name: "sendSignalToNative")
3. Conform to protocol WKScriptMessageHandler and define the following variables in your Webview controller:
var txnToken: String = " "
var pspAppsAvailableInDevice : [PspSchema] = [ ]
4. Implement the required function of the protocol.
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
let data = message.body as? [String : Any]
if let json = data, let message = json["message"] as? String {
if let tokenData = json["tokenData"] as? [String : Any], let token = tokenData["token"] as? String{
txnToken = token
}
if message == Signal.callPSPApp.rawValue {
setPSP( )
} else if message == Signal.invokePSPApp.rawValue, let deeplink = json["deeplink"] as? String {
invokeIntentApps(urlString: deeplink)
}
}
}
5. Make a model to parse data from the API.
enum Signal : String {
case callPSPApp = "Execute setUpiIntentApps"
case invokePSPApp = "Invoke PSP app"
}
struct APIResponse : Decodable {
let body: Body
}
struct Body : Decodable {
let pspSchemas: [PspSchema]?
}
struct PspSchema: Decodable {
let displayName, name, icon, scheme: String
}
6. Now add the following helper methods.
i) To convert the array to the dictionary, use below code snippet.
func arrayToDict(apps: [PspSchema]) -> [[String:Any]] {
var dict = [[String:String]]()
for app in apps {
var localDict : [String:String] = [ : ]
localDict["displayName"] = app.displayName
localDict["icon"] = app.icon
localDict["name"] = app.name
localDict["scheme"] = app.scheme
dict.append(localDict)
}
return dict
}
ii) Check which apps are installed on the user’s device.
func checkAppsInDevice( psps: [PspSchema] ) {
pspAppsAvailableInDevice = [ ]
for app in psps {
let fullScheme = app.scheme + "://"
if let url = URL(string: fullScheme), UIApplication.shared.canOpenURL(url) {
self.pspAppsAvailableInDevice.append(app)
}
}
}
iii) Convert the array of installed app names to JSON and send it to our web team to show intent.
func convertIntoJSONString(arrayObject: [Any]) -> String {
do {
let jsonData: Data = try JSONSerialization.data(withJSONObject: arrayObject, options: [ ] )
if let jsonString = NSString(data: jsonData, encoding: String.Encoding.utf8.rawValue){
return jsonString as String
}
} catch let error as NSError {
debugPrint("Array convertIntoJSON - \(error.description)")
}
return ""
}
func setPSP( ) {
fetchPspApps { [unowned self] PSPScheme, message, status in
self.checkAppsInDevice(psps: PSPScheme)
let dataToSent = self.convertIntoJSONString(arrayObject: self.arrayToDict(apps: self.pspAppsAvailableInDevice))
DispatchQueue.main.async {
self.webView.evaluateJavaScript("window.upiIntent.setUpiIntentApps('\(String(describing: dataToSent))');" )
}
}
}
iv) Make the API call
func fetchPspApps(completionHandler: @escaping (([PspSchema], String, Bool) -> Void)) {
executeRequest(urlString: "https://securegw.paytm.in/theia/api/v1/fetchPspApps", httpMethod: "POST") { PSPScheme,_, status in
completionHandler(pspScheme, "", status)
}
}
func executeRequest(urlString: String, httpMethod:String, completionHandler: @escaping (([PspSchema], String, Bool) -> Void) ) {
do {
let url = URL(string: urlString)
guard let requestUrl = url else { return }
var request = URLRequest(url: requestUrl)
request.httpMethod = httpMethod
let bodyParams = [
"head": [
"tokenType":"TXN_TOKEN",
"txnToken": txnToken
],
"body": [ : ]
] as [String : Any]
do {
let data = try JSONSerialization.data(withJSONObject: bodyParams, options: .prettyPrinted)
request.httpBody = data
} catch{
print("🔴", error) // Handle accordingly
}
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let task = URLSession.shared.dataTask(with: request) {(data,response,error) in
guard error == nil else {
print(error!.localizedDescription) // Handle accordingly
completionHandler([ ], "", false)
return
}
guard data != nil else {
completionHandler([ ], "", false)
return
}
do {
let json = try JSONDecoder().decode(APIResponse.self, from: data!)
if let schemas = json.body.pspSchemas, !schemas.isEmpty {
completionHandler(schemas, "", true)
}else{
completionHandler( [ ], "", false)
}
} catch {
completionHandler( [ ], "", false)
}
}
task.resume( )
}
}