For any question, we are one click away

Contact us

SDK Forms process

Web View for 3DS

The diagram below shows the SDK Forms 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 participant 3DS as 3DSS/ACS/DS 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 Forms SDK ->> SDK: 5. client enters data SDK -->> MA: 6. callback with seToken MA ->> MS: 7. send seToken to server MS ->> PG: 8. call payment API alt Payment finished PG -->> MS: 9. response with payment status (go to 17) else 3DS2 required PG -->> MS: 10. response with 3DS redirect MS ->> MA: 11. open Web View for 3DS MA ->> ACS: 12. client enters password ACS -->> PG: 13. redirect to Payment Gateway PG ->> PG: 14. make payment PG -->> MA: 15. redirect to returnUrl MA ->> MA: 16. close Web View end opt Callback is configured PG -->> MS: 17. callback notification end MS ->> PG: 18. check payment status MS ->> MA: 19. 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 Forms to collect Client's payment data
  5. Client fills in payment data
  6. SDK makes a callback with seToken. (Android: CryptogramData.seToken in onActivityResult; iOS: seToken from cardKitViewController)
  7. Mobile App sends seToken to Mobile Server
  8. 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
  9. Mobile Server gets a response with no ACS redirect. It means that payment is completed and we need to go to step 17
  10. Mobile Server gets a response with ACS redirect.
  11. Mobile App opens Web View with ACS redirect data
  12. Clients enters his one time password to ACS form
  13. ACS redirects Client to Payment Gateway
  14. Payment Gateway makes a payment
  15. Payment Gateway redirects Client to returnUrl, that can be used as a marker to close Web View
  16. Mobile App closes Web View
  17. Payment Gateway sends callback notification to Merchant server if it's configured for merchant
  18. Mobile Server checks the final payment status via getOrderStatusExtended.do
  19. 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.

sequenceDiagram participant MA as Mobile App participant MS as Mobile Server participant SDK as SDK participant SDK2 as 3DS2 SDK participant PG as Payment Gateway participant 3DS as 3DSS/ACS/DS 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 Forms SDK ->> SDK: 5. client enters data SDK -->> MA: 6. callback with seToken MA ->> MS: 7. send seToken to server MS ->> PG: 8. call payment API with threeDSSDK alt Payment finished PG -->> MS: 9. response with payment status (go to 24) else 3DS2 required PG -->> MS: 10. response with 3DS2 SDK keys MS ->> MA: 11. send data to SDK MA ->> SDK2: 12. init 3DS2 SDK SDK2 -->> MA: 13. collect device data MA ->> MS: 14. send device data MS ->> PG: 15. second payment API call PG -->> MS: 16. acs signed content MS ->> MA: 17. send acs data MA ->> SDK2: 18. init Challenge flow SDK2 ->> ACS: 19. communicates via CReq/CRes ACS -->> PG: 20. confirms transaction with AReq SDK2 -->> MA: 21. 3DS procedure is over MS ->> PG: 22. finish 3DS2 payment PG ->> PG: 23. make payment end opt Callback is configured PG -->> MS: 24. callback notification end MS ->> PG: 25. check payment status MS ->> MA: 26. show payment result to Client
  1. Client creates an Order
  2. Mobile Server registers that order in Payment Gateway via register.do.
  3. Mobile Server receives a unique order number mdOrder in response.
  4. Mobile App initiates SDK Forms to collect Client's payment data
  5. Client fills in payment data
  6. SDK makes a callback with seToken. (Android: CryptogramData.seToken in onActivityResult; iOS: seToken from cardKitViewController)
  7. Mobile App sends seToken to Mobile Server
  8. 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
  9. Mobile Server gets a response with no ACS keys. It means that payment is completed and we need to go to step 24
  10. Mobile Server gets a response with ACS keys.
    • Response must contain threeDSServerTransId and threeDSSDKKey
    • Response should not contain threeDSMethodURL which is used in case of browser based redirect to ACS
  11. Mobile Server sends 3DS2 SDK data to Mobile App
  12. 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
    • pemPublicKeyfor Android and for iOS is a pem certificate that is got in response in threeDSSDKKey on step 10
    • dsRootfor Android and for iOS depends on Payment System. Download test key
  13. 3DS2 SDK collects device data and encrypts it
  14. Mobile App sends encrypted device data to Mobile Server
  15. Mobile Server initiates second payment API call via paymentorder.do request
    • sdkEncData encrypted device data that is returned in createTransaction 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 in threeDSServerTransId parameter
    • sdkEphemPubKey it is returned in createTransaction method in 3DS2 SDK
    • sdkAppID it is returned in createTransaction method in 3DS2 SDK
    • sdkTransID it is returned in createTransaction method in 3DS2 SDK
  16. Payment Gateway returns new special parameters for 3DS2 SDK
  17. Mobile Server sends those parameters to Mobile App
  18. Mobile App initiates challenge flow via doChallenge method
    • doChallenge acsTransactionIDparameter corresponds to threeDSAcsTransactionId in Payment Gateway response
    • doChallenge acsRefNumberparameter corresponds to threeDSAcsRefNumber in Payment Gateway response
    • doChallenge acsSignedContentparameter corresponds to threeDSAcsSignedContent in Payment Gateway response
    • doChallenge 3DSServerTransactionIDparameter corresponds to threeDSServerTransId in Payment Gateway response
  19. 3DS2 SDK communicates with issuers ACS via CReq/CRes API until Client confirms his payment
  20. ACS sends RReq to Payment Gateway to confirm or reject the payment
  21. 3DS2 SDK informs Mobile App that 3DS2 flow is over via ChallengeStatusReceiver
  22. Mobile Server finalizes the payment with finish3dsVer2Payment.do
  23. Payment Gateway makes a payment
  24. Payment Gateway sends callback notification to Merchant server if it's configured for merchant
  25. Mobile Server checks the final payment status via getOrderStatusExtended.do
  26. Mobile App shows payment result to client

IOS

CardKit is based CardKitCore.frameworks. Therefore these are required for import.

iOS Integration

CardKit.framework integration

You can integrate CardKit.framework in two ways:

CardKit.framework

Image 5. Adding the CardKit.framework file


Image 6. Changing CardKitCore.framework properties


Pod

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

//ViewController.swift
...
import CardKit
...

iOS Configuration

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:

Image 7. Button
Image 8. Card entry form

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";
 CardKConfig.shared.language = language;

3. CardKConfig object properties

Property name Data type Default 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.

Upload trust certificate

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 Default 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 Default 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

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 Default 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
Picture 2. List of stored credentials

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

Image 3a. bindingCVCRequired = true
Image 3b. bindingCVCRequired = 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

Image 5a. allowSaveBinding = true, isSaveBinding = true
Image 5b. allowSaveBinding = false
Image 5c. allowSaveBinding = true, isSaveBinding = false

iPad support. Displaying a form in Popover

  1. 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
...
  1. 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.

Image 6. Popover iPadOS 13
Image 7. Popover 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 CardKViewControler 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

  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 "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

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

Android Integration

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") }

Android Configuration

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.

drawing

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.

drawing

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.

drawing

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.

drawing

Configuration description

Localization

The payment form can be displayed using the following languages:

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:

val paymentConfig = PaymentConfigBuilder(order)
    .theme(Theme.SYSTEM)
    .build()

drawing

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.

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()

drawing

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.

drawing

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:

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.

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.

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.

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

It is possible to delete 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.

drawing drawing

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 stored credentials. 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()); } }); } }

FAQ

Q: What is CardIOUtilities?
A: CardIOUtilities is interface of a CardIO lib. You can read a manual here https://github.com/card-io/card.io-iOS-source.

Categories:
eCommerce SDK
Categories
Search results