Juspay Developer Guide

Welcome to the Juspay Developer Guide. You'll find comprehensive guides and documentation to help you start working with Juspay Docs as quickly as possible, as well as support if you get stuck. Let's jump right in!

Get Started

iOS Integration

Introduction

JusPay Safe Browser (codename: Godel) aims to reduce friction in Second Factor Authentication for Cards and Netbanking.

The library provides various convenience and security features for users to be able to complete the transaction quickly. The library also provides much deeper insight into the events occurring in the payment flow. With Godel, you will be able to provide a pleasing payments experience to your iOS users.

This documentation explains the steps to integrate the library into your native iOS application.

Get SDK

Using cocoapods (recommended)

The recommended installation mechanism for JuspaySafeBrowser is via CocoaPods. CocoaPods is an Objective-C library dependency manager (Link).

Add pod "JuspaySafeBrowser" to your profile. (Latest version is 0.1.57)

pod 'JuspaySafeBrowser'

Run pod install.

You should now be able to add SDK to any of your target's source files and begin using JuspaySafeBrowser SDK.

Add a Bridging header

You can easily use the SDK with Swift using Objective-C Bridging Header. Apple has some nice documentation on the subject.

To begin, create a new file (File > New > File > iOS > Source > Header File) and name it YourProjectName-Bridging-Header.h.

#import <JuspaySafeBrowser/JuspaySafeBrowser.h>
#import <JuspaySafeBrowser/UIViewController+BackButtonHandler.h>

Start the browser

Pre-requisites

Before being able to start the browser, you have to create the following

  1. Setup a mechanism to tell when the payment ends
  2. Create the parameters bundle to initialize the browser correctly
  3. Start the browser in a new view controller
  4. Override back button is compulsory.

Note: Minimum supported OS version is 8.0

The below sections explain how to achieve these steps.

Starting the browser

Import Framework

#import <JuspaySafeBrowser/JuspaySafeBrowser.h>

Create a property for JuspaySafeBrowser.

@property (nonatomic, strong) JuspaySafeBrowser *browser;
let browser: JuspaySafeBrowser = JuspaySafeBrowser()

Initialize the property before using.

//add the below code to viewDidLoad of view controller where payment will start.

self.browser = [[JuspaySafeBrowser alloc] init];

Back button handling (Required)

We need access to the back button for the view controller where payment will start which cannot be done if you have interactive Pop gesture enabled. To disable it for the current view controller.

//Add to viewDidAppear

if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
      self.navigationController.interactivePopGestureRecognizer.enabled = NO;
}
//Add to viewDidAppear

if((self.navigationController?.respondsToSelector(Selector("interactivePopGestureRecognizer"))) != nil) {
    self.navigationController?.interactivePopGestureRecognizer?.enabled = false;
}

To renable interactive pop gesture.

//Add to viewWillDisappear

if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
    self.navigationController.interactivePopGestureRecognizer.enabled = YES;
}
//Add to viewWillDisappear

if((self.navigationController?.respondsToSelector(Selector("interactivePopGestureRecognizer"))) != nil) {
    self.navigationController?.interactivePopGestureRecognizer?.enabled = true;
}

For default Push Navigation style

Add the navigationShouldPopOnBackButton method somewhere to check if controller is allowed to pop.

- (BOOL)navigationShouldPopOnBackButton {
    [self.browser backButtonPressed];
    return self.browser.isControllerAllowedToPop;
}
override func navigationShouldPopOnBackButton() -> Bool {
    self.browser.backButtonPressed()
    return self.browser.isControllerAllowedToPop;
}

For a custom Navigation Bar

Call backButtonPressed method on back/cancel button tap event.

[self.browser backButtonPressed];
self.browser.backButtonPressed()

Create BrowserParams Object

BrowserParams class provides an easy-to-use interface to add all the parameters that must be sent to JusPay Safe browser. Create an instance of the class to get started:

BrowserParams *browserParams = [[BrowserParams alloc] init];
let browserParams:BrowserParams = BrowserParams()

Find below the set of parameters which must be sent:

Variable Description Type Required
url Start URL for payment String Yes
postData POST parameters that must be passed to the URL String No
merchantId Identifies the merchant. Eg. 'merchant' String Yes
clientId merchantId followed by platform name. Eg. 'merchant_ios' String Yes
transactionId Represents the current transactionId String Yes
orderId Represents the order number assigned by the merchant String Yes
amount Amount of the transaction String Yes
customerId Unique identifier of the customer String No
customerEmail Email address of the customer String Yes
customerPhoneNumber Mobile number of the customer String No
displayNote Short note about transaction shown to the customer. ex. 'Paying INR 200 for Order 123456' String No
remarks Remarks about transaction. This will be automatically filled up in the bank page. String No
cookies Cookies need to be set in WebView Array of dictionaries No
customActivityIndicator Activity indicator to show on merchant page View No
confirmationAlertContents Contents of confirmation alert view to show while cancelling the transaction Array No

See the example below:

//Array of endURLs to be checked for completion of transaction.
NSArray *endUrlRegexes = @[@"<Regex_of_your_choice>"];
browserParams.endUrlRegexes = endUrlRegexes;

// client identification
browserParams.clientId = @"<client_id>";
browserParams.merchantId = @"<merchant_id>";
browserParams.transactionId = @"<transaction_id>";

// customer identification
browserParams.customerId = @"<customer_id>";
browserParams.customerEmail = @"<customer_email>";
browserParams.customerPhoneNumber = @"<customer_phone_number>";

// order meta information
browserParams.displayNote = @"<Dispay_note_of_your_choice>";
browserParams.remarks = @"<desired_remarks>";

// authentication data
browserParams.url = @"<url>";
browserParams.postData = @"<post_data>";

// cookies
NSDictionary *cookieProperties = [NSDictionary dictionaryWithObjectsAndKeys:
                                      @"<cookie_name>", NSHTTPCookieName,
                                      @"<cookie_value>", NSHTTPCookieValue,
                                      @"<cookie_domain>", NSHTTPCookieDomain,
                                      @"<cookie_path>", NSHTTPCookiePath,
                                      <cookie_expires>, NSHTTPCookieExpires, nil];
browserParams.cookies = @[cookieProperties];

// UX customization
NSMutableDictionary *customParameters = [[NSMutableDictionary alloc] init];
[customParameters setValue:@"<custom_value>" forKey:@"<custom_key>"];
browserParams.customParameters = customParameters;

UIView *activityIndicator = [[UIView alloc] ...];
...
//Adding subviews and customizing activityIndicator
...
browserParams.customActivityIndicator = activityIndicator;
browserParams.confirmationAlertContents = @[@"<title>",
                                            @"<message>",
                                            @"<yes_button_title>",
                                            @"<no_button_title>"];
//Array of endURLs to be checked for completion of transaction.
let endUrlRegexes = ["<Regex_of_your_choice>"]
browserParams.endUrlRegexes = endUrlRegexes

// Client identification
browserParams.clientId = "<client_id>"
browserParams.merchantId = "<merchant_id>"
browserParams.transactionId = "<transaction_id>"

// customer identification
browserParams.customerId = "<customer_id>"
browserParams.customerEmail = "<customer_email>"
browserParams.customerPhoneNumber = "<customer_phone_number>"

// order meta information
browserParams.displayNote = "<Dispay_note_of_your_choice>"
browserParams.remarks = "<desired_remarks>"

// authentication data
browserParams.url = netBankingURL
browserParams.postData = netBankingPostData

// cookies
let cookieProperties : NSDictionary = [
            NSHTTPCookieName : "<cookie_name>",
            NSHTTPCookieValue : "<cookie_value>",
            NSHTTPCookieDomain : "<cookie_domain>",
            NSHTTPCookiePath : "<cookie_path>",
            NSHTTPCookieExpires : <cookie_expires>
]
browserParams.cookies = [cookieProperties]

// UX customization
let customParameters = NSMutableDictionary()
customParameters.setValue("<custom_value>", forKey: "<custom_key>")
browserParams.customParameters = customParameters as [NSObject : AnyObject]

let activityIndicator = UIView(...)
...
//Adding subviews and customizing activityIndicator
...
browserParams.customActivityIndicator = activityIndicator
browserParams.confirmationAlertContents = ["<title>",
                                          "<message>",
                                          "<yes_button_title>",
                                          "<no_button_title>"]

All values in postData should be URL encoded before setting browserParams.postData = postData;. If you do not URL encode the values, then your gateway/aggregator will show error message in the browser.

Browser Properties (Optional)

Variable Description Type
shouldLoadEndURL Set it as true if EndURL should be loaded Boolean
shouldNotPopOnEndURL Set it as true if the browser should not be popped when EndURL is reached Boolean
shouldNotPopAfterPayment Set it as true if the browser should not be popped after payment canceled or failed Boolean

See the example below:

self.browser.shouldLoadEndURL = TRUE
self.browser.shouldNotPopOnEndURL = TRUE
self.browser.shouldNotPopAfterPayment = TRUE
self.browser.shouldLoadEndURL = true
self.browser.shouldNotPopOnEndURL = true
self.browser.shouldNotPopAfterPayment = true

Browser Delegate

JuspaySafeBrowserDelegate protocol gives url loading status. Setup delegate using jpBrowserDelegate property.

See the example below:

self.browser.jpBrowserDelegate = self

Optional delegation methods in JuspaySafeBrowserDelegate:

//To check if url should be loaded. Return true if url should be loaded, else false.
- (BOOL)browserShouldStartLoadingUrl:(NSURL * _Nullable)url;

//Indicates start of a url loading.
- (void)browserDidStartLoadingUrl:(NSURL * _Nullable)url;

//Indicates finish of a url loading.
- (void)browserDidFinishLoadUrl:(NSURL * _Nullable)url;

//Indicates failure of a url loading.
- (void)browserDidFailLoadingUrl:(NSURL* _Nullable)url withError:(NSError *_Nullable)error;
//To check if url should be loaded. Return true if url should be loaded, else false.
func browserShouldStartLoadingUrl(url: NSURL?) -> Bool

//Indicates start of a url loading.
func browserDidStartLoadingUrl(url: NSURL?)

//Indicates finish of a url loading.
func browserDidFinishLoadUrl(url: NSURL?)

//Indicates failure of a url loading.
func browserDidFailLoadingUrl(url: NSURL?, withError error: NSError?)

Start the Payment process

Now, start the browser and wait for it to complete. This starts the payment process and gives the callback when aborted or successful.

Recommending to call this function in ViewController's viewDidAppear function and pass the root view of the ViewController for the view parameter.

// In viewDidAppear
[self.browser startpaymentWithJuspayInView:self.view
              withParameters:browserParams
              callback:^(BOOL status, NSError *error, id info) {

}];
// In viewDidAppear
self.browser.startpaymentWithJuspay(in: self.view, withParameters: browserParams) { (status, error, info) in
                                                                   }

view is the view in which payment will start.
browserParams is the browser parameters object for this request.
callback is callback which you receive when the trasaction has reached endURL or cancelled by user.
staus is true if transaction has reached one of the endURLRegex else false;
error shows the reason for failure. For list of error codes check Error Codes
info is a NSDictionary which contains all the info like EndUrl , TransactionID (passed in browser params), etc.

To make the browser's view fit into the bounds, set ViewController's edgesForExtendedLayout property as [] (empty array) inside viewDidLoad

Instantiate BrowserCallback

Callback will be invoked when a user completes the payment or cancels it midway. Whenever a payment is completed (successfully or not), the aggregator/gateway will send the user back to your website. Usually, you would pass single return_url or multiple URLs to aggregator to represent success, failure or cancel.

JusPay Browser expects endUrlRegexes as an input. The input technique will be explained in the section below. This variable represents an array of String which are regular expressions to match the final return URL. You can send as many URLs as you want here. If the return flow (from gateway to merchant) uses webView: shouldStartLoadWithRequest: is used to check if the URL matches the regex. If matched, then endUrlReached is invoked by the browser. You will now have control of the user session.

Perform Logout

It invalidates the logged in user sessions.

[JuspaySafeBrowser performLogout];
JuspaySafeBrowser.performLogout()

Send Payment Status

Sending Status
Once payment has been completed, you should inform us if the payment was successful. This is important to provide better analytics in identifying top areas of failure points. Payment status can be one of: SUCCESS, FAILURE or CANCELLED.

Among all the three following techniques, sending the status from the server is the most reliable as the network between servers is much more robust.

From endUrlReached function

Since we always invoke the callback method for all payment completions, you could send us the payment status at that point.

// Putting payment status
[self.browser startpaymentWithJuspayInView:self.view withParameters:parms callback:^(BOOL status, NSError *error, id info) {

    JPTransactionStatus *transactionStatus = [[JPTransactionStatus alloc] init];
    if (status) {
        NSDictionary *data = (NSDictionary*)info;
        NSString *endURL = [data objectForKey:kENDURL];

        if (endURL.length) {
            if (<payment success>) {
                transactionStatus.paymentID = kTRANSACTIONID;
                transactionStatus.paymentStatus = SUCCESS;
            } else if (<payment failure>) {
                transactionStatus.paymentID = kTRANSACTIONID;
                transactionStatus.paymentStatus = FAILURE;
            }
        }
    } else {
        transactionStatus.paymentID = kTRANSACTIONID;
        if (error.code==JSCancelledByUser) {
            transactionStatus.paymentStatus = CANCELLED;
        } else {
            transactionStatus.paymentStatus = UNKNOWNSTATUS;
        }
    }
    [Logger logPaymentStatus:transactionStatus];
}];
// Putting payment status
self.browser.startpaymentWithJuspay(in: self.view, withParameters: browserParams) { (status, error, info) in
        let transactionStatus = JPTransactionStatus()
        if (status) {
            let data = info as! NSDictionary
            let endURL = data.objectForKey("EndUrl") as! String
            if (<payment success>) {
                transactionStatus.paymentID = "TransactionID"
                transactionStatus.paymentStatus = SUCCESS
            } else if (<payment failure>) {
                transactionStatus.paymentID = "TransactionID"
                transactionStatus.paymentStatus = FAILURE
            }
        } else {
            transactionStatus.paymentID = "TransactionID"
            if (error.code==101) {
                transactionStatus.paymentStatus = CANCELLED
            } else {
                transactionStatus.paymentStatus = UNKNOWNSTATUS
            }
        }
        JPLogger.sharedInstance()?.logPaymentStatus(transactionStatus)
}

However, it may not always be possible to have the payment status at this point, especially if the aggregator uses HTTP POST for sending the payment details. In such cases, you can POST the status from your server.

From your Server

Payment status can also be sent from your servers. Please make sure that the following parameters are sent correctly along with the status:

  • client_id
  • merchant_id
  • order_id
  • transaction_id

Data must be sent as a properly formatted JSON. Example payload:

{
  "data":[
    {
      "merchant_id":"shop",
      "client_id":"shop_ios",
      "order_id":"ord_112233",
      "transaction_id":"txn_112233",
      "payment_status":"SUCCESS"
    }
  ]
}

Payment status can be one of: SUCCESS, FAILURE or CANCELLED. Example HTTP POST to send payment status is provided below:

# Post status directly to Juspay server
curl https://logs.juspay.in/godel/analytics \
  -H 'Content-Type: application/json' \
  -d '{"data":[{"merchant_id":"shop", "client_id":"shop_ios", "order_id":"ord_112233", "transaction_id": "txn_112233", "payment_status":"SUCCESS"}]}'

Note: This is the preferred method for sending Status to JusPay. Please send if Failure also. Otherwise, the transactions will be categorized as unknown.

Integration checklist

Integration Checklist

  1. The view controller holding the Juspay Fragment should fix the orientation as portrait.

  2. Ensure that correct value is sent for the following:

    • merchantId
    • clientId
    • orderId
    • transactionId
    • amount
  3. Send payment_status via JPTransactionStatus or POST this information to our servers directly.

  4. Lastly, please get your final build checked by our QA team. You may write to support@juspay.in for this.

Error codes

Error Code Description Value
CancelledByUser When the user cancels the transaction by pressing back button 101

List of error codes is available in JuspayCodes.h

iOS9 update

iOS9 has higher security TLS version requirements. Some banks and payment gateways do not support these requirements to allow them to do some you need to add following to info.plist

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>
<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>

iOS Integration


Suggested Edits are limited on API Reference Pages

You can only suggest edits to Markdown body content, but not to the API spec.