Android PG SDK Integration

Step 1: Android PG SDK setup

Install PhonePe PG SDK using Android Studio. To add the SDK to the android app please follow the steps given below:

  1. Add the below code to ‘repositories’ section of your project level build.gradle file
 //https://dash.readme.com/project/phonepe-docs/v1/docs/android-pg-sdk-integration Top-level build file where you can add configuration options common to all sub-projects/modules.

allprojects {
   repositories {
       google()
       maven {
       	url  "https://phonepe.mycloudrepo.io/public/repositories/phonepe-intentsdk-android"
       }
   }
}
  1. Add the below line to the ‘dependencies’ section of your app build.gradle.
implementation 'phonepe.intentsdk.android.release:IntentSDK:2.4.3'

For, our SDK

  • We have compileSdkVersion: 28, minSdkVersion: 21, targetSdkVersion: 28

Step 2: SDK Initialization

  1. Initialize the PhonePe SDK in an Activity that gets launched before a user selects the PhonePe option on your checkout screen.

Note: Don’t initialize in the Application class of your project.

PhonePe.init(Context context, PhonePeEnvironment environment, String merchantId, String appId)
  • PhonePeEnvironment environment can be
    • PhonePeEnvironment.RELEASE
    • PhonePeEnvironment.SANDBOX
PhonePe.init(context, PhonePeEnvironment.SANDBOX, ”REPLACE_WITH_YOUR_MID”, “”)

Note

appId is not mandatory in both the environments. So, you can pass empty String.

  1. Get a list of UPI Apps installed on the user’s device:
try {
	PhonePe.setFlowId("Unique Id of the user") // Recommended, not mandatory , An alphanumeric string without any special character
	List<UPIApplicationInfo> upiApps = PhonePe.getUpiApps();
} catch (PhonePeInitException exception) {
	exception.printStackTrace();
}

UPIApplicationInfo Model

public class UPIApplicationInfo {
	String packageName;
	String applicationName;
	Long version;
}

Flow Id

  • Recommended but not Mandatory.
  • It acts as a common ID between the merchant app user journey and PhonePe SDK.
  • It can be user id, mobile number or any random id which merchant app logs so that there is a track of user between merchant app and PhonePe SDK.
  • It should be alphanumeric string without any special character.
  1. Show your custom UI containing the list of apps obtained from step 2. Make Sure packageName is accessible when the user selects any of the UPI Applications.

Step 3: Server-Side Setup

  1. Use the below PhonePe Pay API endPoint value for the debit request.
String apiEndPoint = "/pg/v1/pay";
  1. Construct the request body and encode it using Base64.
  2. Please note: Integration with the PAY_PAGE is available for all merchants. If you prefer a custom integration (UPI_INTENT, UPI_QR, CARD etc) , please get in touch with your business POC.
{
  "merchantId": "MERCHANTUAT",
  "merchantTransactionId": "transaction_123",
  "merchantUserId": "90223250",
  "amount": 1000,
  "mobileNumber": "9999999999",
  "callbackUrl": "https://webhook.site/callback-url",
  "paymentInstrument": {
    "type": "UPI_INTENT",
    "targetApp": "com.phonepe.app"
  },
  "deviceContext": {
    "deviceOS": "ANDROID"
  }
}
{
    "merchantId": "MERCHANTUAT",
    "merchantTransactionId": "OD620471739210623",
    "merchantUserId": "MU933037302229373",
    "amount": 10000,
    "callbackUrl": "https://webhook.site/callback-url",
    "mobileNumber": "9999999999",
    "paymentInstrument": {
        "type": "CARD",
        "authMode": "3DS",
        "saveCard": false,
        "cardDetails": {
            "encryptedCardNumber": "<encrypted_card_number>",
            "encryptionKeyId": 10,
            "cardHolderName": "Carlos Sainz",
            "expiry": {
                "month":"06",
                "year":"2025"
            },
            "encryptedCvv": "<encrypted_cvv_number>",
            "billingAddress": {
                "line1": "Unit No.001, Ground Floor, Boston House",
                "line2": "Suren Road, Andheri(East)",
                "city": "Mumbai",
                "state": "Maharashtra",
                "zip": "400093",
                "country": "India"
            }
        }
    }
}
{
  "merchantId": "MERCHANTUAT",
  "merchantTransactionId": "MT7850590068188104",
  "merchantUserId": "MUID123",
  "amount": 10000,
  "redirectUrl": "https://webhook.site/redirect-url",
  "redirectMode": "REDIRECT",
  "callbackUrl": "https://webhook.site/callback-url",
  "mobileNumber": "9999999999",
  "paymentInstrument": {
    "type": "NET_BANKING",
    "bankId": "HDFC"
  }
}
{
  "merchantId": "MERCHANTUAT",
  "merchantTransactionId": "MT7850590068188104",
  "merchantUserId": "MUID123",
  "amount": 10000,
  "callbackUrl": "https://webhook.site/callback-url",
  "mobileNumber": "9999999999",
  "paymentInstrument": {
    "type": "PAY_PAGE"
  }
}
ewogICJtZXJjaGFudElkIjogIk1FUkNIQU5UVUFUIiwKICAibWVyY2hhbnRUcmFuc2FjdGlvbklkIjogInRyYW5zYWN0aW9uXzEyMyIsCiAgIm1lcmNoYW50VXNlcklkIjogIjkwMjIzMjUwIiwKICAiYW1vdW50IjogMTAwMCwKICAibW9iaWxlTnVtYmVyIjogIjk5OTk5OTk5OTkiLAogICJjYWxsYmFja1VybCI6ICJodHRwczovL3dlYmhvb2suc2l0ZS9jYWxsYmFjay11cmwiLAogICJwYXltZW50SW5zdHJ1bWVudCI6IHsKICAgICJ0eXBlIjogIlVQSV9JTlRFTlQiLAogICAgInRhcmdldEFwcCI6ICJjb20ucGhvbmVwZS5hcHAiCiAgfSwKICAiZGV2aWNlQ29udGV4dCI6IHsKICAgICJkZXZpY2VPUyI6ICJBTkRST0lEIgogIH0KfQ==
ewogICAgIm1lcmNoYW50SWQiOiAiTUVSQ0hBTlRVQVQiLAogICAgIm1lcmNoYW50VHJhbnNhY3Rpb25JZCI6ICJPRDYyMDQ3MTczOTIxMDYyMyIsCiAgICAibWVyY2hhbnRVc2VySWQiOiAiTVU5MzMwMzczMDIyMjkzNzMiLAogICAgImFtb3VudCI6IDEwMDAwLAogICAgImNhbGxiYWNrVXJsIjogImh0dHBzOi8vd2ViaG9vay5zaXRlL2NhbGxiYWNrLXVybCIsCiAgICAibW9iaWxlTnVtYmVyIjogIjk5OTk5OTk5OTkiLAogICAgInBheW1lbnRJbnN0cnVtZW50IjogewogICAgICAgICJ0eXBlIjogIkNBUkQiLAogICAgICAgICJhdXRoTW9kZSI6ICIzRFMiLAogICAgICAgICJzYXZlQ2FyZCI6IGZhbHNlLAogICAgICAgICJjYXJkRGV0YWlscyI6IHsKICAgICAgICAgICAgImVuY3J5cHRlZENhcmROdW1iZXIiOiAiPGVuY3J5cHRlZF9jYXJkX251bWJlcj4iLAogICAgICAgICAgICAiZW5jcnlwdGlvbktleUlkIjogMTAsCiAgICAgICAgICAgICJjYXJkSG9sZGVyTmFtZSI6ICJDYXJsb3MgU2FpbnoiLAogICAgICAgICAgICAiZXhwaXJ5IjogewogICAgICAgICAgICAgICAgIm1vbnRoIjoiMDYiLAogICAgICAgICAgICAgICAgInllYXIiOiIyMDI1IgogICAgICAgICAgICB9LAogICAgICAgICAgICAiZW5jcnlwdGVkQ3Z2IjogIjxlbmNyeXB0ZWRfY3Z2X251bWJlcj4iLAogICAgICAgICAgICAiYmlsbGluZ0FkZHJlc3MiOiB7CiAgICAgICAgICAgICAgICAibGluZTEiOiAiVW5pdCBOby4wMDEsIEdyb3VuZCBGbG9vciwgQm9zdG9uIEhvdXNlIiwKICAgICAgICAgICAgICAgICJsaW5lMiI6ICJTdXJlbiBSb2FkLCBBbmRoZXJpKEVhc3QpIiwKICAgICAgICAgICAgICAgICJjaXR5IjogIk11bWJhaSIsCiAgICAgICAgICAgICAgICAic3RhdGUiOiAiTWFoYXJhc2h0cmEiLAogICAgICAgICAgICAgICAgInppcCI6ICI0MDAwOTMiLAogICAgICAgICAgICAgICAgImNvdW50cnkiOiAiSW5kaWEiCiAgICAgICAgICAgIH0KICAgICAgICB9CiAgICB9Cn0=
ewogICJtZXJjaGFudElkIjogIk1FUkNIQU5UVUFUIiwKICAibWVyY2hhbnRUcmFuc2FjdGlvbklkIjogIk1UNzg1MDU5MDA2ODE4ODEwNCIsCiAgIm1lcmNoYW50VXNlcklkIjogIk1VSUQxMjMiLAogICJhbW91bnQiOiAxMDAwMCwKICAicmVkaXJlY3RVcmwiOiAiaHR0cHM6Ly93ZWJob29rLnNpdGUvcmVkaXJlY3QtdXJsIiwKICAicmVkaXJlY3RNb2RlIjogIlJFRElSRUNUIiwKICAiY2FsbGJhY2tVcmwiOiAiaHR0cHM6Ly93ZWJob29rLnNpdGUvY2FsbGJhY2stdXJsIiwKICAibW9iaWxlTnVtYmVyIjogIjk5OTk5OTk5OTkiLAogICJwYXltZW50SW5zdHJ1bWVudCI6IHsKICAgICJ0eXBlIjogIk5FVF9CQU5LSU5HIiwKICAgICJiYW5rSWQiOiAiSERGQyIKICB9Cn0=
ewogICJtZXJjaGFudElkIjogIk1FUkNIQU5UVUFUIiwKICAibWVyY2hhbnRUcmFuc2FjdGlvbklkIjogIk1UNzg1MDU5MDA2ODE4ODEwNCIsCiAgIm1lcmNoYW50VXNlcklkIjogIk1VSUQxMjMiLAogICJhbW91bnQiOiAxMDAwMCwKICAiY2FsbGJhY2tVcmwiOiAiaHR0cHM6Ly93ZWJob29rLnNpdGUvY2FsbGJhY2stdXJsIiwKICAibW9iaWxlTnVtYmVyIjogIjk5OTk5OTk5OTkiLAogICJwYXltZW50SW5zdHJ1bWVudCI6IHsKICAgICJ0eXBlIjogIlBBWV9QQUdFIgogIH0KfQ==
  1. Select one of the salt keys and saltIndex. Refer link link for salt & index credentials. Use these values to construct the checksum as follows:
String checksum = sha256(base64Body + apiEndPoint + salt) + ### + saltIndex;
  1. Pass on these values to your Android app in an API.

Step 4: Payment using the SDK

  1. Use the B2BPGRequestBuilder to construct B2BPGRequest from the data obtained from your server as mentioned in Step-3.
B2BPGRequest b2BPGRequest = new B2BPGRequestBuilder()
	.setData(base64Body)
	.setChecksum(checksum)
	.setUrl(apiEndPoint)
	.build();
  1. Define an intent request code for initiating a PhonePe transaction.
private static int B2B_PG_REQUEST_CODE = 777;
  1. Initiate payment using any UPI app.
//For SDK call below function 
try {
	startActivityForResult(PhonePe.getImplicitIntent(
    /* Context */ this, b2BPGRequest, “input package name of upi app to be opened”),B2B_PG_REQUEST_CODE);
} catch(PhonePeInitException e){
}
  1. Override onActivityResult to receive a callback once the PhonePe app exits.
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
 
    if (requestCode == B2B_PG_REQUEST_CODE) {
      
      /*This callback indicates only about completion of UI flow.
            Inform your server to make the transaction
            status call to get the status. Update your app with the
            success/failure status.*/
    
    }   
}

Step 5: Handling callback from PhonePe

In order to receive server to server callback from the PhonePe server, we recommend merchants to pass callbackUrl in the request body of the PAY API. Before considering this response you need to validate the checksum and amount parameter. This will provide the payment status response as success/failure only.

  1. After the transaction is complete:
    Once the transaction is complete, you will get a Base64 Encoded JSON response along with the headers with checksum value. Find the below code snippet:
{
"response":"ewogICJzdWNjZXNzIjogdHJ1ZSwKICAiY29kZSI6ICJQQVlNRU5UX1NVQ0NFU1MiLAogICJtZXNzYWdlIjogIllvdXIgcGF5bWVudCBpcyBzdWNjZXNzZnVsLiIsCiAgImRhdGEiOiB7CiAgICAibWVyY2hhbnRJZCI6ICJNRVJDSEFOVFVBVCIsCiAgICAibWVyY2hhbnRUcmFuc2FjdGlvbklkIjogIjEzMWJlN2RmLWE1NjItNGQxMy05OGZkLTIwOTYxMDYzNzZmZiIsCiAgICAidHJhbnNhY3Rpb25JZCI6ICJUMjIwMzA5MTMyMzM2MjYyNzIzNjEyNyIsCiAgICAiYW1vdW50IjogMTAwLAogICAgInN0YXRlIjogIkNPTVBMRVRFRCIsCiAgICAicmVzcG9uc2VDb2RlIjogIlBBWU1FTlRfU1VDQ0VTUyIsCiAgICAicGF5bWVudEluc3RydW1lbnQiOiB7CiAgICAgICJ2cGEiOiBudWxsLAogICAgICAibWFza2VkQWNjb3VudE51bWJlciI6ICJYWFhYWFhYWFhYODkwMTI1IiwKICAgICAgImlmc2MiOiAiQUFCRjAwMDkwMDkiLAogICAgICAidXRyIjogIjIwNjg1MDY3OTA3MiIsCiAgICAgICJ1cGlUcmFuc2FjdGlvbklkIjogbnVsbAogICAgfQogIH0KfQ=="
}
{ 
    "headers": { 
	    "x-verify":"2abaa82a4810c57dcd6aa52680dd772173b1e40770afe028131f31ddbe5487a8###1"
    }
}
  1. To calculate the checksum in the server to server callback response. Find the code snippet
Content-Type       application/json
X-VERIFY           SHA256(response + salt key) + ### + salt index
  1. To validate the amount, the merchant needs to compare the amount parameter received in the server to server callback response with the amount parameter passed in the Pay API
  2. You need to validate the X-verify. Once both checksum and the amount parameters are validated, order can be fulfilled based on the response. Note that completion of a transaction does not imply that payment is successful. Payment can be in a successful or failed state which needs to be derived from JSON response. Here is the code snippet:
{
  "success": true,
  "code": "PAYMENT_SUCCESS",
  "message": "Your payment is successful.",
  "data": {
    "merchantId": "MERCHANTUAT",
    "merchantTransactionId": "131be7df-a562-4d13-98fd-2096106376ff",
    "transactionId": "T2203091323362627236127",
    "amount": 100,
    "state": "COMPLETED",
    "responseCode": "PAYMENT_SUCCESS",
    "paymentInstrument": {
      "vpa": null,
      "maskedAccountNumber": "XXXXXXXXXX890125",
      "ifsc": "AABF0009009",
      "utr": "206850679072",
      "upiTransactionId": null
    }
  }
}
  1. When the back button is pressed by customer, SDK will return a UI callback to the App.You should take action as mentioned below
UI callbacksServer to Server callbacksCheck Transaction Status APIAction
RESULT_OKSuccess / FailureSuccess / FailureTake the decision based on response.
RESULT_CANCELLEDSuccess / FailureSuccess / FailureTake the decision based on response.
RESULT_CANCELLEDXPayment PendingTemporarily Mark transaction as payment failure.
Timeout is 10 minutes.
Call Check Transaction Status API after 10 to 15 minutes.
If response is failure then mark as failure transaction in the system.
If the response is successful then process the Refund.
RESULT_CANCELLEDXInternal Server Error
Timed Out
Call Check Transaction Status API – Take decisions based on the response received.

Step 6: Validating the X-verify

Once the response is received, you need to validate the X-verify in order to ensure that the response received is not tampered with. In order to validate the X-verify, please follow the below steps:

  • Generate the checksum by using a SHA256 of the encoded body received in the response, salt key and salt index
  • The salt we would use will be appended with ### to the checksum value in the checksum attribute. Upon receiving this payload, you need to look at the salt_index in the checksum attribute after ### delimiter and use the appropriate salt_index to be able to calculate checksum at their end for the said payload. If it doesn’t match, then you need to abort the request.
  • Salts are already pre-shared with you at the time of onboarding
  • If the value of any parameter is null, it will not be included in the calculation of the checksum.