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 payment 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.63)

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:

VariableDescriptionTypeRequired
urlStart URL for paymentStringYes
postDataPOST parameters that must be passed to the URLStringNo
merchantIdIdentifies the merchant. Eg. 'merchant'StringYes
clientIdmerchantId followed by platform name. Eg. 'merchant_ios'StringYes
transactionIdRepresents the current transactionIdStringYes
orderIdRepresents the order number assigned by the merchantStringYes
endUrlRegexesArray of endURLs to be checked for completion of transaction and close the browserArray StringYes
amountAmount of the transactionStringYes
customerIdUnique identifier of the customerStringNo
customerEmailEmail address of the customerStringYes
customerPhoneNumberMobile number of the customerStringNo
displayNoteShort note about transaction shown to the customer. ex. 'Paying INR 200 for Order 123456'StringNo
remarksRemarks about transaction. This will be automatically filled up in the bank page.StringNo
cookiesCookies need to be set in WebViewArray of dictionariesNo
customActivityIndicatorActivity indicator to show on merchant pageViewNo
confirmationAlertContentsContents of confirmation alert view to show while cancelling the transactionArrayNo

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)

VariableDescriptionType
shouldLoadEndURLSet it as true if EndURL should be loadedBoolean
shouldNotPopOnEndURLSet it as true if the browser should not be popped when EndURL is reachedBoolean
shouldNotPopAfterPaymentSet it as true if the browser should not be popped after payment canceled or failedBoolean

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:

/**
 Delegate method will be triggered for WKNavigationDelegate's didStartProvisionalNavigation.
 
 @param webView The web view invoking the delegate method.
 @param navigation The navigation.
*/
- (void)webView:(WKWebView * _Nonnull)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation;

/**
 Delegate method will be triggered for WKNavigationDelegate's decidePolicyForNavigationAction.

 @param navigationAction The navigation action information object.
 @param webView The web view invoking the delegate method.
 @return True for WKNavigationActionPolicyAllow and false for WKNavigationActionPolicyCancel.
*/
- (bool)shouldAllowNavigationAction:(WKNavigationAction * _Nonnull)navigationAction webView:(WKWebView * _Nonnull)webView;

/**
 Delegate method will be triggered for WKNavigationDelegate's decidePolicyForNavigationResponse.

 @param navigationResponse The navigation response information object.
 @param webView The web view invoking the delegate method.
 @return True for WKNavigationResponsePolicyAllow and false for WKNavigationResponsePolicyCancel.
*/
- (bool)shouldAllowNavigationResponse:(WKNavigationResponse * _Nonnull)navigationResponse webView:(WKWebView * _Nonnull)webView;

/**
 Delegate method will be triggered for WKNavigationDelegate's didFinishNavigation.

 @param webView The web view invoking the delegate method.
 @param navigation The navigation.
*/
- (void)webView:(WKWebView * _Nonnull)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation;

/**
 Delegate method will be triggered for WKNavigationDelegate's didFailProvisionalNavigation.

 @param webView The web view invoking the delegate method.
 @param navigation The navigation.
 @param error The error that occurred.
*/
- (void)webView:(WKWebView * _Nonnull)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError * _Nonnull)error;
/**
 Delegate method will be triggered for WKNavigationDelegate's didStartProvisionalNavigation.
 
 @param webView The web view invoking the delegate method.
 @param navigation The navigation.
*/
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!)

/**
 Delegate method will be triggered for WKNavigationDelegate's decidePolicyForNavigationAction.

 @param navigationAction The navigation action information object.
 @param webView The web view invoking the delegate method.
 @return True for WKNavigationActionPolicyAllow and false for WKNavigationActionPolicyCancel.
*/
func shouldAllow(_ navigationAction: WKNavigationAction, webView: WKWebView) -> Bool

/**
 Delegate method will be triggered for WKNavigationDelegate's decidePolicyForNavigationResponse.

 @param navigationResponse The navigation response information object.
 @param webView The web view invoking the delegate method.
 @return True for WKNavigationResponsePolicyAllow and false for WKNavigationResponsePolicyCancel.
*/
func shouldAllow(_ navigationResponse: WKNavigationResponse, webView: WKWebView) -> Bool

/**
 Delegate method will be triggered for WKNavigationDelegate's didFinishNavigation.

 @param webView The web view invoking the delegate method.
 @param navigation The navigation.
*/
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!)

/**
 Delegate method will be triggered for WKNavigationDelegate's didFailProvisionalNavigation.

 @param webView The web view invoking the delegate method.
 @param navigation The navigation.
 @param error The error that occurred.
*/
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error)

Start Payment

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 completion, 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 = "<TRANSACTION_ID>";
                transactionStatus.paymentStatus = SUCCESS;
            } else if (<payment failure>) {
                transactionStatus.paymentID = "<TRANSACTION_ID>";
                transactionStatus.paymentStatus = FAILURE;
            }
        }
    } else {
        transactionStatus.paymentID = "<TRANSACTION_ID>";
        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 = "<TRANSACTION_ID>"
                transactionStatus.paymentStatus = SUCCESS
            } else if (<payment failure>) {
                transactionStatus.paymentID = "<TRANSACTION_ID>"
                transactionStatus.paymentStatus = FAILURE
            }
        } else {
            transactionStatus.paymentID = "<TRANSACTION_ID>"
            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 [email protected] for this.

Error codes

Error CodeDescriptionValue
CancelledByUserWhen the user cancels the transaction by pressing back button101

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>

Updated 10 days ago


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.