iOS PG SDKLess Integration

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>

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