# Non-3DS transactions

Integrate a frictionless checkout experience.

## Overview

By implementing non-3DS transactions into your payment flow, you benefit from:

* **Streamlined checkout:** No additional authentication required, providing faster payment completion
* **Lower transaction friction:** Reduced cart abandonment with seamless payment flow
* **Faster processing:** Immediate payment completion without authentication delays
* **Better mobile experience:** Optimised for mobile checkouts where 3DS challenges can be problematic


Non-3DS transactions are ideal for low-risk scenarios, trusted customers, or when implementing other fraud prevention measures. However, they may have different liability arrangements and potentially higher processing fees.

## Payment flow

The non-3DS flow is made up of six key steps.

### Step 1: Submission

The customer taps the submit button or the payment is triggered programmatically. The `submit()` method in `CardSubmitComponent` is invoked and validation occurs inside it. If validation passes, the method continues with the payment processing. If it fails, the method exits early and doesn't proceed with the transaction.

### Step 2: Card tokenisation

If it's a new card, the SDK sends the card details to the tokenisation service. If it's a saved card, the SDK retrieves the existing token.

### Step 3: Evaluation

The SDK evaluates whether 3DS authentication is required. In this flow, no 3DS authentication is performed - the payment proceeds directly to authorisation.

### Step 4: Authorisation

The SDK sends the transaction to the payment processor for authorisation without requiring additional customer authentication.

### Step 5: Capture

Depending on your configuration, the transaction may be automatically captured or require manual capture later.

### Step 6: Completion

The payment is completed and your application receives the final result through the `onPostAuthorisation` callback.

## Non-3DS callback details

Non-3DS transactions use simplified callbacks without authentication complexity:

### Core callbacks

* `onPostAuthorisation`: `(SubmitResult) -> Unit`
  - **Purpose:** Receives the final transaction result from the payment gateway.
  - **Parameter:** `SubmitResult` (one of `AuthorisedSubmitResult`, `CapturedSubmitResult`, `RefusedSubmitResult`, `FailedSubmitResult`).
  - **Contains:** Transaction status, provider response, transaction reference, processing details.
  - **Usage:** Handle payment success/failure, navigate to appropriate screens, update order status.
* `onPreAuthorisation`: `(PreAuthorisationData) -> TransactionInitiationData?` (optional)
  - **Purpose:** Last chance to modify transaction data before authorisation.
  - **Parameter:** `PreAuthorisationData` containing transaction details (no 3DS data).
  - **Return:** `TransactionInitiationData` with additional metadata, exemption data, or custom parameters.
  - **Return null:** To proceed with default authorisation settings.


### Additional callbacks

* `onPreTokenisation`: `() -> Boolean` (optional)
  - **Purpose:** Called before card tokenisation begins.
  - **Return:** `true` to proceed with tokenisation, `false` to abort.
  - **Usage:** Perform final validation, show loading states.
* `onPostTokenisation`: `(CardTokenisationResult) -> Unit` (optional)
  - **Purpose:** Receives tokenisation results.
  - **Parameter:** `CardTokenisationResult` (either success with token ID or failure with error).
  - **Usage:** Handle tokenisation success/failure, store token references.
* `onSubmitError`: `(BaseSdkException) -> Unit` (optional)
  - **Purpose:** Handles submission errors.
  - **Parameter:** `BaseSdkException` containing error details and codes.
  - **Usage:** Display error messages, implement retry logic, track failures.


## SDK configuration

To process non-3DS transactions, configure your SDK without 3DS callbacks:

```kotlin
val sdkConfig = PxpSdkConfig(
    environment = Environment.TEST,
    session = SessionConfig(
        sessionId = "your_session_id",
        sessionData = "your_session_data"
    ),
    transactionData = TransactionData(
        amount = 99.99,
        currency = CurrencyType.USD,
        entryType = EntryType.Ecom,
        intent = IntentType.Authorisation,
        merchantTransactionId = "order-123",
        merchantTransactionDate = { Instant.now().toString() },
        shopper = Shopper(
            email = "customer@example.com",
            firstName = "John",
            lastName = "Doe"
        )
    ),
    clientId = "your_client_id",
    ownerId = "Unity",
    ownerType = "MerchantGroup",
    merchantShopperId = "shopper-123"
)

val pxpCheckout = PxpCheckout.builder()
    .withConfig(sdkConfig)
    .withContext(this)
    .withDebugMode(true)
    .build()
```

### Step 1: Create components

```kotlin
val cardNumberConfig = CardNumberComponentConfig(
    label = "Card number",
    isRequired = true,
    validateOnChange = true
)

val cardExpiryConfig = CardExpiryDateComponentConfig(
    label = "Expiry date",
    isRequired = true,
    validateOnChange = true
)

val cardCvcConfig = CardCvcComponentConfig(
    label = "Security code",
    isRequired = true,
    validateOnChange = true
)

val cardSubmitConfig = CardSubmitComponentConfig(
    buttonText = "Pay £99.99",
    // Note: No 3DS callbacks = non-3DS transaction
    onPostAuthorisation = { result ->
        when (result) {
            is AuthorizedSubmitResult -> {
                Log.d("NonThreeDS", "Payment successful: ${result.providerResponse.message}")
                navigateToSuccessScreen()
            }
            is CapturedSubmitResult -> {
                Log.d("NonThreeDS", "Payment captured: ${result.providerResponse.message}")
                navigateToSuccessScreen()
            }
            is RefusedSubmitResult -> {
                Log.e("NonThreeDS", "Payment declined: ${result.stateData.message}")
                showError("Payment was declined by your bank")
            }
            is FailedSubmitResult -> {
                Log.e("NonThreeDS", "Payment failed: ${result.errorReason}")
                showError("Payment failed. Please try again.")
            }
            else -> {
                Log.e("NonThreeDS", "Unknown result: ${result::class.simpleName}")
                showError("Payment completed with unknown status.")
            }
        }
    },
    onSubmitError = { error ->
        Log.e("NonThreeDS", "Payment error: ${error.message}")
        showError("Payment failed: ${error.message}")
    }
)

val cardNumberComponent = pxpCheckout.createComponent(ComponentType.CARD_NUMBER, cardNumberConfig)
val cardExpiryComponent = pxpCheckout.createComponent(ComponentType.CARD_EXPIRY_DATE, cardExpiryConfig)
val cardCvcComponent = pxpCheckout.createComponent(ComponentType.CARD_CVC, cardCvcConfig)
val cardSubmitComponent = pxpCheckout.createComponent(ComponentType.CARD_SUBMIT, cardSubmitConfig)
```

### Step 2: Handle common scenarios

#### Low-value transactions

For low-value transactions where 3DS exemptions apply:

```kotlin
class LowValuePaymentManager {
    
    fun createNon3DSConfig(amount: Double): CardSubmitComponentConfig {
        return CardSubmitComponentConfig(
            buttonText = "Pay ${formatCurrency(amount)}",
            onPostAuthorisation = { result ->
                when (result) {
                    is AuthorizedSubmitResult -> {
                        Log.d("LowValue", "Low-value payment successful: ${result.providerResponse.message}")
                        // Track successful low-value payment
                        trackPaymentSuccess(amount, "low_value_non_3ds")
                        navigateToSuccessScreen()
                    }
                    is RefusedSubmitResult -> {
                        Log.e("LowValue", "Low-value payment declined: ${result.stateData.message}")
                        showError("Payment declined. Please try a different card.")
                    }
                    else -> {
                        Log.e("LowValue", "Low-value payment failed")
                        showError("Payment failed. Please try again.")
                    }
                }
            }
        )
    }
    
    private fun formatCurrency(amount: Double): String {
        return "£%.2f".format(amount)
    }
    
    private fun trackPaymentSuccess(amount: Double, type: String) {
        // Analytics tracking for successful payments
        Log.d("Analytics", "Payment successful: amount=$amount, type=$type")
    }
}
```

#### Trusted customer transactions

For returning customers with established trust:

```kotlin
class TrustedCustomerPaymentManager(private val customerId: String) {
    
    fun createTrustedCustomerConfig(): CardSubmitComponentConfig {
        return CardSubmitComponentConfig(
            buttonText = "Pay securely",
            onPreAuthorisation = { preAuthData ->
                // Return transaction initiation data with risk screening
                // Note: TransactionInitiationData only supports psd2Data and riskScreeningData
                TransactionInitiationData(
                    psd2Data = null, // Set to Psd2Data(scaExemption = ...) if needed
                    riskScreeningData = RiskScreeningData(
                        performRiskScreening = true,
                        userIp = "192.168.1.100",
                        account = RiskScreeningAccount(
                            id = customerId,
                            creationDateTime = "2024-01-15T10:30:00.000Z"
                        ),
                        items = listOf(
                            RiskScreeningItem(
                                price = 45.00,
                                quantity = 1,
                                category = "General"
                            )
                        ),
                        fulfillments = listOf(
                            RiskScreeningFulfillment(
                                type = FulfillmentType.SHIPPED,
                                recipientPerson = RiskScreeningRecipientPerson(
                                    phoneNumber = "+1234567890"
                                )
                            )
                        )
                    )
                )
            },
    onPostAuthorisation = { result ->
                when (result) {
                    is AuthorizedSubmitResult -> {
                        Log.d("Trusted", "Trusted customer payment successful")
                        updateCustomerTrustScore(customerId, "successful_payment")
                        navigateToSuccessScreen()
                    }
                    is RefusedSubmitResult -> {
                        Log.e("Trusted", "Trusted customer payment declined")
                        updateCustomerTrustScore(customerId, "declined_payment")
                        showError("Payment declined. Please verify your card details.")
                    }
                    else -> {
                        Log.e("Trusted", "Unknown payment result")
                        showError("Payment completed with unknown status.")
                    }
                }
            }
        )
    }
    
    private fun updateCustomerTrustScore(customerId: String, event: String) {
        Log.d("TrustScore", "Updating trust score for $customerId: $event")
    }
}
```

### Step 3: Handle errors

Implement comprehensive error handling for non-3DS transactions:

```kotlin
class Non3DSErrorHandler {
    
    fun handlePaymentError(error: PaymentError) {
        when (error.code) {
            "CARD_DECLINED" -> {
                showError("Your card was declined. Please try a different payment method.")
            }
            "INSUFFICIENT_FUNDS" -> {
                showError("Insufficient funds. Please try a different card.")
            }
            "INVALID_CARD" -> {
                showError("Invalid card details. Please check and try again.")
            }
            "EXPIRED_CARD" -> {
                showError("Your card has expired. Please use a different card.")
            }
            "BLOCKED_CARD" -> {
                showError("Your card is blocked. Please contact your bank.")
            }
            "NETWORK_ERROR" -> {
                showError("Network error. Please check your connection and try again.")
            }
            "VALIDATION_FAILED" -> {
                showError("Please check all required fields are completed correctly.")
            }
            else -> {
                showError("Payment failed. Please try again or use a different payment method.")
            }
        }
    }
    
    private fun showError(message: String) {
        // Implementation depends on your UI framework
        Log.e("Non3DS", message)
    }
}
```

## Complete example

The following example shows a complete non-3DS implementation:

```kotlin
class Non3DSPaymentActivity : ComponentActivity() {
    private lateinit var pxpCheckout: PxpCheckout
    private lateinit var newCardComponent: NewCardComponent
    private val errorHandler = Non3DSErrorHandler()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        setupPxpCheckout()
        setupComponents()
        
        setContent {
            Non3DSPaymentScreen()
        }
    }
    
    private fun setupPxpCheckout() {
        val sdkConfig = PxpSdkConfig(
            environment = Environment.TEST,
            session = SessionConfig(
                sessionId = "session-${System.currentTimeMillis()}",
                sessionData = "your_session_data"
            ),
            transactionData = TransactionData(
                amount = 29.99,
                currency = CurrencyType.GBP,
                entryType = EntryType.ECOM,
                intent = IntentType.AUTHORISATION,
                merchantTransactionId = "order-${System.currentTimeMillis()}",
                merchantTransactionDate = { Instant.now().toString() },
                shopper = Shopper(
                    email = "customer@example.com",
                    firstName = "Jane",
                    lastName = "Smith"
                )
            ),
            clientId = "your_client_id",
            ownerId = "Unity",
            ownerType = "MerchantGroup",
            merchantShopperId = "shopper-456"
        )
        
        pxpCheckout = PxpCheckout.builder()
            .withConfig(sdkConfig)
            .withContext(this)
            .withDebugMode(true)
            .build()
    }
    
    private fun setupComponents() {
        val newCardConfig = NewCardComponentConfig(
            submit = CardSubmitComponentConfig(
                // No 3DS callbacks - transaction will be non-3DS
                onPostAuthorisation = { result ->
                    when (result) {
                        is AuthorizedSubmitResult -> {
                            Log.d("Non3DS", "Payment successful: ${result.providerResponse.message}")
                            navigateToSuccessScreen()
                        }
                        is CapturedSubmitResult -> {
                            Log.d("Non3DS", "Payment captured: ${result.providerResponse.message}")
                            navigateToSuccessScreen()
                        }
                        is RefusedSubmitResult -> {
                            Log.e("Non3DS", "Payment declined: ${result.stateData.message}")
                            showDeclinedMessage(result.stateData.code)
                        }
                        is FailedSubmitResult -> {
                            Log.e("Non3DS", "Payment failed: ${result.errorReason}")
                            showError("Payment failed. Please try again.")
                        }
                        else -> {
                            Log.e("Non3DS", "Unknown result: ${result::class.simpleName}")
                            showError("Payment completed with unknown status.")
                        }
                    }
                },
                onSubmitError = { error ->
                    Log.e("Non3DS", "Submit error: ${error.message}")
                    errorHandler.handlePaymentError(error)
                }
            )
        )
        
        newCardComponent = pxpCheckout.createComponent(ComponentType.NEW_CARD, newCardConfig)
    }
    
    @Composable
    private fun Non3DSPaymentScreen() {
        var isLoading by remember { mutableStateOf(false) }
        var errorMessage by remember { mutableStateOf<String?>(null) }
        
        Column(
            modifier = Modifier
                .fillMaxSize()
                .padding(16.dp)
        ) {
            Text(
                text = "Express checkout",
                style = MaterialTheme.typography.headlineMedium,
                modifier = Modifier.padding(bottom = 8.dp)
            )
            
            Text(
                text = "Fast and secure payment without additional verification",
                style = MaterialTheme.typography.bodyMedium,
                color = MaterialTheme.colorScheme.onSurfaceVariant,
                modifier = Modifier.padding(bottom = 16.dp)
            )
            
            // Show error message if any
            errorMessage?.let { message ->
                Card(
                    colors = CardDefaults.cardColors(containerColor = Color.Red.copy(alpha = 0.1f)),
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(bottom = 16.dp)
                ) {
                    Text(
                        text = message,
                        color = Color.Red,
                        modifier = Modifier.padding(16.dp)
                    )
                }
            }
            
            // Render payment components
            pxpCheckout.buildComponentView(
                component = newCardComponent,
                modifier = Modifier.fillMaxWidth()
            )
            
            // Loading indicator
            if (isLoading) {
                Box(
                    modifier = Modifier.fillMaxWidth(),
                    contentAlignment = Alignment.Center
                ) {
                    CircularProgressIndicator()
            Text(
                        text = "Processing payment...",
                        modifier = Modifier.padding(top = 60.dp)
                    )
                }
            }
        }
    }
    
    private fun navigateToSuccessScreen(transactionId: String?) {
        val intent = Intent(this, PaymentSuccessActivity::class.java).apply {
            putExtra("transaction_id", transactionId)
            putExtra("payment_type", "non_3ds")
        }
        startActivity(intent)
        finish()
    }
    
    private fun showDeclinedMessage(reasonCode: String?) {
        val message = when (reasonCode) {
            "insufficient_funds" -> "Insufficient funds. Please try a different card."
            "expired_card" -> "Your card has expired. Please use a different card."
            "invalid_card" -> "Invalid card details. Please check and try again."
            else -> "Payment was declined. Please try a different card."
        }
        showError(message)
    }
    
    private fun showError(message: String) {
        AlertDialog.Builder(this)
            .setTitle("Payment Error")
            .setMessage(message)
            .setPositiveButton("OK") { dialog, _ ->
                dialog.dismiss()
            }
            .show()
    }
}
```

## Callback data

This section describes the data received by the different callbacks as part of the non-3DS flow.

### onPreAuthorisation

The `onPreAuthorisation` callback receives:

* Pre-authorisation data (`preAuthData`): Transaction data ready for authorisation.
* Error data (`error`): Any error that occurred during pre-authorisation.


For non-3DS transactions, the 3DS-related data will be `null`.

#### Pre-authorisation data

The pre-authorisation data includes transaction initiation data (for new cards) or card token data (for saved cards).

**With transaction initiation data:**

```kotlin
PreAuthorisationData(
    transactionInitiationData = TransactionInitiationData(
        threeDSecureData = null, // Always null for non-3DS
        psd2Data = PSD2Data(
            scaExemption = "LowValue"
        ),
        identityVerification = IdentityVerification(
            nameVerification = true
        ),
        addressVerification = AddressVerification(
            countryCode = "US",
            houseNumberOrName = "123",
            postalCode = "10001"
        )
    )
)
```

**With card token data:**

```kotlin
PreAuthorisationData(
    transactionInitiationData = null,
    cardTokenData = CardTokenData(
        gatewayTokenId = "gw_token_abc123def456789",
        schemeTokenId = null,
        maskedPrimaryAccountNumber = "****-****-****-4242",
        cardExpiryMonth = "12",
        cardExpiryYear = "2025",
        scheme = "VISA",
        fundingSource = "CREDIT",
        ownerType = "PERSONAL",
        issuerName = "Chase Bank",
        issuerCountryCode = "US",
        lastSuccessfulPurchaseDate = "2024-01-15T10:30:00Z",
        lastSuccessfulPayoutDate = null
    )
)
```

| Parameter | Description |
|  --- | --- |
| `transactionInitiationData` | Details about the transaction, if associated with a new card, `null` otherwise. |
| `transactionInitiationData.threeDSecureData` | This is always `null` for non-3DS transactions. |
| `transactionInitiationData.psd2Data` | Details about PSD2. This is required for non-3DS transactions. |
| `transactionInitiationData.psd2Data.scaExemption` | The type of SCA exemption that applies to this transaction.Possible values:`AnonymousCard``LowValue``SecureCorporate``TransactionRiskAnalysis``TrustedBeneficiary` |
| `transactionInitiationData.identityVerification` | Details about the identity verification. |
| `transactionInitiationData.identityVerification.nameVerification` | Whether the cardholder's name matches the name associated with the registered address on file. |
| `transactionInitiationData.addressVerification` | Details about the address verification. |
| `transactionInitiationData.addressVerification.countryCode` | The country code associated with the cardholder's address, in ISO-3166-1 alpha-2 format. |
| `transactionInitiationData.addressVerification.houseNumberOrName` | The cardholder's street address. |
| `transactionInitiationData.addressVerification.postalCode` | The postal or ZIP code associated with the cardholder's address. |
| `cardTokenData` | Details about the card token if associated with a saved card, `null` otherwise. |


Here's an example of what to do with this data:

```kotlin
onPreAuthorisation = { preAuthData ->
    Log.d("Payment", "Card transaction data: $preAuthData")
    
    // Return transaction initiation data with risk screening
    // Note: TransactionInitiationData only supports psd2Data and riskScreeningData
    TransactionInitiationData(
        psd2Data = null, // Set to Psd2Data(...) if needed
        riskScreeningData = RiskScreeningData(
            performRiskScreening = true,
            excludeDeviceData = false,
            userIp = "192.168.1.100",
            account = RiskScreeningAccount(
                id = "user_12345678",
                creationDateTime = "2024-01-15T10:30:00.000Z"
            ),
            items = listOf(
                RiskScreeningItem(
                    price = 45.00,
                    quantity = 1,
                    category = "General"
                )
            ),
            fulfillments = listOf(
                RiskScreeningFulfillment(
                    type = FulfillmentType.SHIPPED,
                    shipping = RiskScreeningShipping(
                        shippingMethod = ShippingMethod.STANDARD
                    ),
                    recipientPerson = RiskScreeningRecipientPerson(
                        phoneNumber = "+1234567890",
                        email = "customer@example.com"
                    )
                )
            )
        )
    )
}

// Helper data classes
data class DeviceData(
    val userAgent: String,
    val language: String,
    val screenResolution: String,
    val timezone: String
)

data class SessionData(
    val sessionId: String,
    val timestamp: String,
    val ipAddress: String?
)

data class RiskIndicators(
    val customerType: String,
    val paymentHistory: String,
    val velocityCheck: String
)
```

### onPostAuthorisation

The `onPostAuthorisation` callback receives the final transaction result (`SubmitResult`).

#### Success

If the transaction was successful, you'll receive either an `AuthorizedSubmitResult` or a `CapturedSubmitResult`.

```kotlin
AuthorizedSubmitResult(
    state = "Authorised",
    providerResponse = ProviderResponse(
        code = "00",
        message = "Approved",
        cardVerificationCodeResult = "M",
        addressVerificationServiceResult = "Y"
    ),
    fundingData = FundingData(
        cardVerificationCodeResult = "Matched",
        addressVerificationServiceResult = "Y"
    )
)
```

| Parameter | Description |
|  --- | --- |
| `state` | The final state of the transaction.Possible values:`Authorised``Captured``Refused` |
| `providerResponse` | Details about the provider's response. |
| `providerResponse.code` | The raw result code returned by the provider that processed the transaction. |
| `providerResponse.message` | The raw message associated with the result code from the provider that processed the transaction. |
| `providerResponse.cardVerificationCodeResult` | The Card Verification Code (CVC) result returned by the provider. |
| `providerResponse.addressVerificationServiceResult` | The Address Verification Service (AVS) result returned by the provider. |
| `fundingData` | Details about the payment method. |
| `fundingData.cardVerificationCodeResult` | The Card Verification Code (CVC) result in human-readable format. |
| `fundingData.addressVerificationServiceResult` | The Address Verification Service (AVS) result in human-readable format. |


Here's an example of what to do with this data:

```kotlin
onPostAuthorisation = { result ->
    Log.d("Payment", "Non-3DS payment result: $result")
    
    when (result) {
        is AuthorizedSubmitResult -> {
            Log.d("Payment", "Payment successful!")
            Log.d("Payment", "Provider response: ${result.providerResponse.message}")
            
            // Check verification results
            val fundingData = result.fundingData
            if (fundingData.cardVerificationCodeResult == "Matched") {
                Log.d("Payment", "CVC verification passed")
            }
            
            if (fundingData.addressVerificationServiceResult == "Y") {
                Log.d("Payment", "Address verification passed")
            }
            
            // Store transaction details
            storeTransactionRecord(TransactionRecord(
                amount = 99.99,
                currency = "USD",
                cardType = "VISA",
                processingType = "non-3ds",
                timestamp = Instant.now().toString()
            ))
            
            // Navigate to success screen
            navigateToSuccessScreen()
        }
        is CapturedSubmitResult -> {
            Log.d("Payment", "Payment captured successfully!")
            Log.d("Payment", "Provider response: ${result.providerResponse.message}")
            navigateToSuccessScreen()
        }
        is RefusedSubmitResult -> {
            Log.e("Payment", "Payment declined: ${result.stateData.message}")
            handlePaymentFailure(result)
        }
        is FailedSubmitResult -> {
            Log.e("Payment", "Payment failed: ${result.errorReason}")
            showError("Payment failed: ${result.errorReason}")
        }
        else -> {
            Log.e("Payment", "Unknown result type: ${result::class.simpleName}")
            showError("Payment completed with unknown status")
        }
    }
}

data class TransactionRecord(
    val amount: Double,
    val currency: String,
    val cardType: String,
    val processingType: String,
    val timestamp: String
)
```

#### Failure (Declined)

If the bank or issuer declines the transaction, you'll receive a `RefusedSubmitResult`.

```kotlin
RefusedSubmitResult(
    state = "Refused",
    stateData = StateData(
        code = "05",
        message = "Do not honour"
    ),
    providerResponse = ProviderResponse(
        code = "05",
        message = "Do not honour",
        merchantAdvice = MerchantAdvice(
            code = "01",
            message = "Try another payment method"
        ),
        cardVerificationCodeResult = "M",
        addressVerificationServiceResult = "Y"
    ),
    fundingData = FundingData(
        cardVerificationCodeResult = "Matched",
        addressVerificationServiceResult = "Y"
    )
)
```

Here's an example of how to handle failures:

```kotlin
private fun handlePaymentFailure(result: RefusedSubmitResult) {
    Log.e("Payment", "Payment declined: ${result.stateData.message}")
    
    // Check merchant advice for next steps
    result.providerResponse?.merchantAdvice?.let { advice ->
        when (advice.code) {
            "01" -> {
                // Try another payment method
                showError("Payment declined. Please try a different card.")
                enableAlternativePaymentMethods()
            }
            "02" -> {
                // Retry with different amount
                showError("Transaction amount issue. Please contact support.")
            }
            "03" -> {
                // Contact issuer
                showError("Please contact your bank to authorise this payment.")
            }
            else -> {
                showError("Payment declined: ${advice.message}")
            }
        }
    } ?: run {
        // Generic decline message
        val declineReason = when (result.stateData.code) {
            "05" -> "Payment declined by your bank"
            "14" -> "Invalid card number"
            "54" -> "Card has expired"
            "61" -> "Amount limit exceeded"
            else -> "Payment was declined"
        }
        showError(declineReason)
    }
    
    // Track decline for analytics
    trackDeclineEvent(DeclineEvent(
        declineCode = result.stateData.code,
        declineReason = result.stateData.message,
        merchantAdvice = result.providerResponse?.merchantAdvice?.code,
        timestamp = Instant.now().toString()
    ))
}

data class DeclineEvent(
    val declineCode: String,
    val declineReason: String,
    val merchantAdvice: String?,
    val timestamp: String
)

private fun enableAlternativePaymentMethods() {
    // Show alternative payment options
    Log.d("Payment", "Enabling alternative payment methods")
}

private fun trackDeclineEvent(event: DeclineEvent) {
    // Track decline for fraud prevention and analytics
    Log.d("Analytics", "Decline event: $event")
}
```

### Error handling

For both callbacks, implement comprehensive error handling to ensure a smooth user experience:

```kotlin
onSubmitError = { error ->
    Log.e("Payment", "Submit error: ${error.message}")
    
    when (error.errorCode) {
        "NETWORK_ERROR" -> {
            showError("Network connection issue. Please check your internet and try again.")
        }
        "VALIDATION_FAILED" -> {
            showError("Please check all required fields are completed correctly.")
        }
        "CARD_EXPIRED" -> {
            showError("Your card has expired. Please use a different card.")
        }
        "INSUFFICIENT_FUNDS" -> {
            showError("Insufficient funds. Please try a different card.")
        }
        else -> {
            showError("Payment failed. Please try again or use a different payment method.")
        }
    }
}
```