{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-guides/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":["sub-heading","tabs","tab","code-group","admonition"]},"type":"markdown"},"seo":{"title":"How it works","description":"Transform your commerce with PXP's unified platform—seamless payments, real-time insights, and global growth in one powerful integration.","lang":"en-UK","siteUrl":"https://developer.pxp.io","llmstxt":{"hide":false,"sections":[{"title":"Table of contents","includeFiles":["**/*"],"excludeFiles":[]}],"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"how-it-works","__idx":0},"children":["How it works"]},{"$$mdtype":"Tag","name":"SubHeading","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Learn about how card components work."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"overview","__idx":1},"children":["Overview"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Card components allow you to easily integrate a highly secure and customisable payment solution into your Android application. By offering full control over layout, styling, and field visibility, card components ensure a seamless and branded payment experience, while maintaining top-tier security with PXP's Token Vault service and 3DS service."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The Android SDK provides native Jetpack Compose components that integrate seamlessly with modern Android development practices, offering type-safe configuration, reactive state management, and comprehensive error handling."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"integration-steps","__idx":2},"children":["Integration steps"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A full integration involves the following steps:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Set up the Unity Portal:"]}," To use the SDK, you first need to activate the ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["Component"]}," and ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["Card"]}," services in the Unity Portal. You also need to whitelist any package names that you'll be sending requests from."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Install the SDK:"]}," Add the latest version of the Android SDK to your project using Gradle and set it up."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Embed components:"]}," Initialise the PXP Checkout SDK, create your components, and render them in your Composable UI."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Handle results:"]}," Add proper handling for events and errors. Implement analytics to monitor your customers' payment journeys."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Submit and view transactions:"]}," Call the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["submit()"]}," method. The SDK then validates all form data, tokenises the card (if needed), and sends the transaction request to PXP."," ","After PXP processes the payment, the SDK receives the response and triggers an ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPostAuthorisation"]}," callback that receives a transaction response."," ","This response includes:",{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The transaction state, which tells you whether or not it was successful."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The transaction's unique identifier."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The provider response, if there's no 3DS challenge."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The authentication data, if there is a 3DS challenge."]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"android-specific-integration-features","__idx":3},"children":["Android-specific integration features"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Jetpack Compose Integration:"]}," Native composable components that integrate with your existing UI"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Type safety:"]}," Kotlin data classes and sealed classes for configuration and results"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Lifecycle awareness:"]}," Proper Android lifecycle management for payment flows"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["State management:"]}," Reactive state handling with Compose state management"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Error handling:"]}," Comprehensive error handling with Android-specific patterns"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Configuration changes:"]}," Automatic state preservation during device rotation and configuration changes"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Memory management:"]}," Efficient component cleanup and resource management"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Thread safety:"]}," All component operations are main-thread safe with background processing for network calls"]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"component-types","__idx":4},"children":["Component types"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["There are two types of card components:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Pre-built components:"]}," For convenience and speed. They offer ready-to-use user interfaces, pre-designed and responsive layouts, and a consistent experience."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Standalone components:"]}," For maximum flexibility and control. They offer individual field control, custom layouts, and fine-grained validation."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You can think of pre-built components as composable containers that manage multiple standalone components internally, while standalone components are individual building blocks you assemble yourself in your Compose UI."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"use-cases","__idx":5},"children":["Use cases"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Use pre-built components if:"]}]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["You want a complete payment form with minimal setup."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["You're happy with pre-designed, responsive payment forms that follow Material Design principles."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["You want to get up and running quickly, with minimal custom styling."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["You want a proven user interface that follows payment best practices and Android UI guidelines."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Use standalone components if:"]}]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["You want to position and style each payment field (card number, expiry, CVC, etc.) separately in your own custom Compose UI."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["You're building a completely custom payment form layout that doesn't fit standard patterns."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["You need to handle validation for each field independently with custom UI feedback."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["You're integrating with existing Compose forms or complex UI frameworks."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"available-components","__idx":6},"children":["Available components"]},{"$$mdtype":"Tag","name":"Tabs","attributes":{"size":"medium"},"children":[{"$$mdtype":"Tag","name":"div","attributes":{"label":"Pre-built components","disable":false},"children":[{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Component"},"children":["Component"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Description"},"children":["Description"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Billing address"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Collect a customer's billing address to use for AVS checks during authorisation."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Card-on-file"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Allow customers to select previously tokenised cards for quick and easy payments."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Click-once"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Allow customers to pay with just one click, using a tokenised card."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["New card"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Securely capture full card details for first-time users."]}]}]}]}]}]},{"$$mdtype":"Tag","name":"div","attributes":{"label":"Standalone components","disable":false},"children":[{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Component"},"children":["Component"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Description"},"children":["Description"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Address input field"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Collect the first line of a customer's shipping address."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Card brand selector"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Display all accepted card brands and highlight the card brand as the user types their card details."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Card CVC"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Collect a card's CVC."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Card consent"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Collect a customer's consent. For example, to save their card for reuse."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Card expiry date"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Collect a card's expiry date."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Cardholder name"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Collect a cardholder's name."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Card number"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Collect a card's number."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Card submit"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Display a submit button to finalise the payment process."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Country selection"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Allow customers to select their shipping country using a customisable drop-down."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Dynamic card image"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Display a live preview of a customer's card with dynamically updated information."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Postcode"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Collect a customer's postal or ZIP code."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Pre-fill billing address checkbox"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Allow customers to pre-fill their billing address using their shipping address details."]}]}]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"android-implementation-examples","__idx":7},"children":["Android implementation examples"]},{"$$mdtype":"Tag","name":"CodeGroup","attributes":{"mode":"tabs"},"children":[{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"kotlin","data-title":"Pre-built component usage","header":{"title":"Pre-built component usage","controls":{"copy":{}}},"source":"class PaymentActivity : ComponentActivity() {\n    private lateinit var pxpCheckout: PxpCheckout\n    private lateinit var newCardComponent: NewCardComponent\n    \n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        \n        setupPxpCheckout()\n        setupNewCardComponent()\n        \n        setContent {\n            PaymentScreen()\n        }\n    }\n    \n    private fun setupNewCardComponent() {\n        val config = NewCardComponentConfig(\n            fields = NewCardComponentConfig.Fields().apply {\n                cardNumber = CardNumberComponentConfig(\n                    isRequired = true,\n                    validateOnChange = true\n                )\n                expiryDate = CardExpiryDateComponentConfig(\n                    isRequired = true\n                )\n                cvc = CardCvcComponentConfig(\n                    isRequired = true\n                )\n                holderName = CardHolderNameComponentConfig(\n                    isRequired = false\n                )\n            },\n            submit = CardSubmitComponentConfig(\n                onPostAuthorisation = { result ->\n                    handlePaymentResult(result)\n                }\n            )\n        )\n        \n        newCardComponent = pxpCheckout.createComponent(ComponentType.NEW_CARD, config)\n    }\n    \n    @Composable\n    private fun PaymentScreen() {\n        Column(\n            modifier = Modifier\n                .fillMaxSize()\n                .padding(16.dp)\n        ) {\n            Text(\n                text = \"Payment Information\",\n                style = MaterialTheme.typography.headlineMedium,\n                modifier = Modifier.padding(bottom = 16.dp)\n            )\n            \n            // Render the pre-built new card component\n            pxpCheckout.buildComponentView(\n                component = newCardComponent,\n                modifier = Modifier.fillMaxWidth()\n            )\n        }\n    }\n}\n","lang":"kotlin"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"kotlin","data-title":"Standalone component usage","header":{"title":"Standalone component usage","controls":{"copy":{}}},"source":"class CustomPaymentActivity : ComponentActivity() {\n    private lateinit var pxpCheckout: PxpCheckout\n    private lateinit var cardNumberComponent: CardNumberComponent\n    private lateinit var expiryDateComponent: CardExpiryDateComponent\n    private lateinit var cvcComponent: CardCvcComponent\n    private lateinit var submitComponent: CardSubmitComponent\n    \n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        \n        setupPxpCheckout()\n        setupStandaloneComponents()\n        \n        setContent {\n            CustomPaymentScreen()\n        }\n    }\n    \n    private fun setupStandaloneComponents() {\n        // Individual component configurations\n        cardNumberComponent = pxpCheckout.createComponent(\n            ComponentType.CARD_NUMBER,\n            CardNumberComponentConfig(\n                onCardBrandDetected = { event ->\n                    updateCardBrandUI(event.cardBrand)\n                },\n                onValidationPassed = { \n                    enableNextField() \n                }\n            )\n        )\n        \n        expiryDateComponent = pxpCheckout.createComponent(\n            ComponentType.CARD_EXPIRY_DATE,\n            CardExpiryDateComponentConfig(\n                onValidationPassed = {\n                    enableCvcField()\n                }\n            )\n        )\n        \n        cvcComponent = pxpCheckout.createComponent(\n            ComponentType.CARD_CVC,\n            CardCvcComponentConfig(\n                onValidationPassed = {\n                    enableSubmitButton()\n                }\n            )\n        )\n        \n        submitComponent = pxpCheckout.createComponent(\n            ComponentType.CARD_SUBMIT,\n            CardSubmitComponentConfig(\n                onPostAuthorisation = { result ->\n                    handlePaymentResult(result)\n                }\n            )\n        )\n    }\n    \n    @Composable\n    private fun CustomPaymentScreen() {\n        var cardBrand by remember { mutableStateOf<CardBrand?>(null) }\n        \n        Column(\n            modifier = Modifier\n                .fillMaxSize()\n                .padding(16.dp),\n            verticalArrangement = Arrangement.spacedBy(16.dp)\n        ) {\n            Text(\n                text = \"Custom Payment Form\",\n                style = MaterialTheme.typography.headlineMedium\n            )\n            \n            // Custom layout with individual components\n            Card(\n                modifier = Modifier.fillMaxWidth(),\n                elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)\n            ) {\n                Column(\n                    modifier = Modifier.padding(16.dp),\n                    verticalArrangement = Arrangement.spacedBy(12.dp)\n                ) {\n                    // Card number with brand icon\n                    Row(\n                        modifier = Modifier.fillMaxWidth(),\n                        verticalAlignment = Alignment.CenterVertically\n                    ) {\n                        pxpCheckout.buildComponentView(\n                            component = cardNumberComponent,\n                            modifier = Modifier.weight(1f)\n                        )\n                        \n                        cardBrand?.let { brand ->\n                            Image(\n                                painter = painterResource(getCardBrandIcon(brand)),\n                                contentDescription = brand.name,\n                                modifier = Modifier\n                                    .size(32.dp)\n                                    .padding(start = 8.dp)\n                            )\n                        }\n                    }\n                    \n                    // Expiry and CVC in a row\n                    Row(\n                        modifier = Modifier.fillMaxWidth(),\n                        horizontalArrangement = Arrangement.spacedBy(12.dp)\n                    ) {\n                        pxpCheckout.buildComponentView(\n                            component = expiryDateComponent,\n                            modifier = Modifier.weight(1f)\n                        )\n                        \n                        pxpCheckout.buildComponentView(\n                            component = cvcComponent,\n                            modifier = Modifier.weight(1f)\n                        )\n                    }\n                }\n            }\n            \n            // Submit button\n            pxpCheckout.buildComponentView(\n                component = submitComponent,\n                modifier = Modifier.fillMaxWidth()\n            )\n        }\n    }\n    \n    private fun updateCardBrandUI(brand: CardBrand) {\n        // Update UI state with detected card brand\n    }\n    \n    private fun getCardBrandIcon(brand: CardBrand): Int {\n        return when (brand) {\n            CardBrand.VISA -> R.drawable.ic_visa\n            CardBrand.MASTERCARD -> R.drawable.ic_mastercard\n            CardBrand.AMEX -> R.drawable.ic_amex\n            else -> R.drawable.ic_card_generic\n        }\n    }\n}\n","lang":"kotlin"},"children":[]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"supported-transaction-intents","__idx":8},"children":["Supported transaction intents"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When you initiate a transaction, you have to provide key information about the transaction method, as well as the amount and currency."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The transaction method is made up of three elements:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["entry type"]},", which describes the origin of the transaction and determines the supported payment methods and available features. For Android components, this is always ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ECOM"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["funding type"]},", which describes the method used to fund the transaction. In this case, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Card"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["intent"]},", which describes the purpose of a transaction, indicating the intended money flow direction. Each intent dictates a specific transaction flow and affects how the transaction is handled by the system."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For card transactions, we support the following intents:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Intent"},"children":["Intent"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Description"},"children":["Description"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["AUTHORISATION"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Reserve funds on the customer's payment method."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ESTIMATED_AUTHORISATION"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Reserve funds on the customer's payment method, based on an estimated amount. This method is particularly useful in environments such as hotels, car rental agencies, and fuel stations, where the final charge may vary based on additional services or usage."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PURCHASE"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Capture funds immediately after authorisation."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PAYOUT"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Send funds to a recipient."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["REFUND"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Return funds to a customer."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["VERIFICATION"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Verify that a card is legitimate and active, without reserving any funds or completing a purchase. This method is particularly useful in environments such as hotels, car rental agencies, and other scenarios where it's important to validate the card upfront, but the final transaction amount may not be known or processed immediately."]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"android-transaction-configuration","__idx":9},"children":["Android transaction configuration"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"kotlin","header":{"controls":{"copy":{}}},"source":"val transactionData = TransactionData(\n    amount = 99.99,\n    currency = CurrencyType.USD,\n    entryType = EntryType.ECOM,\n    intent = IntentType.AUTHORISATION, // or other supported intents\n    merchantTransactionId = \"order-${System.currentTimeMillis()}\",\n    merchantTransactionDate = { Instant.now().toString() },\n    shopper = Shopper(\n        email = \"customer@example.com\",\n        firstName = \"John\",\n        lastName = \"Doe\",\n        // Additional shopper data\n        phone = \"+1234567890\",\n        dateOfBirth = \"1990-01-01\"\n    )\n)\n\nval sdkConfig = PxpSdkConfig(\n    environment = Environment.TEST,\n    session = SessionConfig(\n        sessionId = \"session-${System.currentTimeMillis()}\",\n        sessionData = \"additional_session_data\"\n    ),\n    transactionData = transactionData,\n    clientId = \"your_client_id\",\n    ownerId = \"Unity\",\n    ownerType = \"MerchantGroup\",\n    merchantShopperId = \"shopper-123\"\n    // Optional: Restrict accepted card types for frontend validation\n    // restrictions = Restrictions(\n    //     card = RestrictionsCard(\n    //         ownerTypes = listOf(RestrictionOwnerType.CONSUMER),\n    //         fundingSources = listOf(RestrictionFundingSource.CREDIT, RestrictionFundingSource.DEBIT)\n    //     )\n    // )\n)\n","lang":"kotlin"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"intent-specific-configurations","__idx":10},"children":["Intent-specific configurations"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"kotlin","header":{"controls":{"copy":{}}},"source":"class TransactionIntentManager {\n    \n    fun createAuthorisationConfig(): TransactionData {\n        return TransactionData(\n            amount = 99.99,\n            currency = CurrencyType.USD,\n            entryType = EntryType.ECOM,\n            intent = IntentType.AUTHORISATION,\n            merchantTransactionId = \"auth-${System.currentTimeMillis()}\",\n            merchantTransactionDate = { Instant.now().toString() },\n            // Authorisation-specific data\n            expiryTime = Instant.now().plus(24, ChronoUnit.HOURS).toString()\n        )\n    }\n    \n    fun createPurchaseConfig(): TransactionData {\n        return TransactionData(\n            amount = 99.99,\n            currency = CurrencyType.USD,\n            entryType = EntryType.ECOM,\n            intent = IntentType.PURCHASE,\n            merchantTransactionId = \"purchase-${System.currentTimeMillis()}\",\n            merchantTransactionDate = { Instant.now().toString() },\n            // Purchase-specific data\n            deliveryAddress = DeliveryAddress(\n                addressLine1 = \"123 Main St\",\n                city = \"New York\",\n                postalCode = \"10001\",\n                countryCode = \"US\"\n            )\n        )\n    }\n    \n    fun createVerificationConfig(): TransactionData {\n        return TransactionData(\n            amount = 0.0, // No amount for verification\n            currency = CurrencyType.USD,\n            entryType = EntryType.Ecom,\n            intent = IntentType.Verification,\n            merchantTransactionId = \"verify-${System.currentTimeMillis()}\",\n            merchantTransactionDate = { Instant.now().toString() },\n            // Verification-specific data\n            verificationPurpose = \"account_setup\"\n        )\n    }\n    \n    fun createRefundConfig(originalTransactionId: String, refundAmount: Double): TransactionData {\n        return TransactionData(\n            amount = refundAmount,\n            currency = CurrencyType.USD,\n            entryType = EntryType.Ecom,\n            intent = IntentType.Purchase, // Note: Refund is not available in actual SDK\n            merchantTransactionId = \"refund-${System.currentTimeMillis()}\",\n            merchantTransactionDate = { Instant.now().toString() },\n            // Refund-specific data\n            originalTransaction = OriginalTransactionReference(\n                transactionId = originalTransactionId,\n                merchantTransactionId = \"original-transaction-id\"\n            )\n        )\n    }\n}\n\n// Supporting data classes\ndata class DeliveryAddress(\n    val addressLine1: String,\n    val city: String,\n    val postalCode: String,\n    val countryCode: String\n)\n\ndata class OriginalTransactionReference(\n    val transactionId: String,\n    val merchantTransactionId: String\n)\n\n// These are sealed classes in the actual SDK, not enums\nsealed class IntentType {\n    object Authorisation : IntentType()\n    object EstimatedAuthorisation : IntentType()\n    object Purchase : IntentType()\n    object Payout : IntentType()\n    object Verification : IntentType()\n}\n\nsealed class EntryType {\n    object Ecom : EntryType()\n    object MOTO : EntryType()\n}\n\nsealed class CurrencyType {\n    object USD : CurrencyType()\n    object EUR : CurrencyType()\n    object GBP : CurrencyType()\n    object BHD : CurrencyType()\n    object JPY : CurrencyType()\n}\n","lang":"kotlin"},"children":[]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["We support recurring payments for subscription-based services where payments are automatically made on a regular schedule (e.g., monthly memberships, subscription services)."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"android-specific-features","__idx":11},"children":["Android-specific features"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"component-lifecycle-management","__idx":12},"children":["Component lifecycle management"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The Android SDK integrates seamlessly with Android's component lifecycle to ensure proper resource management and state preservation:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"kotlin","header":{"controls":{"copy":{}}},"source":"class PaymentActivity : ComponentActivity() {\n    private lateinit var pxpCheckout: PxpCheckout\n    private lateinit var newCardComponent: NewCardComponent\n    \n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        \n        // Initialise SDK - only once per activity lifecycle\n        initializePxpSdk()\n        \n        // Create components - can be recreated on configuration changes\n        createComponents()\n        \n        setContent {\n            PaymentScreen()\n        }\n    }\n    \n    override fun onSaveInstanceState(outState: Bundle) {\n        super.onSaveInstanceState(outState)\n        // SDK automatically preserves component state\n        // You can save additional app-specific state here\n        outState.putString(\"payment_session_id\", currentSessionId)\n        outState.putSerializable(\"payment_state\", currentPaymentState)\n    }\n    \n    override fun onRestoreInstanceState(savedInstanceState: Bundle) {\n        super.onRestoreInstanceState(savedInstanceState)\n        // SDK automatically restores component state\n        // Restore your app-specific state here\n        currentSessionId = savedInstanceState.getString(\"payment_session_id\", \"\")\n        currentPaymentState = savedInstanceState.getSerializable(\"payment_state\") as? PaymentState\n    }\n    \n    override fun onDestroy() {\n        super.onDestroy()\n        // SDK components are automatically cleaned up\n        // Additional cleanup for your app logic here\n        clearPaymentState()\n    }\n    \n    override fun onPause() {\n        super.onPause()\n        // SDK handles background state appropriately\n        // Payment flows are paused safely\n    }\n    \n    override fun onResume() {\n        super.onResume()\n        // SDK resumes any paused payment flows\n        // Refresh payment state if needed\n        refreshPaymentStatus()\n    }\n}\n","lang":"kotlin"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"configuration-change-handling","__idx":13},"children":["Configuration change handling"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Components automatically handle configuration changes like device rotation:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"kotlin","header":{"controls":{"copy":{}}},"source":"@Composable\nfun AdaptivePaymentScreen() {\n    val configuration = LocalConfiguration.current\n    val isLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE\n    \n    // Component state is preserved automatically during rotation\n    if (isLandscape) {\n        Row(\n            modifier = Modifier.fillMaxSize(),\n            horizontalArrangement = Arrangement.spacedBy(16.dp)\n        ) {\n            PaymentFieldsColumn(modifier = Modifier.weight(1f))\n            PaymentSummaryColumn(modifier = Modifier.weight(1f))\n        }\n    } else {\n        Column(\n            modifier = Modifier.fillMaxSize(),\n            verticalArrangement = Arrangement.spacedBy(16.dp)\n        ) {\n            PaymentFieldsColumn()\n            PaymentSummaryColumn()\n        }\n    }\n}\n","lang":"kotlin"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"memory-management","__idx":14},"children":["Memory management"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The SDK efficiently manages memory and resources:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"kotlin","header":{"controls":{"copy":{}}},"source":"class PaymentViewModel : ViewModel() {\n    private var pxpCheckout: PxpCheckout? = null\n    private val paymentComponents = mutableListOf<Component<*>>()\n    \n    fun initializePayment(context: Context) {\n        // Initialise only if not already done\n        if (pxpCheckout == null) {\n            pxpCheckout = PxpCheckout.builder()\n                .withConfig(sdkConfig)\n                .withContext(context)\n                .build()\n        }\n    }\n    \n    fun createPaymentComponents() {\n        // Clean up existing components before creating new ones\n        paymentComponents.clear()\n        \n        // Create new components\n        val newCardComponent = pxpCheckout?.createComponent(\n            ComponentType.NEW_CARD,\n            newCardConfig\n        )\n        newCardComponent?.let { paymentComponents.add(it) }\n    }\n    \n    override fun onCleared() {\n        super.onCleared()\n        // ViewModel cleanup - SDK handles component cleanup automatically\n        paymentComponents.clear()\n        pxpCheckout = null\n    }\n}\n","lang":"kotlin"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"state-management","__idx":15},"children":["State management"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"kotlin","header":{"controls":{"copy":{}}},"source":"@Composable\nfun PaymentScreenWithState() {\n    var paymentState by remember { mutableStateOf(PaymentState.IDLE) }\n    var errorMessage by remember { mutableStateOf<String?>(null) }\n    var cardBrand by remember { mutableStateOf<CardBrand?>(null) }\n    \n    LaunchedEffect(paymentState) {\n        when (paymentState) {\n            PaymentState.PROCESSING -> {\n                // Show loading indicator\n            }\n            PaymentState.SUCCESS -> {\n                // Navigate to success screen\n                delay(2000)\n                // Navigate away\n            }\n            PaymentState.ERROR -> {\n                // Show error message\n            }\n            else -> { /* Handle other states */ }\n        }\n    }\n    \n    Column(modifier = Modifier.fillMaxSize()) {\n        when (paymentState) {\n            PaymentState.PROCESSING -> {\n                Box(\n                    modifier = Modifier.fillMaxSize(),\n                    contentAlignment = Alignment.Center\n                ) {\n                    Column(horizontalAlignment = Alignment.CenterHorizontally) {\n                        CircularProgressIndicator()\n                        Text(\n                            text = \"Processing payment...\",\n                            modifier = Modifier.padding(top = 16.dp)\n                        )\n                    }\n                }\n            }\n            else -> {\n                // Show payment form\n                PaymentForm(\n                    onPaymentStateChange = { newState ->\n                        paymentState = newState\n                    },\n                    onErrorChange = { error ->\n                        errorMessage = error\n                    }\n                )\n            }\n        }\n    }\n}\n\nenum class PaymentState {\n    IDLE, PROCESSING, SUCCESS, ERROR\n}\n","lang":"kotlin"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"lifecycle-integration","__idx":16},"children":["Lifecycle integration"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"kotlin","header":{"controls":{"copy":{}}},"source":"class PaymentActivity : ComponentActivity() {\n    private lateinit var pxpCheckout: PxpCheckout\n    \n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        \n        // Initialise SDK\n        setupPxpCheckout()\n        \n        setContent {\n            PaymentApp()\n        }\n    }\n    \n    override fun onResume() {\n        super.onResume()\n        // Resume any paused payment flows\n        pxpCheckout.resumePaymentFlow()\n    }\n    \n    override fun onPause() {\n        super.onPause()\n        // Pause payment flows if needed\n        pxpCheckout.pausePaymentFlow()\n    }\n    \n    override fun onDestroy() {\n        super.onDestroy()\n        // Clean up SDK resources\n        pxpCheckout.cleanup()\n    }\n}\n","lang":"kotlin"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"error-handling","__idx":17},"children":["Error handling"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"kotlin","header":{"controls":{"copy":{}}},"source":"class PaymentErrorHandler {\n    \n    @Composable\n    fun ErrorDisplay(\n        error: PaymentError?,\n        onDismiss: () -> Unit,\n        onRetry: () -> Unit\n    ) {\n        error?.let { \n            AlertDialog(\n                onDismissRequest = onDismiss,\n                title = {\n                    Text(\"Payment Error\")\n                },\n                text = {\n                    Text(getErrorMessage(it))\n                },\n                confirmButton = {\n                    TextButton(onClick = onRetry) {\n                        Text(\"Retry\")\n                    }\n                },\n                dismissButton = {\n                    TextButton(onClick = onDismiss) {\n                        Text(\"Cancel\")\n                    }\n                }\n            )\n        }\n    }\n    \n    private fun getErrorMessage(error: PaymentError): String {\n        return when (error.code) {\n            \"NETWORK_ERROR\" -> \"Please check your internet connection and try again.\"\n            \"INVALID_CARD\" -> \"Please check your card details and try again.\"\n            \"INSUFFICIENT_FUNDS\" -> \"This card has insufficient funds.\"\n            \"CARD_DECLINED\" -> \"This card was declined. Please try a different card.\"\n            else -> error.errorReason ?: \"An unexpected error occurred.\"\n        }\n    }\n}\n","lang":"kotlin"},"children":[]}]},"headings":[{"value":"How it works","id":"how-it-works","depth":1},{"value":"Overview","id":"overview","depth":2},{"value":"Integration steps","id":"integration-steps","depth":2},{"value":"Android-specific integration features","id":"android-specific-integration-features","depth":3},{"value":"Component types","id":"component-types","depth":2},{"value":"Use cases","id":"use-cases","depth":3},{"value":"Available components","id":"available-components","depth":2},{"value":"Android implementation examples","id":"android-implementation-examples","depth":2},{"value":"Supported transaction intents","id":"supported-transaction-intents","depth":2},{"value":"Android transaction configuration","id":"android-transaction-configuration","depth":3},{"value":"Intent-specific configurations","id":"intent-specific-configurations","depth":3},{"value":"Android-specific features","id":"android-specific-features","depth":2},{"value":"Component lifecycle management","id":"component-lifecycle-management","depth":3},{"value":"Configuration change handling","id":"configuration-change-handling","depth":3},{"value":"Memory management","id":"memory-management","depth":3},{"value":"State management","id":"state-management","depth":3},{"value":"Lifecycle integration","id":"lifecycle-integration","depth":3},{"value":"Error handling","id":"error-handling","depth":3}],"frontmatter":{"seo":{"title":"How it works"}},"lastModified":"2026-05-07T11:25:00.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/guides/checkout/components/android/card/how-it-works","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}