Skip to main content

Card Data

Card payments are handled through secure input components - Secure Fields. These components ensure secure entry, validation, and protection of card data. The host app never has access to sensitive data in unprotected form.

Secure Fields

The library provides four components for entering data in the payment form:

SecurePanField

Field for entering the card number (PAN - Primary Account Number).

  • Accepts digits only
  • Automatically detects card network (Visa, Mastercard)
  • Formats card number with spaces according to card network (for example 4111 1111 1111 1111)
  • Limits input length according to detected card network
  • Validates card number using the Luhn algorithm
  • Supports system autofill
  • Exposes last4 property - the last 4 digits of the card (safe for UI display)
import cz.comgate.sdk.compose.*

val panState = rememberSecurePanFieldState()

SecurePanField(
state = panState,
modifier = Modifier.fillMaxWidth()
)

SecureExpiryField

Field for entering the card expiry date.

  • Accepts digits only
  • Automatically formats input as MM/YY
  • Validates month (1-12) and checks whether card is expired
  • Supports autofill in MMYY and MMYYYY formats
val expiryState = rememberSecureExpiryFieldState()

SecureExpiryField(
state = expiryState,
modifier = Modifier.fillMaxWidth()
)

SecureCvvField

Field for entering the card security code (CVV/CVC).

  • Accepts digits only
  • Length automatically adapts to detected card network
  • Validates minimum required length
val cvvState = rememberSecureCvvFieldState()

SecureCvvField(
state = cvvState,
modifier = Modifier.fillMaxWidth()
)

SecureFullNameField

Field for entering the payer's first and last name.

  • Accepts text input (including spaces)
  • Supports system name autofill
  • Valid when value is non-empty
  • After attaching to ComgateSecureSession, its value is automatically used as fullName
import androidx.compose.runtime.DisposableEffect
import cz.comgate.sdk.compose.*

val fullNameState = rememberSecureFullNameFieldState()

DisposableEffect(session) {
fullNameState.attachTo(session)
onDispose { fullNameState.detachFrom(session) }
}

SecureFullNameField(
state = fullNameState,
modifier = Modifier.fillMaxWidth()
)
Information

PaymentParams.fullName always has priority over SecureFullNameField value. If fullName in PaymentParams is filled, SecureFullNameField is not required. If fullName is empty, value from the attached field is used. If both are empty, payment fails with MISSING_CARDHOLDER_NAME.

Common properties

All Secure Fields (SecurePanField, SecureExpiryField, SecureCvvField, SecureFullNameField) share these properties and methods:

Property / MethodTypeDescription
isFieldValidBooleanCurrent field validation state (read-only).
onValidationChanged(Boolean) -> UnitCallback called when validation state changes.
showLabelBooleanShows label above the field. Default true. Set false to hide label.
labelTextString?Custom label text. If null, default translated label from session.translation is used.
setHint(hint)StringSets placeholder text.
setEnabled(enabled)BooleanEnables/disables field.
clear()-Clears field content and resets validation.

Input field labels

Each field has a label above the input (SecurePanField, SecureExpiryField, SecureCvvField, SecureFullNameField).

  • Default label text is taken from session.translation
  • Label can be hidden via showLabel = false
  • Default translation can be overridden via labelText
SecurePanField(
state = panState,
modifier = Modifier.fillMaxWidth(),
update = {
showLabel = true
labelText = "Payment card number"
}
)

SecureCardDataCollector

SecureCardDataCollector links three separate Secure Fields and tracks their overall validation state. It is required for payment processing.

import cz.comgate.sdk.compose.*

// Field states - raw card data stays inside SDK
val panState = rememberSecurePanFieldState()
val expiryState = rememberSecureExpiryFieldState()
val cvvState = rememberSecureCvvFieldState()

// Collector linking fields and tracking overall validation
val collector = rememberSecureCardCollector(
panState = panState,
expiryState = expiryState,
cvvState = cvvState,
onValidationChanged = { isValid ->
// isValid is true when all three fields are valid
}
)

Focus handoff between fields

rememberSecureCardCollector automatically moves focus between fields in this order by default:

PAN -> Expiry -> CVV

The move happens when the current field is fully filled and valid.

val collector = rememberSecureCardCollector(
panState = panState,
expiryState = expiryState,
cvvState = cvvState,
autoAdvanceFocus = true // default value
)

If you want to control focus manually, disable auto behavior:

val collector = rememberSecureCardCollector(
panState = panState,
expiryState = expiryState,
cvvState = cvvState,
autoAdvanceFocus = false
)

When implementing with Android View, you can connect focus handoff manually via nextSecureField:

panField.nextSecureField = expiryField
expiryField.nextSecureField = cvvField
Property / MethodTypeDescription
isValidBooleantrue if all three fields are valid.
onValidationChanged(Boolean) -> UnitCallback called when validation state changes in any field.

SecurePayButton

SecurePayButton is a prebuilt payment button that automatically integrates with session and collector:

  • Automatically enables/disables based on card field validation state
  • Stays disabled until session initialization succeeds
  • Starts payment processing through ComgateSecureSession on click
  • Shows shimmer sweep animation over the button during processing (can be disabled)
  • Returns payment result via callback

Button setup

Composable SecurePayButton accepts session, collector, callback, and payment parameter provider:

import cz.comgate.sdk.compose.*

SecurePayButton(
session = session,
collector = collector,
onPaymentResult = { result ->
// Handle payment result
},
paymentParamsProvider = {
PaymentParams(
email = "customer@example.com",
price = 100,
curr = "CZK",
label = "Payment name",
refId = "ref-123",
fullName = "John Smith",
country = "CZ", // optional
expirationTime = "1h", // optional
billingAddrCity = "Hradec Kralove", // optional
billingAddrStreet = "Jiraskova 115", // optional
billingAddrPostalCode = "50304", // optional
billingAddrCountry = "CZ" // optional
)
},
modifier = Modifier.fillMaxWidth()
)

paymentParamsProvider is a lambda called at click time, allowing dynamic reads of current UI values (for example amount from a text field).

PaymentParams

ParameterTypeRequiredDescription
emailStringYesPayer email address.
priceIntYesPayment amount in minor units (for example 10000 = 100.00 CZK).
currStringYesCurrency code - ISO 4217 (for example "CZK", "EUR").
labelStringYesShort product description (1-16 characters).
refIdStringYesVariable symbol or order number (your internal ID).
fullNameStringConditionallyPayer first and last name. If non-empty, it has priority over SecureFullNameField. If empty, value from attached SecureFullNameField is used (then required). If both are empty, payment fails with MISSING_CARDHOLDER_NAME.
countryStringNoISO 3166-1 alpha-2 country code (for example "CZ", "SK"). Default: "CZ". Allowed: AT, BE, CY, CZ, DE, EE, EL, ES, FI, FR, GB, HR, HU, IE, IT, LT, LU, LV, MT, NL, NO, PL, PT, RO, SI, SK, SE, US. Any other value (including empty string) is normalized to "ALL" before sending. Value is also passed to Google Pay dialog.
accountString?NoClient bank account identifier in Comgate system.
nameString?NoProduct identifier (shown in daily CSV as "Product").
preauthBoolean?NoMarks payment as preauthorization - reserves funds without immediate capture. true - preauth, null - default behavior. Cannot be combined with initRecurring = true.
initRecurringBoolean?NoMarks payment as first in a recurring payment series. true - initial recurring payment, null - default behavior. Cannot be combined with preauth = true.
expirationTimeString?NoPayment expiration time (for example "30m", "3h", "5d"). Range: 30 minutes - 7 days.
dynamicExpirationBoolean?NoDynamic payment expiration (true to enable).
billingAddrCityString?NoBilling address - city.
billingAddrStreetString?NoBilling address - street.
billingAddrPostalCodeString?NoBilling address - postal code.
billingAddrCountryString?NoBilling address - country code (ISO 3166-1 alpha-2).
deliveryString?NoDelivery method ("HOME_DELIVERY", "PICKUP", "ELECTRONIC_DELIVERY").
homeDeliveryCityString?NoDelivery address - city (only with delivery = "HOME_DELIVERY").
homeDeliveryStreetString?NoDelivery address - street (only with delivery = "HOME_DELIVERY").
homeDeliveryPostalCodeString?NoDelivery address - postal code (only with delivery = "HOME_DELIVERY").
homeDeliveryCountryString?NoDelivery address - country code (only with delivery = "HOME_DELIVERY").
categoryString?NoProduct category ("PHYSICAL_GOODS_ONLY", "OTHER").
require3dsBoolean?NoOnly in dev mode (devMode = true). Forces 3DS flow type on test server. true - force challenge flow, false - force frictionless flow (no challenge), null - server decides. Ignored in production.
errorReasonErrorReason?NoOnly in dev mode (devMode = true). Simulates specific rejection/failure reason. Value (including null) is always sent to server in dev mode. Ignored in production. See ErrorReason.

Recurring payments and preauthorization

initRecurring

initRecurring = true marks payment as the first (initial) transaction in a recurring payment series. This signals the bank that future automatic payments will follow (for example subscription, periodic fees).

PaymentParams(
email = "customer@example.com",
price = 9900,
curr = "CZK",
label = "Subscription - first payment",
refId = "sub-001",
fullName = "John Smith",
initRecurring = true // This payment starts recurring series
)
Information

initRecurring cannot be combined with preauth = true.

preauth

preauth = true marks payment as preauthorization - the bank temporarily reserves the requested amount on payer card, but funds are not captured immediately. Capture happens later when confirmed on backend side.

Preauthorization is typically used when final amount is unknown at authorization time (for example rentals, hotels, fueling).

PaymentParams(
email = "customer@example.com",
price = 50000,
curr = "CZK",
label = "Vehicle reservation",
refId = "reservation-42",
fullName = "John Smith",
preauth = true // Funds reserved without immediate capture
)
Information

preauth cannot be combined with initRecurring = true.

Static parameters

If payment parameters are known in advance, you can use setup() variant with static object:

SecurePayButton(
session = session,
collector = collector,
onPaymentResult = { result -> /* ... */ },
paymentParams = PaymentParams(
email = "customer@example.com",
price = 100,
curr = "CZK",
label = "Payment name",
refId = "ref-123",
fullName = "John Smith",
country = "CZ", // optional
expirationTime = "1h", // optional
billingAddrCity = "Hradec Kralove", // optional
billingAddrStreet = "Jiraskova 115", // optional
billingAddrPostalCode = "50304", // optional
billingAddrCountry = "CZ" // optional
),
modifier = Modifier.fillMaxWidth()
)

Button styling

Optional update block lets you style button programmatically on each recomposition. Available methods are described in Component Styling.

import cz.comgate.sdk.compose.*

// Field states and collector
val panState = rememberSecurePanFieldState()
val expiryState = rememberSecureExpiryFieldState()
val cvvState = rememberSecureCvvFieldState()
val collector = rememberSecureCardCollector(panState, expiryState, cvvState)

// Payment button
SecurePayButton(
session = session,
collector = collector,
onPaymentResult = { result -> handleResult(result) },
paymentParamsProvider = {
PaymentParams(
price = 10000, curr = "CZK",
label = "Order", refId = "ref-1", fullName = "John Smith"
)
},
modifier = Modifier.fillMaxWidth(),
update = {
// Optional programmatic styling - see Styling section
setText("Pay")
setButtonBackgroundColor(Color.parseColor("#1E88E5"))
setButtonTextColor(Color.WHITE)
setButtonCornerRadius(12f * resources.displayMetrics.density)
setDisabledBackgroundColor(Color.parseColor("#CFD8DC"))
setDisabledTextColor(Color.parseColor("#78909C"))
setLoadingAnimationEnabled(true)
}
)

Custom button and direct processPayment call

If prebuilt SecurePayButton does not fit your needs (for example custom design, animations, or more complex logic), you can trigger payment directly by calling session.processPayment() on ComgateSecureSession.

Tip

processPayment encrypts card data, sends it to payment gateway, and automatically performs 3D Secure authentication if needed. Sensitive data never leaves the library in unprotected form.

Method signature

suspend fun processPayment(
activity: Activity,
collector: SecureCardDataCollector,
params: PaymentParams
): PaymentResult
ParameterTypeDescription
activityActivityCurrent activity - required to display 3DS challenge UI.
collectorSecureCardDataCollectorCollector linking Secure Fields (PAN, expiry, CVV).
paramsPaymentParamsPayment parameters (amount, currency, description, refId, payer name, etc.).

Implementation example

class PaymentActivity : AppCompatActivity() {

private lateinit var session: ComgateSecureSession

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

// Session initialization
session = ComgateSecureSession(
checkoutId = "your-checkout-id",
context = applicationContext,
threeDSConfig = ThreeDSConfig(),
lifecycleOwner = this,
onInitialized = { result ->
result.onFailure { e ->
Toast.makeText(this, "Initialization error: ${e.message}", Toast.LENGTH_LONG).show()
}
}
)

setContent {
MaterialTheme {
Surface(modifier = Modifier.fillMaxSize()) {
CustomPaymentScreen(session)
}
}
}
}
}

@Composable
private fun CustomPaymentScreen(session: ComgateSecureSession) {
val activity = LocalContext.current as Activity
var isProcessing by remember { mutableStateOf(false) }
var isFormValid by remember { mutableStateOf(false) }

val panState = rememberSecurePanFieldState()
val expiryState = rememberSecureExpiryFieldState()
val cvvState = rememberSecureCvvFieldState()
val statusState = rememberPaymentStatusState()

val collector = rememberSecureCardCollector(
panState = panState,
expiryState = expiryState,
cvvState = cvvState,
onValidationChanged = { isValid ->
isFormValid = isValid
}
)

val scope = rememberCoroutineScope()

Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
SecurePanField(state = panState, modifier = Modifier.fillMaxWidth())
Spacer(modifier = Modifier.height(8.dp))
SecureExpiryField(state = expiryState, modifier = Modifier.fillMaxWidth())
Spacer(modifier = Modifier.height(8.dp))
SecureCvvField(state = cvvState, modifier = Modifier.fillMaxWidth())
Spacer(modifier = Modifier.height(16.dp))

// Custom button - direct processPayment call
Button(
onClick = {
if (!isProcessing) {
isProcessing = true
scope.launch {
val result = session.processPayment(
activity = activity,
collector = collector,
params = PaymentParams(
email = "customer@example.com",
price = 100,
curr = "CZK",
label = "Order #123",
refId = "order-123",
fullName = "John Smith"
)
)
isProcessing = false
statusState.showStatus(result)

when (result) {
is PaymentResult.Paid -> { /* Payment successful */ }
is PaymentResult.Pending -> { /* Payment is processing */ }
is PaymentResult.Cancelled -> { /* Payment canceled / rejected */ }
is PaymentResult.Failed -> { /* SDK error */ }
}
}
}
},
enabled = isFormValid && !isProcessing,
modifier = Modifier.fillMaxWidth()
) {
Text(if (isProcessing) "Processing..." else "Pay")
}

SecurePaymentStatusView(
state = statusState,
modifier = Modifier.fillMaxWidth().padding(top = 8.dp)
)
}
}
Warning

Before calling processPayment, ensure that:

  1. Session was initialized successfully (onInitialized callback returned Result.success).
  2. Card data is valid (collector.isValid == true).

If these conditions are not met, method immediately returns PaymentResult.Cancelled.

Implementation modes

The library supports implementation using three standalone components (SecurePanField, SecureExpiryField, SecureCvvField) linked via SecureCardDataCollector.

Separate fields

Each field is placed independently in Compose layout. This approach provides full control over layout and styling.

class PaymentActivity : AppCompatActivity() {

private lateinit var session: ComgateSecureSession

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

session = ComgateSecureSession(/* ... */)

setContent {
MaterialTheme {
Surface(modifier = Modifier.fillMaxSize()) {
PaymentScreen(session)
}
}
}
}
}

@Composable
private fun PaymentScreen(session: ComgateSecureSession) {
val panState = rememberSecurePanFieldState()
val expiryState = rememberSecureExpiryFieldState()
val cvvState = rememberSecureCvvFieldState()
val statusState = rememberPaymentStatusState()

val collector = rememberSecureCardCollector(panState, expiryState, cvvState)

Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
SecurePanField(state = panState, modifier = Modifier.fillMaxWidth())
Spacer(modifier = Modifier.height(8.dp))
SecureExpiryField(state = expiryState, modifier = Modifier.fillMaxWidth())
Spacer(modifier = Modifier.height(8.dp))
SecureCvvField(state = cvvState, modifier = Modifier.fillMaxWidth())
Spacer(modifier = Modifier.height(16.dp))

SecurePayButton(
session = session,
collector = collector,
onPaymentResult = { result ->
when (result) {
is PaymentResult.Paid -> statusState.showStatus(result)
is PaymentResult.Cancelled -> statusState.showStatus(result)
is PaymentResult.Failed -> statusState.showStatus(result)
else -> statusState.showStatus(result)
}
},
paymentParamsProvider = {
PaymentParams(
email = "customer@example.com",
price = 100,
curr = "CZK",
label = "Test payment",
refId = "ref-123",
fullName = "John Smith",
country = "CZ", // optional
expirationTime = "1h" // optional
)
},
modifier = Modifier.fillMaxWidth()
)

SecurePaymentStatusView(
state = statusState,
modifier = Modifier.fillMaxWidth().padding(top = 8.dp)
)
}
}

3D Secure

The library provides complete support for 3D Secure authentication. If 3DS is configured, the library automatically:

  1. Prepares authentication parameters during payment processing
  2. Evaluates server response (frictionless / challenge)
  3. Displays challenge screen when needed
  4. Returns authentication result via PaymentResult

Configuration

3DS is configured via ThreeDSConfig, passed to the ComgateSecureSession constructor:

val threeDSConfig = ThreeDSConfig(
uiCustomization = threeDSUi, // Appearance customization (optional)
challengeTimeoutMinutes = 5, // Challenge timeout (default: 5)
defaultMessageVersion = "2.2.0", // 3DS protocol version (default: "2.2.0")
challengeWindowCornerRadiusDp = 16 // Challenge window corner radius (optional)
)

val session = ComgateSecureSession(
checkoutId = "your-checkout-id",
context = applicationContext,
threeDSConfig = threeDSConfig,
lifecycleOwner = this
)
Usage in Fragment

lifecycleOwner controls when session resources are automatically released. In examples above, this (activity) is used.

If you create session in a Fragment, pass viewLifecycleOwner (available from onViewCreated):

  • Activity: lifecycleOwner = this
  • Fragment: lifecycleOwner = viewLifecycleOwner (not this - Fragment outlives recreated view and would cause memory leak)

ThreeDSConfig parameters

ParameterTypeDefaultDescription
uiCustomizationThreeDSUiCustomization?nullChallenge screen appearance customization. If null, default styles are used.
defaultMessageVersionString"2.2.0"3DS protocol version.
challengeTimeoutMinutesInt5Maximum challenge completion time in minutes (1-30).
challengeWindowCornerRadiusDpInt?nullChallenge window corner radius in dp. If null, default theme value is used.

Challenge screen appearance customization

ThreeDSUiCustomization enables detailed challenge screen styling:

val threeDSUi = ThreeDSUiCustomization(
buttons = mapOf(
ThreeDSButtonType.SUBMIT to ThreeDSButtonStyle(
backgroundColor = "#4287f5",
cornerRadius = 48,
textColor = "#FFFFFF",
textFontSize = 16
),
ThreeDSButtonType.CONTINUE to ThreeDSButtonStyle(
backgroundColor = "#4287f5",
cornerRadius = 48,
textColor = "#FFFFFF",
textFontSize = 16
),
ThreeDSButtonType.RESEND to ThreeDSButtonStyle(
backgroundColor = "#F0F0F0",
cornerRadius = 48,
textColor = "#4287f5",
textFontSize = 16
)
),
labelStyle = ThreeDSLabelStyle(
headingTextColor = "#4287f5",
textColor = "#333333"
),
textBoxStyle = ThreeDSTextBoxStyle(
cornerRadius = 16,
borderColor = "#4287f5",
textColor = "#333333",
borderWidth = 4
),
toolbarStyle = ThreeDSToolbarStyle(
buttonText = "Cancel",
headerText = "Payment verification",
backgroundColor = "#F0F0F0",
textColor = "#333333"
)
)

ThreeDSButtonStyle

Customization of challenge screen buttons.

ParameterTypeDescription
backgroundColorString?Background color in #RRGGBB or #AARRGGBB.
cornerRadiusInt?Button corner radius in dp.
textColorString?Text color in #RRGGBB or #AARRGGBB.
textFontSizeInt?Font size in sp (8-48).

Buttons are configured in a map by type:

Button typeDescription
ThreeDSButtonType.SUBMITSubmit (confirm) button.
ThreeDSButtonType.CONTINUEContinue button.
ThreeDSButtonType.NEXTNext step button.
ThreeDSButtonType.CANCELCancel button.
ThreeDSButtonType.RESENDResend code button.

ThreeDSLabelStyle

Text label customization.

ParameterTypeDescription
headingTextColorString?Heading color.
headingTextFontSizeInt?Heading size in sp (8-48).
textColorString?Body text color.
textFontSizeInt?Body text size in sp (8-48).

ThreeDSTextBoxStyle

Challenge screen input field customization.

ParameterTypeDescription
borderWidthInt?Border width in dp (0-10).
borderColorString?Border color.
cornerRadiusInt?Corner radius in dp.
textColorString?Text color.
textFontSizeInt?Text size in sp (8-48).

ThreeDSToolbarStyle

Challenge screen toolbar customization.

ParameterTypeDescription
backgroundColorString?Toolbar background color.
headerTextString?Toolbar header text.
buttonTextString?Toolbar button text (typically "Cancel").
textColorString?Text color.
textFontSizeInt?Text size in sp (8-48).
Information

Colors are provided as strings in #RRGGBB or #AARRGGBB format. Invalid formats are ignored at runtime and default values are used.

Challenge window themes

The library includes four prebuilt themes for challenge screen:

ThemeDescription
Theme.ComgateSdk.3DS.DialogLight dialog (90 % of screen width, centered) - default
Theme.ComgateSdk.3DS.Dialog.DarkDark dialog
Theme.ComgateSdk.3DS.FullScreenLight full-screen mode
Theme.ComgateSdk.3DS.FullScreen.DarkDark full-screen mode

To switch theme, add this style override in app res/values/styles.xml:

<!-- res/values/styles.xml -->
<style name="Theme.ComgateSdk.3DS.Challenge"
parent="Theme.ComgateSdk.3DS.FullScreen" />
Tip

Dialog corner radius can be customized by defining securefields_3ds_dialog_corner_radius in your app resources:

<!-- res/values/dimens.xml -->
<dimen name="securefields_3ds_dialog_corner_radius">16dp</dimen>

Alternatively, set corner radius programmatically using challengeWindowCornerRadiusDp in ThreeDSConfig.

Testing 3DS payments

To simplify development and testing, in dev mode (devMode = true) the library can simulate different 3DS authentication flows without a real card.

Test behavior is controlled by require3ds parameter in PaymentParams:

require3ds valuePayment flow
trueServer returns challenge - 3DS challenge screen is shown, user must enter OTP or verify.
falseServer returns frictionless result - payment proceeds without challenge screen.
null (default)Server decides; standard behavior in production.
Warning

require3ds works only in dev mode (devMode = true). In production (devMode = false), this value is ignored and payment runs standard flow.

Simulating 3DS challenge

Set require3ds = true. Library displays 3DS challenge screen where user verifies. Payment result depends on user action:

  • Verification completed -> PaymentResult.Paid
  • Challenge canceled -> PaymentResult.Failed(ComgateError.ThreeDSChallengeCancelled)
  • Timeout expired -> PaymentResult.Failed(ComgateError.ThreeDSChallengeTimeout)
SecurePayButton(
session = session,
collector = collector,
onPaymentResult = { result -> /* ... */ },
paymentParamsProvider = {
PaymentParams(
email = "customer@example.com",
price = 100,
curr = "CZK",
label = "Test payment",
refId = "test-001",
fullName = "John Smith",
require3ds = true // Force 3DS challenge flow - devMode only
)
},
modifier = Modifier.fillMaxWidth()
)

Simulating frictionless flow

Set require3ds = false. Payment proceeds without challenge screen - authentication is evaluated server-side in background. Result is PaymentResult.Paid if successful.

SecurePayButton(
session = session,
collector = collector,
onPaymentResult = { result -> /* ... */ },
paymentParamsProvider = {
PaymentParams(
email = "customer@example.com",
price = 100,
curr = "CZK",
label = "Test payment",
refId = "test-001",
fullName = "John Smith",
require3ds = false // Frictionless flow without challenge - devMode only
)
},
modifier = Modifier.fillMaxWidth()
)

Error reason simulation - ErrorReason

In dev mode (devMode = true), you can simulate specific rejection/failure reasons with errorReason in PaymentParams. This helps test app behavior for different error scenarios without real card transactions.

import cz.comgate.sdk.ErrorReason

SecurePayButton(
session = session,
collector = collector,
onPaymentResult = { result -> /* ... */ },
paymentParamsProvider = {
PaymentParams(
price = 100,
curr = "CZK",
label = "Test payment",
refId = "test-001",
fullName = "John Smith",
errorReason = ErrorReason.NO_FUNDS // Simulate insufficient funds - devMode only
)
},
modifier = Modifier.fillMaxWidth()
)
Warning

errorReason works only in dev mode (devMode = true). Value (including null) is always sent to server in dev mode. In production, this parameter is completely ignored.

Available ErrorReason enum values:

ValueDescription
CUSTOMER_CLICKCanceled by payer.
FRAUD_SUSPECTEDFraud suspected.
ESHOP_CANCELLEDCanceled by merchant.
PROVIDER_REPORTCanceled by provider.
PROVIDER_TIMEOUTProvider timeout.
CUSTOMER_TIMEOUTPayment timeout.
ACS_TIMEOUTVerification timeout.
INVALID_CARDNO_EXPIRYInvalid card number or expiry date.
INVALID_CVCInvalid CVC / CVV code.
LIMIT_EXCEEDEDCard limit exceeded.
NO_FUNDSInsufficient account balance.
REJECTED_BY_BANKRejected by bank.
3DS_AUTH_FAIL3DS verification failed.
NOT_SPECIFIEDNot specified.