# Troubleshooting

Learn how to diagnose and fix common issues with the Paze Android component.

## Exception types and error codes

### Component exceptions

The Paze Android component surfaces `BaseSdkException` errors with structured codes in the `SDK12XX` range. The following table maps common exception types to their error codes:

| Exception  | Error code | Description |
|  --- | --- | --- |
| `PazeCheckoutLaunchException` / `PazeLoadFailedException` | `SDK1200` | Failed to launch Paze checkout. For example, Custom Tab launch failed. |
| `PazeConfigurationValidationFailedException`(client ID) | `SDK1202` | Missing `session.allowedFundingTypes.wallets.paze.clientId`. |
| `PazeMerchantCategoryCodeRequiredException` | `SDK1202A` | Missing `session.allowedFundingTypes.wallets.paze.merchantCategoryCode` at complete. |
| `PazeConfigurationValidationFailedException`(client name) | `SDK1203` | `clientName` exceeds 50 characters when set. |
| `PazeConfigurationValidationFailedException`(brand name) | `SDK1204` | `siteName` exceeds 50 characters when set. |
| `PazeConfigurationValidationFailedException`(statement descriptor) | `SDK1205` | `paymentDescription` exceeds 25 characters when set. |
| `PazeInitializationFailedException` | `SDK1206` | Paze initialisation or create-session failed. |
| `PazeIdentityValidationFailedException`(email) | `SDK1209`-`SDK1210` | Email too long or invalid format. |
| `PazeIdentityValidationFailedException`(phone) | `SDK1211`-`SDK1212` | Phone too long or invalid US E.164 format. |
| `PazeConfigurationValidationFailedException`(currency) | `SDK1213` | Transaction currency is not `USD`. |
| `PazePaymentFailedException` | `SDK1214` | Generic Paze checkout or wallet flow failure. |
| `PazeCheckoutResponseMissingException` | `SDK1215` | Paze checkout did not return a response for a `COMPLETE` result. |
| `PazeCompleteResponseMissingException` | `SDK1216` | Paze complete did not return an expected response. |
| `PazeCompleteSecuredPayloadMissingException` | `SDK1219` | Paze complete response did not contain a `securedPayload`. |
| `PazeConfigurationValidationFailedException`(session ID) | `SDK1222` | Component `sessionId` exceeds 255 characters. |
| `PazeConfigurationValidationFailedException`(amount) | `SDK1229` | Transaction amount is 0 or negative at checkout. |
| `PazeInitializationFailedException` / `PazeProviderException`(shipping countries) | `SDK1231` | `acceptedShippingCountries` contains invalid ISO 3166-1 alpha-2 country codes. Typically returned by the create-session API at checkout. |
| `PazeInitializationFailedException` / `PazeProviderException`(cobrand) | `SDK1232` | A `cobrand` entry is missing a non-empty `cobrandName`. Typically returned by the create-session API at checkout. |
| `PazeDecryptFailedException` | `SDK1233` | Failed to decrypt the Paze secured payload. |
| `PazeTransactionFailedException` | `SDK1235` | Transaction submission failed. Also surfaced through `onSubmitError`. |


Shopper cancellation and closing the Custom Tab without completing checkout call `onCheckoutIncomplete` rather than throwing an exception. Missing Paze `clientId` surfaces as `SDK1202` at presentment. Authorisation submission failures (`SDK1235`) route to `onSubmitError`, not `onError`. Create-session and complete API provider errors may also surface through `PazeProviderException` or `PazeInitializationFailedException` with provider-specific codes — map these using `error.message` and your backend logs. Codes such as `SDK1231` and `SDK1232` are defined in the SDK but are typically returned by the create-session API rather than thrown locally at presentment.

## Error handling best practices

### Comprehensive error handler

Route `onError`, `onCheckoutIncomplete`, and `onSubmitError` to appropriate user messaging and logging:

```kotlin
config.onError = { error ->
    Log.e("Paze", "Error: ${error.errorCode} ${error.message}")

    when (error.errorCode) {
        "SDK1202", "SDK1202A" -> {
            showAlternativePaymentMethods()
            showMessage("Paze is not available for this order.")
        }
        "SDK1210", "SDK1212" -> showMessage("Please check your contact details.")
        "SDK1213" -> showMessage("Paze is only available for USD transactions.")
        "SDK1233" -> showMessage("Payment token could not be processed. Please try again.")
        "SDK1200", "SDK1214" -> showMessage("Paze checkout failed. Please try again.")
        else -> showMessage("Payment could not be completed. Please try again.")
    }
}

config.onCheckoutIncomplete = { result ->
    if (result.reason?.contains("cancel", ignoreCase = true) == true) {
        showMessage("Payment cancelled.")
    } else {
        showMessage("Payment was not completed. Please try again.")
    }
}

config.onSubmitError = { submitResult ->
    val failed = submitResult as? FailedSubmitResult
    Log.e("Paze", "Submit error: ${failed?.errorCode} ${failed?.errorReason}")
    showMessage("Payment could not be processed. Please try another method.")
}
```

### Logging for support

Include these fields when logging errors for PXP or Paze support:

```kotlin
fun logPazeError(error: BaseSdkException, context: Map<String, Any> = emptyMap()) {
    val payload = buildMap {
        put("errorCode", error.errorCode)
        put("errorMessage", error.message ?: "")
        put("timestamp", Instant.now().toString())
        putAll(context)
    }
    // Send to your logging or crash reporting service
    Log.e("Paze", "Support log: $payload")
}
```

## Portal and session setup

Use this section when Paze is not appearing in the Unity Portal, missing from your session response, or failing during presentment with `SDK1202`. For portal setup steps, see [Onboarding](/guides/checkout/components/android/paze/onboarding).

### Paze service not visible on site

**Symptom:** The Paze service doesn't appear in your site's Services tab.

**Solution:** Enable Paze at the merchant group level first ([Onboarding, Step 1](/guides/checkout/components/android/paze/onboarding#step-1-ensure-paze-is-enabled-at-the-merchant-group-level)), then return to the site configuration.

### Missing client ID in session

**Symptom:** Session response doesn't include `allowedFundingTypes.wallets.paze.clientId`.

**Solution:**

1. Verify the client ID is saved in **Paze Account Settings** for the correct site.
2. Create a new session after saving portal changes.
3. Confirm you are using the correct site name in your session request.


### SDK1202 at presentment

**Symptom:** Presentment fails with missing `clientId`.

**Solution:** Confirm `clientId` is in the session response from your backend and passed through `SessionConfig` when initialising the SDK. Re-create the session after saving portal changes. Non-USD currency surfaces separately as `SDK1213`; invalid identity fields surface as `SDK1210` or `SDK1212`.

## Troubleshooting common issues

### Paze button hidden or validation errors at presentment

The symptoms of this are:

- `onPresentmentResolved(false)` fires alongside `onError`.
- The button is hidden in your Compose UI after you render `Content()`.
- Checkout fails immediately on tap when presentment succeeded but checkout-time validation fails.


#### Diagnostic steps

Log session and component configuration when presentment fails:

```kotlin
fun diagnosePazePresentment(sdkConfig: PxpSdkConfig, config: PazeButtonComponentConfig) {
    Log.d("Paze", "=== Presentment Diagnostics ===")
    val paze = sdkConfig.session.allowedFundingTypes?.wallets?.paze
    Log.d("Paze", "Client ID: ${if (paze?.clientId.isNullOrBlank()) "Missing" else "Present"}")
    Log.d("Paze", "MCC: ${paze?.merchantCategoryCode ?: "Not set"}")
    Log.d("Paze", "Currency: ${sdkConfig.transactionData.currency}")
    Log.d("Paze", "Email: ${config.emailAddress ?: "Not set"}")
    Log.d("Paze", "Phone: ${config.phoneNumber ?: "Not set"}")
}
```

#### Common causes

These presentment and checkout validation issues map to the most frequent error codes:

| Cause | Error code | Solution |
|  --- | --- | --- |
| Missing `clientId` in session | `SDK1202` | Configure Paze Account Settings and re-create session |
| Non-USD currency | `SDK1213` | Set `transactionData.currency` to `"USD"` |
| Invalid email or phone | `SDK1210`, `SDK1212` | Fix format; see [Data validation](/guides/checkout/components/android/paze/data-validation#identity-validation-rules) |
| `clientName` or `siteName` too long when set | `SDK1203`, `SDK1204` | Shorten to 50 characters or fewer |
| Statement descriptor too long when set | `SDK1205` | Shorten `paymentDescription` to 25 characters or fewer |
| Amount ≤ 0 at checkout | `SDK1229` | Set a positive `transactionData.amount` |
| Invalid shipping country codes | `SDK1231` | Use valid ISO 3166-1 alpha-2 codes (for example `US`, `CA`); usually returned at create-session |
| Empty cobrand name | `SDK1232` | Set a non-empty `cobrandName` on every `PazeCobrandConfig` entry; usually returned at create-session |
| No Custom Tab browser | `onPresentmentResolved(false)` with button visible | Install Chrome or another Custom Tabs-compatible browser |


When presentment succeeds but no Custom Tabs-compatible browser is installed, the button remains visible and `onPresentmentResolved(false)` fires. Use the callback to show warnings or fallback payment methods.

### Checkout fails or gets cancelled

The symptoms of this are:

- The shopper taps the Paze button but checkout doesn't complete.
- `onCheckoutIncomplete` fires unexpectedly.
- The Custom Tab opens but the shopper cannot complete payment.


#### Solutions

**Currency validation:** Confirm `transactionData.currency` is `"USD"` before showing the Paze section.

**User cancellation:** When the shopper closes the Custom Tab, expect `onCheckoutIncomplete`:

```kotlin
config.onCheckoutIncomplete = { result ->
    if (result.reason?.contains("cancel", ignoreCase = true) == true) {
        showMessage("Payment cancelled. You can try again when ready.")
    } else {
        showMessage("Unable to complete checkout. Please try a different payment method.")
    }
}
```

**Tab closed without redirect:** If the shopper dismisses the Custom Tab without a redirect callback, `onCheckoutIncomplete` fires when the host Activity resumes.

**Merchant rejection:** Returning `false` from `onCheckoutComplete` triggers `onCheckoutIncomplete`.

**Pre-checkout block:** Disable the button in your UI until prerequisites are met. `onPazeButtonClicked` cannot cancel checkout — the SDK opens the Custom Tab when that callback completes.

**UAT wallet access:** In UAT, confirm the test shopper email or phone is provisioned for your Paze merchant account.

### App doesn't return from checkout

The symptoms of this are:

- The Paze Custom Tab completes but your app doesn't process the result.
- Checkout hangs after the shopper finishes in the browser.
- `onCheckoutComplete` never fires.


#### Diagnostic steps

Verify your host Activity handles redirects:

```kotlin
import com.pxp.checkout.components.paze.payment.deliverPazeCheckoutResult

override fun onNewIntent(intent: Intent) {
    super.onNewIntent(intent)
    setIntent(intent)
    intent.deliverPazeCheckoutResult(pazeButton)
}
```

Confirm checkout was launched from an Activity context so `PazeRedirectRegistry` could register the host.

Common redirect handling issues and fixes:

| Issue | Solution |
|  --- | --- |
| `onNewIntent` not implemented | Add `deliverPazeCheckoutResult` handler |
| `setIntent(intent)` omitted | Call `setIntent` before delivering the result |
| Non-Activity context | Create `PxpCheckout` with an Activity `Context` |
| Custom scheme mismatch | Register custom scheme via `PazeRedirectRegistry` and matching intent filter |


The default `pxpcheckout://callback` and `pxpcheckout://paze` schemes are handled by the SDK-merged `PazeSdkRedirectActivity`. Your host Activity still must receive the forwarded intent and call `deliverPazeCheckoutResult`.

### Complete or MCC errors

The symptoms of this are:

- Checkout completes but the flow fails before decryption.
- `onError` fires with `SDK1202A`.


**Solution:** Include `merchantCategoryCode` in the session response from your backend. The SDK requires it when building the Paze complete request.

### Token decryption fails

The symptoms of this are:

- Checkout completes but payment doesn't process.
- `onPostDecryption` never fires.
- `onError` fires with `SDK1233`.


`onPostDecryption` does not run when `onPreDecryption` returns `false` (merchant-managed decryption). That is expected — use `onComplete` to receive the `securedPayload` instead. See [Events](/guides/checkout/components/android/paze/events#oncomplete).

#### Solutions

Common decryption failure causes:

| Cause | Solution |
|  --- | --- |
| Invalid or expired session | Create a fresh session and retry. |
| `securedPayload` missing | Check complete response; verify Paze wallet settings in the Unity Portal. |
| Manual decryption path | Return `false` from `onPreDecryption` and decrypt in `onComplete`; `onPostDecryption` is skipped by design |
| Manual decryption misconfigured | Ensure `onPreDecryption` returns `false` and backend decrypts the payload from `onComplete` |
| Network error during decrypt | Retry; check device connectivity. |


Decryption failures surface through `onError` with `SDK1233`. See [Analytics](/guides/checkout/components/android/paze/analytics) for `PazeDecryptionFailed` events.

### Authorisation fails after decryption

The symptoms of this are:

- `onSubmitError` fires with `FailedSubmitResult`.
- `onPostAuthorisation` may still fire with transaction IDs when Unity returns an error state and the callback is registered — always verify the outcome on your backend.


#### Solutions

Log submission failures with correlation IDs for support:

```kotlin
config.onSubmitError = { result ->
    val failed = result as? FailedSubmitResult
    Log.e("Paze", "Submit failed: ${failed?.errorCode} ${failed?.errorReason}")
    Log.e("Paze", "Correlation ID: ${failed?.correlationId}")
}
```

Common authorisation submission failure causes:

| Cause | Solution |
|  --- | --- |
| Missing shopper data | Implement `onGetShopper` on `PxpSdkConfig` with a valid shopper `id`, or set `transactionData.shopper` |
| Invalid Kount data | Fix `riskScreeningData` in `onPreAuthorisation` |
| Transaction declined | Check PXP transaction response; verify amount and merchant setup |
| Missing `entryType` | Set `transactionData.entryType` to `EntryType.Ecom` when initialising the SDK. The standalone button does not validate this at presentment |


Hard submission failures surface through `onSubmitError`.

### onPostAuthorisation behaviour

**Symptom:** You expected a successful payment but the transaction outcome is unclear, or the callback did not run.

**Solution:** Register `onPostAuthorisation` on the component config — the SDK only invokes it when the callback is set. The SDK calls it when it receives a `MerchantSubmitResult` with `merchantTransactionId` and `systemTransactionId`. It does not run on the merchant-managed decryption path (`onPreDecryption` returns `false`). Verify the payment outcome on your backend — Unity may return an error state while still providing transaction identifiers. Hard submission failures route to `onSubmitError` instead.

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