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
last4property - 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 asfullName
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()
)
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 / Method | Type | Description |
|---|---|---|
isFieldValid | Boolean | Current field validation state (read-only). |
onValidationChanged | (Boolean) -> Unit | Callback called when validation state changes. |
showLabel | Boolean | Shows label above the field. Default true. Set false to hide label. |
labelText | String? | Custom label text. If null, default translated label from session.translation is used. |
setHint(hint) | String | Sets placeholder text. |
setEnabled(enabled) | Boolean | Enables/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 / Method | Type | Description |
|---|---|---|
isValid | Boolean | true if all three fields are valid. |
onValidationChanged | (Boolean) -> Unit | Callback 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
ComgateSecureSessionon 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
| Parameter | Type | Required | Description |
|---|---|---|---|
email | String | Yes | Payer email address. |
price | Int | Yes | Payment amount in minor units (for example 10000 = 100.00 CZK). |
curr | String | Yes | Currency code - ISO 4217 (for example "CZK", "EUR"). |
label | String | Yes | Short product description (1-16 characters). |
refId | String | Yes | Variable symbol or order number (your internal ID). |
fullName | String | Conditionally | Payer 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. |
country | String | No | ISO 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. |
account | String? | No | Client bank account identifier in Comgate system. |
name | String? | No | Product identifier (shown in daily CSV as "Product"). |
preauth | Boolean? | No | Marks payment as preauthorization - reserves funds without immediate capture. true - preauth, null - default behavior. Cannot be combined with initRecurring = true. |
initRecurring | Boolean? | No | Marks payment as first in a recurring payment series. true - initial recurring payment, null - default behavior. Cannot be combined with preauth = true. |
expirationTime | String? | No | Payment expiration time (for example "30m", "3h", "5d"). Range: 30 minutes - 7 days. |
dynamicExpiration | Boolean? | No | Dynamic payment expiration (true to enable). |
billingAddrCity | String? | No | Billing address - city. |
billingAddrStreet | String? | No | Billing address - street. |
billingAddrPostalCode | String? | No | Billing address - postal code. |
billingAddrCountry | String? | No | Billing address - country code (ISO 3166-1 alpha-2). |
delivery | String? | No | Delivery method ("HOME_DELIVERY", "PICKUP", "ELECTRONIC_DELIVERY"). |
homeDeliveryCity | String? | No | Delivery address - city (only with delivery = "HOME_DELIVERY"). |
homeDeliveryStreet | String? | No | Delivery address - street (only with delivery = "HOME_DELIVERY"). |
homeDeliveryPostalCode | String? | No | Delivery address - postal code (only with delivery = "HOME_DELIVERY"). |
homeDeliveryCountry | String? | No | Delivery address - country code (only with delivery = "HOME_DELIVERY"). |
category | String? | No | Product category ("PHYSICAL_GOODS_ONLY", "OTHER"). |
require3ds | Boolean? | No | Only 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. |
errorReason | ErrorReason? | No | Only 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
)
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
)
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.
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
| Parameter | Type | Description |
|---|---|---|
activity | Activity | Current activity - required to display 3DS challenge UI. |
collector | SecureCardDataCollector | Collector linking Secure Fields (PAN, expiry, CVV). |
params | PaymentParams | Payment 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)
)
}
}
Before calling processPayment, ensure that:
- Session was initialized successfully (
onInitializedcallback returnedResult.success). - 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:
- Prepares authentication parameters during payment processing
- Evaluates server response (frictionless / challenge)
- Displays challenge screen when needed
- 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
)
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(notthis- Fragment outlives recreated view and would cause memory leak)
ThreeDSConfig parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
uiCustomization | ThreeDSUiCustomization? | null | Challenge screen appearance customization. If null, default styles are used. |
defaultMessageVersion | String | "2.2.0" | 3DS protocol version. |
challengeTimeoutMinutes | Int | 5 | Maximum challenge completion time in minutes (1-30). |
challengeWindowCornerRadiusDp | Int? | null | Challenge 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.
| Parameter | Type | Description |
|---|---|---|
backgroundColor | String? | Background color in #RRGGBB or #AARRGGBB. |
cornerRadius | Int? | Button corner radius in dp. |
textColor | String? | Text color in #RRGGBB or #AARRGGBB. |
textFontSize | Int? | Font size in sp (8-48). |
Buttons are configured in a map by type:
| Button type | Description |
|---|---|
ThreeDSButtonType.SUBMIT | Submit (confirm) button. |
ThreeDSButtonType.CONTINUE | Continue button. |
ThreeDSButtonType.NEXT | Next step button. |
ThreeDSButtonType.CANCEL | Cancel button. |
ThreeDSButtonType.RESEND | Resend code button. |
ThreeDSLabelStyle
Text label customization.
| Parameter | Type | Description |
|---|---|---|
headingTextColor | String? | Heading color. |
headingTextFontSize | Int? | Heading size in sp (8-48). |
textColor | String? | Body text color. |
textFontSize | Int? | Body text size in sp (8-48). |
ThreeDSTextBoxStyle
Challenge screen input field customization.
| Parameter | Type | Description |
|---|---|---|
borderWidth | Int? | Border width in dp (0-10). |
borderColor | String? | Border color. |
cornerRadius | Int? | Corner radius in dp. |
textColor | String? | Text color. |
textFontSize | Int? | Text size in sp (8-48). |
ThreeDSToolbarStyle
Challenge screen toolbar customization.
| Parameter | Type | Description |
|---|---|---|
backgroundColor | String? | Toolbar background color. |
headerText | String? | Toolbar header text. |
buttonText | String? | Toolbar button text (typically "Cancel"). |
textColor | String? | Text color. |
textFontSize | Int? | Text size in sp (8-48). |
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:
| Theme | Description |
|---|---|
Theme.ComgateSdk.3DS.Dialog | Light dialog (90 % of screen width, centered) - default |
Theme.ComgateSdk.3DS.Dialog.Dark | Dark dialog |
Theme.ComgateSdk.3DS.FullScreen | Light full-screen mode |
Theme.ComgateSdk.3DS.FullScreen.Dark | Dark 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" />
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 value | Payment flow |
|---|---|
true | Server returns challenge - 3DS challenge screen is shown, user must enter OTP or verify. |
false | Server returns frictionless result - payment proceeds without challenge screen. |
null (default) | Server decides; standard behavior in production. |
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()
)
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:
| Value | Description |
|---|---|
CUSTOMER_CLICK | Canceled by payer. |
FRAUD_SUSPECTED | Fraud suspected. |
ESHOP_CANCELLED | Canceled by merchant. |
PROVIDER_REPORT | Canceled by provider. |
PROVIDER_TIMEOUT | Provider timeout. |
CUSTOMER_TIMEOUT | Payment timeout. |
ACS_TIMEOUT | Verification timeout. |
INVALID_CARDNO_EXPIRY | Invalid card number or expiry date. |
INVALID_CVC | Invalid CVC / CVV code. |
LIMIT_EXCEEDED | Card limit exceeded. |
NO_FUNDS | Insufficient account balance. |
REJECTED_BY_BANK | Rejected by bank. |
3DS_AUTH_FAIL | 3DS verification failed. |
NOT_SPECIFIED | Not specified. |