- Get Started
- Guides
- Integrations
- References
- API Reference
- Basic Payment
- Forex
- Authentication
- Card Account
- Apple Pay
- Virtual Account
- Bank Account
- Token Account
- Customer
- Billing Address
- Merchant Billing Address
- Shipping Address
- Merchant Shipping Address
- Corporate
- Sender
- Recipient
- Merchant
- Marketplace & Cart
- Airline
- Lodging
- Passenger
- Tokenization
- Recurring Migration
- 3D Secure
- Custom Parameters
- Async Payments
- Webhook notifications
- Job
- Risk
- Point of Sale
- Response Parameters
- Card On File
- Chargeback
- Result Codes
- Payment Methods
- Transaction Flows
- Regression Testing
- Data Retention Policy
- Release Notes
- API Reference
- Support
Card UI Component
The Card UI Component collects card details and additional information required for card payment.
Basic implementation
You need to provide an instance of UIViewController
class conforming to OPPCardDetailsDataSource
protocol in order to implement you custom Card View Controller.
Take note that some of the OPPCardDetailsDataSource
methods are required like cardControllerCardNumberTextField
and other are optional. If you want to get all features of this view controller make sure to implement all optional methods.
Required methods are just a simple UITextField
links:
- cardControllerCardNumberTextField
- cardControllerCardHolderTextField
- cardControllerExpirationDateTextField
- cardControllerCVVTextField
Optional mehods contains couple of optional TextView fields and validation/detection callbacks.
- cardControllerCountryCodeField
- cardControllerMobilePhoneField
- cardControllerTextField:errorDidHappen:
- cardControllerTextField:shouldChangeVisibility:
- cardControllerOnPaynentBrandsDetected:error:
If merchant controller is written in Objective-C language protocol property cardActionDelegate
should be added like using @synthesize
keyword. See code sample below
You ViewController will communicate with Card Component (View Model) using OPPCardDetailsActionDelegate
delegate.
This code sample demonstrates a basic implementation of Card ViewController. It uses references to the UITextField
views to format, validate and collect the input. Optional protocol methods used for notifying your ViewController about the events which require the UI update.
#import "OPPTestCardViewController.h"
#import "OPPCardDetailsProtocol.h"
@interface OPPTestCardViewController ()
@property (nonatomic, strong) UITextField *cardNumberField;
@property (nonatomic, strong) UITextField *cardHolderFiled;
@property (nonatomic, strong) UITextField *cvvField;
@property (nonatomic, strong) UITextField *expDateField;
@property (nonatomic, strong) UITextField *countryCodeField;
@property (nonatomic, strong) UITextField *phoneViewFiled;
@property (nonatomic, strong) UIButton *payButton;
@property (nonatomic, strong) UITextView *errrorView;
@property (nonatomic, strong) UITextView *brandsView;
@end
@implementation OPPTestCardViewController
@synthesize cardActionDelegate;
- (void)viewDidLoad {
[super viewDidLoad];
//Required UI Component call
[self.cardActionDelegate viewControllerDidLoaded];
}
//return required fields
- (nonnull UITextField *)cardControllerCVVTextField {
return self.cvvField;
}
- (nonnull UITextField *)cardControllerCardHolderTextField {
return self.cardHolderFiled;
}
- (nonnull UITextField *)cardControllerCardNumberTextField {
return self.cardNumberField;
}
- (nonnull UITextField *)cardControllerExpirationDateTextField {
return self.expDateField;
}
//optional
- (UITextField *)cardControllerCountryCodeField {
return self.countryCodeField;
}
- (UITextField *)cardControllerMobilePhoneField {
return self.phoneViewFiled;
}
//handle TextFields verity messages
- (void)cardControllerTextField:(nonnull UITextField *)textField errorDidHappen:(nullable NSError *)error {
if (error) {
self.errrorView.text = [error localizedDescription];
} else {
self.errrorView.text = nil;
}
}
//handle views visibility changes
- (void)cardControllerTextField:(nonnull UITextField *)textField shouldChangeVisibility:(BOOL)hidden {
}
//handle brands detection
- (void)cardControllerOnPaynentBrandsDetected:(nullable NSArray *)paymentBrands error:(nullable NSError *)error {
if (paymentBrands.count > 0) {
self.brandsView.text = [NSString stringWithFormat:@"%lu",(unsigned long)[paymentBrands count]];
} else {
self.brandsView.text = nil;
}
}
@end
import Foundation
import OPPWAMobile
class CardViewController: UIViewController, OPPCardDetailsDataSource {
var cardActionDelegate: OPPCardDetailsActionDelegate? = nil
@IBOutlet weak var cardNumberField: UITextField!
@IBOutlet weak var cardHolderField: UITextField!
@IBOutlet weak var expiryDateFiled: UITextField!
@IBOutlet weak var cvvField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
//required
self.cardActionDelegate?.viewControllerDidLoaded()
}
@IBAction func payAction(_ sender: Any) {
self.cardActionDelegate?.submitPaymentTransaction(completionHandler: { result in
if (result) {
print("Transaction successfully submitted")
} else {
print("Transaction submit failed")
}
})
}
//required protocol methods
func cardControllerCardNumberTextField() -> UITextField {
return self.cardNumberField
}
func cardControllerCardHolderTextField() -> UITextField {
return self.cardHolderField
}
func cardControllerExpirationDateTextField() -> UITextField {
return self.expiryDateFiled
}
func cardControllerCVVTextField() -> UITextField {
return self.cvvField
}
//optional protocol methods
func cardControllerTextField(_ textField: UITextField, errorDidHappen error: Error?) {
if let error = error {
print(error.localizedDescription)
}
}
func cardController(onPaynentBrandsDetected paymentBrands: [String]?, error: Error?) {
if let paymentBrands = paymentBrands {
print("Detected brands: ", paymentBrands)
}
}
}
Dont forget to call viewControllerDidLoaded method of cardActionDelegate
In order to submit transaction you need to call submitPaymentTransaction
of OPPCardDetailsActionDelegate
delegate.
Please check api doc for OPPCardDetailsActionDelegate
available methods.
Additional features
Tokenization
Card Component support displaying saved token right out of the box - all respective field will be prepopulated if token was selected in payment scheme selection ViewController. Validation logic will be altered to support token. submit payment as usual card using submitPaymentTransaction
.
Additionaly you could store payment details as a token if supported with storePaymentDetails
of the OPPCardDetailsActionDelegate
method call.
Installment options
You can use updateNumberOfInstallments
method of the OPPCardDetailsActionDelegate
and provide a number of instalments if required by payment method.
Billing address
You can use updateBillingAddress
method of the OPPCardDetailsActionDelegate
and prodifing an OPPBillingAddress
as a parameter if required by payment method.
Card scanning
You can use startCardScan
method of the OPPCardDetailsActionDelegate
to start OCR card scan. Card number will be automaticaly populated into card number text field.
More info in the Card scanning.
This code sample demonstrates a basic implementation of Card UI Component. It uses links to the EditText views to format, validate and collect the input. The callbacks notify your fragment about the events which require the UI update.
Implementation
Layout
public class CardUiComponentFragment extends Fragment implements CardUiComponent {
private CardUiComponentFragmentBinding binding;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = CardUiComponentFragmentBinding.inflate(inflater, container, false);
return binding.getRoot();
}
@Override
public void onUiComponentCreated(@NonNull CardUiComponentInteraction interaction) {
// this method will be called after onViewCreated() and before onStart(),
// it provides the link to the UI Component interaction, use it to initialize your UI
binding.payButton.setOnClickListener(
view -> interaction.submitPaymentDetails());
}
@Override
public void onInputValidation(@NonNull EditText editText,
@Nullable String error) {
// use this callback to show or hide input validation errors
editText.setError(error);
}
@Override
public void onViewVisibilityChange(@NonNull View view,
int visibility) {
// some views might be displayed or hidden depends on the card brand
// or checkout settings, this callback provides the view and required visibility value
view.setVisibility(visibility);
}
@Override
public void onCardBrandDetection(@NonNull Set cardBrands) {
// if multiple card brands are detected you can display them
// and let shopper to make a choice, the first detected brand will be set by default
}
@Override
public void onCardBrandChange(@Nullable String cardBrand) {
// you can use this callback to display the card brand.
}
@NonNull
@Override
public EditText getCardNumberEditText() {
return binding.cardNumber;
}
@NonNull
@Override
public EditText getCardHolderEditText() {
return binding.cardHolder;
}
@NonNull
@Override
public EditText getCardExpiryDateEditText() {
return binding.cardExpiryDate;
}
@NonNull
@Override
public EditText getCardSecurityCodeEditText() {
return binding.cardSecurityCode;
}
@Nullable
@Override
public EditText getPhoneCountryCodeEditText() {
// some card brands require phone country code and number,
// if you're going to use it, provide a link to the EditText for phone country code
return null;
}
@Nullable
@Override
public EditText getPhoneNumberEditText() {
// some card brands require phone country code and number,
// if you're going to use it, provide a link to the EditText for phone number
return null;
}
}
class CardUiComponentFragment : Fragment(), CardUiComponent {
private var _binding: CardUiComponentFragmentBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = CardUiComponentFragmentBinding.inflate(
inflater, container, false)
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
override fun onUiComponentCreated(interaction: CardUiComponentInteraction) {
// this method will be called after onViewCreated() and before onStart(),
// it provides the link to the UI Component interaction, use it to initialize your UI
binding.payButton.setOnClickListener { interaction.submitPaymentDetails() }
}
override fun onInputValidation(editText: EditText, error: String?) {
// use this callback to show or hide input validation errors
editText.error = error
}
override fun onCardBrandDetection(cardBrands: MutableSet) {
// if multiple card brands are detected you can display them
// and let shopper make a choice, the first detected brand will be set by default
}
override fun onCardBrandChange(cardBrand: String?) {
// you can use this callback to display the card brand
}
override fun onViewVisibilityChange(view: View, visibility: Int) {
// some views might be displayed or hidden depends on the card brand
// or checkout settings, this callback provides the view and required visibility value
view.visibility = visibility
}
override fun getCardNumberEditText(): EditText {
return binding.cardNumber
}
override fun getCardHolderEditText(): EditText {
return binding.cardHolder
}
override fun getCardExpiryDateEditText(): EditText {
return binding.cardExpiryDate
}
override fun getCardSecurityCodeEditText(): EditText {
return binding.cardSecurityCode
}
override fun getPhoneCountryCodeEditText(): EditText? {
// some card brands require phone country code and number,
// if you're going to use it, provide a link to the EditText for phone country code
return null
}
override fun getPhoneNumberEditText(): EditText? {
// some card brands require phone country code and number,
// if you're going to use it, provide a link to the EditText for phone number
return null
}
}
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/card_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/card_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/card_number"/>
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/card_expiry_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/card_holder"/>
<androidx.appcompat.widget.AppCompatEditText
android:id="@+id/card_security_code"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/card_expiry_date"/>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/pay_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/card_security_code"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Additional features
Tokenization
Payment form can be extended to display an option to the shopper that allows to store payment details.
@Override
public void onUiComponentCreated(@NonNull CardUiComponentInteraction interaction) {
// use the interaction to get the tokenization mode from CheckoutSettings
CheckoutStorePaymentDetailsMode storePaymentDetailsMode
= interaction.getCheckoutSettings().getStorePaymentDetailsMode();
switch (storePaymentDetailsMode) {
case NEVER:
// hide the tokenization option
break;
case PROMPT:
// display the tokenization option to shopper
break;
case ALWAYS:
// hide the tokenization option and enable it by default
interaction.setTokenizationEnabled(true);
break;
}
}
override fun onUiComponentCreated(interaction: DirectDebitSepaUiComponentInteraction) {
// use the interaction to get the tokenization mode from CheckoutSettings
when (interaction.checkoutSettings.storePaymentDetailsMode) {
CheckoutStorePaymentDetailsMode.NEVER -> {} // hide the tokenization option
CheckoutStorePaymentDetailsMode.PROMPT -> {} // display the tokenization option to shopper
CheckoutStorePaymentDetailsMode.ALWAYS ->
// hide the tokenization option and enable it by default
interaction.setTokenizationEnabled(true)
}
}
More info in the Tokenization guide.
Installment options
Payment form can be extended to display the installment options to the shopper.
@Override
public void onUiComponentCreated(@NonNull CardUiComponentInteraction interaction) {
// use the interaction to check if installment is enabled in the CheckoutSettings
boolean isInstallmentEnabled = interaction.getCheckoutSettings().isInstallmentEnabled();
if (isInstallmentEnabled) {
Integer[] installmentOptions = interaction.getCheckoutSettings().getInstallmentOptions();
// display the installment options to the shopper and use
// interaction.setNumberOfInstallments(numberOfInstallments) method to set selected value
}
}
override fun onUiComponentCreated(interaction: CardUiComponentInteraction) {
// use the interaction to check if installment is enabled in the CheckoutSettings
val isInstallmentEnabled = interaction.getCheckoutSettings().isInstallmentEnabled
if (isInstallmentEnabled) {
val installmentOptions = interaction.getCheckoutSettings().installmentOptions
// display the installment options to the shopper and use
// interaction.setNumberOfInstallments(numberOfInstallments) method to set selected value
}
}
More info in the Display installment options.
Billing address
Payment form can be extended to display the billing address to the shopper.
@Override
public void onUiComponentCreated(@NonNull CardUiComponentInteraction interaction) {
// use the interaction to get the billing address
BillingAddress billingAddress = interaction.getCheckoutSettings().getBillingAddress();
// display the billing address to the shopper,
// use interaction.getCheckoutSettings().setBillingAddress(billingAddress) if billing address was modified
}
override fun onUiComponentCreated(interaction: CardUiComponentInteraction) {
// use the interaction to get the billing address
val billingAddress = interaction.getCheckoutSettings().billingAddress
// display the billing address to the shopper,
// use interaction.getCheckoutSettings().billingAddress = billingAddress if billing address was modified
}
More info in the Display billing address.
Card scanning
The card details can be scanned using device camera or NFC when available.
@Override
public void onUiComponentCreated(@NonNull CardUiComponentInteraction interaction) {
// use the interaction to check if card scanning option should be displayed
boolean isCardScanningAvailable = interaction.isCardScanningAvailable();
if (isCardScanningAvailable) {
// display card scanning option,
// use interaction.scanCard() to initiate the scanning process
}
}
override fun onUiComponentCreated(interaction: CardUiComponentInteraction) {
// use the interaction to check if card scanning option should be displayed
val isCardScanningAvailable = interaction.isCardScanningAvailable
if (isCardScanningAvailable) {
// display card scanning option,
// use interaction.scanCard() to initiate the scanning process
}
}
More info in the Card scanning.