iOS SDK Setup

In this part we will talk about how you can integrate iOS SDK and initiate payment:

  • XCode 12.5 or newer
  • Cocoapod
  • Onboarded on PhonePe as a Merchant (Given a MerchantID, Client Secret and version to generate the o-auth token )
  • The PhonePePayment framework is needed for the SDK to work for both Sandbox (Testing) and Production (Deployment) environments.
    • The PhonePePayment SDK is available through CocoaPods [Refer to this link ], Latest Phonepe iOS SDK Version: 5.0.0.
    • To install it, simply add the following line to your Podfile:
pod 'PhonePePayment'
then run:
pod install
  • 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.
import PhonePePayment

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>
	<string>credpay</string>
	<string>amazonpay</string>
	<string>bhim</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.

Step 3. Create B2BPGTransactionRequest Object

Using the Payload from your server, create a B2BPGTransactionRequest object, with appSchema as the URLType created in the step above.
Example: appSchema : iOSIntentIntegration

To Enable Debug Logs

Update the PPPayment.enableDebugLogs = true or enableLogging flag to true in startPG method.

Environment :

  • .Production for Live
  • .Sandbox for Sandbox testing

Step 4. Initialize the PG SDK

let ppPayment = PPPayment(environment: .production,
                          flowId: "FLOW_ID",
                          merchantId: "MERCHANT_ID")
Parameter NameTypeDescription
environmentEnum.production for Production
.sandbox for PreProd
flowIdStringPass the merchant user Id or the unique string (UUID().uuidString) for every init for analytics purposes.
merchantIdStringThe merchant Id provided by PhonePe
enableLoggingBoolean[Optional Parameter]
– True 
(To enable the SDK logs)
– False
 (To disable the SDK logs)
Note: In Prod, make sure to set as False.
  • Initialize the PPPayment with the environment
  • The B2BPGTransactionRequest is an object that you need to pass the SDK and SDK will handle the request and get back the response after the payment completion.
  • In the completion handle you get the state of the payment. After that, You need to call the status API from the backend to get the status of the transaction.

Pass the merchant ID, Order ID and the Token generated using create order API here along with payment mode and app schema details.

public func startTransaction(request: PhonePePayment.B2BPGTransactionRequest, on viewController: UIViewController, completion: @escaping PhonePePayment.PPTransactionCompletion)

Example:
ppPayment.startTransaction(request: request,
                            on: vc) { _, state in
   print(state)
}


//In B2BPGTransactionRequest, We need 5 parameters to init the object.  

public struct B2BPGTransactionRequest {
    public init(merchantId: String, orderId: String, token: String, appSchema: String, paymentMode: PhonePePayment.PaymentMode)
}

//Example below for B2BPGTransactionRequest for UPI Intent Payment Mode:
let request = B2BPGTransactionRequest(merchantId: "TEST",
                                      orderId: "TXT",
                                      token: token,
                                      appSchema: "YOUR_APP_SCHEMA",
                                      paymentMode: .upiIntent(request: UPIIntentPaymentMode(targetApp: "PHONEPE")))
Parameter NameTypeDescription
merchantIdStringProvided by PhonePe
orderIdStringThe PhonePe Order ID received in the Create Order API response.
tokenStringThe order token received in the backend from Create Order API call.
appSchemaLongThe app schema that you need to pass for redirect to the merchant app after the payment succeeded.
paymentModeObjectThe Payment mode that you need to pass as per your use case.

Use the particular instrument that you need for your request.

public enum PaymentMode {
    case upiIntent(request: PhonePePayment.UPIIntentPaymentMode)
    case upiCollect(request: PhonePePayment.UpiCollectPaymentMode)
    case netBanking(request: PhonePePayment.NetbankingPaymentMode)
    case newCard(request: PhonePePayment.NewCardPaymentMode)
}
public struct UPIIntentPaymentMode {
    public init(targetApp: String)
}
//For targetApp parameter - Allowed Values:[PHONEPE, GPAY, PAYTM, CRED, AMAZON, BHIM]
public struct UpiCollectPaymentMode {
    public init(message: String, details: PhonePePayment.PGBaseCollect)
}

// VPA Model that is needed for Collect Payment
public struct VPACollectDetails : PhonePePayment.PGBaseCollect {
    public private(set) var type: String? { get }
    public init(vpa: String)
    public func encode(to encoder: Encoder) throws
}

// PhoneNumber Model is needed for Collect Payment
public class PhoneNumberDetails : PhonePePayment.PGBaseCollect {
    public private(set) var type: String? { get }
    public init(phoneNumber: String)
    public func encode(to encoder: Encoder) throws
}

Note: When you initialise the collect payment, you need to pass the VPA or phoneNumber. We have two models under collect, that you need to pass as per your use case.

public struct NetbankingPaymentMode {
    
public 
init(bankId: String? = nil)
}

⚠️ Compliance for SDK Payments!


To accept card payments via the SDK, you must be either PCI or SAQ-A-EP compliant.

public struct NewCardPaymentMode {
    public init(cardNumber: String, 
    cardHolderName: String, 
    cvv: String,
    expiryMonth: String,
    expiryYear: String, 
    merchantUserId: String)
}
Parameters
ParameterDataTypeDescription
cardNumberStringCard number entered by user.
cardHolderNameStringCard holder name entered by user.
cvvStringCard’s CVV or security code.
expiryMonthStringCard’s expiration month entered in mm format.
expiryYearStringCard’s expiration year entered in yyyy format.
merchantUserIdStringYour unique user ID or a random string you’ve generated for this transaction.

Now you need to call the validateCardDetails method, to check the card details before calling the startTransaction:

public func validateCardDetails(mode: PhonePePayment.NewCardPaymentMode, completion: @escaping (_ isValid: Bool, _ errorMessage: String?) -> Void)
Example code:
ppPayment.validateCardDetails(mode: NewCardPaymentMode) { (isValid, errorMessage) in
            if isValid {
                // StartTransaction
            } else {
                consoleLog(errorMessage.emptyIfNil, type: .error)
            }
  }

Card Validation

  • If a customer enters invalid card details like a wrong card number, CVV, expiry date, or cardholder name, we will let you know with a specific error message. In this case, the system will show that the card details are not valid.
  • You can share this error message with your customer so they can correct the issue and try again.

Successful Transaction

  • Once the customer enters all the correct card information, the system will confirm that the details are valid. Only then can you proceed by calling the startTransaction method to begin the payment.

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)
    if handled {
       // Phonepe is handling this, no need for any processing
       return true
   }
   //Process your own deeplinks here
   return true
}

After the method calls, you will get the control in the completion block.

Once the redirection back to the merchant app then back to the Order Status API /check for webhook response accordingly update the original status to Users.

Code Snippet to Check App Availability

PhonePe App

Swiftprivate 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

Swiftprivate 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

Swiftprivate 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
  }
}
Is this article helpful?