Method states
A method the session offers but that cannot be used right now stays a PaymentItem with a non-null disabled: DisabledState. The state has a code, a displayable message, and five boolean behaviours that tell you how to render the method and whether it will come back. The vocabulary is the web SDK's DisabledState, so both platforms react the same way.
val state = item.disabled ?: return Enabled
when {
state.isFiltered -> Unit // never in paymentMethods: you will not see it
state.isTechnicallyDisabled -> HideOrGreyOut(state.message) // no UI can be mounted
state.isRecoverable -> Dim(state.message) // keep the element mounted, it comes back
else -> GreyOut(state.message) // amount-driven or SUBMITTING, lifts by itself
}
The five behaviours
| Behaviour | Property | Meaning | Expected reaction |
|---|---|---|---|
| Filtered | isFiltered | The item is removed from paymentMethods altogether | Nothing to render. The method never existed for this session |
| Technically disabled | isTechnicallyDisabled | A hard runtime failure. rememberPaymentElement mounts no UI, and submit() reports Failed(MethodDisabled) | Grey the row out with message, or hide it. Final for the session |
| Requires UI clear | requiresUiClear | Any mounted UI for the item should be torn down | rememberPaymentElement does it. If you drive a coordinator yourself, dispose it |
| Recoverable | isRecoverable | The partner reported broken-for-now, not broken-for-good | Dim the row, keep the element mounted. It comes back on its own with a Ready event |
| Recomputable | isRecomputable | Re-evaluated whenever the amount left to pay changes | Nothing: the state lifts by itself when the split moves. Everything outside this group is final |
The codes
Fourteen codes are shared with the web SDK. Eleven are declared on Android (DisabledState.Code); the last three describe browser situations and are absent.
| Code | Cause | Filtered | Technical | Clear UI | Recoverable | Recomputable | Expected action |
|---|---|---|---|---|---|---|---|
METHOD_UNSTABLE | The platform suspended the method for this session (stable == false in your Purse configuration) | ✓ | None: not listed | ||||
AMOUNT_CONDITION_UNREACHABLE | The method's minimum amount exceeds the whole order: no split could ever enable it | ✓ | None: not listed | ||||
PLUGIN_INCOMPATIBLE | The plugin rejected this client or session in its eligibility check | ✓ | None: not listed | ||||
FATAL_ERROR_BEFORE_RENDER | Declared for parity; the Android SDK reports an /init failure as PARTNER_UI_FATAL_ERROR instead | ✓ | None | ||||
TOKEN_INIT_FAILED | A saved gift card's balance could not be read from the issuer this session | ✓ | ✓ | Show the saved card as unavailable; offer to enter it again | |||
PARTNER_UI_FATAL_ERROR | The partner's step failed for good: /init failed, the wallet or widget could not initialize, the partner reported a fatal error | ✓ | ✓ | Grey out or hide the method with message; offer the others | |||
PARTNER_UI_RECOVERABLE_ERROR | The partner reported a failure it expects to recover from | ✓ | ✓ | Dim the method, keep its UI mounted; it re-enables itself | |||
MIN_AMOUNT_NOT_MET | What is left to pay is under the method's minimum (a gift card took too much) | ✓ | ✓ | Grey out with message; removing a gift card lifts it | |||
MAX_AMOUNT_EXCEEDED | What is left to pay is over the method's maximum (primaries only) | ✓ | ✓ | Grey out; applying a gift card lifts it | |||
PAYMENT_FULFILLED | Gift cards cover the whole amount: no primary is needed | ✓ | ✓ | Grey out every primary; show a Pay button that calls submitPayment() | |||
SUBMITTING | A payment is in flight: every otherwise enabled method is locked until submitPayment returns | Show a progress indicator; lifted whatever the outcome | |||||
PAYMENT_ELEMENT_FATAL_ERROR | Web only: the DOM element failed | ✓ | ✓ | Not raised on Android | |||
PAYMENT_ELEMENT_RENDER_FAILED | Web only: the DOM element could not render | ✓ | ✓ | Not raised on Android | |||
DOES_NOT_BELONG_TO_CURRENT_SESSION | Web only: an element outlived setSession. Android rebuilds such elements through itemGenerations | ✓ | ✓ | Not raised on Android |
The amount-driven states carry their context: MinAmountNotMet(minAmount, restToPay), MaxAmountExceeded(maxAmount, restToPay), AmountConditionUnreachable(minAmount, maxAmount, orderAmount). PartnerUiFatalError and PartnerUiRecoverableError carry the partner's reason.
Where states come from
| Moment | States set |
|---|---|
Session build (init, setSession) | METHOD_UNSTABLE, AMOUNT_CONDITION_UNREACHABLE, PLUGIN_INCOMPATIBLE (filtered out before you see the list) |
Every change of remainingAmountToPay | MIN_AMOUNT_NOT_MET, MAX_AMOUNT_EXCEEDED, PAYMENT_FULFILLED recomputed; PARTNER_UI_RECOVERABLE_ERROR re-evaluated |
A partner event (InitFailed, ClientError, PartnerError) | PARTNER_UI_FATAL_ERROR, or PARTNER_UI_RECOVERABLE_ERROR when the partner says it can recover |
A saved gift card's background /init | TOKEN_INIT_FAILED on that SavedSecondaryToken |
submitPayment entered and returned | SUBMITTING overlaid, then lifted |
Every transition is also reported to Purse's monitoring as HEADLESS_CHECKOUT_METHOD_DISABLED, at warning level for a technical failure and at info level for a nominal one (Events and monitoring).
setPrimary and setPrimaryToken refuse a disabled item with MethodDisabled (METHOD_DISABLED), whose disabled property is this state. Rendering the row from item.disabled avoids the exception altogether.