Learn how to diagnose and fix common issues with the Paze iOS component.
The Paze iOS component surfaces BaseSdkException errors with structured codes for different failure scenarios:
| Exception | Error code | Description |
|---|---|---|
UnsupportedFundingTypePazeSdkException | SDK0118 | Paze is not included in the allowed funding types for this session. |
PazeLoadFailedException | SDK1200 | Paze checkout session failed to start. For example, due to network issues or an error returned by the Paze API. |
PazeConfigurationValidationFailedException | SDK1202–SDK1205, SDK1231, SDK1232 | Configuration validation failed. For example, missing client ID, invalid field lengths, invalid shipping countries, or empty cobrand names. |
PazeMerchantCategoryCodeRequiredException | SDK1202A | merchantCategoryCode is missing from the session. Required on iOS for payment completion. |
PazeInitializationFailedException | SDK1206 | Paze initialisation failed. |
PazeIdentityValidationFailedException | SDK1209–SDK1212 | Email or phone number validation failed. Check format and length requirements. |
PazeCurrencyNotSupportedException | SDK1213 | Paze only supports USD currency. |
PazePaymentFailedException | SDK1214 | Paze checkout or wallet flow failed before authorisation. |
PazeCheckoutResponseMissingException | SDK1215 | Paze checkout did not return a checkoutResponse for a COMPLETE result. |
PazeCompleteResponseMissingException | SDK1216 | Paze complete() did not return a completeResponse. |
PazeCompleteSecuredPayloadMissingException | SDK1219 | Paze completeResponse did not contain a securedPayload for decryption. |
PazeCompleteSecuredPayloadMissingException / PazePaymentFailedException | SDK1219 / SDK1214 (or provider error codes) | Token decryption failed. Check session encryption key and Paze wallet settings in Unity Portal. Surfaced via onError and the PazeDecryptionFailed analytics event. |
PazeTransactionFailedException | SDK1235 | Transaction submission failed. Surfaced via onSubmitError, not onError. The callback typically receives a FailedSubmitResult with gateway or provider error codes. |
User cancellation isn't thrown as an exception. When the shopper dismisses the ASWebAuthenticationSession, the SDK calls onCheckoutIncomplete with reason: "User cancelled.".
Route onError, onCheckoutIncomplete, and onSubmitError to appropriate user messaging and logging:
config.onError = { error in
print("Paze error:", error.errorCode, error.errorMessage)
switch error.errorCode {
case "SDK0118", "SDK1202":
hidePazeButton()
showAlternativePaymentMethods()
case "SDK1202A":
showMessage("Paze merchant category code is missing from the session.")
case "SDK1213":
showMessage("Paze is only available for USD transactions.")
case "SDK1210", "SDK1212":
showMessage("Please check your contact details and try again.")
case "SDK1214", "SDK1200":
showMessage("Paze checkout failed. Please try again.")
default:
showMessage("Payment could not be completed. Please try again.")
}
}
config.onCheckoutIncomplete = { result async in
if result.reason?.lowercased().contains("cancel") == true {
showMessage("Payment cancelled.")
} else {
showMessage("Payment was not completed. Please try again.")
}
}
config.onSubmitError = { submitResult async in
if let failed = submitResult as? FailedSubmitResult {
print("Submit error:", failed.errorCode ?? "", failed.errorReason ?? "")
}
showMessage("Payment could not be processed. Please try another method.")
}Include these fields when logging errors for PXP or Paze support:
func logPazeError(_ error: BaseSdkException, context: [String: Any] = [:]) {
var payload: [String: Any] = [
"errorCode": error.errorCode,
"errorMessage": error.errorMessage,
"timestamp": ISO8601DateFormatter().string(from: Date())
]
context.forEach { payload[$0.key] = $0.value }
// Send to your logging or crash reporting service
print("Paze support log:", payload)
}Use this section when Paze isn't appearing in the Unity Portal, missing from your session response, or failing during component creation with SDK0118. For portal setup steps, see Onboarding.
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), then return to the site configuration.
Symptom: Session response doesn't include allowedFundingTypes.wallets.paze.clientId.
Solution:
- Verify the client ID is saved in Paze Account Settings for the correct site.
- Create a new session after saving portal changes.
- Confirm you are using the correct site name in your session request.
Symptom: Component creation fails with Paze is missing in allow funding types.
Solution: The session was created before Paze was configured, or Paze isn't enabled for the site. Complete Onboarding, Steps 3–4, then re-create the session and re-initialise the SDK.
Symptom: Presentment fails with Missing session.allowedFundingTypes.wallets.paze.clientId and the button doesn't appear.
Solution: Confirm clientId is in the session response from your backend and passed through SessionData when initialising the SDK. Re-create the session after saving portal changes.
Symptom: Complete step fails after checkout returns COMPLETE with Missing session.allowedFundingTypes.wallets.paze.merchantCategoryCode.
Solution: Include merchantCategoryCode in your backend session response and pass it through SessionData when initialising the SDK. See Onboarding, Step 4.
The symptoms of this are:
onPresentmentResolved(false)fires and no button appears in your SwiftUI view.- The Paze option isn't visible to shoppers.
Run these checks to narrow down why the button isn't visible:
func diagnosePazePresentment(checkoutConfig: CheckoutConfig, componentConfig: PazeButtonComponentConfig) {
print("=== Paze Presentment Diagnostics ===")
let paze = checkoutConfig.session.allowedFundingTypes?.wallets?.paze
print("Client ID:", paze?.clientId?.isEmpty == false ? "Present" : "Missing")
print("MCC:", paze?.merchantCategoryCode?.isEmpty == false ? "Present" : "Missing (required at complete, not for button visibility)")
print("Currency:", checkoutConfig.transactionData.currency)
print("Amount:", checkoutConfig.transactionData.amount)
print("Email:", componentConfig.emailAddress ?? "Not set")
print("Phone:", componentConfig.phoneNumber ?? "Not set")
}Listen for presentment and error callbacks:
config.onPresentmentResolved = { isVisible in
print("Presentment resolved:", isVisible)
}
config.onError = { error in
print("Presentment error:", error.errorCode, error.errorMessage)
}| Cause | Error code | Solution |
|---|---|---|
Paze not in session at create() | SDK0118 | Enable Paze in Portal and include clientId in session |
Missing clientId at presentment | 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 |
clientName or siteName too long | SDK1203, SDK1204 | Shorten to 50 characters or fewer |
| Statement descriptor too long | SDK1205 | Shorten paymentDescription to 25 characters or fewer |
On iOS, the button uses static presentment. It appears after SDK validation without calling Paze canCheckout(). If the button is hidden, check onError for validation failures rather than wallet eligibility.
When Paze is unavailable, hide the button and show alternatives:
config.onPresentmentResolved = { isVisible in
showPazeSection(isVisible)
}
config.onError = { error in
if ["SDK0118", "SDK1202", "SDK1213"].contains(error.errorCode) {
showPazeSection(false)
showAlternativePaymentMethods()
}
}The symptoms of this are:
- The shopper taps the Paze button but checkout doesn't complete.
onCheckoutIncompletefires unexpectedly.- The Paze web session opens but the shopper can't complete payment.
Instrument checkout callbacks to log each step:
config.onPazeButtonClicked = { async in
print("Step 1: Button clicked")
print("Currency:", checkoutConfig.transactionData.currency)
print("Amount:", checkoutConfig.transactionData.amount)
}
config.onCheckoutComplete = { result async in
print("Step 2: Checkout complete")
print("Session ID:", result.checkoutDecodedResponse?.sessionId ?? "")
return true
}
config.onCheckoutIncomplete = { result async in
print("Checkout incomplete:", result.reason ?? "")
}
config.onError = { error in
print("Checkout error:", error.errorCode, error.errorMessage)
}Currency validation: Confirm transactionData.currency is "USD" before showing the Paze section.
User cancellation: When the shopper dismisses the web session, expect onCheckoutIncomplete with a cancellation reason — not onError:
config.onCheckoutIncomplete = { result async in
if result.reason?.lowercased().contains("cancel") == true {
showMessage("Payment cancelled. You can try again when ready.")
} else {
showMessage("Unable to complete checkout. Please try a different payment method.")
}
}Merchant rejection: Returning false from onCheckoutComplete triggers onCheckoutIncomplete:
config.onCheckoutComplete = { result async in
guard validateCheckout(result) else { return false }
return true
}UAT wallet access: In UAT, confirm the test shopper email or phone is provisioned for your Paze merchant account. Checkout may fail if the wallet account exists but isn't accessible to the merchant.
The symptoms of this are:
- The Paze web session completes but your app doesn't resume.
- Checkout hangs after the shopper finishes in the browser sheet.
onCheckoutCompletenever fires.
Verify your URL callback scheme is registered:
<!-- Info.plist -->
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>pxpcheckout</string>
</array>
</dict>
</array>Confirm the scheme matches your SDK build configuration. The default callback URL is pxpcheckout://paze.
| Issue | Solution |
|---|---|
Scheme not in Info.plist | Register CFBundleURLSchemes before testing checkout |
| Scheme mismatch | Ensure PXP_CALLBACK_SCHEME matches your registered scheme |
| Another app claims the scheme | Use a unique scheme for your app |
| Testing only in Simulator | Test full return flow on a physical device |
| Presentation context missing | Present checkout from a view controller with a valid window |
If multiple apps register the same URL scheme, iOS may route the callback to the wrong app. Use a scheme unique to your application.
The symptoms of this are:
- Checkout completes but payment doesn't process.
onPostDecryptionnever fires.onErrorfires or thePazeDecryptionFailedanalytics event is recorded.
Log each stage of the decryption flow:
config.onCheckoutComplete = { result async in
print("Step 1: Checkout complete, merchant approved")
return true
}
config.onComplete = { result async in
print("Step 2: Complete API succeeded")
print("Secured payload present:", result.completeDecodedResponse?.securedPayload != nil)
}
config.onPreDecryption = { async in
print("Step 3: Pre-decryption")
let hasKey = !checkoutConfig.session.encryptionKey.isEmpty
print("Session encryption key present:", hasKey)
return true
}
config.onPostDecryption = { payload async in
print("Step 4: Decryption successful")
print("Network token present:", !payload.fundingData.networkToken.isEmpty)
}
config.onError = { error in
print("Decryption or checkout error:", error.errorCode, error.errorMessage)
}SDK-managed decryption: Return true from onPreDecryption (default) and confirm session.encryptionKey is present in the session from your backend.
Manual decryption: Return false from onPreDecryption and decrypt securedPayload on your backend. See Backend decryption.
Missing secured payload: SDK1219 means the complete response had no securedPayload. Contact PXP support with the checkout session ID.
The symptoms of this are:
- Decryption succeeds but the transaction doesn't complete.
onSubmitErrorfires with error details.onSubmitErrorfires withFailedSubmitResultdetails (typically gateway or provider codes such asDECLINED, notSDK1235).
config.onPreAuthorisation = { async in
print("Pre-authorisation gate")
return .proceed
}
config.onPostAuthorisation = { data async in
print("Authorisation result:", data.systemTransactionId)
}
config.onSubmitError = { submitResult async in
if let failed = submitResult as? FailedSubmitResult {
print("Submit failed:", failed.errorCode ?? "", failed.errorReason ?? "")
}
}| Cause | Solution |
|---|---|
| Missing shopper data | Implement onGetShopper on CheckoutConfig with valid shopper details |
| Pre-auth cancelled | Returning .cancel from onPreAuthorisation aborts submission intentionally |
| Backend rejection | Inspect failed.errorCode and failed.errorReason from onSubmitError |
| Session expired | Create a new session and re-initialise the SDK |
Symptom: SDK1210 or SDK1212 during presentment or checkout.
Solution:
- Email: valid RFC 5322 format, 128 characters or fewer.
- Phone: US E.164 without
+, exactly1followed by 10 digits (e.g.15551234567).
// Incorrect
config.phoneNumber = "+1 (555) 123-4567"
// Correct
config.phoneNumber = "15551234567"| Situation | Callback | Action |
|---|---|---|
| Configuration or SDK error | onError | Show message, log errorCode, offer alternatives |
| Shopper dismissed web checkout | onCheckoutIncomplete | Show cancellation message |
| Merchant rejects checkout data | onCheckoutComplete → false | Triggers onCheckoutIncomplete |
| Decryption failure | onError or PazeDecryptionFailed analytics | Retry or offer alternative payment |
| Unity submission failure | onSubmitError (FailedSubmitResult) | Show failure message, log errorCode and errorReason |
| Button visibility | onPresentmentResolved | Show or hide Paze section |
The following tables list the most common Paze error codes surfaced through onError and onSubmitError. For the complete SDK reference — including complete-step validation (SDK1222–SDK1230), decode failures, and dynamic-presentment codes — see Full Paze error reference.
| Error code | Message | Cause |
|---|---|---|
SDK0118 | Paze is missing in allow funding types. | session.allowedFundingTypes.wallets.paze.clientId is missing or empty at create(.pazeButton) time |
Presentment errors occur during component initialisation before the button is shown. The button remains hidden and onPresentmentResolved(false) is called.
| Error code | Message | Cause |
|---|---|---|
SDK1202 | Missing session.allowedFundingTypes.wallets.paze.clientId. | Paze clientId not in session data |
SDK1203 | client.name must be 50 characters or fewer. | CheckoutConfig.clientName too long |
SDK1204 | client.brandName must be 50 characters or fewer. | CheckoutConfig.siteName too long |
SDK1205 | client.statementDescriptor must be 25 characters or fewer. | paymentDescription too long |
SDK1209 | emailAddress must be 128 characters or fewer. | Email exceeds max length |
SDK1210 | emailAddress must be a valid RFC 5322 email address. | Invalid email format |
SDK1211 | phoneNumber must be 15 characters or fewer. | Phone exceeds max length |
SDK1212 | phoneNumber must be a US E.164 number without '+', for example 15551234567. | Invalid phone format |
SDK1213 | Paze only supports USD currency. Received: '{currency}'. | Transaction currency is not USD |
SDK1206 | Failed to initialize Paze SDK. | Generic presentment failure |
| Error code | Message | Cause |
|---|---|---|
SDK1200 | Failed to load Paze SDK. | Session creation failed |
SDK1214 | Paze checkout failed. | Generic checkout failure |
SDK1215 | Paze checkout did not return checkoutResponse for a COMPLETE result. | Missing checkout response |
SDK1216 | Paze complete did not return completeResponse. | Missing complete response code |
SDK1219 | Paze completeResponse did not contain securedPayload. | Secured payload missing after complete |
SDK1202A | Missing session.allowedFundingTypes.wallets.paze.merchantCategoryCode. | MCC not configured in session |
SDK1231 | acceptedShippingCountries contains invalid country codes: {invalidCountryCodes}. | Invalid ISO country codes |
SDK1232 | cobrand.cobrandName is required for each cobrand entry. | Empty cobrand name |
SDK1235 | Paze payment transaction failed. | Authorisation request failed. onSubmitError typically receives a FailedSubmitResult with gateway or provider error codes |
Additional codes (SDK1201, SDK1207, SDK1208, SDK1217, SDK1218, SDK1220-SDK1230, SDK1233, SDK1234) are documented in the Full Paze error reference.