SDK Payment process
Web View for 3DS
The diagram below shows the SDK Payment payment process with 3DS redirect via Web View
- Client creates an Order
- 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 - Mobile Server receives a unique order number
mdOrder
in response. - Mobile App initiates SDK Payment to collect Client's payment data
- Client fills in payment data
- SDK sends encrypted payment data (seToken) to Payment Gateway
- 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.
- Payment Gateway sends callback notification to Merchant server if it's configured for merchant
- Mobile Server checks the final payment status via getOrderStatusExtended.do
- 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
- Take the
CardKitCore.framework
file and add it to the project folder.

- Open Targets -> General -> Frameworks, Libraries, and Embedded Content. For
CardKitCore.framework
, in the Embed column, changeDo not Embed
toEmbed & Sign
.

Once done, import framework in the ViewController.swift
file.
Pod
- Initialize Pod
- Add the line
pod 'CardKitPayment' to the Podfile, :git => 'https://github.com/Radarpayments/ios-sdk.git'
//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 |
- (void) presentViewController:(UINavigationController *)navController - start card kit flow
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
- Import CardKitPayment:
import CardKitPayment
- Initiate CardKPaymentManager
_paymentManager = CardKPaymentManager();
_paymentManager.cardKPaymentDelegate = self;
- Fill the CardKConfig (You can read the example here)
- 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);
- 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
....
- 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
- Initialization of CardKPaymentView
let cardKPaymentView = CardKPaymentView.init(delegate: self);
cardKPaymentView.controller = self;
cardKPaymentView.frame = CGReact(x: 0, y: 0, width: 100, height: 100);
- 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

- Getting the payment result
To get PKPayment, you need to implement the function cardKPaymentView
.
- paymentView - class object
CardKPaymentView
; - pKPayment - payment result, class object PKPayment.
func cardKPaymentView(_ paymentView: CardKPaymentView, didAuthorizePayment pKPayment: PKPayment) {
...
}
Working with Card.io
To work with Card.io you need:
- Implement the
SampleAppCardIO
class with thecardIOView
function;
- cardIOView - class object
CardIOView
; - didScanCard - card data after scanning;
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()
}
}
- Implement the
cardKitViewControllerScanCardRequest function()
- cotroller - class object
CardKViewController
;
// 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)
}
assign
True
value toallowedCardScaner
attribute. It is advisable to use the functionCardIOUtilities.canReadCardWithCamera()
;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:
- SDKAlreadyPaymentException - payment of a successfully paid order
- SDKCryptogramException - error while creating cryptogram
- SDKDeclinedException - order was canceled on previous payment cycle
- SDKPaymentApiException - error when working with gateway API methods
- SDKTransactionException - error when creating a transaction when paying through 3ds
- SDKOrderNotExistException - payment for a non-existent order
- SDKNotConfigureException - Merchant is not properly configured for 3DS redirect via WebView
Screen samples
![]() |
![]() |
---|---|
Card payment | 3DSecure confirmation |
![]() |
![]() |
![]() |
---|---|---|
Saved cards | CVC confirmation | 3DSecure confirmation |
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

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.
- To log own events you should call
Logger
-methodlog()
.
For example:
...
Logger.log(this.javaClass, "MyTag", "My process...", null)
...