NAV

For any question, we are one click away

Contact us

SDK Payment process

Web View for 3DS

The diagram below shows the SDK Payment payment process with 3DS redirect via Web View

sequenceDiagram participant MA as Mobile App participant MS as Mobile Server participant SDK as SDK participant PG as Payment Gateway MA ->> MS: 1. client creates an Order MS ->> PG: 2. register order via API PG -->> MS: 3. unique order number (mdOrder) MA ->> SDK: 4. init SDK Payment SDK ->> SDK: 5. client enters data SDK -->> PG: 6. send seToken PG ->> PG: 7. make payment (includes 3DS) opt Callback is configured PG -->> MS: 8. callback notification end MS ->> PG: 9. check payment status MS ->> MA: 10. show payment result to Client
  1. Client creates an Order
  2. Mobile Server registers that order in Payment Gateway via register.do. Use returnUrl parameter as a marker to close Web View after redirect from ACS on step 15
  3. Mobile Server receives a unique order number mdOrder in response.
  4. Mobile App initiates SDK Payment to collect Client's payment data
  5. Client fills in payment data
  6. SDK sends encrypted payment data (seToken) to Payment Gateway
  7. Payment Gateway makes a payment.
    • If it's required Payment Gateway handles 3DS Secure communication
    • Be aware that SDK Payment uses 3DS2 SDK and many issuers' ACS don't work properly with 3DS Mobile SDKs.
  8. Payment Gateway sends callback notification to Merchant server if it's configured for merchant
  9. Mobile Server checks the final payment status via getOrderStatusExtended.do
  10. Mobile App shows payment result to client

iOS

The project setup is described below. Framework can be installed manually or via cocoapods.

CardKitPayment is based ThreeDSSDK, CardKit frameworks. Therefore these are required for import.

To install using cocoapods, in Podfile, add the lines

 pod 'CardKit', :git => 'https://github.com/Radarpayments/ios-sdk.git'
 pod 'CardKitPayment', :git => 'https://github.com/Radarpayments/ios-sdk.git'
 pod 'ThreeDSSDK', :podspec => 'https://raw.githubusercontent.com/Radarpayments/ios-sdk/master/ThreeDSSDK.podspec'


To install frameworks manually, download and add them to project:

CardKitPayment.framework integration

CardKitPayment.framework

Image 1. Adding the CardKit.framework file
Image 2. Changing CardKitCore.framework properties

Once done, import framework in the ViewController.swift file.

Pod

//ViewController.swift
...
import CardKitPayment
...

CardKPaymentManager

Property name Data type Defaul value Optional Description
cardKPaymentView CardKPaymentView nil No Apple pay/New card buttons
url String nil No URL of the backend
primaryColor UIColor .systemBlue Yes Color of button/checkboxes/cancel in 3DS SDK forms
textDoneButtonColor UIColor .white Yes Text color of a button
allowedCardScaner BOOL false Yes Allow user to use CardIO
headerLabel String "" Yes Label in 3DS SDK form
cardKPaymentDelegate id nil Yes Payment flow delegate
mdOrder string nil No Payment flow delegate
use3ds2sdk BOOL true Yes Either use 3DS2 SDK or WebView for 3DS redirect
sdkNavigationController BOOL UINavigationController NO (Read only) Navigation controller of CardKit form

CardKPaymentError

Property name Data type Defaul value Optional Description
message NSString nil YES Message with error text

CardKPaymentFlowDelegate

- (void)didFinishPaymentFlow:(NSDictionary *) paymentInfo; - called when payment flow finishes in success/unsuccess;
- (void)didErrorPaymentFlow:(CardKPaymentError *) paymentError; - called when payment flow finishes with error;
- (void)didCancelPaymentFlow; - called when a user cancels payment flow;
- (void)scanCardRequest:(CardKViewController *)controller - called when a user wants to scan a physical card;

Implementation of PaymentFlowController

  1. Import CardKitPayment: import CardKitPayment
  2. Initiate CardKPaymentManager
  _paymentManager = CardKPaymentManager();
  _paymentManager.cardKPaymentDelegate = self;
  1. Fill the CardKConfig (You can read the example here)
  2. Register an order and set parameters in a CardKPaymentController instance.
  let paymentRequest = PKPaymentRequest();
  paymentRequest.currencyCode = "USD";
  paymentRequest.countryCode = "US";
  paymentRequest.merchantCapabilities = PKMerchantCapability.capability3DS
  paymentRequest.supportedNetworks = [.visa, .masterCard];

  let cardPayButton = UIButton();
  cardPayButton.backgroundColor = .systemBlue;
  cardPayButton.setTitleColor(.white, for: .normal)
  cardPayButton.setTitle("Custom title", for: .normal)

  let cardKPaymentView = CardKPaymentView();
  cardKPaymentView.merchantId = "merchant.cardkit";
  cardKPaymentView.paymentRequest = paymentRequest;
  cardKPaymentView.paymentButtonStyle = .black;
  cardKPaymentView.paymentButtonType = .buy;
  cardKPaymentView.cardPaybutton = cardPayButton

  self._paymentManager.url = "https://dev.bpcbt.com/payment/";
  self._paymentManager.cardKPaymentView = cardKPaymentView;
  self._paymentManager.allowedCardScaner = CardIOUtilities.canReadCardWithCamera();
  self._paymentManager.headerLabel = "Custom header label";

  self._paymentManager.textDoneButtonColor = .white
  self._paymentManager.primaryColor = .systemBlue
  if #available(iOS 13.0, *) {
    self._paymentManager.textDoneButtonColor = .white
  }

  self.navigationController?.modalPresentationStyle = .overCurrentContext
  self._paymentManager.presentViewController(self.navigationController);
  1. Run 3DS via WebView If you need to do 3DS redirect via WebView set use3ds2sdk parameter to false A registered order must have returnUrl = sdk://done
....
  self._paymentManager.use3ds2sdk = false; // OPTIONAL! if you need to do 3DS redirect via WebView 
....
  1. Implement delegate's functions
extension PaymentController: CardKPaymentManagerDelegate {
  func didFinishPaymentFlow(_ paymentInfo: [AnyHashable : Any]!) {
    Log.i(object: self, message: "didFinishPaymentFlow")
    var message = ""
    for key in paymentInfo.keys {
      message = "\(message) \n \(key) = \(paymentInfo[key] ?? "")"
    }

    let alert = UIAlertController(title: "Success", message: message, preferredStyle: .alert)
    alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: {_ in
      self.dismiss(animated: true, completion: nil)
    }))

    self._paymentFlowManager.sdkNavigationController.present(alert, animated: true, completion: nil)
  }

  func didErrorPaymentFlow(_ paymentError: CardKPaymentError!) {
    let alert = UIAlertController(title: "Error", message: paymentError.message, preferredStyle: .alert)
    alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: nil))
    self._paymentFlowManager.sdkNavigationController.present(alert, animated: true, completion: nil)
  }

  func didCancelPaymentFlow() {
    let alert = UIAlertController(title: "Cancel", message: "Action was canceled", preferredStyle: .alert)
    alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: {_ in
      self.dismiss(animated: true, completion: nil)
    }))

    self._paymentFlowManager.sdkNavigationController.present(alert, animated: true, completion: nil)
  }

  func scanCardRequest(_ controller: CardKViewController) {
    let cardIO = CardIOView(frame: controller.view.bounds)
    cardIO.hideCardIOLogo = true
    cardIO.scanExpiry = false
    cardIO.autoresizingMask = [.flexibleWidth, .flexibleHeight]

    sampleAppCardIO = SampleAppCardIO()
    sampleAppCardIO?.cardKController = controller
    cardIO.delegate = sampleAppCardIO

    controller.showScanCardView(cardIO, animated: true)
  }
}

Configuring SDK Settings

1. Theme selection

// Light theme
 CardKConfig.shared.theme = CardKTheme.light();

// Dark theme
 CardKConfig.shared.theme = CardKTheme.dark();

// iOS System Theme ONLY FOR IOS 13.0+ (supports auto-switch)
 CardKConfig.shared.theme = CardKTheme.system();

2. Localization

 // language =  "en" | "es" | "de" | "fr" | "uk" | "ru";
 CardKConfig.shared.language = language;

3. CardKConfig object properties

Property name Data type Defaul value Optional Description
theme CardKTheme CardKTheme.defaultTheme() Yes UI color theme
language String nil Yes UI Language
bindingCVCRequired BOOL false Yes Whether CVC is required when paying with a previously saved card
isTestMod BOOL false Yes Run in test mode, to select test keys.
mdOrder String - No ID of order to be paid with cryptogram
bindings [CardKBinding] - No Array of stored credentials
cardKProdKey String <Public key> Yes Public key for production
cardKTestKey String <Public key> Yes Public key for testing
testURL String <URL> Yes Test Key Request URL
prodURL String <URL> Yes Production Key Request URL
mrBinURL String nil No URL root for image display example: https://mrbin.io/bins/
mrBinApiURL String nil No Bank identification URL
bindingsSectionTitle String nil Yes Title text of the stored credentials list section
fetchKeys Function - - The function for requesting a public key
seTokenTimestamp String nil Yes Time will use for generating a token
timestampForDate Function - - The function formats NSDate to timestamp with type String

4. Example

  ...
  CardKConfig.shared.theme = CardKTheme.dark();
  CardKConfig.shared.language = "";
  CardKConfig.shared.bindingCVCRequired = true;
  CardKConfig.shared.bindings = [];
  CardKConfig.shared.isTestMod = true;
  CardKConfig.shared.mdOrder = "mdOrder";
  ...

Setting up the Apple pay button

  1. Initialization of CardKPaymentView
let cardKPaymentView = CardKPaymentView.init(delegate: self);
cardKPaymentView.controller = self;
cardKPaymentView.frame = CGReact(x: 0, y: 0, width: 100, height: 100);
  1. Setting up PKPaymentView
Argument name Data type Default value Optiona; Description
merchantId String nil No merchantId for apple pay
paymentRequest PKPaymentRequest nil No Object to describe payment data
paymentButtonType PKPaymentButtonType nil No ApplePay button type
paymentButtonStyle PKPaymentButtonStyle nil No ApplePay button style
cardPaybutton UIButton nil Yes Setting up the "Pay by card" button"
func willShow(_ paymentView: CardKPaymentView) {
  let paymentNetworks = [PKPaymentNetwork.amex, .discover, .masterCard, .visa]
  let paymentItem = PKPaymentSummaryItem.init(label: "Test", amount: NSDecimalNumber(value: 10))
  let merchandId = "t";
  paymentView.merchantId = merchandId
  paymentView.paymentRequest.currencyCode = "USD"
  paymentView.paymentRequest.countryCode = "US"
  paymentView.paymentRequest.merchantIdentifier = merchandId
  paymentView.paymentRequest.merchantCapabilities = PKMerchantCapability.capability3DS
  paymentView.paymentRequest.supportedNetworks = paymentNetworks
  paymentView.paymentRequest.paymentSummaryItems = [paymentItem]
  paymentView.paymentButtonStyle = .black;
  paymentView.paymentButtonType = .buy;

  paymentView.cardPaybutton.backgroundColor = .white;
  paymentView.cardPaybutton.setTitleColor(.black, for: .normal);
  paymentView.cardPaybutton.setTitle("Custom title", for: .normal);
}

Example of button display

Image 9. Example of the Apple Pay button display
  1. Getting the payment result

To get PKPayment, you need to implement the function cardKPaymentView.

func cardKPaymentView(_ paymentView: CardKPaymentView, didAuthorizePayment pKPayment: PKPayment) {
...
}

Working with Card.io

To work with Card.io you need:

  1. Implement the SampleAppCardIO class with the cardIOView function;

If there is card data, then call the setCardNumber function and assign the card data.

// ViewController.swift
class SampleAppCardIO: NSObject, CardIOViewDelegate {
  weak var cardKController: CardKViewController? = nil

func cardIOView(_ cardIOView: CardIOView!, didScanCard cardInfo: CardIOCreditCardInfo!) { if let info = cardInfo { cardKController?.setCardNumber(info.cardNumber, holderName: info.cardholderName, expirationDate: nil, cvc: nil) } cardIOView?.removeFromSuperview() } }

  1. Implement the cardKitViewControllerScanCardRequest function()
// ViewController.swift
func cardKitViewControllerScanCardRequest(_ controller: CardKViewController) {
  let cardIO = CardIOView(frame: controller.view.bounds)
  cardIO.hideCardIOLogo = true
  cardIO.scanExpiry = false
  cardIO.autoresizingMask = [.flexibleWidth, .flexibleHeight]

  sampleAppCardIO = SampleAppCardIO()
  sampleAppCardIO?.cardKController = controller
  cardIO.delegate = sampleAppCardIO

  controller.showScanCardView(cardIO, animated: true)
}
  1. assign True value to allowedCardScaner attribute. It is advisable to use the function CardIOUtilities.canReadCardWithCamera();

  2. Call the function CardIOUtilities.preloadCardIO();

// ViewController.swift
func _openController() {
  ...
  controller.allowedCardScaner = CardIOUtilities.canReadCardWithCamera();
  ...
  CardIOUtilities.preloadCardIO()
}

Android

Connecting to a Gradle project by adding .aar library files

You must add the sdk_payment-release.aar library file to the libs folder, then specify the dependency of the added library.

build.gradle.kts

    allprojects {
    repositories {
        // ...
        flatDir {
            dirs("libs")
        }
    }
}

dependencies {
    // dependency is mandatory to add
    implementation(group = "", name = "sdk_payment-release", ext = "aar")
}

build.gradle

    allprojects {
    repositories {
        // ...
        flatDir {
            dirs 'libs'
        }
    }
}

dependencies {
    // dependency is mandatory to add
    implementation(group = "", name = "sdk_payment-release", ext = "aar")
}

sdk configuration

For initialization it is necessary to set the source of receiving the key. This can be done using the build method. .keyProvider(). If necessary, you can override the card type information using the .cardInfoProvider() method.Ready-made solutions can be used as well: RemoteKeyProvider, RemoteCardInfoProvider. You can also use your provider that implements the KeyProvider or CardInfoProvider interfaces, respectively.

SDKForms.init(
    SDKConfigBuilder()
        .keyProvider(
            RemoteKeyProvider("https://dev.bpcbt.com/payment/se/keys.do")
        )
        .cardInfoProvider(
            RemoteCardInfoProvider(
                url = "https://mrbin.io/bins/display",
                urlBin = "https://mrbin.io/bins/"
            )
        ).build()
)

The start of the payment process with ThreeDS(Version 2) and the receipt of the result is carried out through the SDK Payment class, which is included in thesdk_payment module.

val dsRoot = "MII2HI58HN4KY89..." //your root-certificate in base64 format
val paymentConfig =
    SDKPaymentConfig(
        baseURL = "https://dev.bpcbt.com/payment/rest",
        dsRoot = dsRoot
    )
SDKPayment.init(paymentConfig)
// A link to an activity or a fragment is required. Order number is required. 
SDKPayment.checkout(activity = this, mdOrder = "eecbbe96-973e-422e-a220-e9fa8d6cb124")

Add an optional use3ds2sdk setting to use 3DS via WebView:


SDKPayment.init(paymentConfig, use3ds2sdk = false)

3DS2 SDK is used for 3DS redirect by default.(use3ds2sdk = true)

When registering an mdOrder, the request must contain returnUrl and no other parameters with Url, like failUrl. returnUrl must equals "sdk://done".

The base URL for accessing payment gateway methods, and root certificate for verification signatures are specified through the object of the SDKPaymentConfig class.

The checkout method is available in two variations, to be called from Activity andFragment:

checkout(activity: Activity, mdOrder: String) checkout(fragment: Fragment, mdOrder: String)

Payment result processing

For Activity andFragment, you need to override the onActivityResult method.

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    // Processing the result of the payment cycle. 
    SDKPayment.handleCheckoutResult(requestCode, data, object :
        ResultPaymentCallback<PaymentData> {
        override fun onSuccess(result: PaymentData) {
            // The result of payment. 
            log("PaymentData(${result.mdOrder}, ${result.status})")
        }

        override fun onFail(e: SDKException) {
            // An error has occurred.
            when (e) {
                is SDKAlreadyPaymentException -> "Some action"
                is SDKCryptogramException -> "Some action"
                is SDKDeclinedException -> "Some action"
                is SDKPaymentApiException -> "Some action"
                is SDKTransactionException -> "Some action"
                is SDKOrderNotExistException -> "Some action"
                is SDKNotConfigureException -> "Some action"
                else -> log("${e.message} ${e.cause}")
            }
        }
    })
}

On successful payment, a PaymentData object is returned containing thestatus text field with the result of the payment.

If an error occurs, SDK Exception is returned. Types of errors that occur when working with the SDK:

Screen samples

Card payment 3DSecure confirmation
Image 10. Paying with a new card



Saved cards CVC confirmation 3DSecure confirmation
Image 11. Paying with a bound card

Payment via Google pay by the SDK Payment module

To make a payment via Google Pay by the SDK Payment module, you need to call the payment method
checkout() while passing true value of the gPayClicked flag. The default is false.

fun checkout(activity: Activity, mdOrder: String, gPayClicked: Boolean = false) {
}

Wallet button screens

Image 11. Google Pay button design

Logging

Internal processes are logged with SDK-Core tag. Also you can log your processes.

Logging is available through the object Logger. - To add log- interfaces you should call Logger-method addLogInterface().

For example to log into LogCat:

...
    Logger.addLogInterface(object : LogInterface {
        override fun log(classMethod: Class<Any>, tag: String, message: String, exception: Exception?) {
                Log.i(tag, "$classMethod: $message", exception)
            }
        })
...

The default is the tag SDK-Core. You can set your own one if you like.

For example: ... Logger.log(this.javaClass, "MyTag", "My process...", null) ...