Flutter
The Checkout SDK is a Flutter framework (library) developed by Ottu, designed to seamlessly integrate an Ottu-powered checkout process into Flutter applications for both iOS and Android platforms. This framework functions as a wrapper over the corresponding native SDKs, ensuring a smooth and efficient payment experience.
With the Checkout SDK, both the visual appearance and the forms of payment available during the checkout process can be fully customized to meet specific requirements.
To integrate the Checkout SDK, the library must be added to the Flutter application and initialized with the following parameters:
Additionally, optional configurations such as the forms of payment to be accepted and the theme styling for the checkout interface can be specified.
To install the Flutter SDK plugin, the following section must be added to the pubspec.yaml
file:
dependencies:
flutter:
sdk: flutter
ottu_flutter_checkout:
# To use ottu_flutter_checkout SDK from a local source, uncomment the line below
# and comment out the three lines specifying the Git repository.
# path: ../ottu_flutter_checkout
git:
url: https://github.com/ottuco/ottu-flutter.git
ref: main
And then run flutter pub get
command in the terminal.
After adding the dependency, run the following command in the terminal to fetch the required packages: flutter pub get
Minimum Requirements
The SDK can be used on devices running Android 8 (Android SDK 26) or higher.
Minimum Requirements
The SDK can be used on devices running iOS 15 or higher.
The SDK UI is embedded as a fragment
within any part of an activity
in the merchant's application.
Example:
If only one payment option is available and it is a wallet, the UI is automatically minimized.
The SDK supports two languages: English and Arabic, with English set as the default.
The SDK automatically applies the language based on the device settings, eliminating the need for manual adjustments within the application.
However, if the transaction is created in a different language and setup preload is enabled, texts retrieved from the backend (such as fee descriptions) will be displayed in the transaction language, regardless of the device’s language settings.
To ensure consistency, the current device language should be taken into account when specifying a language code in the transaction creation request of the Checkout API.
The SDK supports automatic UI adjustments based on the device's theme settings (light or dark mode).
The appropriate theme is applied during SDK initialization, aligning with the device's configuration. Similar to language settings, no manual adjustments are required within the application.
The Checkout SDK is initialized using the CheckoutArguments
class, which includes the properties listed below.
To initialize the SDK, an instance of CheckoutArguments
must be passed as an argument to the OttuCheckoutWidget
object.
For a detailed implementation example, refer to the Example section.
It is used to define the Ottu merchant domain and must be set to the root domain of the Ottu account, excluding the https://
or http://
prefix.
For example, if the Ottu URL is https://example.ottu.com
, the corresponding merchant_id is example.ottu.com
.
This property is required to ensure that the checkout process is correctly linked to the associated Ottu merchant account.
It is the Ottu API public key, used for authentication when communicating with Ottu's servers during the checkout process.
Ensure that only the public key is used. The private key must remain confidential and must never be shared with any clients.
It is a unique identifier for the payment transaction associated with the checkout process.
This identifier is automatically generated when a payment transaction is created. For further details on how to use the session_id
parameter in the Checkout API, refer to the session_id documentation.
The formsOfPayment
parameter is used to customize the forms of payment displayed in the checkout process. By default, all forms of payment are enabled.
Available options for formsOfPayment:
applePay
: The Apple Pay payment method is supported, allowing purchases to be made using Apple Pay-enabled devices.cardOnsite
: A direct (onsite) payment method, where customers are required to enter their card details directly within the SDK.tokenPay
: A method utilizing tokenization, ensuring that customer payment information is securely stored and processed.redirect
: A payment method where customers are redirected to an external payment gateway or a third-party processor to complete the transaction.stcPay
: A method where customers enter their mobile number and authenticate using an OTP sent to their mobile device.
An ApiTransactionDetails
class object is used to store transaction details. If provided, transaction details will not be requested from the backend, thereby reducing processing time.
A Theme class object is used for UI customization. All fields are optional and may include values for background colors, text colors, and fonts for various UI components.
For more details, refer to Android Customization Theme.
The displaySettings
object accepts a PaymentOptionsDisplaySettings
configuration, which defines how payment options are presented to the user during checkout. For more details, refer to the Payment Options Display Mode section.
Callback functions are used to retrieve the payment status. These must be provided directly to the Checkout initialization function. For more information, please check here.
The callbacks are handled by the native frameworks. Please see the links here:
It is not necessary to modify anything for the callbacks, as they are managed by the native SDK.
However, the following examples demonstrate how they function on both platforms:
The callbacks
are defined within the body of the Checkout.init
function:
val sdkFragment = Checkout.init(
context = checkoutView.context,
builder = builder,
setupPreload = apiTransactionDetails,
successCallback = {
Log.e("TAG", "successCallback: $it")
showResultDialog(checkoutView.context, it)
},
cancelCallback = {
Log.e("TAG", "cancelCallback: $it")
showResultDialog(checkoutView.context, it)
},
errorCallback = { errorData, throwable ->
Log.e("TAG", "errorCallback: $errorData")
showResultDialog(checkoutView.context, errorData, throwable)
},
Here is an example of a delegate:
extension CheckoutPlatformView: OttuDelegate {
public func errorCallback(_ data: [String: Any]?) {
debugPrint("errorCallback\n")
DispatchQueue.main.async {
self.paymentViewController?.view.isHidden = true
self.paymentViewController?.view.setNeedsLayout()
self.paymentViewController?.view.layoutIfNeeded()
self._view.heightHandlerView.setNeedsLayout()
self._view.heightHandlerView.layoutIfNeeded()
self._view.setNeedsLayout()
self._view.layoutIfNeeded()
let alert = UIAlertController(
title: "Error",
message: data?.debugDescription ?? "",
preferredStyle: .alert
)
alert.addAction(
UIAlertAction(title: "OK", style: .cancel)
)
debugPrint("errorCallback, show alert\n")
self.paymentViewController?.present(alert, animated: true)
}
}
public func cancelCallback(_ data: [String: Any]?) {
debugPrint("cancelCallback\n")
DispatchQueue.main.async {
var message = ""
if let paymentGatewayInfo = data?["payment_gateway_info"] as? [String: Any],
let pgName = paymentGatewayInfo["pg_name"] as? String,
pgName == "kpay" {
message = paymentGatewayInfo["pg_response"].debugDescription
} else {
message = data?.debugDescription ?? ""
}
self.paymentViewController?.view.isHidden = true
self.paymentViewController?.view.setNeedsLayout()
self.paymentViewController?.view.layoutIfNeeded()
self._view.heightHandlerView.setNeedsLayout()
self._view.heightHandlerView.layoutIfNeeded()
self._view.setNeedsLayout()
self._view.layoutIfNeeded()
let alert = UIAlertController(
title: "Cancel",
message: message,
preferredStyle: .alert
)
alert.addAction(
UIAlertAction(title: "OK", style: .cancel)
)
debugPrint("cancelCallback, show alert\n")
self.paymentViewController?.present(alert, animated: true)
}
}
public func successCallback(_ data: [String: Any]?) {
debugPrint("successCallback\n")
DispatchQueue.main.async {
self.paymentViewController?.view.isHidden = true
self._view.paymentSuccessfullLabel.isHidden = false
self.paymentViewController?.view.setNeedsLayout()
self.paymentViewController?.view.layoutIfNeeded()
self._view.heightHandlerView.setNeedsLayout()
self._view.heightHandlerView.layoutIfNeeded()
self._view.setNeedsLayout()
self._view.layoutIfNeeded()
let alert = UIAlertController(
title: "Success",
message: data?.debugDescription ?? "",
preferredStyle: .alert
)
alert.addAction(
UIAlertAction(title: "OK", style: .cancel)
)
debugPrint("successCallback, showing alert\n")
self.paymentViewController?.present(alert, animated: true)
}
}
}
final checkoutArguments = CheckoutArguments(
merchantId: state.merchantId,
apiKey: state.apiKey,
sessionId: state.sessionId ?? "",
amount: amount,
showPaymentDetails: state.showPaymentDetails,
paymentOptionsListMode: state.paymentOptionsDisplayMode ?? PaymentOptionsListMode.BOTTOM_SHEET,
apiTransactionDetails: state.preloadPayload == true ? _apiTransactionDetails : null,
formsOfPayment: formOfPayments?.isNotEmpty == true ? formOfPayments : null,
theme: _theme,
);
//
Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
SizedBox(height: 46),
Text(
"Customer Application",
textAlign: TextAlign.center,
style: ts.TextStyle(fontSize: 24),
),
// Start of Merchant content
const Padding(
padding: EdgeInsets.all(12.0),
child: Text(
"Some users UI elements, Some users UI elements, Some users UI elements, Some users UI elements, Some users UI elements",
),
),
// End of Merchant content
Padding(
padding: const EdgeInsets.all(12.0),
child: ValueListenableBuilder<int>(
builder: (BuildContext context, int height, Widget? child) {
return SizedBox(
height: height.toDouble(),
child: OttuCheckoutWidget(arguments: widget.checkoutArguments),
);
},
valueListenable: _checkoutHeight,
),
),
const SizedBox(height: 20),
],
),
),
);
The code samples can be found here.
To access the actual code samples, navigate to the lib
directory.
Additionally, the android
and ios
directories contain platform-specific code relevant to each respective environment.
The class responsible for defining the theme is called CheckoutTheme
. It utilizes additional component classes, including:
ButtonComponent
LabelComponent
TextFieldComponent
The CheckoutTheme
class consists of objects representing various UI components. While the component names generally align with those listed above, they also contain platform-specific fields for further customization.
Below is the structure of the Customization Theme
class:
class CheckoutTheme extends Equatable {
@_UiModeJsonConverter()
final CustomerUiMode uiMode;
final TextStyle? mainTitleText;
final TextStyle? titleText;
final TextStyle? subtitleText;
final TextStyle? feesTitleText;
final TextStyle? feesSubtitleText;
final TextStyle? dataLabelText;
final TextStyle? dataValueText;
final TextStyle? errorMessageText;
final TextFieldStyle? inputTextField;
final ButtonComponent? button;
final RippleColor? backButton;
final ButtonComponent? selectorButton;
final SwitchComponent? switchControl;
final Margins? margins;
final ColorState? sdkBackgroundColor;
final ColorState? modalBackgroundColor;
final ColorState? paymentItemBackgroundColor;
final ColorState? selectorIconColor;
final ColorState? savePhoneNumberIconColor;
}
Specifies the device Theme
mode, which can be set to one of the following:
light
– Forces the UI to use light mode.dark
– Forces the UI to use dark mode.auto
– Adapts the UI based on the device's system settings.
All properties in the CheckoutTheme
class are optional, allowing users to customize any of them as needed.
If a property is not set, the default value (as specified in the Figma design here) will be applied automatically.
General
mainTitleText
Font and color for all “Captions”
titleText
Font and color for payment options in the list
subtitleText
Font and color for payment options details (like expiration date)
Fees
feesTitleText
Font and color of fees value in the payment options list
feesSubtitleText
Font and color of fees description in the payment options list
Data
dataLabelText
Font and color of payment details fields (like “Amount”)
dataValueText
Font and color of payment details values
Other
errorMessageText
Font and color of error message text in pop-ups
inputTextField
Font and color of text in any input field (including disabled state)
sdkbackgroundColor
The main background of the SDK view component
modalBackgroundColor
The background of any modal window
paymentItemBackgroundColor
The background of an item in payment options list
selectorIconColor
The color of the icon of the payment
savePhoneNumberIconColor
The color of “Diskette” button for saving phone number
button
Background, text color and font for any button
backButton
Color of the “Back” navigation button
selectorButton
Background, text color and font for payment item selection button
switch
Colors of the switch background and its toggle in different states (on, off and disabled)
margins
Top, left, bottom and right margins between component
color
Main color integer value
Int
colorDisabled
Disabled stated color integer value
Int
color
Main color integer value
Int
rippleColor
Ripple color integer value
Int
colorDisaled
Disabled stated color integer value
Int
textColor
Main color integer value
fontType
Font resource ID
Int
background
Background color integer value
primaryColor
Text color
focusedColor
Selected text color
text
Text value
error
Text value
rippleColor
Button background color
fontType
Button text font ID
Int
textColor
Button text color
checkedThumbTintColor
Toggle color in checked state
Int
uncheckedThumbTintColor
Toggle color in unchecked state
Int
checkedTrackTintColor
Track color in checked state
Int
uncheckedTrackTintColor
Track color in unchecked state
Int
checkedTrackDecorationColor
Decoration color in checked state
Int
uncheckedTrackDecorationColor
Decoration color in unchecked state
Int
left
Int
top
Int
right
Int
bottom
Int
To build the theme
, the user must follow similar steps as described in the corresponding file of the test app.
Here is a code snippet demonstrating the process:
final checkoutTheme = ch.CheckoutTheme(
uiMode: ch.CustomerUiMode.dark,
titleText: ch.TextStyle(),
modalBackgroundColor: ch.ColorState(color: Colors.amber));
The SDK provides flexible customization for how payment options are displayed. It supports the following optional parameters:
mode
: Determines the layout style—eitherBottomSheet
(default) orList
.BottomSheet: This is the default layout used in previous SDK versions.
List: A new layout that shows payment options in a vertical list placed above the Payment Details section and the Pay button.
visibleItemsCount
: Sets how many payment options are shown at once (default is5
). Applicable only inList
mode.This unsigned integer controls how many payment options are visible simultaneously in List mode.
If the number of available options is less than
visibleItemsCount
, the list automatically resizes to fit the actual number of options.
Passing 0
will cause the SDK to throw an exception. This exception must be caught and handled by the parent application.
defaultSelectedPgCode
: Specifies a payment gateway (PG) code to be pre-selected by default.This field accepts a PG code to auto-select a specific payment option.
If the SDK finds a payment method matching the provided PG code, it will be selected by default.
If no match is found, no option is selected.
All of these parameters are optional and are demonstrated in the following figures.
Android
The List
The List mode is displayed as illustrated in the figure below
A view with a selected payment option
A view with an expanded listExpanded list view
Here is a code sample:
val paymentOptionsDisplayMode =
if (showPaymentOptionsList) Checkout.PaymentOptionsDisplaySettings.PaymentOptionsDisplayMode.List(
visiblePaymentItemsCount = paymentOptionsListCount
) else Checkout.PaymentOptionsDisplaySettings.PaymentOptionsDisplayMode.BottomSheet
val paymentOptionsDisplaySettings = Checkout.PaymentOptionsDisplaySettings(
mode = paymentOptionsDisplayMode,
defaultSelectedPgCode = defaultSelectedPgCode
)
and passed to Checkout.init
builder class via the following object:
.paymentOptionsDisplaySettings(paymentOptionsDisplaySettings)
iOS
The List
mode looks like the following
Selected item view
Expanded list view
Here is a code sample:
let paymentOptionsDisplaySettings: PaymentOptionsDisplaySettings =
if arguments.showPaymentOptionsList {
PaymentOptionsDisplaySettings(
mode: .list,
visibleItemsCount: UInt(arguments.paymentOptionsListCount),
defaultSelectedPgCode: arguments.defaultSelectedPgCode
)
} else {
PaymentOptionsDisplaySettings(
mode: .bottomSheet,
defaultSelectedPgCode: arguments.defaultSelectedPgCode
)
}
and passed to Checkout.init
via the following object:
displaySettings:paymentOptionsDisplaySettings
To see the full function call, please refer the code snippet in the Ottu SDK - Flutter | Example section.
If the STC Pay integration between Ottu and STC Pay has been completed, the Checkout SDK will automatically handle the necessary checks to display the STC Pay button seamlessly.
When the Checkout SDK is initialized with the session_id and payment gateway codes (pg_codes), the SDK will verify the following conditions:
The
session_id
andpg_codes
provided during initialization must be associated with the STC Pay Payment Service. This ensures that the STC Pay option is available for the customer.In the Android SDK, the STC Pay button is displayed regardless of whether the customer has entered a mobile number while creating the transaction.
Due to compliance requirements, for iOS, the KNET payment gateway requires a popup notification displaying the payment result after each failed payment. This notification is triggered only in the cancelCallback, but only if a response is received from the payment gateway.
As a result, the user cannot retry the payment without manually clicking on Apple Pay again.
To properly handle the KNET popup notification, the following Swift code snippet must be implemented into the payment processing flow:
func cancelCallback(_ data: [String: Any]?) {
var message = ""
if let paymentGatewayInfo = data?["payment_gateway_info"] as? [String: Any],
let pgName = paymentGatewayInfo["pg_name"] as? String,
pgName == "kpay" {
message = paymentGatewayInfo["pg_response"].debugDescription
} else {
message = data?.debugDescription ?? ""
}
navigationController?.popViewController(animated: true)
let alert = UIAlertController(
title: "Cancel",
message: message,
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: "OK", style: .cancel))
self.present(alert, animated: true)
}
Function Breakdown
The above code performs the following checks and actions:
Checks if the
cancelCallback
object contains payment gateway informationIt verifies whether the
payment_gateway_info
field is available in the response.
Identifies if the payment gateway used is
KNET
It checks if the
pg_name
property equalskpay
, confirming that the transaction was processed using KNET.
Check the above two conditions are met by retrieving the payment gateway response
If the gateway response (
pg_response
) is available, it is displayed; otherwise, a default message (Payment was cancelled.
) is used.
Navigates back and displays an alert
The user is returned to the previous screen (
navigationController?.popViewController(animated: true)
).A popup notification is displayed using
self.present(alert, animated: true)
, informing the user about the failed payment.
This payment option enables direct payments through the mobile SDK. The SDK presents a user interface where the customer can enter their cardholder details (CHD). If supported by the backend, the user can also save the card for future payments—stored as a tokenized payment method.
The onsite checkout screen appears identical to the native platform version.
Android
iOS
The SDK supports multiple instances of onsite checkout payments. Therefore, for each payment method with a PG code equal to ottu_pg
, the card form (as shown above) will be displayed.
Android
iOS
The SDK utilizes Sentry for error logging and reporting. It is initialized based on the configuration provided by SDK Studio.
However, since the SDK is a framework embedded within the merchant's app, conflicts may arise if the app also integrates Sentry.
To prevent conflicts, the merchant can disable Sentry within the Checkout SDK by setting the is_enabled
flag to false
in the configuration.
Rooting and Jailbreak Detection
The Flutter SDK does not perform rooting or jailbreak detection independently. Instead, these security checks are entirely handled by the native SDKs.
For more details, refer to the following links:
Screen Capture Prevention
The SDK includes mechanisms to prevent screen capturing (such as screenshots and video recordings) on screens that display sensitive information. The Flutter SDK does not handle this independently; instead, it relies on the logic implemented in the native SDKs for Android and iOS.
Since the implementation differs between the two platforms, please refer to the respective native documentation for more details.
The SDK supports the following forms of payment:
tokenPay
redirect
stcPay
cardOnsite
applePay
(iOS only)
Merchants can configure the forms of payment displayed according to their needs.
For example, to display only the STC Pay button, use:
formsOfPayment = [stcPay]
This ensures that only the STC Pay button is shown. The same approach applies to other payment methods.
The SDK requires a device running:
Android 8 or higher (API level 26 or higher)
iOS 14 or higher
Yes, customization is supported. For more details, refer to the Customization Theme section.
Last updated