The steps to integrate iOS PG SDK on the native iOS App.
1. PreRequisites
2. iOS PG SDK setup
3. iOS PG App Side Implementation
4. iOS PG Server Side Implementation
5. Code Snippet to Check App Availability
1. PreRequisites
- XCode 12.5 or newer
- Cocoapod
- Onboarded on PhonePe as a Merchant (Given a MerchantID, & Saltkeys to generate the checksum on your servers)
- PhonePeAppId (Mandatory to pass in SDK initialization)
2. iOS PG SDK setup
The PhonePePayment framework is needed for the SDK to work for both Sandbox (Testing) and Production (Deployment) environments.
Step 1. The PhonePePayment SDK is available through CocoaPods [Refer to this link ]
Latest Phonepe iOS SDK Version: 2.7.3
Step 2. Installation
To install it, simply add the following line to your Podfile:
pod 'PhonePePayment'
then run
pod install
Step 3. Import the framework in your project using “import PhonePePayment”
Remove the “DirectPaymentSDK” from your project if you have already integrated and want to migrate to CocoaPods.
3. iOS PG 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. Update PhonePe App Id
Pass the PhonePe App ID value in the init method using the key: appId. Refer to Step 5
To Get AppId
Share the Apple Team ID with the PhonePe Integration team to generate the AppId to be used.
Step 3. 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.
Step 4. Create DPSTransactionRequest Object
Using the Payload from your server, create a DPSTransactionRequest object, with appSchema as the URLType created in the step above.
Example: appSchema : iOSIntentIntegration
Step 5. Initialize the PG SDK
Initialize the PPPayment and pass the DPSTransactionRequest object to the SDK. After the payment is complete and a callback is received back to your app on the completionHandler, check the status of the transaction with your backend.
let headers: [String: String] = [:]
let request : DPSTransactionRequest = DPSTransactionRequest (
body : base64EncodedString,
apiEndPoint : apiEndPoint,
checksum : payloadChecksum,
headers : headers,
appSchema : "iOSIntentIntegration"
);
PPPayment(environment: .sandbox, enableLogging: true, appId: phonePeAppId)
.startPG(transactionRequest: request, on: self, animated: true)
{ _, result in
let text = "\(result)"
print(text)
print("Completion:---------------------")
// Indicates the UI Callback is given back to the Merchant app.
//Now, check the status with your server and update it to the user accordingly
}
To Enable Debug Logs
Update the PPPayment.enableDebugLogs = true or enableLogging flag to true in startPG method.
Step 6. Setup the Environment
Set the environment as .sandbox to test in the UAT Simulator environment. While moving to Production, set the environment as .production.
Step 7. In your AppDelegate, check for a callback from the PhonePe app and if found, pass it to the SDK.
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
let handled = PPPayment.checkDeeplink(url, options: options)
if handled {
// Phonepe is handling this, no need for any processing
return true
}
//Process your own deeplinks here
return true
}
4. iOS PG Server Side Implementation
Step 1. Save the below-assigned value on your server
String apiEndPoint = "/pg/v1/pay";
Step 2. Construct the request body and encode it in Base 64 at your server as follows:
{
"merchantId": "MERCHANTUAT",
"merchantTransactionId": "MT7850590068188104",
"merchantUserId": "MU933037302229373",
"amount": 10000,
"callbackUrl": "https://webhook.site/callback-url",
"mobileNumber": "9999999999",
"deviceContext": {
"deviceOS": "IOS"
},
"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"
},
"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"
},
"paymentInstrument": {
"type": "UPI_INTENT",
"targetApp": "PAYTM"
}
}
{
"request": "ewogICJtZXJjaGFudElkIjogIk1FUkNIQU5UVUFUIiwKIAkibWVyY2hhbnRUcmFuc2FjdGlvbklkIjogIk1UNzg1MDU5MDA2ODE4ODEwNCIsCiAgIm1lcmNoYW50VXNlcklkIjogIk1VOTMzMDM3MzAyMjI5MzczIiwKICAiYW1vdW50IjogMTAwMDAsCiAgImNhbGxiYWNrVXJsIjogImh0dHBzOi8vd2ViaG9vay5zaXRlL2NhbGxiYWNrLXVybCIsCiAgIm1vYmlsZU51bWJlciI6ICI5OTk5OTk5OTk5IiwKICAiZGV2aWNlQ29udGV4dCI6IHsKICAgICJkZXZpY2VPUyI6ICJJT1MiCiAgfSwKICAicGF5bWVudEluc3RydW1lbnQiOiB7CiAgICAidHlwZSI6ICJVUElfSU5URU5UIiwKICAgICJ0YXJnZXRBcHAiOiAiUEhPTkVQRSIKICB9Cn0="
}
{
"request": "ewogICJtZXJjaGFudElkIjogIk1FUkNIQU5UVUFUIiwKICAibWVyY2hhbnRUcmFuc2FjdGlvbklkIjogIk1UNzg1MDU5MDA2ODE4ODEwNCIsCiAgIm1lcmNoYW50VXNlcklkIjogIk1VOTMzMDM3MzAyMjI5MzczIiwKICAiYW1vdW50IjogMTAwMDAsCiAgImNhbGxiYWNrVXJsIjogImh0dHBzOi8vd2ViaG9vay5zaXRlL2NhbGxiYWNrLXVybCIsCiAgIm1vYmlsZU51bWJlciI6ICI5OTk5OTk5OTk5IiwKICAiZGV2aWNlQ29udGV4dCI6IHsKICAgICJkZXZpY2VPUyI6ICJJT1MiCiAgfSwKICAicGF5bWVudEluc3RydW1lbnQiOiB7CiAgICAidHlwZSI6ICJVUElfSU5URU5UIiwKICAgICJ0YXJnZXRBcHAiOiAiR1BBWSIKICB9Cn0="
}
{
"request": "ewogICJtZXJjaGFudElkIjogIk1FUkNIQU5UVUFUIiwKICAibWVyY2hhbnRUcmFuc2FjdGlvbklkIjogIk1UNzg1MDU5MDA2ODE4ODEwNCIsCiAgIm1lcmNoYW50VXNlcklkIjogIk1VOTMzMDM3MzAyMjI5MzczIiwKICAiYW1vdW50IjogMTAwMDAsCiAgImNhbGxiYWNrVXJsIjogImh0dHBzOi8vd2ViaG9vay5zaXRlL2NhbGxiYWNrLXVybCIsCiAgIm1vYmlsZU51bWJlciI6ICI5OTk5OTk5OTk5IiwKICAiZGV2aWNlQ29udGV4dCI6IHsKICAgICJkZXZpY2VPUyI6ICJJT1MiCiAgfSwKICAicGF5bWVudEluc3RydW1lbnQiOiB7CiAgICAidHlwZSI6ICJVUElfSU5URU5UIiwKICAgICJ0YXJnZXRBcHAiOiAiUEFZVE0iCiAgfQp9"
}
Step 3. Checksum Calculation
Select one of the salts shared with you and note its index. Construct the X-verify at your server as follows:
String checksum = sha256(base64Body + apiEndPoint + salt) + ### + saltIndex;
Step 4. Check the payment status
Once the payment is completed, please call the Check Transaction Status API to validate the response received via the App. You can call the Check Transaction Status at regular intervals to fetch the response from the PhonePe server in case a response is not received in the application even after 10 minutes of initiating the application.
Step 5. Handling Payment Status
The payment status can be Successful, Failed, Pending or any of the codes. For Pending, you should retry until the status changes to Successful or Failed.
5. 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
}
}
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
}
}