Learn how to test your Paze integration effectively before going live.
Thorough testing is essential to ensure your Paze integration works correctly across presentment, checkout, decryption, and error scenarios on Android. This guide covers test environments, configuration, test scenarios, and debugging techniques.
Always test your integration in the sandbox environment before deploying to production. Use test credentials and Paze UAT wallets to avoid processing real payments during development.
Initialise the SDK with test environment settings, USD transaction data, and a test shopper callback:
val sessionData = SessionConfig(
sessionId = session.sessionId,
hmacKey = session.hmacKey,
data = session.data,
encryptionKey = session.encryptionKey,
locale = "en-US",
allowedFundingTypes = AllowedFundingTypes(
wallets = WalletsConfig(
paze = PazeWalletConfig(
clientId = session.pazeClientId,
profileId = session.pazeProfileId,
merchantCategoryCode = session.merchantCategoryCode,
),
),
),
)
val sdkConfig = PxpSdkConfig(
environment = Environment.TEST,
session = sessionData,
transactionData = TransactionData(
amount = 10.00,
currency = "USD",
merchant = "MERCHANT-1",
intent = TransactionIntentData(card = CardIntentType.Authorisation),
merchantTransactionId = "test-${UUID.randomUUID()}",
merchantTransactionDate = { Instant.now().toString() },
entryType = EntryType.Ecom,
),
clientName = "Test Merchant",
siteName = "Test Store",
ownerType = "MerchantGroup",
ownerId = "MERCHANT-1",
kountDisabled = true,
onGetShopper = {
Shopper(
id = "test-shopper-1",
email = "test@example.com",
firstName = "Test",
lastName = "Shopper",
)
},
)
val checkout = PxpCheckout.builder()
.withConfig(sdkConfig)
.withContext(context)
.build()Use Environment.TEST for UAT and Environment.LIVE for production:
| Environment | SDK setting | Purpose |
|---|---|---|
| Test | Environment.TEST | Development and UAT testing |
| Production | Environment.LIVE | Live payments |
Never use production Paze credentials in development. Test and production environments use separate client IDs and API endpoints.
To test checkout in UAT:
- Use UAT credentials end-to-end: Portal Paze client ID on your site,
Environment.TESTin the SDK, and sessions created against the UAT API (api-services.dev.pxp.io). - Register test shoppers with Paze: Use email and phone combinations that Paze has provisioned for your UAT merchant account. Contact your Paze integration contact if you do not have UAT shopper credentials.
- Install Chrome: Custom Tab checkout requires a Custom Tabs–compatible browser. Chrome is recommended for UAT testing.
- Test on a physical device: Redirect handling and Custom Tab behaviour are most reliable on device.
The button appears after presentment validation succeeds when you render Content(). Optional emailAddress and phoneNumber are validated when provided and forwarded to create-session when set.
Create a Paze button with logging callbacks on each stage of the payment flow:
val config = PazeButtonComponentConfig().apply {
emailAddress = "test.shopper@example.com"
phoneNumber = "15551234567"
style = PazeButtonStyleConfig(
color = PazeButtonColor.PAZE_BLUE,
shape = PazeButtonShape.PILL,
label = PazeButtonLabel.CHECKOUT_WITH,
)
onInit = { Log.d("TEST", "Paze initialised") }
onPresentmentResolved = { customTabAvailable -> Log.d("TEST", "Custom Tab available: $customTabAvailable") }
onPazeButtonClicked = { Log.d("TEST", "Button clicked") }
onCheckoutComplete = { result ->
Log.d("TEST", "Checkout complete: $result")
true
}
onComplete = { result ->
Log.d("TEST", "Complete payload ID: ${result.completeDecodedResponse?.payloadId}")
}
onPostDecryption = { payload ->
Log.d("TEST", "Card network: ${payload.fundingData.cardNetwork}")
}
onPostAuthorisation = { result ->
Log.d("TEST", "Authorisation: ${result.systemTransactionId}")
}
onError = { error ->
Log.e("TEST", "Error: ${error.errorCode} ${error.message}")
}
}
val pazeButton = checkout.createComponent<PazeButtonComponent>(ComponentType.PAZE_BUTTON, config)Render the component in your test screen. Presentment runs when Content() is composed:
pazeButton.Content(modifier = Modifier.fillMaxWidth())Ensure your host Activity forwards checkout redirects to the component:
import com.pxp.checkout.components.paze.payment.deliverPazeCheckoutResult
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
setIntent(intent)
intent.deliverPazeCheckoutResult(pazeButton)
}After presentment validation succeeds, onInit fires, the button is shown, and onPresentmentResolved reports Custom Tab availability. Presentment runs when you render Content().
Log presentment resolution during manual testing:
config.onPresentmentResolved = { customTabAvailable ->
Log.d("TEST", "Presentment resolved: customTabAvailable=$customTabAvailable")
}Test presentment failure by initialising with a non-USD currency:
// transactionData.currency = "GBP"
// Expected: SDK1213 via onError; button hiddenTest invalid identity:
config.emailAddress = "not-an-email"
// Expected: SDK1210 via onError at presentmentReturn true from onCheckoutComplete to exercise the full happy path:
config.onCheckoutComplete = { result ->
Log.d("TEST", "Session: ${result.checkoutDecodedResponse?.sessionId}")
Log.d("TEST", "Card brand: ${result.checkoutDecodedResponse?.maskedCard?.paymentCardBrand}")
true
}Return false from onCheckoutComplete to confirm the SDK treats checkout as incomplete:
config.onCheckoutComplete = { _ -> false }
config.onCheckoutIncomplete = { result ->
Log.d("TEST", "Expected incomplete: ${result.reason}")
}Disable the button in your UI until prerequisites are met. onPazeButtonClicked cannot cancel checkout — use it only for logging or other non-blocking side effects.
Close the Custom Tab without completing checkout. Expect onCheckoutIncomplete with a cancellation reason.
Closing the Custom Tab without a redirect (for example, using the back gesture) also triggers onCheckoutIncomplete when the host Activity resumes.
Return true from onPreDecryption (or omit the callback) to test the default SDK decryption path:
config.onPreDecryption = { true }
config.onPostDecryption = { payload ->
Log.d("TEST", "Network token present: ${payload.fundingData.networkToken.isNotEmpty()}")
Log.d("TEST", "Card network: ${payload.fundingData.cardNetwork}")
}Return false from onPreDecryption and handle securedPayload in onComplete. Your test backend should call the PXP decrypt-token API. The SDK skips onPostDecryption, onPreAuthorisation, onPostAuthorisation, and onSubmitError on this path. See Backend decryption.
Exercise common validation and configuration failures:
// Invalid currency — fails at presentment
// transactionData.currency = "GBP" → SDK1213
// Invalid phone format (contains +)
config.phoneNumber = "+15551234567"
// Expected: SDK1212 at presentment or checkout
// Missing clientId in session
// Omit paze.clientId from session → SDK1202 at presentment
// Missing merchantCategoryCode in session
// Omit merchantCategoryCode → SDK1202A at completeLog each callback during manual testing:
config.onInit = { Log.d("Paze", "onInit") }
config.onPresentmentResolved = { Log.d("Paze", "onPresentmentResolved: $it") }
config.onPazeButtonClicked = { Log.d("Paze", "onPazeButtonClicked") }
config.onCheckoutComplete = { Log.d("Paze", "onCheckoutComplete: $it"); true }
config.onCheckoutIncomplete = { Log.d("Paze", "onCheckoutIncomplete: ${it.reason}") }
config.onComplete = { Log.d("Paze", "onComplete") }
config.onPreDecryption = { Log.d("Paze", "onPreDecryption"); true }
config.onPostDecryption = { Log.d("Paze", "onPostDecryption") }
config.onPreAuthorisation = { Log.d("Paze", "onPreAuthorisation"); PazePreAuthorisationResult.Proceed }
config.onPostAuthorisation = { Log.d("Paze", "onPostAuthorisation: ${it.systemTransactionId}") }
config.onSubmitError = { Log.d("Paze", "onSubmitError: $it") }
config.onError = { Log.e("Paze", "onError: ${it.errorCode} ${it.message}") }Log analytics events from PxpSdkConfig. See Analytics for the full Paze* event set:
import com.pxp.checkout.analytics.PazeAnalyticsEvent
import com.pxp.checkout.analytics.ComponentLifecycleAnalyticsEvent
analyticsEvent = { event ->
Log.d("Paze", "Analytics: ${event.eventName}")
when (event) {
is PazeAnalyticsEvent -> {
Log.d("Paze", " componentId=${event.componentId} merchantTransactionId=${event.merchantTransactionId}")
}
is ComponentLifecycleAnalyticsEvent -> {
Log.d("Paze", " lifecycle=${event.eventType} componentId=${event.componentId}")
}
}
}Track these events and callbacks during a full payment test:
| Event/callback | When it fires |
|---|---|
ComponentLifecycleAnalyticsEvent(MOUNT) | Presentment validation succeeded (Content() composed). Event name is ComponentLifecycleEvent. |
PazeButtonRendered | Presentment succeeded. |
onPresentmentResolved | Custom Tab availability resolved. |
onPazeButtonClicked | After checkout validation, before the Custom Tab opens. |
PazeButtonClicked | Immediately after onPazeButtonClicked, before the Custom Tab opens. |
onCheckoutComplete | Checkout returned COMPLETE. |
onCheckoutIncomplete | Checkout incomplete, cancelled, tab closed without redirect, or merchant rejected. |
PazeCheckoutCompleted | Merchant approved checkout. |
onPostDecryption | SDK decryption succeeded. |
onPostAuthorisation | SDK received MerchantSubmitResult (when the callback is registered). Verify the outcome on your backend. |
Run these checks when checkout fails to return to your app:
import com.pxp.checkout.components.paze.payment.CustomTabLauncher
fun diagnosePaze(sdkConfig: PxpSdkConfig, config: PazeButtonComponentConfig, context: Context) {
Log.d("Paze", "=== Diagnostics ===")
Log.d("Paze", "Environment: ${sdkConfig.environment}")
Log.d("Paze", "Currency: ${sdkConfig.transactionData.currency}")
Log.d("Paze", "Amount: ${sdkConfig.transactionData.amount}")
Log.d("Paze", "Client ID: ${sdkConfig.session.allowedFundingTypes?.wallets?.paze?.clientId?.isNotBlank()}")
Log.d("Paze", "MCC: ${sdkConfig.session.allowedFundingTypes?.wallets?.paze?.merchantCategoryCode ?: "not set"}")
Log.d("Paze", "Email: ${config.emailAddress ?: "not set"}")
Log.d("Paze", "Phone: ${config.phoneNumber ?: "not set"}")
Log.d("Paze", "Custom Tab: ${CustomTabLauncher.canLaunchCustomTab(context)}")
}Before going live, verify each scenario in UAT:
| Scenario | Expected outcome |
|---|---|
| Portal Paze client ID saved | Session includes allowedFundingTypes.wallets.paze.clientId |
| MCC in session | merchantCategoryCode present; missing value throws SDK1202A at complete. |
| USD transaction | Presentment succeeds; non-USD throws SDK1213. |
| Static presentment | Button renders after validation when Content() is composed; email/phone optional. |
| Custom Tab checkout | Paze wallet opens; shopper can complete or cancel. |
| Redirect handling | onNewIntent + deliverPazeCheckoutResult processes pxpcheckout://callback or pxpcheckout://paze redirects. |
| Approve checkout | Full SDK-managed flow through onPostDecryption and onPostAuthorisation (when registered). |
| Reject checkout | onCheckoutIncomplete fires. |
| Pre-checkout block | Button disabled in UI until prerequisites are met (onPazeButtonClicked cannot cancel checkout). |
| SDK decryption | onPostDecryption receives token data. |
| Manual decryption | securedPayload forwarded to backend; your backend completes decryption and authorisation. |
| Authorisation failure | onSubmitError with FailedSubmitResult. |
| Backend verification | Order fulfilled only after server-side transaction check. |
| Live credentials | Environment.LIVE with production portal settings. |