Objective
SDKLess PG UPI Intent request feature for iOS.
Steps to Integrate SDKLess Solution
To support SDKLess integration, deviceContext has to be added to the request body of the pg/v1/pay API call.
Sample Request for PG UPI Open Intent
{
"merchantId": "MERCHANTUAT",
"merchantTransactionId": "MT7850590068188104",
"merchantUserId": "MU933037302229373",
"amount": 10000,
"callbackUrl": "https://webhook.site/callback-url",
"mobileNumber": "9999999999",
"deviceContext": {
"deviceOS": "iOS",
"merchantCallBackScheme": "iOSIntentIntegration"
},
"paymentInstrument": {
"type": "UPI_INTENT",
"targetApp": "PHONEPE"
}
}
{
"merchantId": "MERCHANTUAT",
"merchantTransactionId": "MT7850590068188104",
"merchantUserId": "MU933037302229373",
"amount": 10000,
"callbackUrl": "https://webhook.site/callback-url",
"mobileNumber": "9999999999",
"deviceContext": {
"deviceOS": "iOS",
"merchantCallBackScheme": "iOSIntentIntegration",
},
"paymentInstrument": {
"type": "UPI_INTENT",
"targetApp": "GPAY"
}
}
{
"merchantId": "MERCHANTUAT",
"merchantTransactionId": "MT7850590068188104",
"merchantUserId": "MU933037302229373",
"amount": 10000,
"callbackUrl": "https://webhook.site/callback-url",
"mobileNumber": "9999999999",
"deviceContext": {
"deviceOS": "iOS",
"merchantCallBackScheme": "iOSIntentIntegration",
},
"paymentInstrument": {
"type": "UPI_INTENT",
"targetApp": "PAYTM"
}
}
Redirection after Payment Completion (For PhonePe App )
The merchantCallBackScheme can be passed only for the PhonePe App (“openIntentWithApp”: “PHONEPE”) that helps to open the merchant app after the completion of the Payment.
Redirection after Payment Completion (Other UPI Apps)
For GPay and Paytm, the redirection will be manual after the completion of the payment.
{
"request": "ewogICJtZXJjaGFudElkIjogIk1FUkNIQU5UVUFUIiwKICAibWVyY2hhbnRUcmFuc2FjdGlvbklkIjogIk1UNzg1MDU5MDA2ODE4ODEwNCIsCiAgIm1lcmNoYW50VXNlcklkIjogIk1VOTMzMDM3MzAyMjI5MzczIiwKICAiYW1vdW50IjogMTAwMDAsCiAgImNhbGxiYWNrVXJsIjogImh0dHBzOi8vd2ViaG9vay5zaXRlL2NhbGxiYWNrLXVybCIsCiAgIm1vYmlsZU51bWJlciI6ICI5OTk5OTk5OTk5IiwKICAiZGV2aWNlQ29udGV4dCI6IHsKICAgICJkZXZpY2VPUyI6ICJpT1MiLAoJCSJpc1BQQXBwUHJlc2VudCI6IHRydWUsCgkJIm1lcmNoYW50Q2FsbEJhY2tTY2hlbWUiOiAiaU9TSW50ZW50SW50ZWdyYXRpb24iLAoJCSJhcHBTdXBwb3J0ZWRTY2hlbWFzIjogWwoJCSJwcGVtZXJjaGFudHNka3YxIgoJCV0KICB9LAogICJwYXltZW50SW5zdHJ1bWVudCI6IHsKICAgICJ0eXBlIjogIlVQSV9JTlRFTlQiLAogICAgInRhcmdldEFwcCI6ICJQSE9ORVBFIgogIH0KfQ=="
}
{
"request": "ewogICJtZXJjaGFudElkIjogIk1FUkNIQU5UVUFUIiwKICAibWVyY2hhbnRUcmFuc2FjdGlvbklkIjogIk1UNzg1MDU5MDA2ODE4ODEwNCIsCiAgIm1lcmNoYW50VXNlcklkIjogIk1VOTMzMDM3MzAyMjI5MzczIiwKICAiYW1vdW50IjogMTAwMDAsCiAgImNhbGxiYWNrVXJsIjogImh0dHBzOi8vd2ViaG9vay5zaXRlL2NhbGxiYWNrLXVybCIsCiAgIm1vYmlsZU51bWJlciI6ICI5OTk5OTk5OTk5IiwKICAiZGV2aWNlQ29udGV4dCI6IHsKICAgICJkZXZpY2VPUyI6ICJpT1MiLAogICAgIm1lcmNoYW50Q2FsbEJhY2tTY2hlbWUiOiAiaU9TSW50ZW50SW50ZWdyYXRpb24iLAogIH0sCiAgInBheW1lbnRJbnN0cnVtZW50IjogewogICAgInR5cGUiOiAiVVBJX0lOVEVOVCIsCiAgICAidGFyZ2V0QXBwIjogIlBBWVRNIgogIH0KfQ=="
}
{
"request": "ewogICJtZXJjaGFudElkIjogIk1FUkNIQU5UVUFUIiwKICAibWVyY2hhbnRUcmFuc2FjdGlvbklkIjogIk1UNzg1MDU5MDA2ODE4ODEwNCIsCiAgIm1lcmNoYW50VXNlcklkIjogIk1VOTMzMDM3MzAyMjI5MzczIiwKICAiYW1vdW50IjogMTAwMDAsCiAgImNhbGxiYWNrVXJsIjogImh0dHBzOi8vd2ViaG9vay5zaXRlL2NhbGxiYWNrLXVybCIsCiAgIm1vYmlsZU51bWJlciI6ICI5OTk5OTk5OTk5IiwKICAiZGV2aWNlQ29udGV4dCI6IHsKICAgICJkZXZpY2VPUyI6ICJpT1MiLAogICAgIm1lcmNoYW50Q2FsbEJhY2tTY2hlbWUiOiAiaU9TSW50ZW50SW50ZWdyYXRpb24iLAogIH0sCiAgInBheW1lbnRJbnN0cnVtZW50IjogewogICAgInR5cGUiOiAiVVBJX0lOVEVOVCIsCiAgICAidGFyZ2V0QXBwIjogIlBBWVRNIgogIH0KfQ=="
}
Sample Response for PG UPI Open Intent
{
"success": true,
"code": "PAYMENT_INITIATED",
"message": "Payment initiated",
"data": {
"merchantId": "MERCHANTUAT",
"merchantTransactionId": "MT7850590068188104",
"instrumentResponse": {
"type": "UPI_INTENT",
"intentUrl": "ppe://pay?pa=MERCHANTUAT@ybl&pn=MerchantUAT&am=1.00&mam=1.00&tr=MT7850590068188104&tn=Payment%20for%20MT7850590068188104&mc=5311&mode=04&purpose=00&utm_campaign=B2B_PG&utm_medium=JUSPAYUAT&utm_source=MT7850590068188104"
}
}
}
{
"success": true,
"code": "PAYMENT_INITIATED",
"message": "Payment initiated",
"data": {
"merchantId": "JUSPAYUAT",
"merchantTransactionId": "04b4307c-4f2a-400a-bbf4-983bf892cb92",
"instrumentResponse": {
"type": "UPI_INTENT",
"intentUrl": "gpay://upi/pay?pa=JUSPAYUAT@ybl&pn=JUSPAYUAT&am=9.00&mam=9.00&tr=04b4307c-4f2a-400a-bbf4-983bf892cb92&tn=Payment%20for%2004b4307c-4f2a-400a-bbf4-983bf892cb92&mc=5311&mode=04&purpose=00&utm_campaign=B2B_PG&utm_medium=JUSPAYUAT&utm_source=04b4307c-4f2a-400a-bbf4-983bf892cb92"
}
}
}
{
"success": true,
"code": "PAYMENT_INITIATED",
"message": "Payment initiated",
"data": {
"merchantId": "JUSPAYUAT",
"merchantTransactionId": "7d673027-5d0d-424b-abb5-884727db4060",
"instrumentResponse": {
"type": "UPI_INTENT",
"intentUrl": "paytmmp://upi/pay?pa=JUSPAYUAT@ybl&pn=JUSPAYUAT&am=9.00&mam=9.00&tr=7d673027-5d0d-424b-abb5-884727db4060&tn=Payment%20for%207d673027-5d0d-424b-abb5-884727db4060&mc=5311&mode=04&purpose=00&utm_campaign=B2B_PG&utm_medium=JUSPAYUAT&utm_source=7d673027-5d0d-424b-abb5-884727db4060"
}
}
}
Handling UPI Apps on the Checkout Page
Merchants should display only the list of the UPI Apps installed on the user’s device.
Integration Steps
iOS PG SDKLess App Side Implementation
Step 1. In your Info.plist, create or append a new Array type node LSApplicationQueriesSchemes to append the following values:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>ppemerchantsdkv1</string>
<string>ppemerchantsdkv2</string>
<string>ppemerchantsdkv3</string>
<string>paytmmp</string>
<string>gpay</string>
</array>
Step 2. Create DeepLink Schema
Create a URLType for your app (Deeplink), if not already present.
For example, we have used: iOSIntentIntegration. (You can create your own identifier for your app)
URLScheme should match the below conditions
- Only Alphabets (lower and upper case) and numbers are allowed.
- We can allow special characters only like dot and Hyphen
- The name should always start with alphabets.
The schema should be correct to redirect the app otherwise it will not redirect back to the merchant app.
Code Snippet to Generate “deviceContext”
To find appSupportedSchema + isPPAppPresent value, the below code snippet has to be used to check it or to generate deviceContext.
Usage: PhonePeContextGenerator(callbackSchema: “demoAppSchema”).getDeviceContext()
class PhonePeContextGenerator {
private let callbackSchema: String?
init(callbackSchema: String?) {
self.callbackSchema = callbackSchema
}
private struct UriSchemeConstants {
static let uriScheme1 = "ppemerchantsdkv1"
static let uriScheme2 = "ppemerchantsdkv2"
static let uriScheme3 = "ppemerchantsdkv3"
static let hyphenation = "://"
}
private func isPhonePeInstalled() -> Bool {
DispatchQueue.main.sync {
guard let openUrl1 = URL(string: UriSchemeConstants.uriScheme1 + UriSchemeConstants.hyphenation),
let openUrl2 = URL(string: UriSchemeConstants.uriScheme2 + UriSchemeConstants.hyphenation),
let openUrl3 = URL(string: UriSchemeConstants.uriScheme3 + UriSchemeConstants.hyphenation) else {
return false
}
let appInstalled = UIApplication.shared.canOpenURL(openUrl1) ||
UIApplication.shared.canOpenURL(openUrl2) ||
UIApplication.shared.canOpenURL(openUrl3)
return appInstalled
}
}
private func getAppSupportedSchema() -> [String] {
var supportedAppSchemas: [String] = []
DispatchQueue.main.sync {
[UriSchemeConstants.uriScheme1, UriSchemeConstants.uriScheme2, UriSchemeConstants.uriScheme3].forEach { (scheme) in
if let openUrl = URL(string: scheme + UriSchemeConstants.hyphenation),
UIApplication.shared.canOpenURL(openUrl) {
supportedAppSchemas.append(scheme)
}
}
}
return supportedAppSchemas
}
func getDeviceContext() -> [String: Any] {
var context: [String: Any] = [:]
context["appSupportedSchemas"] = getAppSupportedSchema()
context["isPPAppPresent"] = isPhonePeInstalled()
context["deviceOS"] = "iOS"
if let schema = callbackSchema {
context["merchantCallBackScheme"] = schema
}
return context
}
}
Code Snippet to Check App Availability
PhonePe App
private struct UriSchemeConstants {
static let uriScheme1 = "ppemerchantsdkv1"
static let uriScheme2 = "ppemerchantsdkv2"
static let uriScheme3 = "ppemerchantsdkv3"
static let hyphenation = "://"
}
func isPhonePeInstalled() -> Bool {
DispatchQueue.main.sync {
guard let openUrl1 = URL(string: UriSchemeConstants.uriScheme1 + UriSchemeConstants.hyphenation),
let openUrl2 = URL(string: UriSchemeConstants.uriScheme2 + UriSchemeConstants.hyphenation),
let openUrl3 = URL(string: UriSchemeConstants.uriScheme3 + UriSchemeConstants.hyphenation) else {
return false
}
let appInstalled = UIApplication.shared.canOpenURL(openUrl1) ||
UIApplication.shared.canOpenURL(openUrl2) ||
UIApplication.shared.canOpenURL(openUrl3)
return appInstalled
}
}
GPay App
private struct UriSchemeConstants {
static let uriScheme1 = "gpay"
static let hyphenation = "://"
}
func isGPayInstalled() -> Bool {
DispatchQueue.main.sync {
guard let openUrl1 = URL(string: UriSchemeConstants.uriScheme1 + UriSchemeConstants.hyphenation) else {
return false
}
let appInstalled = UIApplication.shared.canOpenURL(openUrl1)
return appInstalled
}
}paytmmp
Paytm App
private struct UriSchemeConstants {
static let uriScheme1 = "paytmmp"
static let hyphenation = "://"
}
func isPaytmInstalled() -> Bool {
DispatchQueue.main.sync {
guard let openUrl1 = URL(string: UriSchemeConstants.uriScheme1 + UriSchemeConstants.hyphenation) else {
return false
}
let appInstalled = UIApplication.shared.canOpenURL(openUrl1)
return appInstalled
}
}