{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-guides/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":["sub-heading","admonition","br","details","required"]},"type":"markdown"},"seo":{"title":"Implementation","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":"implementation","__idx":0},"children":["Implementation"]},{"$$mdtype":"Tag","name":"SubHeading","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Complete guide to integrating the Aeropay component into your Android application."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"overview","__idx":1},"children":["Overview"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The Aeropay component lets shoppers make payments or receive payouts through a linked US bank account. The integration follows a three-stage lifecycle:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Initialise:"]}," Configure the SDK with an Aeropay-enabled session and transaction data."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Create and render:"]}," Create the Aeropay button and show it in your Jetpack Compose UI."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Handle the flow:"]}," Respond to custom validation, user verification, transaction results, cancellation, and errors."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The SDK handles consumer data collection, OTP verification, bank selection, Aerosync bank linking, and pay-by-bank transaction submission."]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"warning"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Backend verification is mandatory. Always verify transactions on your backend before fulfilling orders or confirming payouts. Client callbacks can be manipulated by malicious users."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"before-you-start","__idx":2},"children":["Before you start"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Complete ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/checkout/components/android/aeropay/onboarding"},"children":["Aeropay onboarding"]}," in the Unity Portal before integrating the component. You'll need:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Aeropay enabled at merchant group and site level."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Your Aeropay merchant ID, API key, API secret, and configuration ID added to the Aeropay service."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["PXP API credentials for creating sessions on your backend."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["An Android app that meets the SDK requirements (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["minSdk"]}," 24, Jetpack Compose, and Kotlin 2.x)."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Aeropay transactions must also meet these requirements:"]},{"$$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":"Setting"},"children":["Setting"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Required value"},"children":["Required value"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Currency"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["USD"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Entry type"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Ecom"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Intent"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Authorisation"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Purchase"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["EstimatedAuthorisation"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Payout"]}]}]}]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"step-1-install-the-android-sdk","__idx":3},"children":["Step 1: Install the Android SDK"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["See ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/checkout/components/android/install"},"children":["Install the Android SDK"]}," for full Gradle, Compose plugin, and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["minSdk"]}," setup. Add the PXP Android Components SDK from ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"https://central.sonatype.com/artifact/io.pxp/android-components-sdk","target":"_blank"},"children":["Maven Central"]},", then continue with the Aeropay session and component steps on this page."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"step-2-create-a-session-on-your-backend","__idx":4},"children":["Step 2: Create a session on your backend"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Create sessions on your backend using PXP-UST1 HMAC authentication. Never expose your PXP token value or other API credentials in the Android app."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Send a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["POST"]}," request to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/api/v1/sessions"]}," with the Aeropay intent under ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["transactionMethod.intent.aeropay"]},":"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"merchant\": \"MERCHANT-1\",\n  \"site\": \"SITE-1\",\n  \"sessionTimeout\": 1200,\n  \"merchantTransactionId\": \"0f3501f0-2577-4dee-8be2-bbb6908588e3\",\n  \"amounts\": {\n    \"currencyCode\": \"USD\",\n    \"transactionValue\": 25.00\n  },\n  \"transactionMethod\": {\n    \"intent\": {\n      \"aeropay\": \"Authorisation\"\n    }\n  },\n  \"allowTransaction\": true\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Configure the request with these Aeropay-specific values:"]},{"$$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":"Property"},"children":["Property"]},{"$$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":["merchantTransactionId"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Details","attributes":{},"children":["string"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Required","attributes":{},"children":[]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Your unique identifier for the session transaction. We recommend reusing the value when initialising the SDK to simplify reconciliation."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["amounts.currencyCode"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Details","attributes":{},"children":["string"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Required","attributes":{},"children":[]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Transaction currency. Set this to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["USD"]},"."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["amounts.transactionValue"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Details","attributes":{},"children":["number"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Required","attributes":{},"children":[]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The payment or payout amount."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["transactionMethod.intent.aeropay"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Details","attributes":{},"children":["string"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Required","attributes":{},"children":[]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The Aeropay transaction intent. Possible values:",{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Authorisation"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Purchase"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["EstimatedAuthorisation"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Payout"]}]}]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["allowTransaction"]},{"$$mdtype":"Tag","name":"Break","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Details","attributes":{},"children":["boolean"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Whether the session can be used to submit a transaction."]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"session-response","__idx":5},"children":["Session response"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If Aeropay is configured for the site, the session response includes its funding configuration:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"sessionId\": \"c5f0799b-0839-43ce-abc5-5b462a98f250\",\n  \"hmacKey\": \"904bc42395d4af634e2fd48ee8c2c7f52955a1da97a3aa3d82957ff12980a7bb\",\n  \"encryptionKey\": \"20d175a669ad3f8c195c9c283fc86155\",\n  \"data\": \"eyJzZXNzaW9uSWQiOiJjNWYwNzk5Yi0wODM5LTQzY2UtYWJjNS01YjQ2MmE5OGYyNTAifQ==\",\n  \"sessionExpiry\": \"2026-07-21T12:00:00.000Z\",\n  \"allowedFundingTypes\": {\n    \"payByBanks\": {\n      \"aeropay\": {\n        \"externalMerchantId\": \"aeropay-merchant-id\",\n        \"configurationId\": \"aerosync-configuration-id\"\n      }\n    }\n  }\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Pass ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["data"]}," through to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SessionConfig.data"]}," when you initialise the SDK in Step 3. The SDK also requires both ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["allowedFundingTypes.payByBanks.aeropay.externalMerchantId"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["allowedFundingTypes.payByBanks.aeropay.configurationId"]},"."]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["PXP adds the Aeropay funding configuration to the session automatically from your Unity Portal setup. Don't add Aeropay credentials to the response yourself."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Return the session and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["merchantTransactionId"]}," to your Android app."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"step-3-initialise-the-sdk","__idx":6},"children":["Step 3: Initialise the SDK"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Request the session from your backend, then build ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PxpCheckout"]},":"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"kotlin","header":{"controls":{"copy":{}}},"source":"import android.content.Context\nimport com.pxp.PxpCheckout\nimport com.pxp.checkout.models.AeropayFundingConfig\nimport com.pxp.checkout.models.AeropayIntentType\nimport com.pxp.checkout.models.AllowedFundingTypes\nimport com.pxp.checkout.models.EntryType\nimport com.pxp.checkout.models.Environment\nimport com.pxp.checkout.models.PayByBanksConfig\nimport com.pxp.checkout.models.PxpSdkConfig\nimport com.pxp.checkout.models.SessionConfig\nimport com.pxp.checkout.models.TransactionData\nimport com.pxp.checkout.models.TransactionIntentData\nimport com.pxp.checkout.services.models.transaction.Shopper\nimport java.time.LocalDateTime\nimport java.time.ZoneOffset\nimport java.time.format.DateTimeFormatter\n\nfun createPxpCheckout(\n    context: Context,\n    sessionResult: SessionResult,\n): PxpCheckout {\n    val sessionConfig = SessionConfig(\n        sessionId = sessionResult.session.sessionId,\n        hmacKey = sessionResult.session.hmacKey,\n        encryptionKey = sessionResult.session.encryptionKey,\n        data = sessionResult.session.data,\n        locale = \"en-US\",\n        allowedFundingTypes = AllowedFundingTypes(\n            payByBanks = PayByBanksConfig(\n                aeropay = AeropayFundingConfig(\n                    externalMerchantId = sessionResult.session.aeropayExternalMerchantId,\n                    configurationId = sessionResult.session.aeropayConfigurationId,\n                ),\n            ),\n        ),\n    )\n\n    val transactionData = TransactionData(\n        amount = 25.00,\n        currency = \"USD\",\n        entryType = EntryType.Ecom,\n        intent = TransactionIntentData(aeropay = AeropayIntentType.Authorisation),\n        merchant = \"your-merchant-id\",\n        merchantTransactionId = sessionResult.merchantTransactionId,\n        merchantTransactionDate = {\n            LocalDateTime.now(ZoneOffset.UTC)\n                .format(DateTimeFormatter.ofPattern(\"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'\"))\n        },\n    )\n\n    val sdkConfig = PxpSdkConfig(\n        environment = Environment.TEST,\n        session = sessionConfig,\n        transactionData = transactionData,\n        clientId = \"your-client-id\",\n        ownerType = \"MerchantGroup\",\n        ownerId = \"MERCHANT_GROUP_1\",\n        onGetShopper = {\n            Shopper(\n                id = \"shopper-123\",\n                firstName = \"John\",\n                lastName = \"Doe\",\n                email = \"john.doe@example.com\",\n                phoneNumber = \"+14155550123\",\n            )\n        },\n    )\n\n    return PxpCheckout.builder()\n        .withConfig(sdkConfig)\n        .withContext(context)\n        .build()\n}\n","lang":"kotlin"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Set ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["environment"]}," to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Environment.TEST"]}," for UAT or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Environment.LIVE"]}," for production. Map those values to Aerosync as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SANDBOX"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PROD"]}," respectively."]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"warning"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Keep the session request and SDK transaction data consistent so that you can trace and reconcile transactions. The Android SDK doesn't compare the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["merchantTransactionId"]},", amount, currency, or intent with the values used to create the session. Verify the completed transaction against records stored on your backend."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"shopper-data","__idx":7},"children":["Shopper data"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For new shoppers, the SDK uses ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onGetShopper"]}," to prefill the consumer data screen. It supports these fields:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["firstName"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["lastName"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["email"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["phoneNumber"]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If you provide a field, it must be valid. First and last names accept a maximum of 100 Unicode letters and spaces. Email addresses accept a maximum of 128 characters and must match the SDK's email format. Phone numbers must contain ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["+1"]}," followed by ten digits, such as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["+14155550123"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If a supplied name, email address, or phone number is non-empty but invalid, the SDK invokes ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onError"]}," with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK1300"]}," before opening the popup."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Return only the data required for the flow. Don't include personal data in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["merchantTransactionId"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["shopper.id"]},", or free-text order descriptions."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"step-4-create-the-aeropay-button","__idx":8},"children":["Step 4: Create the Aeropay button"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Create the component with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["checkout.createComponent()"]},":"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"kotlin","header":{"controls":{"copy":{}}},"source":"import com.pxp.checkout.components.aeropaybutton.AeropayButtonComponent\nimport com.pxp.checkout.components.aeropaybutton.types.AeropayButtonComponentConfig\nimport com.pxp.checkout.exceptions.BaseSdkException\nimport com.pxp.checkout.models.FailedSubmitResult\nimport com.pxp.checkout.types.ComponentType\n\nval config = AeropayButtonComponentConfig(label = \"Pay by bank\").apply {\n    onCustomValidation = {\n        validateCheckoutReady() // must return Boolean\n    }\n    onUserVerificationSuccess = { user ->\n        saveAeropayUserId(user.id)\n    }\n    onCancel = {\n        resetCheckoutState()\n    }\n    onPreAuthorisation = {\n        validateOrderOnBackend() // must return Boolean\n    }\n    onPostAuthorisation = { result ->\n        verifyPaymentOnBackend(result)\n    }\n    onSubmitError = { error ->\n        if (error is FailedSubmitResult) {\n            showPaymentError(\"Unable to complete the transaction.\")\n        }\n    }\n    onError = { error ->\n        showPaymentError(\"Unable to continue with Aeropay.\")\n    }\n}\n\nval aeropayButton: AeropayButtonComponent? =\n    try {\n        checkout.createComponent(ComponentType.AERO_PAY_BUTTON, config)\n    } catch (error: BaseSdkException) {\n        // e.g. SDK0113–SDK0116 for funding, entry type, intent, or currency\n        showAlternativePaymentMethods()\n        null\n    }\n","lang":"kotlin"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Invalid Aeropay session or transaction configuration throws from ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["createComponent()"]}," as a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["BaseSdkException"]}," (for example ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK0113"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK0114"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK0115"]},", or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK0116"]},"). Catch it at creation time; the button isn't created when these checks fail."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Both ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onCustomValidation"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPreAuthorisation"]}," are ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["suspend () -> Boolean"]},". Return ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["true"]}," to continue."]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"warning"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Implement ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPreAuthorisation"]}," and return ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["true"]}," when the transaction can proceed. If this callback is omitted or doesn't return ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["true"]},", the SDK doesn't submit the transaction."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onCustomValidation"]}," runs after the optional ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ButtonComponentConfig.onClick"]}," callback (if set) and before the Aeropay popup opens. Return ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["false"]}," to keep the popup closed."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onCancel"]}," runs when the shopper dismisses the popup, including the close control and system back or scrim dismiss. Successful payment dismissal is programmatic and doesn't invoke ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onCancel"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For all component properties, see ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/checkout/components/android/aeropay/configuration"},"children":["Configuration"]},". For callback payloads and examples, see ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/checkout/components/android/aeropay/events"},"children":["Events"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"step-5-render-the-button","__idx":9},"children":["Step 5: Render the button"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Call ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Content()"]}," in your Compose hierarchy:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"kotlin","header":{"controls":{"copy":{}}},"source":"import androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.runtime.setValue\nimport androidx.compose.ui.Modifier\n\n@Composable\nfun AeropayCheckoutButton(aeropayButton: AeropayButtonComponent?) {\n    aeropayButton?.Content(modifier = Modifier.fillMaxWidth())\n}\n","lang":"kotlin"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Create the component in a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["LaunchedEffect"]}," or equivalent lifecycle scope, then store it in Compose state so recomposition can render it."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"step-6-verify-the-transaction-on-your-backend","__idx":10},"children":["Step 6: Verify the transaction on your backend"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["After transaction submission, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPostAuthorisation"]}," receives the merchant and PXP transaction identifiers. Send the result to your backend and retrieve the transaction from PXP before updating the order or payout:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"kotlin","header":{"controls":{"copy":{}}},"source":"config.onPostAuthorisation = { submitResult ->\n    verifyPaymentOnBackend(\n        merchantTransactionId = submitResult.merchantTransactionId,\n        systemTransactionId = submitResult.systemTransactionId,\n    )\n}\n","lang":"kotlin"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Your backend verification must:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Authenticate directly with PXP."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Retrieve the transaction using its transaction identifier."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Confirm that the merchant, amount, currency, intent, and final state match your records."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Update the order or payout only after every check passes."]}]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"warning"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Don't trust values sent only from the app. Use them to identify the transaction, then compare the authoritative PXP response with values stored on your backend."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"manage-post-transaction-operations","__idx":11},"children":["Manage post-transaction operations"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["After a successful Aeropay transaction, use the merchant and system transaction identifiers from ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onPostAuthorisation"]}," to look up the payment and manage follow-up operations on your backend or in the Unity Portal. Available actions depend on the transaction intent and current state. Typical post-authorisation actions include capture, increment, void, and refund. A ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Purchase"]}," captures funds at submission, so it doesn't need a separate capture."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"new-shopper-flow","__idx":12},"children":["New-shopper flow"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use the new-shopper flow when you don't have a verified Aeropay user ID. Don't set ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["userId"]}," on the component."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The shopper completes these stages:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Reviews or enters their first name, last name, email address, and phone number."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Enters the OTP sent to their phone."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Selects a linked bank account or links a new account through Aerosync."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Confirms the transaction."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Save the verified Aeropay user ID in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onUserVerificationSuccess"]},":"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"kotlin","header":{"controls":{"copy":{}}},"source":"config.onUserVerificationSuccess = { user ->\n    saveAeropayUserId(\n        shopperId = \"shopper-123\",\n        aeropayUserId = user.id,\n    )\n}\n","lang":"kotlin"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"skip-consumer-data-collection","__idx":13},"children":["Skip consumer data collection"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If your application already has all four consumer fields, set ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["skipConsumerDataCollection"]}," to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["true"]}," to begin with OTP verification:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"kotlin","header":{"controls":{"copy":{}}},"source":"val config = AeropayButtonComponentConfig().apply {\n    skipConsumerDataCollection = true\n    onUserVerificationSuccess = { user ->\n        saveAeropayUserId(user.id)\n    }\n    onPreAuthorisation = { true }\n}\n","lang":"kotlin"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The SDK skips the screen only when ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onGetShopper"]}," returns valid, non-empty ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["firstName"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["lastName"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["email"]},", and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["phoneNumber"]}," values, and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["consumerDataCollectionConfig.editableFields"]}," is omitted, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["null"]},", or empty."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"returning-shopper-flow","__idx":14},"children":["Returning-shopper flow"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For a returning shopper, get their stored Aeropay user ID from your backend and pass it to the component:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"kotlin","header":{"controls":{"copy":{}}},"source":"val config = AeropayButtonComponentConfig().apply {\n    userId = aeropayProfile.userId\n    onPreAuthorisation = { true }\n    onPostAuthorisation = { result ->\n        verifyPaymentOnBackend(result)\n    }\n    onError = { error ->\n        when (error.errorCode) {\n            \"SDK1307\", \"SDK1308\" -> offerNewShopperFlow()\n            \"SDK0500\", \"SDK0000\" -> showPaymentError(\"Unable to look up Aeropay user.\")\n            else -> showPaymentError(\"Unable to continue with Aeropay.\")\n        }\n    }\n}\n","lang":"kotlin"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The SDK validates that the Aeropay user exists and is active. It then skips consumer data collection and OTP verification, and opens bank selection."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If the user isn't active, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onError"]}," receives ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK1307"]},". If the get-user API returns a recognised failure response, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["onError"]}," receives ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK1308"]},". Network or transport failures on the same call use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK0500"]}," (or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK0000"]},"), not ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["SDK1308"]},". See ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/guides/checkout/components/android/aeropay/troubleshooting#sdk1307-or-sdk1308"},"children":["Troubleshooting — Returning-shopper issues"]},". Remove an invalid stored ID and let the shopper restart the new-shopper flow."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Associate each Aeropay user ID with the authenticated customer on your backend. Retrieve it from your backend rather than device storage alone, and don't accept a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["userId"]}," supplied or changed by untrusted client input."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The SDK and provider handle bank linking. Don't collect routing numbers or full account numbers in your checkout. If you store bank metadata for a business need, restrict access and retain only the required values."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"payout-flow","__idx":15},"children":["Payout flow"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["AeropayIntentType.Payout"]}," in the SDK and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Payout"]}," in the session request:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"kotlin","header":{"controls":{"copy":{}}},"source":"val transactionData = TransactionData(\n    amount = 25.00,\n    currency = \"USD\",\n    entryType = EntryType.Ecom,\n    intent = TransactionIntentData(aeropay = AeropayIntentType.Payout),\n    merchant = \"your-merchant-id\",\n    merchantTransactionId = sessionResult.merchantTransactionId,\n    merchantTransactionDate = { /* ISO 8601 UTC timestamp */ },\n)\n","lang":"kotlin"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["By default, shoppers can only select an existing linked bank account during a payout. To allow bank linking, set ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["allowLinkBankOnPayout"]}," to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["true"]},":"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"kotlin","header":{"controls":{"copy":{}}},"source":"import com.pxp.checkout.components.aeropaybutton.types.BankSelectionConfig\n\nval config = AeropayButtonComponentConfig().apply {\n    userId = aeropayProfile.userId\n    bankSelectionConfig = BankSelectionConfig(\n        allowLinkBankOnPayout = true,\n    )\n    onPreAuthorisation = { true }\n    onPostAuthorisation = { result ->\n        verifyPayoutOnBackend(result)\n    }\n}\n","lang":"kotlin"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Before enabling payouts, confirm that your PXP and Aeropay accounts support them. Validate customer eligibility, amount limits, account status, and duplicate requests on your backend."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"control-the-component","__idx":16},"children":["Control the component"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Set ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["disabled"]}," on the config before you create the component if the button should start disabled. After creation, use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["setDisabled()"]}," so the button state updates:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"kotlin","header":{"controls":{"copy":{}}},"source":"val config = AeropayButtonComponentConfig().apply {\n    disabled = true\n}\n\nval aeropayButton = checkout.createComponent(\n    ComponentType.AERO_PAY_BUTTON,\n    config,\n)\n\naeropayButton.setDisabled(false)\naeropayButton.setDisabled(true)\n","lang":"kotlin"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Changing ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["config.disabled"]}," after creation doesn't update the rendered button on its own. The button is also non-interactive while loading. The component manages the loading overlay during API calls and popup operations."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"complete-compose-example","__idx":17},"children":["Complete Compose example"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The following example creates a new-shopper ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Authorisation"]}," flow:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"kotlin","header":{"controls":{"copy":{}}},"source":"import android.content.Context\nimport androidx.compose.foundation.layout.fillMaxWidth\nimport androidx.compose.foundation.layout.padding\nimport androidx.compose.material3.CircularProgressIndicator\nimport androidx.compose.runtime.Composable\nimport androidx.compose.runtime.LaunchedEffect\nimport androidx.compose.runtime.getValue\nimport androidx.compose.runtime.mutableStateOf\nimport androidx.compose.runtime.remember\nimport androidx.compose.runtime.setValue\nimport androidx.compose.ui.Modifier\nimport androidx.compose.ui.unit.dp\nimport com.pxp.checkout.components.aeropaybutton.AeropayButtonComponent\nimport com.pxp.checkout.components.aeropaybutton.types.AeropayButtonComponentConfig\nimport com.pxp.checkout.exceptions.BaseSdkException\nimport com.pxp.checkout.models.FailedSubmitResult\nimport com.pxp.checkout.types.ComponentType\n\n@Composable\nfun AeropayPaymentScreen(context: Context) {\n    var aeropayComponent by remember { mutableStateOf<AeropayButtonComponent?>(null) }\n    var isLoading by remember { mutableStateOf(true) }\n\n    LaunchedEffect(Unit) {\n        try {\n            val checkout = createPxpCheckout(context, fetchSessionFromBackend())\n            val config = AeropayButtonComponentConfig(label = \"Pay by bank\").apply {\n                onCustomValidation = { true }\n                onUserVerificationSuccess = { user ->\n                    saveAeropayUserId(user.id)\n                }\n                onCancel = {\n                    resetCheckoutState()\n                }\n                onPreAuthorisation = {\n                    validateOrderOnBackend() // must return Boolean\n                }\n                onPostAuthorisation = { result ->\n                    verifyPaymentOnBackend(result)\n                }\n                onSubmitError = { error ->\n                    if (error is FailedSubmitResult) {\n                        showPaymentError(\"Unable to complete the transaction.\")\n                    }\n                }\n                onError = { error ->\n                    showPaymentError(\"Unable to continue with Aeropay.\")\n                }\n            }\n            aeropayComponent = checkout.createComponent(\n                ComponentType.AERO_PAY_BUTTON,\n                config,\n            )\n        } catch (error: BaseSdkException) {\n            // Log error.errorCode in restricted diagnostics (e.g. SDK0113–SDK0116).\n            showPaymentError(\"Pay by Bank is not available for this checkout.\")\n        } finally {\n            isLoading = false\n        }\n    }\n\n    if (isLoading) {\n        CircularProgressIndicator(modifier = Modifier.padding(16.dp))\n    } else {\n        aeropayComponent?.Content(modifier = Modifier.fillMaxWidth())\n    }\n}\n","lang":"kotlin"},"children":[]}]},"headings":[{"value":"Implementation","id":"implementation","depth":1},{"value":"Overview","id":"overview","depth":2},{"value":"Before you start","id":"before-you-start","depth":2},{"value":"Step 1: Install the Android SDK","id":"step-1-install-the-android-sdk","depth":2},{"value":"Step 2: Create a session on your backend","id":"step-2-create-a-session-on-your-backend","depth":2},{"value":"Session response","id":"session-response","depth":3},{"value":"Step 3: Initialise the SDK","id":"step-3-initialise-the-sdk","depth":2},{"value":"Shopper data","id":"shopper-data","depth":3},{"value":"Step 4: Create the Aeropay button","id":"step-4-create-the-aeropay-button","depth":2},{"value":"Step 5: Render the button","id":"step-5-render-the-button","depth":2},{"value":"Step 6: Verify the transaction on your backend","id":"step-6-verify-the-transaction-on-your-backend","depth":2},{"value":"Manage post-transaction operations","id":"manage-post-transaction-operations","depth":2},{"value":"New-shopper flow","id":"new-shopper-flow","depth":2},{"value":"Skip consumer data collection","id":"skip-consumer-data-collection","depth":3},{"value":"Returning-shopper flow","id":"returning-shopper-flow","depth":2},{"value":"Payout flow","id":"payout-flow","depth":2},{"value":"Control the component","id":"control-the-component","depth":2},{"value":"Complete Compose example","id":"complete-compose-example","depth":2}],"frontmatter":{"seo":{"title":"Implementation"}},"lastModified":"2026-08-13T10:57:11.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/guides/checkout/components/android/aeropay/implementation","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}