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 iOS. This guide covers test environments, configuration, test scenarios, and debugging techniques.
Always test your integration in the UAT 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:
let sessionData = SessionData(
sessionId: session.sessionId,
hmacKey: session.hmacKey,
encryptionKey: session.encryptionKey,
allowedFundingTypes: AllowedFundingType(
wallets: Wallets(
paze: Paze(
clientId: session.pazeClientId,
profileId: session.pazeProfileId,
merchantCategoryCode: session.merchantCategoryCode
)
)
)
)
let checkoutConfig = CheckoutConfig(
environment: .test,
session: sessionData,
transactionData: TransactionData(
amount: 10.00,
currency: "USD",
entryType: .ecom,
intent: TransactionIntentData(card: .authorisation),
merchantTransactionId: "test-\(UUID().uuidString)",
merchantTransactionDate: { Date() }
),
merchantShopperId: "test-shopper-1",
ownerId: "MERCHANT-GROUP-1",
clientName: "Test Merchant",
siteName: "Test Store",
kountDisabled: true,
onGetShopper: { async in
TransactionShopper(
id: "test-shopper-1",
email: "test@example.com",
firstName: "Test",
lastName: "Shopper"
)
}
)
let pxpCheckout = try PxpCheckout.initialize(config: checkoutConfig)The SDK maps portal environments to these settings:
| Environment | SDK setting | Purpose |
|---|---|---|
| UAT | .test | Development and UAT testing |
| Production | .live | Live payments |
Never use production Paze credentials in development. UAT and live 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 don't have UAT shopper credentials.
- Register your URL scheme: Confirm
pxpcheckout(or your customPXP_CALLBACK_SCHEME) is inInfo.plist. The default return URL ispxpcheckout://paze. - Test on a physical device:
ASWebAuthenticationSessionbehaviour is most reliable on device. Use the Simulator for presentment and validation testing.
The iOS component uses static presentment. The button appears after SDK validation without calling canCheckout(). This simplifies testing checkout and payment flows before you have UAT-eligible wallet identities.
Create a Paze button with logging callbacks on each stage of the payment flow:
let config = PazeButtonComponentConfig()
config.emailAddress = "test.shopper@example.com"
config.phoneNumber = "15551234567"
config.style = PazeButtonStyleConfig(color: .pazeBlue, shape: .pill, label: .checkoutWith)
config.onInit = { print("TEST: Paze initialised") }
config.onPresentmentResolved = { isVisible in print("TEST: Button visible:", isVisible) }
config.onPazeButtonClicked = { async in print("TEST: Button clicked") }
config.onCheckoutComplete = { result async in
print("TEST: Checkout complete:", result)
return true
}
config.onComplete = { result async in
print("TEST: Complete payload ID:", result.completeDecodedResponse?.payloadId ?? "")
}
config.onPostDecryption = { payload async in
print("TEST: Card network:", payload.fundingData.cardNetwork)
}
config.onPostAuthorisation = { data async in
print("TEST: Authorisation result:", data.systemTransactionId)
}
config.onError = { error in
print("TEST: Error:", error.errorCode, error.errorMessage)
}
let pazeButton = try pxpCheckout.create(.pazeButton, componentConfig: config)Display the component in your test view:
pazeButton.buildContent()After presentment validation succeeds, onInit fires, the button renders, and onPresentmentResolved(true) is called. If validation fails, the button stays hidden and onPresentmentResolved(false) is called followed by onError.
config.onPresentmentResolved = { isVisible in
print("Presentment resolved:", isVisible)
}Test presentment failure by initialising with a non-USD currency:
let invalidTransactionData = TransactionData(
amount: 10.00,
currency: "GBP",
entryType: .ecom,
intent: TransactionIntentData(card: .authorisation),
merchantTransactionId: "test-gbp",
merchantTransactionDate: { Date() }
)
// Expected: SDK1213 via onError, onPresentmentResolved(false)Return true from onCheckoutComplete to exercise the full happy path:
config.onCheckoutComplete = { result async in
print("Checkout data:", result.checkoutDecodedResponse?.sessionId ?? "")
print("Card brand:", result.checkoutDecodedResponse?.maskedCard?.paymentCardBrand ?? "")
return true
}Return false from onCheckoutComplete to confirm the SDK treats checkout as incomplete:
config.onCheckoutComplete = { _ async in false }
config.onCheckoutIncomplete = { result async in
print("Expected incomplete result:", result.reason ?? "")
// Merchant rejection reason from localised resources
}Dismiss the ASWebAuthenticationSession without completing checkout. Expect onCheckoutIncomplete with reason: "User cancelled.".
Return true from onPreDecryption (or omit the callback) to test the default SDK decryption path:
config.onPreDecryption = { async in true }
config.onPostDecryption = { payload async in
print("Network token present:", !payload.fundingData.networkToken.isEmpty)
print("Card network:", payload.fundingData.cardNetwork)
}Return false from onPreDecryption and handle securedPayload in onComplete. Your test backend should call the PXP decrypt-token API. See Backend decryption.
config.onPreDecryption = { async in false }
config.onComplete = { result async in
let securedPayload = result.completeDecodedResponse?.securedPayload
print("Secured payload received:", securedPayload != nil)
await sendToTestBackend(securedPayload: securedPayload)
}Use these patterns to trigger common validation failures:
// Invalid currency (fails at presentment)
// Set transactionData.currency to "GBP" → SDK1213
// Invalid phone format
config.phoneNumber = "+15551234567"
// Expected: SDK1212 at presentment or checkout
// Missing merchant category code in session
// Omit merchantCategoryCode from session → SDK1202A at complete (via onError)Log each callback during manual testing:
config.onInit = { print("[Paze] onInit") }
config.onPresentmentResolved = { v in print("[Paze] onPresentmentResolved:", v) }
config.onPazeButtonClicked = { async in print("[Paze] onPazeButtonClicked") }
config.onCheckoutComplete = { r async in print("[Paze] onCheckoutComplete:", r); return true }
config.onCheckoutIncomplete = { r async in print("[Paze] onCheckoutIncomplete:", r.reason ?? "") }
config.onComplete = { r async in print("[Paze] onComplete") }
config.onPreDecryption = { async in print("[Paze] onPreDecryption"); return true }
config.onPostDecryption = { r async in print("[Paze] onPostDecryption") }
config.onPreAuthorisation = { async in print("[Paze] onPreAuthorisation"); return .proceed }
config.onPostAuthorisation = { r async in print("[Paze] onPostAuthorisation:", r.systemTransactionId) }
config.onSubmitError = { e async in print("[Paze] onSubmitError:", e) }
config.onError = { e in print("[Paze] onError:", e.errorCode, e.errorMessage) }Log analytics events from CheckoutConfig to verify the payment funnel. For the full event list, see Analytics:
analyticsEvent: { event in
print("Analytics:", event.eventName, event)
}Key events to verify during testing:
| Event | When it fires |
|---|---|
PazeButtonRendered | The Paze button became visible. |
PazeButtonClicked | Checkout-time validation passes after the shopper taps the button, before the Paze web session opens. |
PazePopupLaunched | The Paze web checkout session opened. |
PazeCheckoutCompleted | Checkout returned COMPLETE and the merchant approved. |
PazeCheckoutAbandoned | Checkout was incomplete, the session was dismissed, or the merchant rejected checkout. |
PazeDecryptionInitiated | SDK decryption started. |
PazeDecryptionCompleted | SDK decryption succeeded. |
PazeTransactionInitiated | Transaction submission to PXP started. |
PostAuthorisation | onPostAuthorisation is configured and the SDK receives a MerchantSubmitResult, immediately before your callback runs. |
Button visibility isn't an analytics event. Track it with onPresentmentResolved.
Run these checks when the button doesn't appear or checkout fails to return to your app:
func diagnosePaze(checkoutConfig: CheckoutConfig) {
print("=== Paze Diagnostics ===")
print("Environment:", checkoutConfig.environment == .test ? "test" : "live")
print("Currency:", checkoutConfig.transactionData.currency)
print("Amount:", checkoutConfig.transactionData.amount)
let paze = checkoutConfig.session.allowedFundingTypes?.wallets?.paze
print("Client ID:", paze?.clientId?.isEmpty == false ? "Present" : "Missing")
print("MCC:", paze?.merchantCategoryCode?.isEmpty == false ? "Present" : "Missing")
if let urlTypes = Bundle.main.object(forInfoDictionaryKey: "CFBundleURLTypes") as? [[String: Any]],
let schemes = urlTypes.first?["CFBundleURLSchemes"] as? [String] {
print("URL schemes:", schemes)
} else {
print("URL schemes: NOT REGISTERED")
}
}Before going live, verify each scenario.
Confirm your session and SDK setup:
- Session includes
allowedFundingTypes.wallets.paze.clientId. - Session includes
merchantCategoryCode. - Currency is
USD. transactionData.entryTypeis.ecom(required for authorisation, not validated at presentment).- Amount is greater than 0.
onGetShopperreturns valid shopper data.- URL callback scheme is registered in
Info.plist.
Verify button visibility and presentment callbacks:
- Button renders after successful validation.
onInitandonPresentmentResolved(true)fire when the button is visible.- Validation failures call
onPresentmentResolved(false)andonError.
Exercise the web checkout flow:
- Button tap opens the Paze web checkout session.
- Successful checkout triggers
onCheckoutComplete. - Returning
falsefromonCheckoutCompletetriggersonCheckoutIncomplete. - Dismissing the web session triggers
onCheckoutIncompletewith user cancellation. - App returns via
pxpcheckout://paze(or your configured{scheme}://pazecallback).
Verify decryption and submission callbacks:
- SDK-managed decryption returns token data in
onPostDecryption. - Manual decryption receives
securedPayloadinonComplete. onPreAuthorisationreturning.cancelaborts authorisation.onPostAuthorisationfires when the SDK receives aMerchantSubmitResult.- Submission failures fire
onSubmitError(notonError).
Confirm errors surface through the right callbacks:
- Invalid configuration surfaces errors via
onError. - Network errors are handled gracefully.
- Session expiry is handled with user guidance.
Confirm URL scheme, environment, and device coverage:
- URL callback scheme is registered and unique to your app.
- Paze client ID is configured in Unity Portal for the correct site.
environment: .liveis set with production Paze credentials.- Transaction currency is set to
USD. - You've tested on physical iOS devices (recommended for the full checkout flow).
- Comprehensive error handling covers all payment scenarios.
Verify the Paze button meets branding and layout requirements:
- The integration uses
PazeButtonStyleConfigwith approved style values. - The Paze button has equal prominence with other payment methods.
- The button renders correctly across supported iPhone and iPad layouts.
Confirm credentials and data handling meet your security requirements:
- No sensitive payment data is stored in app storage.
- Session credentials are fetched from the backend at runtime.
- All successful transactions are verified on the backend.
- HMAC credentials are stored securely on the server only.
- The privacy policy covers payment and identity data usage.
Check error handling and fallback behaviour in the app:
- Clear error messages appear when Paze is unavailable.
- The app falls back gracefully to other payment methods.
- Loading indicators appear during checkout and processing.
- Expired sessions are handled with user guidance.
Complete end-to-end tests in UAT before switching to live credentials:
- End-to-end payment testing completed in the UAT environment.
- Checkout approval and rejection flows tested.
- SDK-managed and manual decryption paths tested.
- Transaction failure scenarios tested.