In the merchant and administration portal - allowing to update the brands offered to shoppers without requiring an update of the mobile app.
Located under "Administration > Till.js > Categories > Brands", you optionally can define which brands are displayed in the checkout by configuring the "Allowed Brands" on a channel, or any of its parents/ancestors.
To activate the brand configuration through the merchant and administration portal, first set the "Activate Brand Management" to TRUE.
The last setting "Override shop brands" is used to decide on the policy to propagate new Brands you enter to the checkout:
Either override whatever was defined in the shop. (Value TRUE)
Or offer only brands for payment, that are both specified in the BIP and in the checkout settings in the app (Value FALSE, default)
Display total amount
By default total amount is not shown in ready-to-use UI. Since version 2.23.0 you can enable this option by setting boolean property displayTotalAmount of OPPCheckoutSettings class.
let checkoutSettings = OPPCheckoutSettings()
checkoutSettings.displayTotalAmount = true
By default total amount is not shown in ready-to-use UI. Since version 2.23.0 you can enable this option by setting boolean property isTotalAmountRequired of CheckoutSettings class.
CheckoutSettings checkoutSettings = new CheckoutSettings(checkoutId, paymentBrands, providerMode);
checkoutSettings.setTotalAmountRequired(true);
val checkoutSettings = CheckoutSettings(checkoutId, paymentBrands, providerMode)
checkoutSettings.isTotalAmountRequired = true
Total amount will be shown on the payment method selection screen and on the payment forms right in the button like on the screenshots below.
Custom logos
Since version 2.48.0 ready-to-use UI allows to set your custom logos for the payment brands.
let checkoutSettings = OPPCheckoutSettings()
let privateLabelLogo = UIImage.init(named: "logo.png")
checkoutSettings.customLogos = ["PRIVATE_LABEL" : privateLabelLogo!]
CheckoutSettings checkoutSettings = new CheckoutSettings(checkoutId, paymentBrands, providerMode);
checkoutSettings.setCustomLogo("PRIVATE_LABEL", R.drawable.logo);
val checkoutSettings = CheckoutSettings(checkoutId, paymentBrands, providerMode)
checkoutSettings.setCustomLogo("PRIVATE_LABEL", R.drawable.logo)
Remove back button
By default back button is shown in ready-to-use UI. Since version 4.10.0, you can enable or disable this option by setting boolean property backButtonAvailable of OPPCheckoutSettings class.
let checkoutSettings = OPPCheckoutSettings()
checkoutSettings.backButtonAvailable = false
By default back button is shown in ready-to-use UI. Since version 4.10.0, you can enable or disable this option by setting boolean property isBackButtonAvailable of CheckoutSettings class.
CheckoutSettings checkoutSettings = new CheckoutSettings(checkoutId, paymentBrands, providerMode);
checkoutSettings.setBackButtonAvailable(false);
val checkoutSettings = CheckoutSettings(checkoutId, paymentBrands, providerMode)
checkoutSettings.isBackButtonAvailable = false
Back button will not be shown on payment form and screenshot looks like below.
CREDIT CARD
Skipping CVV
Checkout project provides an opportunity to skip CVV request for all cards or only for stored ones.
You can manage this by updating option skipCVV of OPPCheckoutSettings class:
OPPCheckoutSkipCVVModeNever - Always request CVV for card payments. This is set by default.
OPPCheckoutSkipCVVModeForStoredCards - Skip CVV check only for payments by stored cards.
OPPCheckoutSkipCVVModeAlways - Always skip CVV check. CVV field won't be displayed in card payment forms.
let checkoutSettings = OPPCheckoutSettings()
checkoutSettings.skipCVV = .forStoredCards
You can manage this by updating CheckoutSkipCVVMode of CheckoutSettings class:
CheckoutSkipCVVMode.NEVER - Always request CVV for card payments. This is set by default.
CheckoutSkipCVVMode.FOR_STORED_CARDS - Skip CVV check only for payments by stored cards.
CheckoutSkipCVVMode.ALWAYS - Always skip CVV check. CVV field won't be displayed in card payment forms.
CheckoutSettings checkoutSettings = new CheckoutSettings(checkoutId, paymentBrands, providerMode);
checkoutSettings.setSkipCVVMode(CheckoutSkipCVVMode.FOR_STORED_CARDS);
val checkoutSettings = CheckoutSettings(checkoutId, paymentBrands, providerMode)
checkoutSettings.skipCVVMode = CheckoutSkipCVVMode.FOR_STORED_CARDS
Skipping card holder
If you are not going to collect holder name for the card payments, you can just hide this field using appropriate configuration of the OPPCheckoutSettingsCheckoutSettings:
let checkoutSettings = OPPCheckoutSettings()
checkoutSettings.isCardHolderVisible = false
CheckoutSettings checkoutSettings = new CheckoutSettings(checkoutId, paymentBrands, providerMode);
checkoutSettings.setCardHolderVisible(false);
val checkoutSettings = CheckoutSettings(checkoutId, paymentBrands, providerMode)
checkoutSettings.isCardHolderVisible = false
Override card holder validation
Since version 2.29.0 ready-to-use UI allows you to override holder validation for the card payments.
The OPPCheckoutProviderDelegate protocol provides you a validateCardHolder callback to override holder validation.
// Adopt a OPPCheckoutProviderDelegate protocol
@interface CheckoutViewController () <OPPCheckoutProviderDelegate>
@end
@implementation CheckoutViewController
...
- (IBAction)checkoutButtonAction:(id)sender {
// Set a delegate property for the OPPCheckoutProvider instance
self.checkoutProvider.delegate = self;
...
}
// Implement a callback, it will be called after holder text field loses focus or Pay button is pressed
- (BOOL)checkoutProvider:(OPPCheckoutProvider *)checkoutProvider validateCardHolder:(nullable NSString *)cardHolder {
// Implement your internal validation
// return `YES` if the card holder is valid, otherwise `NO`
}
@end
// Adopt a OPPCheckoutProviderDelegate protocol
class CheckoutViewController: UIViewController, OPPCheckoutProviderDelegate {
...
@IBAction func checkoutButtonAction(_ sender: UIButton) {
// Set a delegate property for the OPPCheckoutProvider instance
self.checkoutProvider.delegate = self
...
}
// Implement a callback, it will be called after holder text field loses focus or Pay button is pressed
func checkoutProvider(_ checkoutProvider: OPPCheckoutProvider, validateCardHolder cardHolder: String?) -> Bool {
// Implement your internal validation
// return `true` if the card holder is valid, otherwise `false`
}
}
Implement IPaymentFormListener listener to catch payment form events and pass it to the CheckoutSettings. Listener also should implement Parcelable interface as it will be passed through intent.
// Create your listener to override holder validation
public class CustomFormListener implements IPaymentFormListener {
public CustomFormListener() {}
@Override
public CheckoutValidationResult onCardHolderValidate(String holder) {
// Your internal validation here
if (isHolderValid(holder)) {
return CheckoutValidationResult.VALID;
} else {
return CheckoutValidationResult.NOT_VALID;
}
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {}
private CustomFormListener(Parcel in) {}
public static final Creator<CustomFormListener> CREATOR = new Creator<CustomFormListener>() {
@Override
public CustomFormListener createFromParcel(Parcel in) {
return new CustomFormListener(in);
}
@Override
public CustomFormListener[] newArray(int size) {
return new CustomFormListener[size];
}
};
}
// Create your listener to override holder validation
class CustomFormListener() : IPaymentFormListener {
constructor(parcel: Parcel) : this() {
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
}
override fun onCardHolderValidate(holder: String?): CheckoutValidationResult {
// Your internal validation here
return if (isCardHolderValid(holder)) {
CheckoutValidationResult.VALID
} else {
CheckoutValidationResult.NOT_VALID
}
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<CustomFormListener> {
override fun createFromParcel(parcel: Parcel): CustomFormListener {
return CustomFormListener(parcel)
}
override fun newArray(size: Int): Array<CustomFormListener?> {
return arrayOfNulls(size)
}
}
}
Pass created listener to the CheckoutSettings.
CheckoutSettings checkoutSettings = new CheckoutSettings(checkoutId, paymentBrands, providerMode);
checkoutSettings.setPaymentFormListener(new CustomFormListener());
val checkoutSettings = CheckoutSettings(checkoutId, paymentBrands, providerMode)
checkoutSettings.paymentFormListener = CustomFormListener()
Display installment options
By default the installment option is disabled. Since version 2.30.0 you can enable this option by configuring OPPCheckoutSettings in the following way:
Enable installment payments
Set your list of options if needed. Default is 1, 3, 5.
Card brand is detected automatically while shopper is typing the card number. However there are some configurations you might want to apply regarding your needs.
Firstly you can choose between different ways of brand detection:
using base of regular expressions
using library of bins, which is more precise.
Then you should choose how to proceed if multiple card brands are detected.
Set the order for detected brands
Decide whether you want either present multiple brands or hide them by default
Below you can see more detailed description for each configuration with user cases and video snippets.
Brand detection type
Brands priority
Brand detection appearance style
REGEX
Regular expression is set of rules to detect card brand. It works fast and quite reliable.
This type of brand detection is used by default, but you can explicitly specify the type in the code:
OPPCheckoutSettings *checkoutSettings = [[OPPCheckoutSettings alloc] init];
// Set configured payment brands
checkoutSettings.paymentBrands = @[@"VISA", @"MASTER", @"MAESTRO", @"AMEX"];
// Regex as a card brand detection type
checkoutSettings.brandDetectionType = OPPCheckoutBrandDetectionTypeRegex;
let checkoutSettings = OPPCheckoutSettings()
// Set configured payment brands
checkoutSettings.paymentBrands = ["VISA", "MASTER", "MAESTRO", "AMEX"];
// Regex as a card brand detection type
checkoutSettings.brandDetectionType = OPPCheckoutBrandDetectionType.regex;
Set<String> paymentBrands = new LinkedHashSet<>();
paymentBrands.add("VISA");
paymentBrands.add("MASTER");
paymentBrands.add("MAESTRO");
paymentBrands.add("AMEX");
CheckoutSettings checkoutSettings = new CheckoutSettings(
checkoutId, paymentBrands, providerMode);
// Regex as a card brand detection type
checkoutSettings.setBrandDetectionType(CheckoutBrandDetectionType.REGEX);
val paymentBrands = hashSetOf("VISA", "MASTER", "MAESTRO", "AMEX")
val checkoutSettings = CheckoutSettings(checkoutId, paymentBrands, providerMode)
// Regex as a card brand detection type
checkoutSettings.brandDetectionType = CheckoutBrandDetectionType.REGEX
Use case:
Configured brands: VISA, MASTER, MAESTRO, AMEX
Shopper types 5710 1000 0000 0008
MASTER, MAESTRO are detected
MASTER is choosen by default
BIN LIST
The library of bins is more precise way to detect card brand, but it works a bit slower.
The library is regularly updated, so it's the most reliable source.
To enable brand detection by bin list, change the following option in CheckoutSettings
OPPCheckoutSettings *checkoutSettings = [[OPPCheckoutSettings alloc] init];
// Set configured payment brands
checkoutSettings.paymentBrands = @[@"VISA", @"MASTER", @"MAESTRO", @"AMEX"];
// Set bin list as a card brand detection type
checkoutSettings.brandDetectionType = OPPCheckoutBrandDetectionTypeBinList;
let checkoutSettings = OPPCheckoutSettings()
// Set configured payment brands
checkoutSettings.paymentBrands = ["VISA", "MASTER", "MAESTRO", "AMEX"];
// Set bin list as a card brand detection type
checkoutSettings.brandDetectionType = OPPCheckoutBrandDetectionType.binList;
Set<String> paymentBrands = new LinkedHashSet<>();
paymentBrands.add("VISA");
paymentBrands.add("MASTER");
paymentBrands.add("MAESTRO");
paymentBrands.add("AMEX");
CheckoutSettings checkoutSettings = new CheckoutSettings(checkoutId, paymentBrands, providerMode);
// Set bin list as a card brand detection type
checkoutSettings.setBrandDetectionType(CheckoutBrandDetectionType.BINLIST);
val paymentBrands = hashSetOf("VISA", "MASTER", "MAESTRO", "AMEX")
val checkoutSettings = CheckoutSettings(checkoutId, paymentBrands, providerMode)
// Set bin list as a card brand detection type
checkoutSettings.brandDetectionType = CheckoutBrandDetectionType.BINLIST
Use case:
Configured brands: VISA, MASTER, MAESTRO, AMEX
Bin list brand detection type is enabled
Shopper types 5710 1000 0000 0008
MASTER, MAESTRO are detected first (still use regular expression as fallback)
After 6 digits typed, card bin is searched in bin list which says it's a MAESTRO card, not MASTER
BRANDS PRIORITY
When multiple brands are detected you might want to show some preffered brand first in the list.
CheckoutSettings provides a special option for it:
OPPCheckoutSettings *checkoutSettings = [[OPPCheckoutSettings alloc] init];
// Set configured payment brands
checkoutSettings.paymentBrands = @[@"VISA", @"MASTER", @"MAESTRO", @"AMEX"];
// Set the preferred order of the detected card brands
checkoutSettings.paymentBrands = @[@"MAESTRO", @"MASTER"];
let checkoutSettings = OPPCheckoutSettings()
// Set configured payment brands
checkoutSettings.paymentBrands = ["VISA", "MASTER", "MAESTRO", "AMEX"];
// Set the preferred order of the detected card brands
checkoutSettings.paymentBrands = ["MAESTRO", "MASTER"];
Set<String> paymentBrands = new LinkedHashSet<>();
paymentBrands.add("VISA");
paymentBrands.add("MASTER");
paymentBrands.add("MAESTRO");
paymentBrands.add("AMEX");
CheckoutSettings checkoutSettings = new CheckoutSettings(
checkoutId, paymentBrands, providerMode);
List<String> brandsPriority = new ArrayList<>();
brandsPriority.add("MAESTRO");
brandsPriority.add("MASTER");
// Set the preferred order of the detected card brands
checkoutSettings.setBrandDetectionPriority(brandsPriority);
val paymentBrands = hashSetOf("VISA", "MASTER", "MAESTRO", "AMEX")
val checkoutSettings = CheckoutSettings(checkoutId, paymentBrands, providerMode)
val brandsPriority = arrayListOf("MAESTRO", "MASTER")
// Set the preferred order of the detected card brands
checkoutSettings.brandDetectionPriority = brandsPriority
Use case:
Configured brands: VISA, MASTER, MAESTRO, AMEX
Priority for detected brands is set to "MAESTRO, MASTER"
Shopper types 5710 1000 0000 0008
MAESTRO, MASTER are detected
MAESTRO is choosen by default
ACTIVE
The detected card brands interface appears automatically. This is default behavior.
INACTIVE
The detected card brands interface is hidden. The user can make it visible by clicking on card icon in the card number text field.
let checkoutSettings = OPPCheckoutSettings()
// Set configured payment brands
checkoutSettings.paymentBrands = ["VISA", "MASTER", "MAESTRO", "AMEX"]
// Hide the detected brands
checkoutSettings.brandDetectionAppearanceStyle = OPPCheckoutBrandDetectionAppearanceStyle.inactive
Set<String> paymentBrands = new LinkedHashSet<>();
paymentBrands.add("VISA");
paymentBrands.add("MASTER");
paymentBrands.add("MAESTRO");
paymentBrands.add("AMEX");
CheckoutSettings checkoutSettings = new CheckoutSettings(
checkoutId, paymentBrands, providerMode);
// Hide the detected brands
checkoutSettings.setBrandDetectionAppearanceStyle(CheckoutBrandDetectionAppearanceStyle.INACTIVE);
val paymentBrands = hashSetOf("VISA", "MASTER", "MAESTRO", "AMEX")
val checkoutSettings = CheckoutSettings(checkoutId, paymentBrands, providerMode)
// Hide the detected brands
checkoutSettings.setBrandDetectionAppearanceStyle(CheckoutBrandDetectionAppearanceStyle.INACTIVE)
Use case:
Configured brands: VISA, MASTER, MAESTRO, AMEX
Brand detection appearance style is set to INACTIVE
Shopper types 5710 1000 0000 0008
MASTER, MAESTRO are detected, but options are not shown under the card number field
MASTER is choosen by default
Shopper is still able to choose another brand manually if they want.
NONE
The detected card brands interface is disabled. The brand will be set based on the brands priority.
OPPCheckoutSettings *checkoutSettings = [[OPPCheckoutSettings alloc] init];
// Set configured payment brands
checkoutSettings.paymentBrands = @[@"VISA", @"MASTER", @"MAESTRO", @"AMEX"];
// Set the preferred order of the detected card brands
checkoutSettings.brandDetectionPriority = @[@"MAESTRO", @"MASTER"];
// Disable the detected brands showing
checkoutSettings.brandDetectionAppearanceStyle = OPPCheckoutBrandDetectionAppearanceStyleNone;
let checkoutSettings = OPPCheckoutSettings()
// Set configured payment brands
checkoutSettings.paymentBrands = ["VISA", "MASTER", "MAESTRO", "AMEX"]
// Set the preferred order of the detected card brands
checkoutSettings.brandDetectionPriority = ["MAESTRO", "MASTER"]
// Hide the detected brands
checkoutSettings.brandDetectionAppearanceStyle = OPPCheckoutBrandDetectionAppearanceStyle.none
Set<String> paymentBrands = new LinkedHashSet<>();
paymentBrands.add("VISA");
paymentBrands.add("MASTER");
paymentBrands.add("MAESTRO");
paymentBrands.add("AMEX");
CheckoutSettings checkoutSettings = new CheckoutSettings(
checkoutId, paymentBrands, providerMode);
List<String> brandsPriority = new ArrayList<>();
brandsPriority.add("MAESTRO");
brandsPriority.add("MASTER");
// Set the preferred order of the detected card brands
checkoutSettings.setBrandDetectionPriority(brandsPriority);
// Disable the detected brands showing
checkoutSettings.setBrandDetectionAppearanceStyle(CheckoutBrandDetectionAppearanceStyle.NONE);
val paymentBrands = hashSetOf("VISA", "MASTER", "MAESTRO", "AMEX")
val checkoutSettings = CheckoutSettings(checkoutId, paymentBrands, providerMode)
val brandsPriority = arrayListOf("MAESTRO", "MASTER")
// Set the preferred order of the detected card brands
checkoutSettings.brandDetectionPriority = brandsPriority
// Disable the detected brands showing
checkoutSettings.setBrandDetectionAppearanceStyle(CheckoutBrandDetectionAppearanceStyle.NONE)
Use case:
Configured brands: VISA, MASTER, MAESTRO, AMEX
Priority for detected brands is set to "MAESTRO, MASTER"
Brand detection appearance style is set to NONE
Shopper types 5710 1000 0000 0008
MASTER, MAESTRO are detected
MAESTRO is chosen by default
NOTE: If the brands priority is not set then the brand will be chosen based on the payment brands order.
SECURITY
Protect payments with Touch ID / Face ID
You can provide additional security for your shoppers by enabling biometric authentication for payment confirmation.
To complete payment shopper will have to authenticate with Touch ID of Face ID, if they are set on device, otherwise device passcode will be requested.
It's recommended to enable this protection for tokens (stored cards and back account information).
Biometrics authentication can be configured for any payment brands as well.
There are three modes of requesting device authentication:
NeverOPPSecurityPolicyModeDeviceAuthNotRequired
Device authentication is disabled.
If availableOPPSecurityPolicyModeDeviceAuthRequiredIfAvailable
App requires device authentication only if Touch ID / Face ID or passcode are set.
AlwaysOPPSecurityPolicyModeDeviceAuthRequired
Device authentication is required to complete payment. Payment brand won't be available if neither Touch ID / Face ID nor passcode are set.
To configure device authentication create an appropriate OPPSecurityPolicy and add it to the securityPolicies option of the OPPCheckoutSettings. OPPSecurityPolicy applies specified mode for tokens or list of payment brands.
let checkoutSettings = OPPCheckoutSettings()
let securityPolicyForTokens = OPPSecurityPolicy(forTokensWith: .deviceAuthRequired)
let paymentBrands = ["VISA", "MASTER"]
let securityPolicyForPaymentBrands = OPPSecurityPolicy(paymentBrands: paymentBrands, mode: .deviceAuthRequiredIfAvailable)
checkoutSettings.securityPolicies = [securityPolicyForPaymentBrands, securityPolicyForTokens]
Protect payments with device authentication
NOTE: This functionality available only for Android 5.0 (API level 21) and higher.
You can provide additional security for your shoppers by enabling payment confirmation with device authentication.
To complete payment shopper will have to provide their credentials (Pattern, PIN, Password or Fingerprint if available).
It's recommended to enable this protection for tokens (stored cards and back account information).
Protection with device authentication can be configured for any payment brands as well.
There are three modes of requesting device authentication
Device authentication is required to complete payment. Payment brand won't be available if device is not secured, i.e. Pattern, PIN, Password or Fingerprint is not set.
You can configure device authentication per payment brand. For example:
Set<String> paymentBrands = new LinkedHashSet<String>();
paymentBrands.add("VISA");
paymentBrands.add("MASTER");
paymentBrands.add("DIRECTDEBIT_SEPA");
CheckoutSettings checkoutSettings = new CheckoutSettings(checkoutId, paymentBrands, providerMode);
checkoutSettings.setSecurityPolicyModeForBrand("VISA", CheckoutSecurityPolicyMode.DEVICE_AUTH_REQUIRED);
checkoutSettings.setSecurityPolicyModeForBrand("MASTER", CheckoutSecurityPolicyMode.DEVICE_AUTH_REQUIRED_IF_AVAILABLE);
val paymentBrands = hashSetOf("VISA", "MASTER", "DIRECTDEBIT_SEPA")
val checkoutSettings = CheckoutSettings(checkoutId, paymentBrands, providerMode)
checkoutSettings.setSecurityPolicyModeForBrand("VISA", CheckoutSecurityPolicyMode.DEVICE_AUTH_REQUIRED)
checkoutSettings.setSecurityPolicyModeForBrand("MASTER", CheckoutSecurityPolicyMode.DEVICE_AUTH_REQUIRED_IF_AVAILABLE)
The CheckoutActivity sends the callback before the payment is submitted. It can be used to review the payment details, request a new checkout id with additional parameters for specific payment brand or cancel the payment. This callback can be handled with BroadcastReceiver or OnBeforeSubmitCallback.
Using BroadcastReceiver (since version 2.13.0)
Create your broadcast receiver to listen the intents from CheckoutActivity.
public class CheckoutBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (CheckoutActivity.ACTION_ON_BEFORE_SUBMIT.equals(action)) {
String paymentBrand = intent.getStringExtra(CheckoutActivity.EXTRA_PAYMENT_BRAND);
String checkoutId = intent.getStringExtra(CheckoutActivity.EXTRA_CHECKOUT_ID);
ComponentName senderComponentName = intent.getParcelableExtra(
CheckoutActivity.EXTRA_SENDER_COMPONENT_NAME);
// this callback can be used to request a new checkout id if selected payment brand requires
// some specific parameters or just send back the same checkout id to continue checkout process
intent = new Intent(CheckoutActivity.ACTION_ON_BEFORE_SUBMIT);
intent.setComponent(senderComponentName);
intent.setPackage(senderComponentName.getPackageName());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(CheckoutActivity.EXTRA_CHECKOUT_ID, checkoutId);
// the transaction can be aborted by sending the CheckoutActivity.EXTRA_TRANSACTION_ABORTED
intent.putExtra(CheckoutActivity.EXTRA_TRANSACTION_ABORTED, true);
context.startActivity(intent);
}
}
}
class CheckoutBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent?) {
val action = intent?.action
if (CheckoutActivity.ACTION_ON_BEFORE_SUBMIT == action) {
val paymentBrand = intent.getStringExtra(CheckoutActivity.EXTRA_PAYMENT_BRAND)
val checkoutId = intent.getStringExtra(CheckoutActivity.EXTRA_CHECKOUT_ID)
val senderComponentName: ComponentName? = intent.getParcelableExtra(
CheckoutActivity.EXTRA_SENDER_COMPONENT_NAME)
// this callback can be used to request a new checkout id if selected payment brand requires
// some specific parameters or just send back the same checkout id to continue checkout process
val newIntent = Intent(CheckoutActivity.ACTION_ON_BEFORE_SUBMIT)
newIntent.component = senderComponentName
newIntent.setPackage(senderComponentName!!.packageName)
newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP)
newIntent.putExtra(CheckoutActivity.EXTRA_CHECKOUT_ID, checkoutId)
// the transaction can be aborted by sending the CheckoutActivity.EXTRA_TRANSACTION_ABORTED
newIntent.putExtra(CheckoutActivity.EXTRA_TRANSACTION_ABORTED, true)
context.startActivity(newIntent)
}
}
}
Declare your broadcast receiver in AndroidManifest.xml.
NOTE: It is important to set android:exported="false" to your broadcast receiver. In this case the only messages the broadcast receiver can receive are those sent by components of the same application or applications with the same user ID.
To provide the best security our SDK uses directed broadcasts. This way our broadcast is received only by the specified BroadcastReceiver. For this reason you should set ComponentName to the CheckoutSettings.
CheckoutSettings checkoutSettings = new CheckoutSettings(checkoutId, paymentBrands, providerMode);
ComponentName receiverComponentName = new ComponentName("yourPackageName", "yourReceiverClassName");
checkoutSettings.setComponentName(receiverComponentName);
val checkoutSettings = CheckoutSettings(checkoutId, paymentBrands, providerMode)
val receiverComponentName = ComponentName("yourPackageName", "yourReceiverClassName")
checkoutSettings.componentName = receiverComponentName
Using OnBeforeSubmitCallback (since version 6.8.0)
Create a class implements OnBeforeSubmitCallback.
public class CheckoutCallback
implements OnBeforeSubmitCallback {
public CheckoutCallback() {
// do nothing
}
public CheckoutCallback(@NonNull Parcel in) {
// do nothing
}
@Override
public void onBeforeSubmit(@NonNull PaymentDetails paymentDetails,
@NonNull Listener listener) {
// review and update the payment details if needed,
// pass the payment details object back to Mobile SDK using listener
listener.onComplete(paymentDetails);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel parcel,
int flags) {
// do nothing
}
public static final Creator CREATOR =
new Creator() {
@Override
public CheckoutCallback createFromParcel(Parcel in) {
return new CheckoutCallback(in);
}
@Override
public CheckoutCallback[] newArray(int size) {
return new CheckoutCallback[size];
}
};
}
class CheckoutCallback() : OnBeforeSubmitCallback {
constructor(parcel: Parcel) : this() {
// do nothing
}
override fun onBeforeSubmit(
paymentDetails: PaymentDetails,
listener: OnBeforeSubmitCallback.Listener
) {
// review and update the payment details if needed,
// pass the payment details object back to Mobile SDK using listener
listener.onComplete(paymentDetails)
}
override fun describeContents(): Int {
return 0
}
override fun writeToParcel(
parcel: Parcel,
flags: Int
) {
// do nothing
}
companion object CREATOR : Creator {
override fun createFromParcel(parcel: Parcel): CheckoutCallback {
return CheckoutCallback(parcel)
}
override fun newArray(size: Int): Array {
return arrayOfNulls(size)
}
}
}
Set OnBeforeSubmitCallback to the CheckoutSettings.
NOTE: If the BroadcastReceiver and OnBeforeSubmitCallback are both set in the CheckoutSettings, then only OnBeforeSubmitCallback will be used.
In some cases you may want to review the transaction before it submitted to the Server. OPPCheckoutProviderDelegate protocol provides you a method to receive such an event with ability to affect the checkout process.
Here are the common scenarios to use the event:
Review the transaction and abort it in case of some internal errors.
Recreate checkout ID for specific payment brands, e.g. if they need some custom parameters to be sent at 1st step.
// Adopt a OPPCheckoutProviderDelegate protocol
@interface CheckoutViewController () <OPPCheckoutProviderDelegate>
@end
@implementation CheckoutViewController
...
- (IBAction)checkoutButtonAction:(id)sender {
// Set a delegate property for the OPPCheckoutProvider instance
self.checkoutProvider.delegate = self;
...
}
// Implement a callback, it will be called right before submitting a transaction to the Server
- (void)checkoutProvider:(OPPCheckoutProvider *)checkoutProvider continueSubmitting:(OPPTransaction *)transaction completion:(void (^)(NSString * _Nullable checkoutID, BOOL abort))completion {
// To continue submitting you should call completion block which expects 2 parameters:
// checkoutID - you can create new checkoutID here or pass current one
// abort - you can abort transaction here by passing 'true'
completion(transaction.paymentParams.checkoutID, false);
}
@end
// Adopt a OPPCheckoutProviderDelegate protocol
class CheckoutViewController: UIViewController, OPPCheckoutProviderDelegate {
...
@IBAction func checkoutButtonAction(_ sender: UIButton) {
// Set a delegate property for the OPPCheckoutProvider instance
self.checkoutProvider.delegate = self
...
}
// Implement a callback, it will be called right before submitting a transaction to the Server
func checkoutProvider(_ checkoutProvider: OPPCheckoutProvider, continueSubmitting transaction: OPPTransaction, completion: @escaping (String?, Bool) -> Void) {
// To continue submitting you should call completion block which expects 2 parameters:
// checkoutID - you can create new checkoutID here or pass current one
// abort - you can abort transaction here by passing 'true'
completion(transaction.paymentParams.checkoutID, false)
}
}
Summary page
It is possible to use Mobile SDK to temporarily store the payment information after submission, rather than executing it straight away. This can be useful if you want to display an order summary page before committing the payment. For this purpose, you should implement your broadcast receiver to listen the intents from CheckoutActivity and use CheckoutActivity.ACTION_ON_BEFORE_SUBMIT callback to display the summary page.
public class CheckoutBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (CheckoutActivity.ACTION_ON_BEFORE_SUBMIT.equals(action)) {
String checkoutId = intent.getStringExtra(CheckoutActivity.EXTRA_CHECKOUT_ID);
ComponentName senderComponent = intent.getParcelableExtra(
CheckoutActivity.EXTRA_SENDER_COMPONENT_NAME);
/* Display the summary page. */
Intent summaryPageIntent = new Intent(context, SummaryPageActivity.class);
summaryPageIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
summaryPageIntent.putExtra(OrderReviewActivity.EXTRA_CHECKOUT_ID, checkoutId);
summaryPageIntent.putExtra(OrderReviewActivity.EXTRA_SENDER, senderComponent);
context.startActivity(summaryPageIntent);
}
}
}
class CheckoutBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent?) {
val action = intent?.action
if (CheckoutActivity.ACTION_ON_BEFORE_SUBMIT == action) {
val checkoutId = intent.getStringExtra(CheckoutActivity.EXTRA_CHECKOUT_ID)
val senderComponent = intent.getParcelableExtra<ComponentName>(
CheckoutActivity.EXTRA_SENDER_COMPONENT_NAME)
/* Display the summary page. */
val summaryPageIntent = Intent(context, SummaryPageActivity::class.java)
summaryPageIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
summaryPageIntent.putExtra(OrderReviewActivity.EXTRA_CHECKOUT_ID, checkoutId)
summaryPageIntent.putExtra(OrderReviewActivity.EXTRA_SENDER, senderComponent)
context.startActivity(summaryPageIntent)
}
}
}
The summary page should consist of at least two actions:
1. Confirm payment. You should just return control back to the CheckoutActivity to submit the payment.
2. Cancel payment. In this case the CheckoutActivity should be started with CheckoutActivity.EXTRA_TRANSACTION_ABORTED flag. Then checkout will be closed with ErrorCode.ERROR_CODE_TRANSACTION_ABORTED code.
Receiving callbacks during checkout process using OnBeforeSubmitCallback
Since the update there is another way to handle before submit event using OnBeforeSubmitCallback.
Create new class which will implement OnBeforeSubmitCallback interface.
public class CheckoutOnBeforeSubmitListener extends OnBeforeSubmitCallback {
@Override
public void onBeforeSubmitUpdate(@NonNull PaymentDetails paymentDetails,
@NonNul Listener callback) {
String currentCheckoutId = paymentDetails.getCheckoutId();
String paymentBrand = paymentDetails.getPaymentBrand();
/* You can use this callback to request a new checkout ID if selected payment brand requires
some specific parameters or just send back the same checkout id to continue checkout process */
paymentDetails.setCheckoutId(newCheckoutId);
/* You also can abort the transaction using this method */
paymentDetails.setAborted(true);
/* Once you are ready to continue the payment flow pass updated paymentDetails to the callback*/
callback.onComplete(paymentDetails);
}
}
class CheckoutOnBeforeSubmitListener() : OnBeforeSubmitCallback {
override fun onBeforeSubmitUpdate(paymentDetails: PaymentDetails, callback: OnBeforeSubmitCallback.Listener) {
val currentCheckoutId = paymentDetails.checkoutId
val paymentBrand = paymentDetails.paymentBrand
/* You can use this callback to request a new checkout ID if selected payment brand requires
some specific parameters or just send back the same checkout id to continue checkout process */
paymentDetails.checkoutId = newCheckoutId
/* Once you are ready to continue the payment flow pass updated paymentDetails to the callback*/
callback.onComplete(paymentDetails)
}
}
You can cancel a payment. In this case set false during paymentDetails.setCanceled() call. Then checkout will be closed with ErrorCode.ERROR_CODE_TRANSACTION_ABORTED code.
It is possible to use Mobile SDK to temporarily store the payment information after submission, rather than executing it straight away. This can be useful if you want to display an order summary page before committing the payment. For this purpose, you can implement your ViewController to display the payment summary. OPPCheckoutProviderDelegate protocol provides you a method to receive such an event with ability to affect the checkout process.
// Adopt a OPPCheckoutProviderDelegate protocol
@interface CheckoutViewController () <OPPCheckoutProviderDelegate>
@end
@implementation CheckoutViewController
...
- (IBAction)checkoutButtonAction:(id)sender {
// Set a delegate property for the OPPCheckoutProvider instance
self.checkoutProvider.delegate = self;
...
}
// Implement a callback, it will be called right before submitting a transaction to the Server
- (void)checkoutProvider:(OPPCheckoutProvider *)checkoutProvider continueSubmitting:(OPPTransaction *)transaction completion:(void (^)(NSString * _Nullable checkoutID, BOOL abort))completion {
// You could display new ViewController with the option to Continue or Abort in this callback.
// And than call completion block with params
// To continue submitting you should call completion block which expects 2 parameters:
// checkoutID - you can create new checkoutID here or pass current one
// abort - you can abort transaction here by passing 'true'
completion(transaction.paymentParams.checkoutID, false);
}
@end
// Adopt a OPPCheckoutProviderDelegate protocol
class CheckoutViewController: UIViewController, OPPCheckoutProviderDelegate {
...
@IBAction func checkoutButtonAction(_ sender: UIButton) {
// Set a delegate property for the OPPCheckoutProvider instance
self.checkoutProvider.delegate = self
...
}
// Implement a callback, it will be called right before submitting a transaction to the Server
func checkoutProvider(_ checkoutProvider: OPPCheckoutProvider, continueSubmitting transaction: OPPTransaction, completion: @escaping (String?, Bool) -> Void) {
// You could display new ViewController with the option to Continue or Abort in this callback.
// And than call completion block with params
// To continue submitting you should call completion block which expects 2 parameters:
// checkoutID - you can create new checkoutID here or pass current one
// abort - you can abort transaction here by passing 'true'
completion(transaction.paymentParams.checkoutID, false)
}
}
The summary page should consist of at least two actions and return control back to the OPPCheckoutProvider.
1. Confirm payment.
2. Cancel payment.
Till.js in mobile SDK
Important: In some rare cases,
a payment method cannot be integrated natively in the mobile SDK,
but we still want to offer that payment method.
For this case, the alternative solution is Till.js integration inside mobile SDK.
Please consider this option as an alternative only and not a default integration.
As it stands, Till.js integration inside Mobile SDK can be used for brand "ONEY".
To configure brand-specific options, you need to create an object of type OPPWpwlOptions.
Please refer to Till.js API for more details.
Initialize OPPWpwlOptions with two dictionaries: one for JSON-like options, the other one for Javascript functions.
The following snippet gives an example which sets the locale and the onReady callback.
NSDictionary *config = @{@"locale": @"fr-BE"};
NSDictionary *jsConfig = @{@"onReady": @"function() {/* function here */}"};
OPPWpwlOptions *wpwlOptions = [OPPWpwlOptions initWithConfiguration:config jsFunctions:jsConfig];
var config = [ "locale" : "fr-BE" ]
var jsConfig = [ "onReady" : "function() {/* function here */}" ] as [String : Any]
let wpwlOptions = OPPWpwlOptions.initWithConfiguration( config , jsFunctions: jsConfig)
Create a dictionary to map the brand to the OPPWpwlOptions object, and set it the OPPCheckoutSettings.wpwlOptions property
let checkoutSettings = OPPCheckoutSettings()
checkoutSettings.wpwlOptions["ONEY"] = wpwlOptions;
To configure brand-specific options, you need to create an object of type WpwlOptions.
Please refer to Till.js API for more details.
With WpwlOptions, use addValue to add an option, and use addJSFunction to add a Javascript function.
The following snippet gives an example which sets the locale and the onReady callback.
WpwlOptions wpwlOption = new WpwlOptions().addValue("locale", "fr-BE");
wpwlOptions.addJSFunction("onReady","function() {/* function here */}");
val wpwlOption = WpwlOptions().addValue("inlineFlow", "fr-BE")
wpwlOptions.addJSFunction("onReady","function() {/* function here */}")
Use Map to add payment brand and WpwlOptions. Add the map to CheckoutSettings.
Map<String, WpwlOptions> wpwlOptionsMap = new HashMap<>();
wpwlOptionsMap.put("ONEY", wpwlOptions);
checkoutSettings.setWpwlOptions(wpwlOptionsMap);
val wpwlOptionsMap = mutableMapOf<String, WpwlOptions>()
wpwlOptionsMap["ONEY"] = wpwlOptions
checkoutSettings.setWpwlOptions(wpwlOptionsMap);