# Events

Implement callbacks to customise your Paze payment flow for Android.

## Overview

The Paze component emits callbacks based on shopper interaction and payment flow state. Use them to inject business logic and UX customisations at critical moments. While the SDK handles checkout, decryption, and authorisation, you retain control over approval gates, error handling, and backend integration.

Callbacks enable you to:

* Validate business rules before payments proceed.
* Display custom error, failure, or success messages.
* Integrate with fraud detection or customer management systems.
* Control how shoppers experience successful and failed transactions.
* Handle Paze Custom Tab checkout and decryption flow requirements.


## SDK data callbacks

Configure these callbacks on `PxpSdkConfig`, not on the Paze button component. The Paze button calls `onGetShopper` when building the Unity transaction request after decryption.

* `onGetShopper` (recommended): Called during authorisation to retrieve shopper data for the transaction payload.


### onGetShopper

The SDK calls this callback when building the transaction request, after decryption and before Unity authorisation. If `onGetShopper` is not set, the SDK uses `transactionData.shopper` when present.

#### Configuration

```kotlin
val sdkConfig = PxpSdkConfig(
    environment = Environment.TEST,
    session = sessionData,
    transactionData = transactionData,
    clientName = "Your Merchant",
    siteName = "Your Store",
    ownerType = "MerchantGroup",
    ownerId = "MERCHANT-1",
    onGetShopper = {
        Shopper(
            id = "shopper-123",
            firstName = "John",
            lastName = "Doe",
            email = "customer@example.com",
        )
    },
)
```

#### Return value

| Property | Description |
|  --- | --- |
| `id`String? | A unique shopper identifier. Return your customer account ID or a persistent guest session ID. |
| `firstName`String? | The shopper's given name. |
| `lastName`String? | The shopper's family name. |
| `email`String? | The shopper's email address. |
| `phoneNumber`String? | The contact phone number for the transaction shopper profile. |
| `dateOfBirth`String? | The date of birth, when required by your integration or risk screening rules. |
| `houseNumberOrName`String? | The first line of the shopper's address. |
| `street`String? | The street name or remaining address line. |
| `city`String? | The city or locality. |
| `postalCode`String? | The postal or ZIP code. |
| `countryCode`String? | The country code in ISO 3166-1 alpha-2 format (e.g., `"US"`). |
| `state`String? | The state, province, or region. |


All properties are optional in the type, but you should always return an `id` so transactions can be linked to your customer records.

Paze wallet identity (`emailAddress` / `phoneNumber` on the component) is separate from `onGetShopper`. Component identity is forwarded to Paze during create-session when provided. `onGetShopper` supplies the shopper object sent to Unity during authorisation.

### Shipping countries

For shipping constraints on Paze checkout, set `acceptedShippingCountries` on the component config. See [Configuration](/guides/checkout/components/android/paze/configuration).

## Paze-specific events

Configure these callbacks on `PazeButtonComponentConfig` before calling `createComponent<PazeButtonComponent>(...)`.

### Callback order

For a successful payment, callbacks fire in this order:

1. `onInit` → `onPresentmentResolved` after presentment validation succeeds, when the component `Content()` is composed.
2. `onPazeButtonClicked` when the shopper taps the button (after checkout validation, before the Custom Tab opens).
3. `onCheckoutComplete` or `onCheckoutIncomplete` after the Custom Tab returns.
4. `onComplete` after the complete API succeeds and the response is decoded.
5. `onPreDecryption` → `onPostDecryption` (if SDK decryption proceeds).
6. `onPreAuthorisation` → Unity authorisation (the SDK calls `onGetShopper` or `transactionData.shopper` here) → `onPostAuthorisation` (if registered) or `onSubmitError`.


`onError` handles presentment, checkout, complete, and decryption errors. `onSubmitError` handles authorisation submission failures, including invalid Kount `riskScreeningData`. Shopper cancellation is surfaced through `onCheckoutIncomplete`, not `onError`.

When `onPreDecryption` returns `false`, the SDK skips steps 5–6. Use `onComplete` to receive the `securedPayload` for backend decryption instead.

SDK initialisation callbacks are configured on `PxpSdkConfig`. The Paze button calls `onGetShopper` during authorisation. Paze component callbacks handle checkout, decryption, and authorisation events. Several Paze callbacks are `suspend` functions and can perform asynchronous work before the SDK continues.

### onInit

Called after presentment validation succeeds, immediately before `onPresentmentResolved` on success.

```kotlin
config.onInit = {
    Log.d("Paze", "Component ready")
}
```

### onPresentmentResolved

Called when presentment resolves.

| Parameter | Description |
|  --- | --- |
| `customTabAvailable`Boolean | When presentment succeeds, `true` when a Custom Tab can launch; `false` when no compatible browser is installed (the button remains visible). When presentment validation fails, the button is hidden and this callback receives `false` (see `onError`). |


```kotlin
config.onPresentmentResolved = { customTabAvailable ->
    if (!customTabAvailable) {
        showCustomTabWarning()
    }
}
```

### onPazeButtonClicked

Called when the shopper taps the button, after checkout validation succeeds and before the Custom Tab opens. Checkout validation failures surface through `onError`, not this callback. Use it for analytics, clearing error messages, or showing a loading state.

`onPazeButtonClicked` is a component callback. The `PazeButtonClicked` analytics event is separate — it fires after `onPazeButtonClicked` completes and immediately before the Custom Tab opens. See [Analytics](/guides/checkout/components/android/paze/analytics#when-each-event-fires).

```kotlin
config.onPazeButtonClicked = {
    clearErrorMessages()
}
```

The SDK opens the Custom Tab after this callback completes. There is no return value — you cannot cancel checkout from this callback. Disable the button in your UI until prerequisites are met. To block after the shopper returns from Paze, return `false` from `onCheckoutComplete`.

### onCheckoutComplete

Called when checkout returns `COMPLETE`, before the SDK calls the complete API.

Return `true` to approve and continue (default when omitted). Return `false` to reject; the SDK calls `onCheckoutIncomplete`.

| Parameter | Description |
|  --- | --- |
| `result`PazeCheckoutResult | The checkout result data. |
| `result.result`PazeCheckoutResultType | The checkout outcome from Paze. In this callback, the value is always `COMPLETE`, meaning the shopper finished checkout in the Custom Tab and Paze returned review data. `INCOMPLETE` is delivered through `onCheckoutIncomplete` instead. |
| `result.checkoutDecodedResponse`PazeCheckoutReviewData? | Decoded session and masked card data when complete. |
| `result.checkoutDecodedResponse?.maskedCard?.panLastFour`String | The last four digits of the selected card. |
| `result.checkoutDecodedResponse?.maskedCard?.paymentCardBrand`String | The card brand returned by Paze (for example, `VISA`). |
| `result.reason`String? | An optional reason from Paze. |


```kotlin
config.onCheckoutComplete = { result ->
    val decoded = result.checkoutDecodedResponse ?: return@onCheckoutComplete false
    validateCheckoutOnBackend(
        sessionId = decoded.sessionId,
        cardLastFour = decoded.maskedCard.panLastFour,
    )
}
```

### onCheckoutIncomplete

Called when checkout ends without completion, the shopper cancels, the Custom Tab closes without a redirect, or you return `false` from `onCheckoutComplete`. When you reject in `onCheckoutComplete`, the reason is set from the SDK's localised merchant-validation message.

```kotlin
config.onCheckoutIncomplete = { result ->
    if (result.reason?.contains("cancel", ignoreCase = true) == true) {
        return@onCheckoutIncomplete
    }
    showError("Checkout was not completed. Please try again.")
}
```

### onComplete

Called when the complete API succeeds and the response is decoded. Runs before `onPreDecryption`.

Use `result.completeDecodedResponse?.securedPayload` for manual decryption when `onPreDecryption` returns `false`.

```kotlin
config.onComplete = { result ->
    Log.d("Paze", "Payload ID: ${result.completeDecodedResponse?.payloadId}")
}
```

### onPreDecryption

Called after `onComplete`, before the SDK decrypts the secured payload.

Return `true` to allow SDK decryption (default when omitted). Return `false` to handle decryption on your backend — the SDK skips decrypt and authorisation; use `onComplete` to forward the `securedPayload`.

**SDK-managed (recommended):**

```kotlin
config.onPreDecryption = { true }
config.onPostDecryption = { decryptedPayload ->
    val masked = decryptedPayload.fundingData.networkToken.take(6) + "..."
    Log.d("Paze", "Decrypted token prefix: $masked")
}
```

**Merchant-managed:**

```kotlin
config.onPreDecryption = { false }
config.onComplete = { result ->
    val securedPayload = result.completeDecodedResponse?.securedPayload ?: return@onComplete
    sendToBackendForDecryption(securedPayload)
}
```

See [Backend decryption](/guides/checkout/components/android/paze/implementation#backend-decryption) for PXP API endpoints.

### onPostDecryption

Called after Unity successfully decrypts the network token. Skipped when `onPreDecryption` returns `false`.

| Parameter | Description |
|  --- | --- |
| `decryptedPayload.shopper`PazeDecryptTokenShopper | Customer name, email, and phone from the decrypt response. |
| `decryptedPayload.billingAddress`PazeDecryptTokenBillingAddress? | Billing address when returned by Paze. |
| `decryptedPayload.fundingData`PazeDecryptTokenFundingData | Network token, expiry, card network, and related fields. |


```kotlin
config.onPostDecryption = { decrypted ->
    Log.d("Paze", "Card network: ${decrypted.fundingData.cardNetwork}")
    val maskedToken = decrypted.fundingData.networkToken.take(6) + "..."
    Log.d("Paze", "Token prefix: $maskedToken")
}
```

### onPreAuthorisation

Called before the transaction is submitted to Unity.

| Return value | Behaviour |
|  --- | --- |
| `Proceed` | Continue without extra data (default when omitted). |
| `Cancel` | Abort authorisation silently. The SDK does not call `onSubmitError`. |
| `TransactionInitData(...)` | Continue with risk screening or other init data. |


```kotlin
config.onPreAuthorisation = {
    if (!validateOrderDetails()) {
        PazePreAuthorisationResult.Cancel
    } else {
        PazePreAuthorisationResult.TransactionInitData(
            PazeTransactionInitData(
                riskScreeningData = buildRiskScreeningData(),
            ),
        )
    }
}
```

### onPostAuthorisation

Called when the SDK receives a `MerchantSubmitResult`. Only invoked when this callback is registered on the config. Verify the transaction outcome on your backend — the Unity response may report an error state while still returning transaction identifiers. Hard submission failures route to `onSubmitError`, not this callback.

| Parameter | Description |
|  --- | --- |
| `result.merchantTransactionId`String | Your transaction identifier. |
| `result.systemTransactionId`String | The system transaction identifier generated by PXP. |


```kotlin
config.onPostAuthorisation = { result ->
    verifyPaymentOnBackend(systemTransactionId = result.systemTransactionId)
}
```

### onSubmitError

Called when authorisation submission fails, including invalid Kount `riskScreeningData` (`ValidationException`). Cast to `FailedSubmitResult` for error details.

```kotlin
config.onSubmitError = { result ->
    val failed = result as? FailedSubmitResult
    showError(failed?.errorReason ?: "Payment failed")
}
```

### onError

Called on presentment, checkout, complete, or decryption errors. Shopper cancellation is surfaced through `onCheckoutIncomplete`, not `onError`.

```kotlin
config.onError = { error ->
    when (error.errorCode) {
        "SDK1202", "SDK1202A" -> showError("Paze is not configured for this session.")
        "SDK1210", "SDK1212" -> showError("Please check your contact details and try again.")
        "SDK1213" -> showError("Paze is only available for USD transactions.")
        "SDK1233" -> showError("Token decryption failed. Please try again.")
        "SDK1200", "SDK1214" -> showError("Paze checkout failed. Please try again.")
        else -> showError(error.message ?: "Payment could not be completed.")
    }
}
```

## Event data structures

### Shopper

Returned by `onGetShopper`:

```kotlin
data class Shopper(
    val id: String? = null,
    val firstName: String? = null,
    val lastName: String? = null,
    val email: String? = null,
    val phoneNumber: String? = null,
    val dateOfBirth: String? = null,
    // address fields...
)
```

### PazeCheckoutResult

Passed to `onCheckoutComplete` and `onCheckoutIncomplete`:

```kotlin
data class PazeCheckoutResult(
    val result: PazeCheckoutResultType,
    val reason: String?,
    val checkoutDecodedResponse: PazeCheckoutReviewData?,
)

data class PazeCheckoutReviewData(
    val sessionId: String?,
    val maskedCard: PazeMaskedCard,
)
```

### PazeMaskedCard

Included on `checkoutDecodedResponse` when checkout completes:

```kotlin
data class PazeMaskedCard(
    val digitalCardId: String?,
    val panLastFour: String,
    val paymentAccountReference: String?,
    val panExpirationMonth: String?,
    val panExpirationYear: String?,
    val paymentCardDescriptor: String,
    val paymentCardType: String,
    val paymentCardBrand: String,
    val paymentCardNetwork: String,
    val digitalCardData: PazeDigitalCardData,
    val billingAddress: PazeCheckoutAddress?,
)
```

### PazeCompleteResult

Passed to `onComplete`:

```kotlin
data class PazeCompleteResult(
    val completeDecodedResponse: PazeCompleteDecoded?,
    val decryptedPayload: PazeDecryptTokenResponseSuccess?,
)
```

At `onComplete`, `decryptedPayload` is always `null`. Decrypted token data is passed to `onPostDecryption` when SDK decryption proceeds.

```kotlin
data class PazeCompleteDecoded(
    val payloadId: String?,
    val sessionId: String?,
    val securedPayload: String?,
)
```

### PazeDecryptTokenResponseSuccess

Passed to `onPostDecryption`:

```kotlin
data class PazeDecryptTokenResponseSuccess(
    val shopper: PazeDecryptTokenShopper,
    val billingAddress: PazeDecryptTokenBillingAddress?,
    val fundingData: PazeDecryptTokenFundingData,
)

data class PazeDecryptTokenShopper(
    val firstName: String?,
    val lastName: String?,
    val fullName: String,
    val emailAddress: String,
    val phoneCountryCode: String?,
    val phoneNumber: String?,
    val countryCode: String?,
    val languageCode: String?,
)

data class PazeDecryptTokenBillingAddress(
    val addressLine1: String,
    val city: String,
    val state: String,
    val countryCode: String,
    val postalCode: String,
)

data class PazeDecryptTokenFundingData(
    val networkToken: String,
    val expirationMonth: String,
    val expirationYear: String,
    val accountReference: String?,
    val cardNetwork: String,
    val tokenUsageType: String?,
    val dynamicDataExpiration: String?,
)
```

### MerchantSubmitResult

Passed to `onPostAuthorisation` when the SDK receives the Unity authorisation response. Verify the outcome on your backend; hard failures route to `onSubmitError`.

```kotlin
data class MerchantSubmitResult(
    val merchantTransactionId: String,
    val systemTransactionId: String,
) : SubmitResult()
```

### FailedSubmitResult

Typical type received in `onSubmitError`:

```kotlin
data class FailedSubmitResult(
    val errorCode: String?,
    val errorReason: String?,
    val correlationId: String?,
    val httpStatusCode: Int?,
    val details: List<String>?,
)
```

### BaseSdkException

Passed to `onError`:

```kotlin
open class BaseSdkException(
    message: String,
    cause: Throwable? = null,
    val errorCode: String,
)
```