Skip to main content
Use onRequestStart when your backend must approve a payment before Payrails authorizes it—revalidating a voucher, confirming a wallet balance, or checking loyalty points the moment the customer commits. For why the gate sits where it does, see SDK Concepts.

Steps

Step 1: Register the handler

Supply it on Options, beside redirectSessionLifecycle:
The handler is a suspend function, so call your backend directly—no callback plumbing.

Step 2: Gate only the methods you care about

The handler fires for every payment method on the session. Return Proceed on the branches you are not gating—otherwise the handler blocks those payments too:

Step 3: Handle an unreachable backend

The SDK waits 10 seconds, then blocks the payment. A handler that throws blocks it too. Both are deliberate—a gate that fails open is not a gate—but they mean your error path is a decision, so make it explicit:
The handler runs on a background dispatcher, so a suspending network call needs no extra withContext. Switch context only to update the UI:
One caveat about the 10-second bound: it can only interrupt a handler that suspends. A handler that blocks its thread—a synchronous HTTP client, Thread.sleep, a blocking database read—runs to completion no matter how long it takes, because coroutine cancellation is cooperative. Use a suspending client, or wrap a blocking one so cancellation reaches it.

Step 4: Handle the block in your delegate

A blocked payment arrives as onAuthorizeFailed with failure.code == VALIDATION_FAILED. Separate it from a decline — nothing reached the backend, so there is no failed payment to explain:
This is why Refuse carries a message rather than the handler returning a bare false: the reason travels with the refusal to the one place you already read failure text from, instead of stashing it somewhere and correlating it back by executionId.

Google Pay

The button opens Google Pay’s sheet, not the SDK core, so the SDK consults the gate the moment the customer taps—before the sheet appears. A refusal means they never see it. Every other method reaches the gate through Session.authorize, which also runs before any provider UI. The practical consequence: for Google Pay, the SDK calls your handler before the customer chooses a card. A basket-only check—a voucher, a wallet balance, loyalty points—works fine here. It cannot depend on which card they picked.

Reference

Only a deliberate Refuse(message) reaches failure.message. The timeout and throw cases log their diagnostic instead of surfacing it—both describe an integration fault rather than anything phrased for a customer, and an exception string can carry internals. Blocking never sends an authorization request, never presents provider UI, and returns the element to ButtonState.ENABLED. onRequestStart is a Kotlin-only API, like onSessionExpired: suspend functions are not implementable from Java.

See Also

Last modified on September 24, 2026