SDK Forms process
Web View for 3DS
The diagram below shows the SDK Forms 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 Forms to collect Client's payment data
- Client fills in payment data
- SDK makes a callback with seToken. (Android: CryptogramData.seToken in onActivityResult; iOS: seToken from cardKitViewController)
- 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 17
- 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 Forms 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.
- Mobile Server receives a unique order number
mdOrder
in response. - Mobile App initiates SDK Forms to collect Client's payment data
- Client fills in payment data
- SDK makes a callback with seToken. (Android: CryptogramData.seToken in onActivityResult; iOS: seToken from cardKitViewController)
- 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 24
- 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
The project setup is described below. Framework can be installed manually or via cocoapods
.
To install using cocoapods
, in Podfile, add the line
pod 'CardKit', :git => 'https://github.com/Radarpayments/ios-sdk'
CardKit.framework integration
You can integrate CardKit.framework in two ways:
- Add
CardKit.framework
manually - Install using Pod
CardKit.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 'CardKit' to the Podfile, :git => 'https://github.com/Radarpayments/ios-sdk.git'
//ViewController.swift
...
import CardKit
...
Using SDK
Implement cardKitViewController
function
//ViewController.swift
extension ViewController: CardKDelegate {
func cardKitViewController( controller: CardKViewController, didCreateSeToken seToken: String, allowSaveBinding: Bool, isNewCard: Bool) {
debugPrint(seToken)
let alert = UIAlertController(title: "SeToken", message: "allowSaveCard = (allowSaveBinding) \n isNewCard = (isNewCard) \n seToken = (seToken)", preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil))
controller.present(alert, animated: true)
}
...
}
Implement bindingViewController
function
//ViewController.swift
extension ViewController: CardKDelegate {
func bindingViewController( controller: BindingViewController, didCreateSeToken seToken: String, allowSaveBinding: Bool, isNewCard: Bool) {
debugPrint(seToken)
let alert = UIAlertController(title: "SeToken", message: "allowSaveCard = (allowSaveBinding) \n isNewCard = (isNewCard) \n seToken = (seToken)", preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil))
controller.present(alert, animated: true)
}
...
}
Implement didLoad(_ controller: CardKViewController)
function
The function didLoad(_ controller: CardKViewController)
assigns attributes to CardKViewController
//ViewController.swift
extension ViewController: CardKDelegate {
...
func didLoad(_ controller: CardKViewController) {
controller.allowedCardScaner = CardIOUtilities.canReadCardWithCamera();
controller.purchaseButtonTitle = "Custom purchase button";
controller.allowSaveBinding = true;
controller.isSaveBinding = true;
}
...
}
Implement a form call function
//ViewController.swift
...
@objc func _openController() {
CardKConfig.shared.language = "";
CardKConfig.shared.bindingCVCRequired = true;
CardKConfig.shared.bindings = [];
CardKConfig.shared.isTestMod = true;
CardKConfig.shared.mdOrder = "mdOrder";
if #available(iOS 13.0, *) {
CardKConfig.shared.theme = CardKTheme.system();
} else {
CardKConfig.shared.theme = CardKTheme.default();
};
let controller = CardKViewController();
controller.cKitDelegate = self
let createdUiController = CardKViewController.create(self, controller: controller);
let navController = UINavigationController(rootViewController: createdUiController);
navController.modalPresentationStyle = .formSheet
self.present(navController, animated: true)
}
...
Implement a form close function
//ViewController.swift
...
@objc func _close(sender:UIButton){
self.navigationController?.dismiss(animated: true, completion: nil)
}
...
Add a button to call the form
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton.init();
button.setTitle("Open Form",for: .normal)
button.addTarget(self, action:#selector(_openController), for: .touchUpInside)
button.frame = CGRect(x: 0, y: 50, width: 100, height: 100)
button.setTitleColor(UIColor.systemBlue, for: .normal)
self.view.backgroundColor = UIColor.white
self.view.addSubview(button)
}
Result:


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 | - | Yes | 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 | - | - | Function for requesting a public key |
seTokenTimestamp | String | nil | Yes | Time will use for generating a token |
bundlePathToTrustCert | String | nil | Yes | Path to your custom trust certificate |
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";
...
5. Your own trusted certificates
Add Transport Security to info.plist
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Upload a certificate to a bundle. Add ”Copy Bundle Resources” into Build Phases.

Set the certificate path to CardKConfig.shared.bundlePathToTrustCert
let path = Bundle.main.path(forResource: "TrsutCert", ofType: "cer");
CardKConfig.shared.bundlePathToTrustCert = path;
Call CardKConfig.fetchKeys(urlString)
let urlString = String("(self.url)/se/keys.do")
CardKConfig.fetchKeys(urlString);
CardKViewController
object properties
Property name | Data type | Defaul value | Optional | Description |
---|---|---|---|---|
cKitDelegate | id |
nil |
No | - |
allowedCardScaner | BOOL | false |
Yes | Allow the use of the card scanner. |
purchaseButtonTitle | String |
Purchase / Pay
|
Yes | Overriding the button text. |
allowSaveBinding | BOOL | false |
Yes | Show the "Save card" toggle |
isSaveBinding | BOOL | false |
Toiggle default value |
Controller initialization
1. Description of arguments
To show sdk, you need to call the static method create
of the CardKViewController class.
Function Arguments create
:
Argument name | Data type | Defaul value | Optional | Description |
---|---|---|---|---|
self | UIViewController | nil |
No | main controller link |
navigationController | UINavigationController | nil |
Yes | navigation controller |
controller | CardKViewController | nil |
No | initialized CardKViewController object |
The output of create
function is class object UIViewController
let controller = CardKViewController();
controller.cKitDelegate = self;
CardKViewController.create(self, controller: controller);
Working with stored credentials (COF)
1. Stored credentials display
A controller with the list of stored credentials will be displayed if the bindings
array in CardKConfing
is not an empty array.
If the array is empty, the form for creating a new card will be displayed.
Object Properties CardKBinding
:
Property name | Data type | Defaul value | Optional | Description |
---|---|---|---|---|
bindingId | Number | - | No | ID of the stored credential |
paymentSystem | String | - | No | Payment system |
cardNumber | String | - | No | Card number |
expireDate | String | - | No | Card expiry date |

2. CVC field display
To display the CVC field in the payment form of the selected stored credential you must assign value true
to bindingCVCRequired
in CardKConfing
.
CardKConfig.shared.bindingCVCRequired = true;
Examples of forms with bindingCVCRequired
set to true
and false


Displaying the "Save Card" toggle
To display the toggle on the form, assign value true
to allowSaveBinding
in CardKViewController
.
To control the default switch value, set the isSaveBinding
value in CardKViewController
.
controller.allowSaveBinding = true;
Examples of forms with allowSaveBinding
set to true
or false



iPad support. Displaying a form in Popover
- Select a theme and initialize
CardKViewController
.
// ViewController.swift
CardKConfig.shared.theme = CardKTheme.dark();
let controller = CardKViewController();
controller.cKitDelegate = self;
let createdUiController = CardKViewController.create(self, controller: controller);
let navController = UINavigationController(rootViewController: createdUiController);
navController.modalPresentationStyle = .formSheet
...
- Displaying the form.
...
self.present(navController, animated: true)
function _close
@objc func _close(sender:UIButton){
self.navigationController?.dismiss(animated: true, completion: nil)
}
Result: Image 6 for IOS 13. Image 7 for IOS 10.


Creating SeToken
To get the SeToken you need to implement the cardKitViewController:didCreateSeToken
and 'bindingViewController:CardKBindingViewController' function. cardKitViewController
is called when generating seToken on "New Card" screen. bindingViewController
is called, when generating seToken on 'Binding form' screen.
cardKitViewController
- cotroller - class object CardKViewController
;
- didCreateSeToken - created SeToken
.
- allowSaveBinding - the user's consent to save the new card details
- isNewCard - payment with a new card or stored credential. A new card - true
, stored credential - false
// ViewController.swift
func cardKitViewController( controller: CardKViewController, didCreateSeToken seToken: String, allowSaveBinding: Bool, isNewCard: Bool) {
debugPrint(seToken)
...
controller.present(alert, animated: true)
}
bindingViewController
- cotroller - class object BindingViewController
;
- didCreateSeToken - created SeToken
.
- allowSaveBinding - the user's consent to save the new card details
- isNewCard - payment with a new card or binding. A new card - true
, bundle - false
// ViewController.swift
func bindingViewController( controller:BindingViewController, didCreateSeToken seToken: String, allowSaveBinding: Bool, isNewCard: Bool) {
debugPrint(seToken)
...
controller.present(alert, animated: true)
}
Setting up CardKViewController
To assign the СardKViewControler
attributes with new parameters, you need to implement the didLoad (_ controller: CardKViewController)
function.
Function didLoad(_ controller: CardKViewController)
assigns attributes of CardKViewController
controller.
//ViewController.swift
func didLoad(_ controller: CardKViewController) {
controller.allowedCardScaner = CardIOUtilities.canReadCardWithCamera();
controller.purchaseButtonTitle = "Custom purchase button";
controller.allowSaveBinding = true;
controller.isSaveBinding = true;
}
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 | "Pay by card" button setting |
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_forms-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_forms-release", ext = "aar")
implementation("androidx.cardview:cardview:1.0.0")
implementation("com.github.devnied.emvnfccard:library:3.0.1")
implementation("com.caverock:androidsvg-aar:1.4")
implementation("io.card:android-sdk:5.5.1")
implementation("com.google.android.gms:play-services-wallet:18.0.0")
}
build.gradle
allprojects {
repositories {
// ...
flatDir {
dirs 'libs'
}
}
}
dependencies {
// dependency is mandatory to add
implementation(group = "", name = "sdk_forms-release", ext = "aar")
implementation("androidx.cardview:cardview:1.0.0")
implementation("com.github.devnied.emvnfccard:library:3.0.1")
implementation("com.caverock:androidsvg-aar:1.4")
implementation("io.card:android-sdk:5.5.1")
implementation("com.google.android.gms:play-services-wallet:18.0.0")
}
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. Can use ready-made solutions 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()
)
Obtaining a key from a remote service
To get a key from a remote service, you can use the ready-made build method .keyProviderUrl()
.
When calling two methods .keyProviderUrl()
and .keyProvider()
in one builder, an error will be
received:
You should use only one key provider build-method
. Use only one method.
SDKForms.init(
SDKConfigBuilder()
.keyProviderUrl("https://dev.bpcbt.com/payment/se/keys.do")
.build()
)
The service should respond in the following format:
{
"keys": [
{
"keyValue": "-----BEGIN PUBLIC KEY-----****-----END PUBLIC KEY-----",
"protocolVersion": "RSA",
"keyExpiration": 1598527672000
}
]
}
An example configuration with a custom sslContext for your own certificate CA.
val sslContext = SSLContextCustomCAFactory.fromPem()
val sslContext = SSLContextCustomCAFactory.fromBase64String()
val sslContext = SSLContextCustomCAFactory.fromInputStream()
val keyProvider = RemoteKeyProvider("https://dev.bpcbt.com/payment/se/keys.do", sslContext)
SDKForms.init(
SDKConfigBuilder()
.keyProvider(keyProvider)
.build()
)
Receiving card information from a remote service
SDKForms.init(
SDKConfigBuilder()
.cardInfoProvider(
RemoteCardInfoProvider(
url = "https://mrbin.io/bins/display",
urlBin = "https://mrbin.io/bins/"
)
).build()
)
The service should respond in the following format:
{
"backgroundColor": "#008bd0",
"backgroundGradient": [
"#00bcf2",
"#004e90"
],
"supportedInvertTheme": true,
"textColor": "#fff",
"logo": "logo/main/a559252b-3772-4b7e-817d-27b16db17580/1.svg",
"logoInvert": "logo/invert/a559252b-3772-4b7e-817d-27b16db17580/1.svg",
"paymentSystem": "mastercard",
"status": "SUCCESS"
}
Implementing your own providers
You can use the providers' own implementations to provide encryption key and card information.
SDKForms.init(
SDKConfigBuilder()
.keyProvider(
KeyProvider() {
// TODO
}
)
.cardInfoProvider(
CardInfoProvider() {
// TODO
}
)
.build()
)
Google pay
Google Pay button
The SDK provides a GooglePayButton
component to display a payment button using the Google Pay.
<net.payrdr.mobile.payment.sdk.ui.GooglePayButton android:id="@+id/gpayButtonXmlConfig"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" />
The appearance of the GooglePayButton
can be customized with the following attributes:
<net.payrdr.mobile.payment.sdk.ui.GooglePayButton
app:payrdr_google_pay_button_image_format="with_text"
app:payrdr_google_pay_button_background_format="shadow" />
<net.payrdr.mobile.payment.sdk.ui.GooglePayButton
app:payrdr_google_pay_button_image_format="without_text"
app:payrdr_google_pay_button_background_format="outlet" />
Payment via Google pay
To start paying via Google Pay, you need to prepare a payment configuration, GooglePayPaymentConfig
.
private fun createGooglePayConfig(): GooglePayPaymentConfig {
val paymentData = GooglePayPaymentDataRequest.paymentDataRequestCreate {
allowedPaymentMethods = AllowedPaymentMethods.allowedPaymentMethodsCreate {
method {
type = GooglePayPaymentMethod.CARD
parameters = PaymentMethodParameters.paymentMethodParametersCreate {
allowedAuthMethods = mutableSetOf(
GooglePayAuthMethod.PAN_ONLY,
GooglePayAuthMethod.CRYPTOGRAM_3DS
)
allowedCardNetworks =
mutableSetOf(
GooglePayCardNetwork.AMEX,
GooglePayCardNetwork.DISCOVER,
GooglePayCardNetwork.INTERAC,
GooglePayCardNetwork.JCB,
GooglePayCardNetwork.MASTERCARD,
GooglePayCardNetwork.VISA
)
}
tokenizationSpecification =
TokenizationSpecification.tokenizationSpecificationCreate {
type = GoogleTokenizationSpecificationType.PAYMENT_GATEWAY
parameters =
TokenizationSpecificationParameters.tokenizationSpecificationParametersCreate {
gateway = "bank"
gatewayMerchantId = "sbersafe_test"
}
}
}
}
transactionInfo = TransactionInfo.transactionInfoCreate {
totalPrice = BigDecimal.valueOf(1)
totalPriceStatus = GooglePayTotalPriceStatus.FINAL
countryCode = "US"
currencyCode = "USD"
checkoutOption = GooglePayCheckoutOption.COMPLETE_IMMEDIATE_PURCHASE
}
merchantInfo = MerchantInfo.merchantInfoCreate {
merchantName = "Example Merchant"
merchantId = "01234567890123456789"
}
}.toJson().toString()
return GooglePayButtonConfigBuilder(
order = "eecbbe96-973e-422e-a220-e9fa8d6cb124",
paymentData = PaymentDataRequest.fromJson(paymentData)
).testEnvironment(true)
.build()
}
The paymentData
parameter can be generated using Google documentation or you can use
GooglePayPaymentDataRequest
.
order
is an order identifier (it will be returned in the SDK response), and paymentData
is
PaymentData object from Google Wallet library.
SDKForms.cryptogram(this@MainActivity, googlePayConfig)
The results of the payment will be returned to the calling Activity.
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
// Result handling.
SDKForms.handleCryptogramResult(requestCode, data, object : ResultCallback<PaymentData> {
override fun onSuccess(result: PaymentData) {
// Cryptogram formation result.
when {
result.status.isSucceeded() -> {
val info = result.info
if (info is PaymentInfoGooglePay) {
log("Google Pay ${info.order}")
}
log("$result")
}
result.status.isCanceled() -> {
log("canceled")
}
}
}
override fun onFail(e: Exception) {
// An error occurred.
log(e.toString())
}
})
}
Checking the possibility to pay via Google Pay
To check the possibility of making a payment via Google Pay, you can use the method
possiblyShowGooglePayButton
:
GooglePayUtils.possiblyShowGooglePayButton(
context = this,
paymentsClient = GooglePayUtils.createPaymentsClient(
context = this,
environment = WalletConstants.ENVIRONMENT_TEST
),
isReadyToPayJson = JSONObject(),
callback = object : GooglePayUtils.GooglePayCheckCallback {
override fun onNoGooglePlayServices() {
// No Google Play services on the device
}
override fun onNotReadyToRequest() {
// the app is not ready to pay via Google Pay
}
override fun onReadyToRequest() {
// the app is ready to pay via Google Pay
}
}
)
Functionality description
New card payment screen
On this screen, the user can make a payment by entering the card details. When paying, the user has an opportunity to save the card for future payments.
Saved card selection screen
On this screen, the user can make a payment by selecting one of the saved cards or specify the details of a new card.
Saved card bottom-sheet screen
On this screen, the user can make payment by selecting one of the saved cards or specify the
details of a new card. You can create bottom-sheet screen by calling cryptogram
-method and passing fragmentManager
and tag
(or null tag) to it.
Saved card payment screen
On this screen, the user confirms the payment with the saved card. Depending on the settings, entering the card secret code may be mandatory or optional.
Configuration description
Localization
The payment form can be displayed using the following languages:
- English
- Russian
- Ukrainian
- German
- French
- Spanish
By default, the current application language is used. If the current language is not on the list of available ones - English is used.
val paymentConfig = PaymentConfigBuilder(order)
.locale(english())
.build()
Light and dark theme
The payment form supports two design themes: light and dark.
Possible setting options:
- LIGHT - light theme.
- DARK - dark theme.
- SYSTEM - The theme is selected based on the current system theme (this option works on Android 10 and higher).
val paymentConfig = PaymentConfigBuilder(order)
.theme(Theme.SYSTEM)
.build()
Card reading via NFC
The new card details can be filled in by placing it against the back of the phone, provided that the card supports contactless payment and the phone is equipped with NFC.
- ENABLED - NFC reading function is enabled
- DISABLED - NFC reading function is disabled.
If NFC reading is enabled, the NFC icon will be displayed on the new card data entry form. If there is no NFC on the device, the icon will not be displayed.
val paymentConfig = PaymentConfigBuilder(order)
.nfcScannerOptions(NfcScannerOptions.ENABLED)
.build()
If NFC is disabled on user's device, tapping the NFC icon displays a dialog with a prompt to turn on NFC with an option to go directly to the phone settings.
Reading card details via camera
The New card details can be filled in using the phone camera. There are two possible values for card scanner:
- ENABLED - the card scanner function via the camera is enabled.
- DISABLED - the card scanner function via the camera is disabled.
val paymentConfig = PaymentConfigBuilder(order)
.cameraScannerOptions(CameraScannerOptions.ENABLED)
.build()
Button text configuration
The payment form allows you to override the text value by default. To override the button text, you need to pass the text via the payment configuration.
val paymentConfig = PaymentConfigBuilder(order)
.buttonText("Pay 200 Ꝑ")
.build()
The text of the button must be in the language used in the application, because this text is displayed as it is and is not translated by the payment form. If the default button text is used, it will be translated into the available languages.
Saving a card
You can control the behavior of the toggle that allows user to save the newly entered card details.
- HIDE - The save card option is hidden.
- YES_BY_DEFAULT - Access card save option, default value: Yes.
- NO_BY_DEFAULT - Access card save option, default value: No.
val paymentConfig = PaymentConfigBuilder(order)
.cardSaveOptions(CardSaveOptions.YES_BY_DEFAULT)
.build()
In the response from the payment form, a field is returned indicating the user's choice - whether they want to save the card for further payments.
Cardholder name input field
You can control the display of the cardholder input field.
- HIDE - The cardholder input option is hidden.
- VISIBLE - The cardholder input option is displayed.
val paymentConfig = PaymentConfigBuilder(order)
.holderInputOptions(HolderInputOptions.VISIBLE)
.build()
List of saved cards
If the user already has some saved cards, their list can be passed through the parameter cards
.
val paymentConfig = PaymentConfigBuilder(order)
.cards(cards)
.build()
If the list of cards is empty or missing, the payment form displays the data input screen for a new card. If there is one or more cards on the list, the form of payment first offers to choose a card from the list or make a payment with a new card.
Request for a card code to pay with a saved card
If the user has chosen to pay with a saved card, they are redirected to the payment confirmation and entering the card code screen. By default, the card code field is required, but it can be desabled:
val paymentConfig = PaymentConfigBuilder(order)
.bindingCVCRequired(false)
.build()
In this case, the payment form will not require the user to fill in the field for entering the card code. But if at least one digit is entered in the field, the form will require the full value.
Payment identifier
When you create a payment configuration, the payment ID is generated automatically. If necessary, you can override it manually:
val paymentConfig = PaymentConfigBuilder(order)
.uuid("27fb1ebf-895e-4b15-bfeb-6ecae378fe8e")
.build()
Payment execution time
When you create a payment configuration, the time of payment is fixed automatically and has the value of the current time on the phone. If necessary, you can override it manually:
val paymentConfig = PaymentConfigBuilder(order)
.timestamp(System.currentTimeMillis())
.build()
Description of the saved card
When you describe a saved card, the card number may be in full format or truncated. If you indicate the full card number, then only the first 6 and last 4 digits will be displayed, the rest of the digits will be hidden.
The binding ID is passed in the required format used by the store, its value will be returned after completion of the payment form.
The validity period of the card is set in a separate class, to indicate the date and month, excluding the possibility to indicate only one of the values.
Example:
Card("492980xxxxxx7724", "aa199a55-cf16-41b2-ac9e-cddc731edd19", ExpiryDate(2025, 12))
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_ui (with GUI)
Example of cryptogram formation
import net.payrdr.mobile.payment.sdk.form.SDKConfigBuilder
import net.payrdr.mobile.payment.sdk.form.SDKForms
class MarketApplication : Application() {
override fun onCreate() {
super.onCreate()
SDKForms.init(
SDKConfigBuilder()
.keyProviderUrl("https://dev.bpcbt.com/payment/se/keys.do")
.build()
)
}
}
import net.payrdr.mobile.payment.sdk.core.model.ExpiryDate
import net.payrdr.mobile.payment.sdk.form.GooglePayConfigBuilder
import net.payrdr.mobile.payment.sdk.form.PaymentConfigBuilder
import net.payrdr.mobile.payment.sdk.form.ResultCryptogramCallback
import net.payrdr.mobile.payment.sdk.form.SDKException
import net.payrdr.mobile.payment.sdk.form.SDKForms
import net.payrdr.mobile.payment.sdk.form.gpay.AllowedPaymentMethods
import net.payrdr.mobile.payment.sdk.form.gpay.GooglePayAuthMethod
import net.payrdr.mobile.payment.sdk.form.gpay.GooglePayCardNetwork
import net.payrdr.mobile.payment.sdk.form.gpay.GooglePayCheckoutOption
import net.payrdr.mobile.payment.sdk.form.gpay.GooglePayPaymentDataRequest
import net.payrdr.mobile.payment.sdk.form.gpay.GooglePayPaymentMethod
import net.payrdr.mobile.payment.sdk.form.gpay.GooglePayTotalPriceStatus
import net.payrdr.mobile.payment.sdk.form.gpay.GooglePayUtils
import net.payrdr.mobile.payment.sdk.form.gpay.GoogleTokenizationSpecificationType
import net.payrdr.mobile.payment.sdk.form.gpay.MerchantInfo
import net.payrdr.mobile.payment.sdk.form.gpay.PaymentMethodParameters
import net.payrdr.mobile.payment.sdk.form.gpay.TokenizationSpecification
import net.payrdr.mobile.payment.sdk.form.gpay.TokenizationSpecificationParameters
import net.payrdr.mobile.payment.sdk.form.gpay.TransactionInfo
import net.payrdr.mobile.payment.sdk.form.model.CameraScannerOptions
import net.payrdr.mobile.payment.sdk.form.model.Card
import net.payrdr.mobile.payment.sdk.form.model.CardSaveOptions
import net.payrdr.mobile.payment.sdk.form.model.CryptogramData
import net.payrdr.mobile.payment.sdk.form.model.GooglePayPaymentConfig
import net.payrdr.mobile.payment.sdk.form.model.HolderInputOptions
import net.payrdr.mobile.payment.sdk.form.model.NfcScannerOptions
import net.payrdr.mobile.payment.sdk.form.model.PaymentInfoBindCard
import net.payrdr.mobile.payment.sdk.form.model.PaymentInfoGooglePay
import net.payrdr.mobile.payment.sdk.form.model.PaymentInfoNewCard
import net.payrdr.mobile.payment.sdk.form.model.Theme
import net.payrdr.mobile.payment.sdk.form.ui.helper.Locales.english
import net.payrdr.mobile.payment.sdk.form.ui.helper.Locales.french
import net.payrdr.mobile.payment.sdk.form.ui.helper.Locales.german
import net.payrdr.mobile.payment.sdk.form.ui.helper.Locales.russian
import net.payrdr.mobile.payment.sdk.form.ui.helper.Locales.spanish
import net.payrdr.mobile.payment.sdk.form.ui.helper.Locales.ukrainian
import net.payrdr.mobile.payment.sdk.form.model.CardDeleteOptions
class MainActivity : AppCompatActivity() {
private fun executeCheckout(bindingCVCRequired: Boolean) {
// List of binding cards.
val cards = setOf(
Card(
"492980xxxxxx7724", "aa199a55-cf16-41b2-ac9e-cddc731edd19",
ExpiryDate(2025, 12)
),
Card(
"558620xxxxxx6614", "6617c0b1-9976-45d9-b659-364ecac099e2",
ExpiryDate(2024, 6)
),
Card(
"415482xxxxxx0000", "3d2d320f-ca9a-4713-977c-c852accf8a7b",
ExpiryDate(2019, 1)
),
Card("411790xxxxxx123456", "ceae68c1-cb02-4804-9526-6d6b2f1f2793")
)
// Order ID is required.
val order = "00210bac-0ed1-474b-8ec2-5648cdfc4212"
val paymentConfig = PaymentConfigBuilder(order)
// Optional, by default localized translation "Pay".
.buttonText("Pay 200 $")
// Optional, default HIDE.
.cardSaveOptions(CardSaveOptions.YES_BY_DEFAULT)
// Optional, default HIDE.
.holderInputOptions(HolderInputOptions.VISIBLE)
// Optional, default true.
.bindingCVCRequired(bindingCVCRequired)
// Optional, default ENABLED.
.cameraScannerOptions(CameraScannerOptions.ENABLED)
// Optional, default ENABLED.
.nfcScannerOptions(NfcScannerOptions.ENABLED)
// Optional, default DEFAULT.
.theme(Theme.DEFAULT)
// Optionally, the locale of the payment form is determined automatically.
.locale(launchLocale)
// Optional, the default is an empty list.
.cards(cards)
// Optionally, a unique payment identifier is generated automatically.
.uuid("27fb1ebf-895e-4b15-bfeb-6ecae378fe8e")
// Optionally, the time for generating the payment is set automatically.
.timestamp(System.currentTimeMillis())
// Optionally, default NO_DELETE.
.cardDeleteOptions(CardDeleteOptions.NO_DELETE)
.build()
// Calling up the payment screen.
SDKForms.cryptogram(this, paymentConfig)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
// Processing the result.
SDKForms.handleCryptogramResult(requestCode, data, object :
ResultCryptogramCallback<CryptogramData> {
override fun onSuccess(result: CryptogramData) {
// The result of creating a cryptogram.
when {
result.status.isSucceeded() -> {
val info = result.info
if (info is PaymentInfoNewCard) {
log("New card ${info.holder} ${info.saveCard}")
} else if (info is PaymentInfoBindCard) {
log("Saved card ${info.bindingId}")
} else if (info is PaymentInfoGooglePay) {
log("Google Pay ${info.order}")
googlePayCryptogram.text = result.cryptogram
}
log("$result")
log("Deleted cards ${result.deletedCardsList}")
}
result.status.isCanceled() -> {
log("canceled")
log("Deleted cards ${result.deletedCardsList}")
}
}
}
override fun onFail(e: SDKException) {
// An error has occurred.
log("${e.message} ${e.cause}")
}
})
}
}
Removing stored credentials (saved cards)
It is possible to delete the stored credential only from the cryptogram creation screen.
You must specify the option to delete a card (YES_DELETE
) when creating a config. The default
is NO_DELETE
. An example can be found in the documentation sections "Example Kotlin_ui (with GUI)"
or "Example java_ui (with GUI)".
After enabling the option, the pencil icon will appear on the screen for creating a cryptogram.
Example Java_ui (with GUI)
Example of cryptogram formation
import net.payrdr.mobile.payment.sdk.form.SDKConfigBuilder;
import net.payrdr.mobile.payment.sdk.form.SDKForms;
public class MarketApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
SDKForms.INSTANCE.init(new SDKConfigBuilder()
.keyProviderUrl("https://dev.bpcbt.com/payment/se/keys.do")
.build()
);
}
}
import net.payrdr.mobile.payment.sdk.core.model.ExpiryDate;
import net.payrdr.mobile.payment.sdk.form.PaymentConfigBuilder;
import net.payrdr.mobile.payment.sdk.form.ResultCryptogramCallback;
import net.payrdr.mobile.payment.sdk.form.SDKException;
import net.payrdr.mobile.payment.sdk.form.SDKForms;
import net.payrdr.mobile.payment.sdk.form.model.CameraScannerOptions;
import net.payrdr.mobile.payment.sdk.form.model.Card;
import net.payrdr.mobile.payment.sdk.form.model.CardSaveOptions;
import net.payrdr.mobile.payment.sdk.form.model.CryptogramData;
import net.payrdr.mobile.payment.sdk.form.model.HolderInputOptions;
import net.payrdr.mobile.payment.sdk.form.model.PaymentConfig;
import net.payrdr.mobile.payment.sdk.form.model.PaymentInfo;
import net.payrdr.mobile.payment.sdk.form.model.PaymentInfoBindCard;
import net.payrdr.mobile.payment.sdk.form.model.PaymentInfoGooglePay;
import net.payrdr.mobile.payment.sdk.form.model.PaymentInfoNewCard;
public class MainActivity extends AppCompatActivity {
private void executeCheckout() {
// List of binding cards.
Set<Card> cards = new HashSet();
cards.add(new Card("492980xxxxxx7724", "ee199a55-cf16-41b2-ac9e-cc1c731edd19", new ExpiryDate(2025, 12)));
cards.add(new Card("558620xxxxxx6614", "6617c0b1-9976-45d9-b659-364ecac099e2", new ExpiryDate(2024, 6)));
cards.add(new Card("415482xxxxxx0000", "3d2d320f-ca9a-4713-977c-c852accf8a7b", new ExpiryDate(2019, 1)));
cards.add(new Card("411790xxxxxx123456", "ceae68c1-cb02-4804-9526-6d6b2f1f2793", null));
// Order ID is required.
String order = "00210bac-0ed1-474b-8ec2-5648cdfc4212";
PaymentConfig paymentConfig = new PaymentConfigBuilder(order)
// Optional, by default localized translation "Pay".
.buttonText("Pay 200 $")
// Optional, default HIDE.
.cardSaveOptions(CardSaveOptions.YES_BY_DEFAULT)
// Optional, default HIDE.
.holderInputOptions(HolderInputOptions.VISIBLE)
// Optional, default true.
.bindingCVCRequired(false)
// Optional, default ENABLED.
.cameraScannerOptions(CameraScannerOptions.ENABLED)
// Optionally, the locale of the payment form is determined automatically.
.locale(launchLocale)
// Optional, the default is an empty list.
.cards(cards)
// Optionally, a unique payment identifier is generated automatically.
.uuid("27fb1ebf-895e-4b15-bfeb-6ecae378fe8e")
// Optionally, the time for generating the payment is set automatically.
.timestamp(System.currentTimeMillis())
.build();
// Calling up the payment screen.
SDKForms.INSTANCE.cryptogram(MainActivity.this, paymentConfig);
}
@Override
protected void onActivityResult(int requestCode, final int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// Processing the result.
SDKForms.INSTANCE.handleCryptogramResult(requestCode, data, new ResultCryptogramCallback<CryptogramData>() {
@Override
public void onSuccess(CryptogramData result) {
// The result of creating a cryptogram.
if (result.getStatus().isSucceeded()) {
PaymentInfo info = result.getInfo();
if (info instanceof PaymentInfoNewCard) {
PaymentInfoNewCard newCardInfo = (PaymentInfoNewCard) info;
log("New card " + newCardInfo.getHolder() + " " + newCardInfo.getSaveCard());
} else if (info instanceof PaymentInfoBindCard) {
PaymentInfoBindCard bindCard = (PaymentInfoBindCard) info;
log("Saved card " + bindCard);
} else if (info instanceof PaymentInfoGooglePay) {
PaymentInfoGooglePay googlePay = (PaymentInfoGooglePay) info;
log("Google pay " + googlePay);
}
log(result.toString());
} else if (result.getStatus().isCanceled()) {
log("canceled");
}
}
@Override
public void onFail(SDKException e) {
// An error has occurred.
log(e.getMessage() + " " + e.getCause());
}
});
}
}