SDK Core process
Web View for 3DS
The diagram below shows the SDK Core 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 13 - Mobile Server receives a unique order number
mdOrder
in response. - Client fills in payment data in Mobile App
- Mobile App calls SDK to create seToken. (Android: sdkCore.generateWithCard; iOS: CKCToken.generateWithCard)
- Mobile App sends seToken to Mobile Server
- Mobile Server uses that seToken to make a payment via paymentorder.do request
- Use seToken instead of pan, cvc and expiry date
- Don't forget to send Cardholder name in
TEXT
field. If you don't collect Cardholder name just send CARDHOLDER
- Mobile Server gets a response with no ACS redirect. It means that payment is completed and we need to go to step 16
- Mobile Server gets a response with ACS redirect.
- Mobile App opens Web View with ACS redirect data
- Clients enters his one time password to ACS form
- ACS redirects Client to Payment Gateway
- Payment Gateway makes a payment
- Payment Gateway redirects Client to
returnUrl
, that can be used as a marker to close Web View - Mobile App closes Web View
- 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
3DS2 SDK for 3DS
The diagram below shows the SDK Core payment process with 3DS redirect 3DS2 SDK. Be aware that many issuers' ACS don't work properly with 3DS Mobile SDKs.
- 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 13 - Mobile Server receives a unique order number
mdOrder
in response. - Client fills in payment data in Mobile App
- Mobile App calls SDK to create seToken. (Android: sdkCore.generateWithCard; iOS: CKCToken.generateWithCard)
- Mobile App sends seToken to Mobile Server
- Mobile Server uses that seToken to make a payment via paymentorder.do request
- Use seToken instead of pan, cvc and expiry date
- Don't forget to send Cardholder name in
TEXT
field. If you don't collect Cardholder name just send CARDHOLDER - Send
threeDSSDK=true
to indicate that 3DS2 SDK should be used
- Mobile Server gets a response with no ACS keys. It means that payment is completed and we need to go to step 23
- Mobile Server gets a response with ACS keys.
- Response must contain
threeDSServerTransId
andthreeDSSDKKey
- Response should not contain
threeDSMethodURL
which is used in case of browser based redirect to ACS
- Response must contain
- Mobile Server sends 3DS2 SDK data to Mobile App
- Mobile App initiates 3DS2 SDK via
createTransaction
method-
directoryServerID
depends on Payment System (for tests A000000003 can be used) -
messageVersion
is 2.1.0 for now -
pemPublicKey
for Android and for iOS is a pem certificate that is got in response inthreeDSSDKKey
on step 10 -
dsRoot
for Android and for iOS depends on Payment System. Download test key
-
- 3DS2 SDK collects device data and encrypts it
- Mobile App sends encrypted device data to Mobile Server
- Mobile Server initiates second payment API call via paymentorder.do request
-
sdkEncData
encrypted device data that is returned increateTransaction
method in 3DS2 SDK -
threeDSSDKReferenceNumber
3DS2 SDK officially ids. Don't hardcode them. iOS: 3DS_LOA_SDK_BPBT_020100_00233, Android: 3DS_LOA_SDK_BPBT_020100_00231 -
threeDSServerTransactionID
it's returned in response to first payment call on step 10 inthreeDSServerTransId
parameter -
sdkEphemPubKey
it is returned increateTransaction
method in 3DS2 SDK -
sdkAppID
it is returned increateTransaction
method in 3DS2 SDK -
sdkTransID
it is returned increateTransaction
method in 3DS2 SDK
-
- Payment Gateway returns new special parameters for 3DS2 SDK
- Mobile Server sends those parameters to Mobile App
- Mobile App initiates challenge flow via
doChallenge
method- doChallenge
acsTransactionID
parameter corresponds tothreeDSAcsTransactionId
in Payment Gateway response - doChallenge
acsRefNumber
parameter corresponds tothreeDSAcsRefNumber
in Payment Gateway response - doChallenge
acsSignedContent
parameter corresponds tothreeDSAcsSignedContent
in Payment Gateway response - doChallenge
3DSServerTransactionID
parameter corresponds tothreeDSServerTransId
in Payment Gateway response
- doChallenge
- 3DS2 SDK communicates with issuers ACS via CReq/CRes API until Client confirms his payment
- ACS sends RReq to Payment Gateway to confirm or reject the payment
- 3DS2 SDK informs Mobile App that 3DS2 flow is over via
ChallengeStatusReceiver
- Mobile Server finalizes the payment with finish3dsVer2Payment.do
- Payment Gateway makes a payment
- 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
CardKitCore.framework integration
You can integrate CardKit.framework in two ways:
- Add CardKit.framework manually
- Install using Pod
CardKitCore.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 'CardKitCore' :git => 'https://github.com/Radarpayments/ios-sdk.git'
to the Podfile
//ViewController.swift
...
import CardKitCore
...
CardKitCore API
type CKCCardParams = {
pan: string // card number (without separators and spaces)
cvc: string
expiryMMYY: string // date in MM/YYY or MMYYY format
cardholder: string | null // if null - not validated
mdOrder: string | null
pubKey: string
seTokenTimestamp: string | null // if null - the sdk gets a smartphone local time
}
type CKCBindingParams = {
bindingID: string
cvc: string | null // if null, then it is not validated
mdOrder: string | null
pubKey: string
seTokenTimestamp: string | null // if null - the sdk gets in a smartphone local time
}
type CKCError =
| 'invalid-pub-key' // empty or invalid key
| 'required' // Required field
| 'invalid-format' // invalid data format (for example, numbers in a cardholder)
| 'invalid-length' // invalid field length
| 'invalid' // invalid field value (general)type CKCField =
| 'pan'
| 'cvc'
| 'expiryMMYY'
| 'cardholder'
| 'bindingID'
| 'mdOrder'
| 'pubKey'
type CKCTokenResult = {
token: string | null
errors: [{field: CKCField, error: CKCError }] | null
}
class CKCPubKey {
// utility for parsing JSON response from public key servers
static fromJSONString(json: string): string | null
}
class CKCToken {
static generateWithBinding(params: CKCBindingParams): CKCTokenResult
static generateWithCard(params: CKCCardParams): CKCTokenResult
static timestampForDate(NSDate: date): string
}
Implementation example
Generating a token from card data
import CardKitCore
let cardParams = CKCCardParams()
cardParams.cardholder= "Korotkov Alex"
cardParams.expiryMMYY= "1222" // or 12/22
cardParams.pan= "5536913776755304"
cardParams.cvc = "123"
cardParams.mdOrder = "mdorder"
cardParams.pubKey = "-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoIITqh9xlGx4tWA+aucb0V0YuFC9aXzJb0epdioSkq3qzNdRSZIxe/dHqcbMN2SyhzvN6MRVl3xyjGAV+lwk8poD4BRW3VwPUkT8xG/P/YLzi5N8lY6ILlfw6WCtRPK5bKGGnERcX5dqL60LhOPRDSYT5NHbbp/J2eFWyLigdU9Sq7jvz9ixOLh6xD7pgNgHtnOJ3Cw0Gqy03r3+m3+CBZwrzcp7ZFs41bit7/t1nIqgx78BCTPugap88Gs+8ZjdfDvuDM+/3EwwK0UVTj0SQOv0E5KcEHENL9QQg3ujmEi+zAavulPqXH5907q21lwQeemzkTJH4o2RCCVeYO+YrQIDAQAB-----END PUBLIC KEY-----"
let res = CKCToken.generate(withCard: cardParams);
Generating a token from BindingId
import CardKitCore
let bindingParams = CKCBindingParams()
bindingParams.bindingID = "das"
bindingParams.cvc = "123"
bindingParams.mdOrder = "mdOrder"
bindingParams.pubKey = "-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoIITqh9xlGx4tWA+aucb0V0YuFC9aXzJb0epdioSkq3qzNdRSZIxe/dHqcbMN2SyhzvN6MRVl3xyjGAV+lwk8poD4BRW3VwPUkT8xG/P/YLzi5N8lY6ILlfw6WCtRPK5bKGGnERcX5dqL60LhOPRDSYT5NHbbp/J2eFWyLigdU9Sq7jvz9ixOLh6xD7pgNgHtnOJ3Cw0Gqy03r3+m3+CBZwrzcp7ZFs41bit7/t1nIqgx78BCTPugap88Gs+8ZjdfDvuDM+/3EwwK0UVTj0SQOv0E5KcEHENL9QQg3ujmEi+zAavulPqXH5907q21lwQeemzkTJH4o2RCCVeYO+YrQIDAQAB-----END PUBLIC KEY-----"
let res = CKCToken.generate(withBinding: bindingParams)
Extracting public key from JSON
Public keys for seToken
are available at https://dev.bpcbt.com/payment/se/keys.do
let JSONWithPubKey = """{
"keys":[{
"keyValue":"-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhjH8R0jfvvEJwAHRhJi2Q4fLi1p2z10PaDMIhHbD3fp4OqypWaE7p6n6EHig9qnwC/4U7hCiOCqY6uYtgEoDHfbNA87/X0jV8UI522WjQH7Rgkmgk35r75G5m4cYeF6OvCHmAJ9ltaFsLBdr+pK6vKz/3AzwAc/5a6QcO/vR3PHnhE/qU2FOU3Vd8OYN2qcw4TFvitXY2H6YdTNF4YmlFtj4CqQoPL1u/uI0UpsG3/epWMOk44FBlXoZ7KNmJU29xbuiNEm1SWRJS2URMcUxAdUfhzQ2+Z4F0eSo2/cxwlkNA+gZcXnLbEWIfYYvASKpdXBIzgncMBro424z/KUr3QIDAQAB-----END PUBLIC KEY-----",
"protocolVersion":"RSA",
"keyExpiration":1661599747000
}
]}""";
CKCPubKey.fromJSONString(JSONWithPubKey); // Return "-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhjH8R0jfvvEJwAHRhJi2Q4fLi1p2z10PaDMIhHbD3fp4OqypWaE7p6n6EHig9qnwC/4U7hCiOCqY6uYtgEoDHfbNA87/X0jV8UI522WjQH7Rgkmgk35r75G5m4cYeF6OvCHmAJ9ltaFsLBdr+pK6vKz/3AzwAc/5a6QcO/vR3PHnhE/qU2FOU3Vd8OYN2qcw4TFvitXY2H6YdTNF4YmlFtj4CqQoPL1u/uI0UpsG3/epWMOk44FBlXoZ7KNmJU29xbuiNEm1SWRJS2URMcUxAdUfhzQ2+Z4F0eSo2/cxwlkNA+gZcXnLbEWIfYYvASKpdXBIzgncMBro424z/KUr3QIDAQAB-----END PUBLIC KEY-----"
Android
Connecting to a Gradle project by adding .aar library files
You must add the sdk_core-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_core-release", ext = "aar")
}
build.gradle
allprojects {
repositories {
// ...
flatDir {
dirs 'libs'
}
}
}dependencies {
// dependency is mandatory to add
implementation(group: '', name: 'sdk_core-release', ext: 'aar')
}
External dependencies
For generation of the token it is necessary to set the public key.
val publicKey: String =
"-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoIITqh9xlGx4tWA+aucb0V0YuFC9aXzJb0epdioSkq3qzNdRSZIxe/dHqcbMN2SyhzvN6MRVl3xyjGAV+lwk8poD4BRW3VwPUkT8xG/P/YLzi5N8lY6ILlfw6WCtRPK5bKGGnERcX5dqL60LhOPRDSYT5NHbbp/J2eFWyLigdU9Sq7jvz9ixOLh6xD7pgNgHtnOJ3Cw0Gqy03r3+m3+CBZwrzcp7ZFs41bit7/t1nIqgx78BCTPugap88Gs+8ZjdfDvuDM+/3EwwK0UVTj0SQOv0E5KcEHENL9QQg3ujmEi+zAavulPqXH5907q21lwQeemzkTJH4o2RCCVeYO+YrQIDAQAB-----END PUBLIC KEY-----"
Token generation method
val context: Context = //android context for getting string resources
val sdkCore: SDKCore = SDKCore(context)val cardParams: CardParams = CardParams(
mdOrder = "mdOrder",
pan = "5536913776755304",
cvc = "123",
expiryMMYY = "12/22",
cardHolder = "Korotkov Alex",
pubKey = "publicKey"
)
val tokenResult = sdkCore.generationWithCard(cardParams)
val bindingParams: BindingParams = BindingParams(
mdOrder = "mdOrder",
bindingID = "das",
cvc = "123",
pubKey = "publicKey"
)
val tokenResult = sdkCore.generateWithBinding(bindingParams)
Models
CardParams
Property name | Data type | Default value | Optional | Description |
---|---|---|---|---|
mdOrder | String | - | No | order number |
pan | String | - | No | card number |
cvc | String | - | No | secret card code |
expiryMMYY | String | - | No | expiry date for card |
cardHolder | String? | - | No | first and last name of cardholder |
pubKey | String | - | No | public key |
BindingParams
Property name | Data type | Default value | Optional | Description |
---|---|---|---|---|
mdOrder | String | - | No | order number |
bindingId | String | - | No | number of binding for card |
cvc | String? | - | No | secret code for card |
pubKey | String | - | No | public key |
TokenResult
Property name | Data type | Default value | Optional | Description |
---|---|---|---|---|
token | String? | - | No | token as string |
errors | Map |
- | No | error while generating token |
Field validation errors
ParamField | Error | Description |
---|---|---|
PAN | required | An empty field is specified |
invalid | Invalid value | |
invalid-format | Invalid characters are used. Only numbers are available | |
CVC | required | An empty field is specified |
invalid | Invalid value | |
EXPIRY | required | An empty field is specified |
invalid | Invalid value | |
invalid-format | The format does not match the template MM/YY | |
CARDHOLDER | required | An empty field is specified |
invalid | Invalid value | |
invalid-format | Invalid characters are used. Only characters and spaces are available | |
BINDING_ID | required | An empty field is specified |
invalid | Invalid value | |
MD_ORDER | required | An empty field is specified |
invalid | Invalid value | |
PUB_KEY | required | An empty field is specified |
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)
...
Example Kotlin_core (no GUI)
Example of cryptogram formation
import net.payrdr.mobile.payment.sdk.core.SDKCore
import net.payrdr.mobile.payment.sdk.core.TokenResult
import net.payrdr.mobile.payment.sdk.core.model.BindingParams
import net.payrdr.mobile.payment.sdk.core.model.CardParams
import net.payrdr.mobile.payment.sdk.core.validation.BaseValidator
import net.payrdr.mobile.payment.sdk.core.validation.CardCodeValidator
import net.payrdr.mobile.payment.sdk.core.validation.CardExpiryValidator
import net.payrdr.mobile.payment.sdk.core.validation.CardHolderValidator
import net.payrdr.mobile.payment.sdk.core.validation.CardNumberValidator
import net.payrdr.mobile.payment.sdk.core.validation.OrderNumberValidatorclass MainActivity : AppCompatActivity() {
// initialization of validators for card information entry fields
private val cardNumberValidator by lazy { CardNumberValidator(this) }
private val cardExpiryValidator by lazy { CardExpiryValidator(this) }
private val cardCodeValidator by lazy { CardCodeValidator(this) }
private val cardHolderValidator by lazy { CardHolderValidator(this) }
private val orderNumberValidator by lazy { OrderNumberValidator(this) }
private val sdkCore by lazy { SDKCore(context = this) }
override fun onCreate(savedInstanceState: Bundle?) {
// installation of validators on the card information entry fields
cardNumberInput.setupValidator(cardNumberValidator)
cardExpiryInput.setupValidator(cardExpiryValidator)
cardCodeInput.setupValidator(cardCodeValidator)
cardHolderInput.setupValidator(cardHolderValidator)
mdOrderInput.setupValidator(orderNumberValidator)
// creation of an object and initialization of fields for a new card
val params = CardParams(
mdOrder = mdOrderInput.text.toString(),
pan = cardNumberInput.text.toString(),
cvc = cardCodeInput.text.toString(),
expiryMMYY = cardExpiryInput.text.toString(),
cardHolder = cardHolderInput.text.toString(),
pubKey = pubKeyInput.text.toString()
)
// method call to get the cryptogram for a new card
sdkCore.generateWithCard(params)
// Creation of an object and initialization of fields for the linked card
val params = BindingParams(
mdOrder = mdOrderInput.text.toString(),
bindingID = bindingIdInput.text.toString(),
cvc = "123",
pubKey = pubKeyInput.text.toString()
)
// method call to get the cryptogram for the linked card
sdkCore.generateWithBinding(params)
}
}