Non-Seamless Flow - iOS

The Tridentity SDK handles the GUI and business logic. These integration steps are applicable for banks on a native iOS framework.

1. SDK Integration

Embed SDK

  1. Drag the TridentitySDK.xcframework into your Xcode project.
  2. Link the framework:
  • Select your project target.
  • Navigate to General > Framework, Libraries, and Embedded Content.
  • Choose Embed & Sign.
  1. Integrate Firebase if push notifications are required.

Add Pod Dependency

Add the following pods and run pod install:

pod 'PayUIndia-Analytics', '~> 4.0'
pod 'PayUIndia-CrashReporter', '~> 4.0'

Permissions Required

Permission

Description

Location optional

For location-based services

Push Notification mandatory

Required for transaction notifications

2. Configuration of SDK

Set up the necessary client details within the SDK. The SDK will perform essential security checks, and if any validation fails, a callback will provide specific error codes and messages. In such cases, terminate the enrollment flow to ensure security compliance.

Parameters

Parameter

Description

clientId mandatory

Client ID provided through offline channels

environment optional

Can be set to "UAT" (default) or "PROD"

themeConfig optional

Optional theme configuration in JSON/dictionary format. Refer to UI Customization for details.

logoConfig optional

Host bundle image names for branding assets: default, rounded, gif. Refer to Logo Configuration for details.

sessionIdleTimeout optional

Inactivity auto-dismiss timeout in milliseconds. When exceeded, the SDK session ends and sdkDidClose is invoked with code 368.

completionHandler mandatory

Callback with status and data after configuration completes.

Code Example

var configData: [String: Any] = [
    "clientId": "yourClientId",
    "environment": "UAT",  // UAT or PROD
    "themeConfig": themeConfig  // Optional
]

// Optional logos from host app assets
configData["logoConfig"] = [
    "default": "bank_logo",
    "rounded": "bank_logo_round",
    "gif": "loader_gif"
]

// Optional session timeout — milliseconds
configData["sessionIdleTimeout"] = 300000

TridentitySDKInterface.shared.configureSDK(jsonObject: configData) { success, response in
    if success {
        print("SDK configured successfully: \(response)")
    } else {
        print("Configuration error: \(response)")
        // Terminate the enrollment flow
    }
}

Sample Responses

Success Response:

["code": 200, "message": "SDK configured successfully"]

Failure Response:

["code": 10, "message": "Push notifications are disabled"]

3. Implement TridentSDKDelegate

Ensure that your class adopts the TridentSDKDelegate protocol to handle callbacks for various SDK interfaces, with the exception of the customer status check. TridentSDKDelegate also inherits SDKClosureDelegate.

Sample Code

extension YourClass: TridentSDKDelegate {
    func fetchStatus(data: [String: Any]) {
        if let statusCode = data["code"] as? Int {
            switch statusCode {
            case 200:
                // Handle success
            case 400:
                // Handle API/network failure
            default:
                // Handle other errors
            }
        }
    }

    func showCustomerStatus(message: String) {
        // Handle customer status message
        print("Customer status: \(message)")
    }

    // Optional — session timeout or logout
    func sdkDidClose(with info: [String: Any]) {
        let code = info["code"] as? Int ?? 0
        let message = info["message"] as? String ?? ""
        // 368 = inactivity timeout, 369 = logoutSDK
    }
}

4. Register FCM

To enable push notifications for transaction processing, you need to register the FCM token. This should be done whenever the FCM token is refreshed.

Parameters

Parameter

Description

fcmToken mandatory

Token generated by Firebase Messaging Service

statusDelegate mandatory

The class implementing TridentSDKDelegate

Code Example

TridentitySDKInterface.shared.registerFCM(fcmToken: fcmToken, statusDelegate: self)

extension YourClass: TridentSDKDelegate {
    func fetchStatus(data: [String: Any]) {
        if let statusCode = data["code"] as? Int,
           let dataKey = data["dataKey"] as? String {
            if statusCode == 200 && dataKey == "registerFCMData" {
                // Handle successful FCM token registration
            } else {
                // Handle other responses
            }
        }
    }
    
    func showCustomerStatus(message: String) {
        // Handle customer status
    }
}

Sample Response

{
"dataKey": "registerFCMData",
"status": "SUCCESS",
"code": 200,
"message": "Successfully Updated FCM token"
}

5. Handling User Registration

The initiateRegistration method in the TridentitySDKInterface is used to manage user registration.

Parameters

Parameter

Description

jsonObject mandatory

Dictionary containing registration parameters (e.g., mobileNumber)

presenter mandatory

UIViewController to present the registration interface

completionHandler mandatory

Closure to handle registration result

Code Example

let userDetails: [String: Any] = [
    "mobileNumber": "user'sMobileNumber",
    "cif": "abc123"  // Optional
]

TridentitySDKInterface.shared.initiateRegistration(
    jsonObject: userDetails,
    presenter: viewController,
    completionHandler: { success, response in
        if success {
            print("User registered successfully")
        } else {
            print("Registration failed: \(response ?? [:])")
        }
    }
)

6. Check Customer Status

Use this method to obtain the registration status of a customer within the Tridentity System. Ensure that you prepend the mobile number with "91".

Code Example

let json: [String: Any] = [
    "mobileNumber": "91***",
    "cif": "abc123" // Conditional
]

TridentitySDKInterface.shared.checkRegistrationStatus(
    jsonObject: json,
    completionHandler: { success, response in
        if success, let response = response {
            print("Status: \(response)")
        } else {
            print("Failed: \(response ?? [:])")
        }
    }
)
📘

Note: At least one of mobileNumber or cif must be provided. The completion handler returns a JSON response with the registration status. The Customer ID is managed internally by the SDK.

Sample Response

["subParam": {
customerStatus = "registration_comm_success";
}, "message": "Customer status retrieved", "status": "SUCCESS", "code": 200]

Registration Status Handling

  • Successful Registration: The status customerStatus: registration_comm_success indicates a successful registration.

  • Successful Registration with Activation Delay: Status values like registration_comm_l1_fail mean registration is successful, but services activate within 24 hours.

  • Failure: Any other status indicates registration failure.

7. Transaction History

To retrieve transaction data, invoke the method with optional limit and offset parameters.

  • Offset (Page Number): Specifies the page number. An offset of 0 fetches the first records.
  • Limit (Number of Records per Request): Maximum records returned per page. A limit of 10 retrieves up to 10 records.

Defaults and Constraints:

  • Defaults are 10 for limit and 0 for offset.
  • Maximum limit is 50.

Examples:

  • First Page: offset: 0, limit: 10 - Retrieves the first 10 records.
  • Second Page: offset: 1, limit: 10 - Retrieves records 11 through 20.

Adjust parameters to efficiently page through transaction records.

Parameters

Parameter

Description

limit optional

Number of records per request (default 10, max 50)

offset optional

Page number to retrieve (0-based, default 0)

statusDelegate mandatory

The class implementing TridentSDKDelegate

Sample Code

let param: [String: Any] = ["limit": 5, "offset": 0]

TridentitySDKInterface.shared.fetchTransactionHistory(jsonObject: param, statusDelegate: self)

extension YourClass: TridentSDKDelegate {
    func fetchStatus(data: [String: Any]) {
        if let statusCode = data["code"] as? Int,
           let dataKey = data["dataKey"] as? String {
            if statusCode == 200 && dataKey == "historyData" {
                // Process successfully retrieved transaction history
            } else {
                // Handle other responses
            }
        }
    }
    
    func showCustomerStatus(message: String) {
        // Handle customer status
    }
}

Sample Response

{
"code": 200,
"subParam": {
"offset": 0,
"totalCount": 21,
"txnHistory": [
{
"amount": 23,
"authType": "PUSH",
"expiryTime": 1721177553000,
"merchant": "Amez standard QA 111306",
"reason": "RazyPay Transaction Request of Rs: 23 at Amez standard QA 111306",
"status": "EXPIRED",
"timeStamp": 1721177493000,
"title": "",
"txnId": "c12b2487-cc65-4c9f-b31a-f60731ece16a"
}
]
},
"status": "SUCCESS",
"message": "Successfully retrieved txn-history",
"dataKey": "historyData"
}

8. Process Transaction

Upon receiving an FCM notification, use this method to process the transaction status based on the authentication action:

Parameters

Parameter

Description

notificationInfo mandatory

Dictionary with the notification payload

presenter mandatory

View controller to present the transaction UI

Code Example

Example Payload:

let notificationInfo: [AnyHashable: Any] = [
"aps": ["alert": "You have a new message!", "badge": 1],
"customDataKey": "customValue"
]
TridentitySDKInterface.shared.processTransaction(notificationInfo: notificationInfo, presenter: viewController)

Sample Response

{"message":"Transaction updated successfully","subParam":{"txnId":"abcd-1234"}}

Important Notes

Expiration: The transaction expires 60 seconds after receipt, so timely processing is crucial.

9. De-Registration

To remove a customer's registration from the system, use the following method:

Parameters

Parameter

Description

statusDelegate mandatory

The class implementing TridentSDKDelegate

Code Example

TridentitySDKInterface.shared.deRegisterUser(statusDelegate: self)

extension YourClass: TridentSDKDelegate {
    func fetchStatus(data: [String: Any]) {
        if let statusCode = data["code"] as? Int,
           let dataKey = data["dataKey"] as? String {
            if statusCode == 200 && dataKey == "deregistrationData" {
                // Handle successful customer deregistration
            } else {
                // Handle other responses
            }
        }
    }

    func showCustomerStatus(message: String) {
        // Handle customer status
    }
}

Sample Response

["dataKey": "deregistrationData", "message": "Device Deregistered Successfully", "status": "SUCCESS", "code": 200]

10. Logout SDK

Use logoutSDK when the host app closes the SecurePay / Tridentity UI without removing the customer registration.

Code Example

TridentitySDKInterface.shared.logoutSDK { success, response in
    if success {
        print("SDK logged out: \(response)")
    } else {
        print("Logout skipped or already in progress: \(response)")
    }
}

Handle session closure via sdkDidClose on your TridentSDKDelegate:

extension YourClass: TridentSDKDelegate {
    func sdkDidClose(with info: [String: Any]) {
        guard let code = info["code"] as? Int else { return }
        switch code {
        case 368:
            // Inactivity timeout (sessionIdleTimeout)
            break
        case 369:
            // Host called logoutSDK()
            break
        default:
            break
        }
    }
}

Sample Response

["code": 369, "message": "You have been signed out of SecurePay."]

11. UI Customization

The SDK allows UI customizations through a configuration object passed in as themeConfig.

UI Customization Parameters

Parameter Description Example
LabelCustomization
optional
Configurations: HeaderCustomization,TextCustomization. Sub Components: textColor, fontSize labelConfigObject
ToolbarCustomization
optional
Configurations: backgroundColor, textColor, fontSize toolbarConfigObject
ButtonCustomization
optional
Configurations: primaryButtonCustomization, secondaryButtonCustomization. Sub Components: enabledBackgroundColor, enabledTextColor, disabledBackgroundColor, disabledTextColor, fontSize, cornerRadius buttonConfigObject

Text Customization Parameters

Configurations SupportedUI Sub Components
consentScreenConfigurationconsentPopUpRegisterButtonText, consentPopUpSkipButtonText
tncScreenConfigurationconsentPopUpOkButtonText
bottomSheetPermissionPopupConfigurationtopHeaderText, topSubHeaderText, headerTextForContents, subTextForContents, buttonTextForAllowPermissions, buttonTextForSkipPermissions
simBindingScreenConfigurationappBarText, topHeaderText, topSubHeaderText, headerTextForContents, subTextForContents, textForFullScreenSubContent, buttonTextForProceed
bottomSheetSimBindingProcessingPopupConfigurationtopHeaderText, topSubHeaderText, headerTextForContents, subTextForContents, numberVerificationProcessingText, numberVerifiedText, biometricSetupText, biometricVerifiedText
bottomSheetRegistrationSuccessfulPopupConfigurationtopHeaderText, topSubHeaderText, headerTextForContents, subTextForContents, buttonText
bottomSheetFailureScreenConfigurationtopHeaderText, topSubHeaderText, headerTextForContents, subTextForContents, buttonText
transactionHistoryScreenConfigurationappBarText, topHeaderText, topSubHeaderText, headerTextForContents, subTextForContents, bottomBarTransactionsText, bottomBarOfflineOTPText
offlineOTPScreenConfigurationappBarText, topHeaderText, topSubHeaderText, headerTextForContents, subTextForContents, copyText
bottomSheetAuthenticationPopupConfigurationtopHeaderText, topSubHeaderText, headerTextForContents, subTextForContents, declineButtontext, swipeToPayButtonText
deRegPopupConfigurationtopHeaderText, topSubHeaderText, headerTextForContents, subTextForContents, okButtonText, cancelButtonText
biometricTextConfigurationlockoutMessage, biometricNotAvailable, registration, transaction, permissionDialog
uiMessageConfigurationpnTapMessageConfiguration: expiredOrNotFoundMessage, alreadyActionedPrefix, enableStrictHistoryValidation
Sample Code
// MARK: - ========== UI CUSTOMIZATION ==========

// Toolbar Customization
let toolbarCustomization = ToolbarThemeCustomization(
    textColor: "#FFFFFF",
    fontName: "Avenir-Medium",
    fontSize: 10,
    backgroundColor: "#F26522"
)

// Label Customization
let labelCustomization = LabelCustomization(
    headingCustomization: HeadingThemeCustomization(
        textColor: "#25272C",
        fontName: "Avenir-Heavy",
        fontSize: 18,
        backgroundColor: "#FFFFFF"
    ),
    subHeadingCustomization: SubHeadingThemeCustomization(
        textColor: "#25272C",
        fontName: "Avenir-Book",
        fontSize: 14,
        backgroundColor: "#FFFFFF"
    )
)

// Button Customization
let buttonCustomization = ButtonCustomization(
    primaryButtonCustomization: PrimaryCustomButtonCustomization(
        enabledBackgroundColor: "#9C27B0",
        enabledTextColor: "#FFFFFF",
        disabledBackgroundColor: "#9e9e9d",
        disabledTextColor: "#FFFFFF"
    ),
    secondaryButtonCustomization: SecondaryCustomButtonCustomization(
        enabledBackgroundColor: "#E02020",
        enabledTextColor: "#FFFFFF",
        disabledBackgroundColor: "#E3361C",
        disabledTextColor: "#FFFFFF"
    ),
    fontName: "Avenir-Medium",
    fontSize: 18,
    buttonTextTransform: .default,
    cornerRadius: 50
)

// UI Customization
let uiCustomization = UICustomization(
    processingCircleColor: "#66676B",
    buttonCustomization: buttonCustomization,
    toolbarCustomization: toolbarCustomization,
    labelCustomization: labelCustomization
)

// MARK: - ========== TEXT CUSTOMIZATION ==========

// Consent Screen Configuration
let consentScreenConfiguration = ConsentScreenConfiguration(
    consentPopUpRegisterButtonText: "Register",
    consentPopUpSkipButtonText: "Skip for now, I'll do it later"
)

// Terms & Conditions Screen Configuration
let tncScreenConfiguration = TNCScreenConfiguration(
    consentPopUpOkButtonText: "OK"
)

// Permission Popup Configuration
let bottomSheetPermissionPopupConfiguration = BottomSheetPermissionPopupConfiguration(
    topHeaderText: "Permissions Required",
    topSubHeaderText: "We need access to proceed",
    headerTextForContents: "Why We Need This",
    subTextForContents: "This permission ensures full functionality.",
    buttonTextForAllowPermissions: "Allow",
    buttonTextForSkipPermissions: "Skip"
)

// SIM Binding Screen Configuration
let simBindingScreenConfiguration = SimBindingScreenConfiguration(
    appBarText: "Registration",
    topHeaderText: "Bind Your SIM",
    topSubHeaderText: "For Secure Access",
    headerTextForContents: "Benefits of Binding",
    subTextForContents: "Faster, secure transactions.",
    textForFullScreenSubContent: "We need to send an SMS to verify the Mobile Number. Select the SIM to send an SMS",
    buttonTextForProceed: "Proceed"
)

// SIM Binding Processing Popup Configuration
let bottomSheetSimBindingProcessingPopupConfiguration = BottomSheetSimBindingProcessingPopupConfiguration(
    topHeaderText: "Set-up Biometric",
    topSubHeaderText: "Please wait",
    headerTextForContents: "Verifying Details",
    subTextForContents: "This may take a moment.",
    numberVerificationProcessingText: "Please wait while we verify your mobile number...",
    numberVerifiedText: "Mobile number successfully verified.",
    biometricSetupText: "Biometric set-up",
    biometricVerifiedText: "Finalizing your biometric set-up.."
)

// Registration Success Popup Configuration
let bottomSheetRegistrationSuccessfulPopupConfiguration = BottomSheetSuccessConfiguration(
    topHeaderText: "",
    topSubHeaderText: "",
    headerTextForContents: "Congratulations",
    subTextForContents: "Flash Pay is setup now! All future payments can be done via your biometric verification.",
    buttonText: "TAKING BACK TO MERCHANT"
)

// Failure Screen Configuration
let bottomSheetFailureScreenConfiguration = BottomSheetFailureConfiguration(
    topHeaderText: "Failed!",
    topSubHeaderText: "",
    headerTextForContents: "Could not verify mobile number",
    subTextForContents: "There was an error verifying your mobile number. Please try again.",
    buttonText: "TAKING BACK TO MERCHANT"
)

// Transaction History Screen Configuration
let transactionHistoryScreenConfiguration = TransactionHistoryScreenConfiguration(
    appBarText: "Card Authentication Services",
    topHeaderText: "Authentication History",
    topSubHeaderText: "",
    headerTextForContents: "",
    subTextForContents: "",
    bottomBarTransactionsText: "Transactions",
    bottomBarOfflineOTPText: "Offline OTP"
)

// Offline OTP Screen Configuration
let offlineOTPScreenConfiguration = OfflineOTPScreenConfiguration(
    appBarText: "Card Authentication Services",
    topHeaderText: "Offline OTP",
    topSubHeaderText: "",
    headerTextForContents: "",
    subTextForContents: "",
    copyText: "Copy to Clipboard"
)

// Authentication Popup Configuration
let bottomSheetAuthenticationPopupConfiguration = BottomSheetAuthenticationPopupConfiguration(
    topHeaderText: "",
    topSubHeaderText: "",
    headerTextForContents: "Swipe To Pay",
    subTextForContents: "",
    declineButtontext: "Decline",
    swipeToPayButtonText: "Swipe to Pay"
)

// De-Registration Popup Configuration
let deRegPopupConfiguration = DeRegPopupConfiguration(
    topHeaderText: "",
    topSubHeaderText: "",
    headerTextForContents: "De-Register",
    subTextForContents: "This action will De-Register from Secure Pay. Is this what you intended to do?",
    okButtonText: "OK",
    cancelButtonText: "Cancel"
)

// Biometric text
let biometricTextConfiguration = BiometricTextConfiguration(
    lockoutMessage: "Biometric locked. Try again later.",
    biometricNotAvailable: "Biometric not available on this device."
    // registration, transaction, permissionDialog sub-blocks optional
)

// PN tap messages
let pnTapConfig = PNTapMessageConfiguration(
    expiredOrNotFoundMessage: "No action can be taken against this transaction!",
    alreadyActionedPrefix: "Transaction Already",
    enableStrictHistoryValidation: false
)
let uiMessageConfiguration = UIMessageConfiguration(
    pnTapMessageConfiguration: pnTapConfig
)

// Text Customization
let textCustomization = TextCustomization(
    consentScreenConfiguration: consentScreenConfiguration,
    tncScreenConfiguration: tncScreenConfiguration,
    bottomSheetPermissionPopupConfiguration: bottomSheetPermissionPopupConfiguration,
    simBindingScreenConfiguration: simBindingScreenConfiguration,
    bottomSheetSimBindingProcessingPopupConfiguration: bottomSheetSimBindingProcessingPopupConfiguration,
    bottomSheetRegistrationSuccessfulPopupConfiguration: bottomSheetRegistrationSuccessfulPopupConfiguration,
    bottomSheetFailureScreenConfiguration: bottomSheetFailureScreenConfiguration,
    transactionHistoryScreenConfiguration: transactionHistoryScreenConfiguration,
    offlineOTPScreenConfiguration: offlineOTPScreenConfiguration,
    bottomSheetAuthenticationPopupConfiguration: bottomSheetAuthenticationPopupConfiguration,
    deRegPopupConfiguration: deRegPopupConfiguration,
    biometricTextConfiguration: biometricTextConfiguration,
    uiMessageConfiguration: uiMessageConfiguration
)

// MARK: - ========== THEME CONFIG ==========

let themeConfig = ThemeModel(
    uiCustomization: uiCustomization,
    textCustomization: textCustomization
)

// MARK: - ========== CONFIGURE SDK ==========

let json: [String: Any] = [
    "clientId": "your_client_id",
    "environment": "uat",
    "themeConfig": themeConfig
]

TridentitySDKInterface.shared.configureSDK(jsonObject: json) { success, response in
    print("SDK configured: \(success)")
}

Logo Configuration

Pass logoConfig during SDK configuration to reference host bundle image asset names:

  • default — Bank logo displayed everywhere
  • rounded — Rounded bank logo displayed on the Swipe to Pay screen slider
  • gif — Loader animation displayed during registration
configData["logoConfig"] = [
    "default": "merchant_square_logo",
    "rounded": "merchant_rounded_logo",
    "gif": "merchant_gif"
]

12. Callback Success/Error Codes

The table lists possible callback success and error codes, and their reasons:

CodeReason
200Configuration Successful
300Biometric authentication disabled due to changes in device biometric settings. Please re-register to continue payments without OTP
301Biometric Authentication failed. Limit exceeded due to incorrect attempts
302Device is not compatible for biometric authentication. Please try registration from another device.
303Biometric is disabled. Please enable the same to register for biometric authentication
304Too many biometric attempts. Please wait or use PIN/password to unlock.
305Biometric authentication disabled due to app reinstallation or data deletion. Please re-register to continue payments without OTP
306Invalid mobile number. Please enter a valid number.
307SDK not configured. Please initialize the SDK with valid configuration parameters before making API calls.
308Invalid parameters provided. Please check your input and try again.
331Device Debugging detected. For security, please disable to proceed.
333Biometric authentication disabled due to changes in device security settings. Please re-register to continue payments without OTP
334Screen recording observed.
335App toggling observed. Please try registration again
336Device is reverse engineered
338Customer not registered. Please complete the registration process to continue.
361No network connection available. Please check your internet connection and try again
362Biometric authentication has timed out. Redirecting to OTP authentication
363Biometric authentication timed out. Please try in future Payments again to enrol for biometric
364The system timed out the SDK session after a period of app inactivity.
365Unable to send SMS. Please check your phone number and network connection, then try again later.
367Device is in airplane mode. Please turn off airplane mode and try again
368SDK session ended — inactivity timeout (sdkDidClose)
369SDK session ended — host logoutSDK() (sdkDidClose)
370SMS sending cancelled. Please try registering again later.
371Unable to send SMS. Please try registering again later.
372Cannot present registration screen. Invalid navigation view controller argument.
373Unable to process server response.
380Biometric not available
381Biometric permission denied
383Biometric max attempts exceeded (registration block or txn OTP fallback)
384Biometric OS lockout
385User skipped biometric permission dialog
390Biometric authentication disabled due to sim movement. Please re-register to continue payments without OTP
400We encountered an error loading this page. Please try again.