SDK API Reference

This document describes the public API surface exposed by payrails-ui-compose, including payment methods, configuration, styling, translations, and event delegates. For setup and dependencies, see README.md.

Looking for guides? Quick Start | Styling Guide | Troubleshooting | SDK Concepts

Table of Contents

  • Getting Started
  • Core Concepts
  • Logging
  • Card Payments
  • Card Tokenization
  • Google Pay
  • Stored Instruments
  • Results and Errors

Compatibility Requirements

  • JDK 17
  • Android minSdk 21
  • Android compileSdk 35

Public Artifact

  • Distributed via Maven Central
  • Maven coordinate: com.payrails.android:checkout:<version>
  • Additional dependency: com.payrails.android:cse:<version>

Getting Started

Create a Session

Use a Configuration built from InitData plus optional Options.

Kotlin

val initData = InitData(version = "...", data = "...")
val configuration = Configuration(initData = initData, option = Options())

// Suspend API
val session = Payrails.createSession(configuration)

// Callback API
Payrails.createSession(configuration) { result ->
    result.onSuccess { /* session ready */ }
    result.onFailure { /* initialization failed */ }
}

Java

// Java — use the callback API (suspend functions are not callable from Java)
InitData initData = new InitData("...", "...");
Configuration configuration = new Configuration(initData, new Options());

Payrails.createSession(configuration, result -> {
    if (result.isSuccess()) {
        Session session = result.getOrNull();
        // session ready
    } else {
        // initialization failed
    }
    return kotlin.Unit.INSTANCE;
});

To enable automatic SDK-managed recovery when a redirect flow is abandoned or remains non-terminal, pass onSessionExpired in Options.redirectSessionLifecycle:

val configuration = Configuration(
    initData = initData,
    option = Options(
        redirectSessionLifecycle = RedirectSessionLifecycle(
            onSessionExpired = {
                // Merchant callback: run your client-init call and return fresh init payload
                val refreshed = fetchInitPayloadFromBackend()
                InitData(version = refreshed.version, data = refreshed.data)
            }
        )
    )
)

Session-Scoped Helpers

// Keep the returned session reference from createSession(...)
val instruments = Payrails.getStoredInstruments()
val cardInstruments = Payrails.getStoredInstruments(forType = PaymentMethod.card)

Breaking change: Payrails.getCurrentSession() has been removed. Keep and pass the Session returned by createSession(...) when direct session APIs are needed.

Querying Session Data

Use Payrails.query() to read data from the active session.

Payrails.query(key)

fun <T> query(key: PayrailsQuery<T>): T

Returns the value for key from the active session, or null if no session is active or the requested data is not present. Never throws.

ParameterTypeDescription
keyPayrailsQuery<T>A typed query key from the PayrailsQuery sealed class.

Example

val executionId: String? = Payrails.query(PayrailsQuery.ExecutionId)
val amount: PayrailsAmount? = Payrails.query(PayrailsQuery.Amount)
val cardConfig: PayrailsPaymentMethodConfig? =
    Payrails.query(PayrailsQuery.PaymentMethodConfig("card"))

PayrailsQuery<T>

Sealed class of typed query keys for Payrails.query().

KeyReturn typeDescription
PayrailsQuery.ExecutionIdString?ID of the active payment execution.
PayrailsQuery.HolderReferenceString?Holder reference for the active session.
PayrailsQuery.AmountPayrailsAmount?Current checkout amount.
PayrailsQuery.BinLookupPayrailsLink?BIN lookup action link.
PayrailsQuery.InstrumentDeletePayrailsLink?Stored instrument delete action link.
PayrailsQuery.InstrumentUpdatePayrailsLink?Stored instrument update action link.
PayrailsQuery.PaymentMethodConfig(code)PayrailsPaymentMethodConfig?Config for the given payment method code.
PayrailsQuery.PaymentMethodInstruments(code)List<StoredInstrument>?Stored instruments for the given payment method code.

All keys return null when no session is active.


PayrailsAmount

data class PayrailsAmount(
    val value: String,
    val currency: String
)

Represents the current checkout amount. value is the amount in wire format (e.g. "54.99"), consistent with AmountUpdate.value. currency is the ISO 4217 code (e.g. "EUR").


PayrailsLink

data class PayrailsLink(
    val href: String?,
    val method: String?
)

A backend action link. href is the URL to call; method is the HTTP method (e.g. "POST", "DELETE"). Either field may be null if not provided by the backend.


PayrailsPaymentMethodConfig

data class PayrailsPaymentMethodConfig(
    val paymentMethodCode: String,
    val description: String?,
    val displayName: String?,
    val flow: String?,
    val supportsSaveInstrument: Boolean?,
    val supportsBillingInfo: Boolean?
)

Configuration for a payment method from the active session. All fields except paymentMethodCode are optional and sourced from the session payload.


Updating Session State

Use Payrails.update() to change the checkout amount after createSession() has completed, without reinitialising the session.

Payrails.update(changes)

fun update(changes: PayrailsUpdate)

Updates in-memory session state. No network request is made. The updated values take effect on the next pay() call.

ParameterTypeDescription
changesPayrailsUpdateContainer of optional updates. Fields set to null are ignored.

Throws

ExceptionWhen
IllegalStateExceptionCalled before createSession() completes or after the session is destroyed.
IllegalArgumentExceptionchanges.amount is non-null and AmountUpdate.value is not a valid positive number string.

Example

Payrails.update(
    PayrailsUpdate(
        amount = AmountUpdate(value = "54.98", currency = "EUR")
    )
)
cardPaymentButton.pay() // uses the updated amount

PayrailsUpdate

data class PayrailsUpdate(
    val amount: AmountUpdate? = null
)

Container for session updates passed to Payrails.update(). Each field is optional — null means "leave unchanged". Additional fields may be added in future SDK versions without breaking existing call sites.

FieldTypeDescription
amountAmountUpdate?New checkout amount, or null to leave unchanged.

AmountUpdate

data class AmountUpdate(
    val value: String,
    val currency: String
)

Represents a new checkout amount. Both fields are required when updating the amount.

FieldTypeDescription
valueStringNew amount as a numeric string (e.g. "49.99"). Must be a valid positive number.
currencyStringISO 4217 currency code (e.g. "EUR", "USD"). Passed to the backend without format validation — invalid codes cause payment errors at execution time.

Note on value format: The SDK validates that value is parseable as a positive number but does not reformat it. Pass the string in the exact format expected by your backend (e.g. "49.99", not "4999").


Core Concepts

Payment Methods

PaymentMethod supports:

  • PaymentMethod.card
  • PaymentMethod.googlePay
  • PaymentMethod.payPal
  • PaymentMethod.genericRedirect

Card and Google Pay flows are creatable via Payrails factories.

Options

Options configures SDK behavior:

  • env: EnvEnv.PRODUCTION (default) or Env.TEST
  • collectMetadata: Boolean — whether the SDK collects client context metadata (default true)
  • redirectSessionLifecycle: RedirectSessionLifecycle — configures automatic session recovery

Redirect Session Lifecycle (onSessionExpired)

onSessionExpired is configured once through Options.redirectSessionLifecycle and is shared for redirect-capable flows (3DS now, other redirect methods in future). The callback should run the merchant client-init call and return fresh InitData.

Behavior:

  • Triggered when redirect reconciliation is classified as abandoned or stuck non-terminal.
  • If callback is configured and succeeds, SDK refreshes active session/execution state automatically.
  • If callback is missing/fails/returns invalid data, SDK does not continue the stale execution.
  • Current payment attempt still resolves through standard failure handling (authorizationFailed path).

Client Context Metadata

The SDK collects client context and attaches it to authorize requests under meta.clientContext by default.

Collected fields:

  • osType (android)
  • userAgent
  • acceptHeader
  • language
  • screenHeight
  • screenWidth
  • colorDepth
  • timeZoneOffset
  • javaEnabled
  • javaScriptEnabled

Opt-out:

val configuration = Configuration(
    initData = initData,
    option = Options(collectMetadata = false)
)

Request Headers

The SDK attaches the following headers to every API request:

HeaderValueDescription
x-client-versionSDK version (e.g. 2.1.0)Automatically set from the version defined in gradle.properties
x-client-typeandroid-sdkIdentifies the SDK platform
x-idempotency-keyUUIDUnique per request, prevents duplicate processing
AuthorizationBearer <token>Session token from init data

The x-client-version value is generated at build time from gradle.properties (sdk.version) and compiled into the SDK as an internal constant. Merchants do not need to configure it.

Logging

The SDK uses an internal logging system with two output channels:

  • In-memory log store — Always active. The SDK maintains an in-memory buffer of the last 500 log entries (with timestamps). This is used by the built-in debug viewer (DebugManager).
  • Logcat — Suppressed by default. Logcat output under the tag PayrailsSDK is gated behind Log.isLoggable(), so it produces no Logcat output unless explicitly enabled.

Enabling Logcat Output

To enable SDK debug logs in Logcat, run:

adb shell setprop log.tag.PayrailsSDK DEBUG

This persists until the device reboots. After enabling, filter Logcat by the PayrailsSDK tag:

adb logcat -s PayrailsSDK:D

To disable again:

adb shell setprop log.tag.PayrailsSDK INFO

Default State

ChannelDefaultWhen active
In-memory (LogStore)Always onUsed by debug viewer; last 500 entries
Logcat (PayrailsSDK tag)OffOnly when Log.isLoggable("PayrailsSDK", Log.DEBUG) returns true

Security

The SDK never logs raw card data, tokens, or PII through its logging system. Log messages contain only operational information (e.g., payment flow steps, error descriptions).

Card Payments

CardForm + CardPaymentButton

Create a CardForm, then a CardPaymentButton. Elements are decoupled and composed explicitly.

Kotlin/Compose only. Element creation and Render() calls require Kotlin and Jetpack Compose. The delegate callbacks below are Java-compatible.

Kotlin

val cardForm = Payrails.createCardForm(
    config = CardFormConfig(
        showCardHolderName = true,
        showStoreInstrumentCheckbox = true
    )
)

val payButton = Payrails.createCardPaymentButton(
    translations = CardPaymenButtonTranslations(label = "Pay Now")
)

payButton.delegate = object : PayrailsCardPaymentButtonDelegate {
    override fun onPaymentButtonClicked(button: CardPaymentButton) {}
    override fun onAuthorizeSuccess(button: CardPaymentButton) {}
    override fun onThreeDSecureChallenge(button: CardPaymentButton) {}
    override fun onAuthorizeFailed(button: CardPaymentButton) {}
}

// In Compose
cardForm.Render()
payButton.Render()

Java — setting the delegate

payButton.setDelegate(new PayrailsCardPaymentButtonDelegate() {
    @Override
    public void onPaymentButtonClicked(CardPaymentButton button) {}
    @Override
    public void onAuthorizeSuccess(CardPaymentButton button) {}
    @Override
    public void onThreeDSecureChallenge(CardPaymentButton button) {}
    @Override
    public void onAuthorizeFailed(CardPaymentButton button) {}
});

Order does not matter. If CardPaymentButton is created before CardForm, creating CardForm later will auto-attach it to the current button (web-sdk-aligned behavior).

Behavior model (aligned with web-sdk):

  • Form mode: if no stored instrument is selected, the button validates/collects card form data, then authorizes.
  • Stored-instrument mode: if a stored instrument is selected, the button authorizes with that instrument and bypasses card form collection/validation.
  • Form interaction reset: if the user focuses/edits card fields, the selected stored instrument is cleared and button behavior returns to form mode.
  • Guardrail: if no stored instrument is selected and no card form is attached, payment does not proceed and the button emits authorization failure.

3DS redirect behavior:

  • The SDK opens 3DS URLs in a Custom Tab when available, falling back to the system browser.
  • On app return, the SDK polls execution status until terminal success/failure or expiry classification.
  • If status remains non-terminal through reconciliation window, SDK emits authorization failure and runs onSessionExpired callback (if configured).
  • On terminal success/failure, SDK performs best-effort Custom Tab close/return-to-app handling.
  • Avoid using WebView for 3DS challenge pages (issuer/ACS compatibility issues, reduced isolation, unreliable deep-link returns).

CardForm Configuration

CardFormConfig controls visibility and behavior:

  • showCardHolderName: Boolean
  • showStoreInstrumentCheckbox: Boolean
  • showSingleExpiryDateField: Boolean
  • layout: List<List<CardFieldType>>?
  • alwaysStoreInstrument: Boolean
  • defaultStoreInstrumentState: DefaultStoreInstrumentState (checked | unchecked)
  • events: CardFormEvents? (onFocus, onChange, onReady, onSaveInstrumentCheckboxChanged)
  • showCardIcon: Boolean (renders built-in Payrails network icons and default field icons)
  • cardIconAlignment: CardIconAlignment (left or right)
  • cardFieldIcons: CardFieldIcons? (custom icon URLs per field; overrides defaults when provided)
  • styles: CardFormStylesConfig?
  • translations: CardTranslations?

Use CardFormConfig.defaultConfig as a baseline. Payrails.createCardForm(...) reads save-instrument visibility only from CardFormConfig.showStoreInstrumentCheckbox; there is no separate standalone visibility argument on createCardForm(...).

Migration rules:

  • defaultStoreInstrumentState defaults to unchecked. Visibility no longer implies a checked state.
  • alwaysStoreInstrument = true forces submission behavior to store the instrument regardless of toggle state.

Card Form Layout Options

CardFormConfig also supports label placement and field variant:

  • labelPlacement: LabelPlacementFLOATING (default) or ABOVE. When ABOVE, labels render as separate Text composables above each field instead of floating inside the text field.
  • labelSpacing: Dp? — spacing between label and field when labelPlacement = ABOVE. Default: 4.dp.
  • fieldVariant: FieldVariantOUTLINED (default) or FILLED. OUTLINED uses OutlinedTextField (full border); FILLED uses TextField (background fill with bottom indicator).

Card Form Styles

CardFormStylesConfig lets you style the wrapper, fields, labels, and errors. Key types:

  • CardWrapperStyle
  • CardFieldSpecificStyles
  • CardStyle (alias of Style)

Card Form Spacing Tokens

CardFormStylesConfig exposes spacing tokens that replace previously hardcoded layout values:

PropertyTypeDefaultDescription
rowSpacingDp?12.dpVertical spacing between form rows
fieldSpacingDp?12.dpHorizontal spacing between fields in a row
errorSpacingDp?4.dpSpacing between a field and its error message (only rendered when an error is present)
contentPaddingPaddingValues?PaddingValues(16.dp)Outer padding of the card form
fieldHeightDp?wrap contentExplicit height for text fields

All tokens are nullable. When null, the hardcoded default is used.

Card Translations

CardTranslations provides:

  • placeholders: CardTranslations.Placeholders
  • labels: CardTranslations.Labels
  • error: CardTranslations.ErrorMessages

Each map is keyed by CardFieldType (alias of ElementType).

Network-Aware State

The card form exposes network-aware metadata derived from the card number:

  • cardNetwork: CardNetwork (visa, mastercard, amex, discover, unknown)
  • bin: String (policy-driven: first 6 or 8 digits based on card network and PAN length; empty until sufficient digits are entered)
  • cvvMaxLength: Int

Field accessory behavior:

  • Card number: when showCardIcon is enabled, the SDK shows the detected network icon (e.g., Visa, Mastercard). When no network is detected, a default card icon is shown (ic-card.png). Card number does not switch to the clear affordance.
  • CVV: when showCardIcon is enabled, the field shows a default CVV icon (ic-cvv.png) while empty. When populated, it shows a clear affordance regardless of showCardIcon.
  • Expiry date: when showCardIcon is enabled, the field shows a default expiration icon (ic-expiration.png) while empty. When populated, it shows a clear affordance regardless of showCardIcon.
  • Split expiry month/year: when showCardIcon is enabled, both fields show the default expiration icon while empty. When populated, each field switches to a clear affordance independently regardless of showCardIcon.
  • Cardholder name: has no default empty-state icon, but still shows a clear affordance while populated regardless of showCardIcon. Merchants can add a custom empty-state icon via cardFieldIcons.cardholderName when showCardIcon is enabled.

All default icons are loaded from the Payrails assets CDN. Merchants can override any default icon — or add a cardholder name icon — by providing custom URLs via cardFieldIcons:

CardFormConfig(
    showCardIcon = true,
    cardFieldIcons = CardFieldIcons(
        cardholderName = "https://example.com/my-name.png",  // adds a custom cardholder icon (no default)
        cardNumber = "https://example.com/my-card.png",      // replaces default card icon (network icons still show when detected)
        cvv = "https://example.com/my-cvv.png",              // replaces default CVV icon
        expiryDate = "https://example.com/my-expiry.png"     // replaces default expiry icon
    )
)

CardFieldIcons fields:

  • cardholderName: String? — custom icon URL for the empty cardholder name field when showCardIcon is enabled. No default icon exists; when populated, the field shows the clear affordance instead.
  • cardNumber: String? — custom icon URL for the card number field when no network is detected. Once a network is detected, the network icon takes precedence.
  • cvv: String? — custom empty-state icon URL for the CVV field when showCardIcon is enabled.
  • expiryDate: String? — custom empty-state icon URL for the expiration date field(s), including split expiry month/year fields, when showCardIcon is enabled.

Icon placement follows cardIconAlignment (left or right).

BIN length policy (PCI SSC FAQ 1091 aligned):

  • 15-digit PANs (Amex): 6-digit BIN
  • 16-digit PANs (Visa/Mastercard/Discover): 8-digit BIN
  • 17–19 digit PANs or unknown networks: 6-digit BIN

Migration note: if your integration assumes a fixed 8-digit BIN, update routing/lookup logic to accept 6-digit BINs when provided. Do not infer or pad BIN length; rely on the SDK's bin value.

Card Payment Button Translations

CardPaymenButtonTranslations(label: String?) sets the pay button text.

Card Payment Button Styling

Button appearance is configured on the button component (for example via createCardPaymentButton(buttonStyle = ...)). CardFormStylesConfig no longer includes button styling.

Migration note: If you previously set CardFormStylesConfig.buttonStyle, move that styling to the button API (createCardPaymentButton(buttonStyle = ...)). Migration note: CardPaymentButton no longer exposes instrument-specific style/translation buckets. Keep one button settings surface and configure stored-instrument list/item UI in StoredInstrumentsStyle / StoredInstrumentsTranslations.

CardButtonStyle is applied in CardPaymentButton.Render for:

  • backgroundColor, textColor
  • cornerRadius
  • borderWidth, borderColor
  • contentPadding
  • font
  • height, fillMaxWidth
  • disabledStyle, loadingStyle (state-variant styles)
  • loadingIndicatorColor

Additional design tokens:

PropertyTypeDefaultDescription
loadingIndicatorSizeDp?20.dpDiameter of the loading spinner
loadingIndicatorStrokeWidthDp?2.dpStroke width of the loading spinner
elevationDp?Material3 defaultButton elevation (shadow depth)
opacityFloat?1.0fButton opacity (0.0 = transparent, 1.0 = opaque)
minHeightDp?Material3 default (40.dp)Minimum button height

These tokens participate in state-variant merging (disabledStyle, loadingStyle).

Stored Instrument Card Button

Single instrument mapping:

val cardForm = Payrails.createCardForm()
val button = Payrails.createCardPaymentButton(
    translations = CardPaymenButtonTranslations("Pay")
)
button.setStoredInstrument(instrument)

Coupled Wrappers (Breaking Change)

  • CardPaymentForm has been removed from the element API.
  • StoredInstrumentPaymentButton has been removed. Use CardPaymentButton + setStoredInstrument(...).
  • The createCardPaymentButton(storedInstrument = ...) overload has been removed. Create the button first, then call setStoredInstrument(...).
  • createStoredInstruments() and createStoredInstrumentView() have been removed. Use getStoredInstruments() to retrieve instruments and CardPaymentButton.setStoredInstrument(...) to pay with them.
  • Migrate to explicit composition with CardForm + CardPaymentButton.

Card Tokenization

Save a card to the Payrails vault without initiating a payment. The tokenize flow encrypts card data using CSE, POSTs it to the vault endpoint from vaultConfiguration.links.saveInstrument, and returns a SaveInstrumentResponse containing an instrument ID for future use.

Requires vault configuration. The session init payload must include vaultConfiguration with a saveInstrument link. Contact Payrails to enable this for your merchant account.

CardForm.tokenize(options?)

Validates the card form, encrypts the card data, and saves the instrument to the vault.

suspend fun tokenize(options: TokenizeOptions = TokenizeOptions()): SaveInstrumentResponse

Parameters

NameTypeRequiredDefaultDescription
optionsTokenizeOptionsNoTokenizeOptions()Controls storage behavior and signals intended future use to the card network

Returns: SaveInstrumentResponse — the saved instrument record including id, status, and card metadata.

Throws

  • PayrailsError.invalidCardData — card form validation failed; field errors are shown automatically on the form
  • PayrailsError.missingData("Session is required for tokenization.")CardForm was created without an active session
  • PayrailsError.missingData("Vault configuration with providerConfigId is required...") — session init payload is missing vaultConfiguration
  • PayrailsError.missingData("holderReference is required...") — session init payload is missing holderReference

Example

val response = cardForm.tokenize(
    options = TokenizeOptions(
        storeInstrument = true,
        futureUsage = FutureUsage.CardOnFile
    )
)
val instrumentId = response.id

TokenizeOptions

Options controlling card vault behavior during tokenization.

data class TokenizeOptions(
    val storeInstrument: Boolean = false,
    val futureUsage: FutureUsage = FutureUsage.CardOnFile
)
PropertyTypeDefaultDescription
storeInstrumentBooleanfalseWhether to persist the instrument for repeated future use after tokenization
futureUsageFutureUsageFutureUsage.CardOnFileSignals the intended future use of this card to the card network

FutureUsage

Enum signaling intended future card use to the card network. Sent as part of the vault request.

enum class FutureUsage(val value: String) {
    CardOnFile("CardOnFile"),
    Subscription("Subscription"),
    UnscheduledCardOnFile("UnscheduledCardOnFile")
}
ValueDescription
CardOnFileCustomer-initiated payments where the cardholder is present at checkout
SubscriptionRecurring charges on a fixed schedule authorized by the cardholder
UnscheduledCardOnFileMerchant-initiated charges with no fixed schedule (e.g., top-ups, threshold billing)

SaveInstrumentResponse

Response returned by tokenize(). Represents the saved instrument record from the vault.

@Serializable
data class SaveInstrumentResponse(
    val id: String,
    val createdAt: String,
    val holderId: String,
    val paymentMethod: String,
    val status: String,
    val data: InstrumentResponseData,
    val fingerprint: String? = null,
    val futureUsage: String? = null
)
PropertyTypeNullableDescription
idStringNoInstrument identifier; use for future payments and instrument management
createdAtStringNoISO 8601 timestamp of instrument creation
holderIdStringNoPayrails holder ID associated with this instrument
paymentMethodStringNoAlways "card" for card tokenization
statusStringNoInstrument lifecycle status (e.g., "active")
dataInstrumentResponseDataNoCard presentation data (BIN, suffix, network, etc.)
fingerprintString?YesCard fingerprint for deduplication across instruments
futureUsageString?YesFuture use value as recorded by the vault

SaveInstrumentResponse.InstrumentResponseData

PropertyTypeNullableDescription
binString?YesCard BIN (first digits, per PCI SSC BIN length policy)
binLookupBinLookup?YesEnriched BIN metadata including network, issuer, and issuer country when available
suffixString?YesCard last digits
networkString?YesCard network identifier (e.g., "visa", "mastercard")
holderNameString?YesCardholder name as entered in the card form
expiryMonthString?YesTwo-digit expiry month
expiryYearString?YesTwo or four-digit expiry year

InstrumentAPIResponse.save

The save variant of the InstrumentAPIResponse sealed class, returned when using lower-level instrument API calls. CardForm.tokenize() returns SaveInstrumentResponse directly rather than wrapping it.

sealed class InstrumentAPIResponse {
    data class delete(val response: DeleteInstrumentResponse) : InstrumentAPIResponse()
    data class update(val response: UpdateInstrumentResponse) : InstrumentAPIResponse()
    data class save(val response: SaveInstrumentResponse) : InstrumentAPIResponse()
}

Google Pay

GooglePayButton

Create a GooglePayButton after initializing a session. The button handles Google Pay availability checks, payment sheet presentation, and authorization automatically.

Kotlin/Compose only. Element creation and Render() require Kotlin and Jetpack Compose. The delegate callbacks below are Java-compatible.

Kotlin

val googlePayButton = Payrails.createGooglePayButton(
    style = GooglePayButtonStyle(),
    translations = GooglePayButtonTranslations(),
    showStoreInstrumentCheckbox = false
)

googlePayButton.delegate = object : PayrailsGooglePayButtonDelegate {
    override fun onGooglePayAvailable(button: GooglePayButton) {
        // Google Pay is supported on this device
    }
    override fun onPaymentButtonClicked(button: GooglePayButton) {
        // User tapped the Google Pay button
    }
    override fun onAuthorizeSuccess(button: GooglePayButton) {
        // Payment succeeded
    }
    override fun onThreeDSecureChallenge(button: GooglePayButton) {
        // 3DS challenge started
    }
    override fun onAuthorizeFailed(button: GooglePayButton) {
        // Payment failed
    }
}

// In Compose
googlePayButton.Render()

Java — setting the delegate

googlePayButton.setDelegate(new PayrailsGooglePayButtonDelegate() {
    @Override
    public void onGooglePayAvailable(GooglePayButton button) {}
    @Override
    public void onPaymentButtonClicked(GooglePayButton button) {}
    @Override
    public void onAuthorizeSuccess(GooglePayButton button) {}
    @Override
    public void onThreeDSecureChallenge(GooglePayButton button) {}
    @Override
    public void onAuthorizeFailed(GooglePayButton button) {}
});

The button is hidden by default and becomes visible only after isReadyToPay() succeeds. The onGooglePayAvailable callback fires when the button becomes visible.

Google Pay Configuration

Google Pay configuration is provided by the Payrails backend through paymentCompositionOptions. The SDK reads clientConfig.additionalConfig for the Google Pay payment option, which contains:

  • apiVersion / apiVersionMinor — Google Pay API version (defaults to 2 / 0)
  • allowedPaymentMethods — Payment methods array including tokenizationSpecification (gateway configuration)
  • merchantInfo — Merchant identifier and name (required in production)
  • emailRequired — Whether to request the payer's email
  • shippingAddressRequired / shippingAddressParameters — Shipping address collection

The countryCode for transactionInfo is extracted from meta.order.billingAddress.country.code in the execution metadata.

No client-side configuration of gateway credentials is needed. The PAYMENT_GATEWAY, gateway, and gatewayMerchantId values are provided by the backend.

GooglePayButtonStyle

GooglePayButtonStyle controls the button appearance:

PropertyTypeDefaultDescription
buttonThemeButtonThemeDarkGoogle Pay button theme (Dark or Light)
buttonTypeButtonTypePayButton label type (Buy, Pay, Checkout, Donate, Order, Plain, Subscribe)
cornerRadiusDp100.dpButton corner radius
containerPaddingPaddingValues?nullPadding around the button container
containerBackgroundColorColor?nullBackground color behind the button

GooglePayButtonTranslations

GooglePayButtonTranslations provides:

  • storeInstrument: String? — Checkbox label text when showStoreInstrumentCheckbox is enabled. Defaults to "Save this payment method".

PayrailsGooglePayButtonDelegate

CallbackWhen it fires
onGooglePayAvailable(button)isReadyToPay() succeeded; button is now visible
onPaymentButtonClicked(button)User tapped the button; payment sheet is about to open
onAuthorizeSuccess(button)Payment authorized successfully
onThreeDSecureChallenge(button)3DS challenge started (Custom Tab opened)
onAuthorizeFailed(button)Payment failed or was rejected
onStateChanged(button, state)Button state changed (ENABLED, DISABLED, LOADING). Optional — default no-op.

Store Instrument Checkbox

Pass showStoreInstrumentCheckbox = true to createGooglePayButton(...) to render a checkbox below the Google Pay button. When checked, the payment token is stored as an instrument for future use.

val googlePayButton = Payrails.createGooglePayButton(
    showStoreInstrumentCheckbox = true,
    translations = GooglePayButtonTranslations(storeInstrument = "Save for next time")
)

3DS for Google Pay

If the authorize response requires 3DS verification, the SDK opens the challenge URL in a Custom Tab and polls for the terminal result, following the same 3DS flow used for card payments. The onThreeDSecureChallenge delegate callback fires when this happens.

PayPal and Redirect Methods

PayPal and generic redirect button factories are not part of the current public Payrails API surface in this build.

Stored Instruments

StoredInstrument Interface

Each StoredInstrument exposes:

PropertyTypeDescription
idStringInstrument identifier
emailString?Email (PayPal instruments)
descriptionString?Legacy display string ("bin***suffix" for cards, email for PayPal)
typePaymentMethodInstrument payment method
displayNameString?Server-provided display name (cards: custom name; PayPal: email)
isDefaultBooleanWhether this is the holder's default instrument
cardMetadataCardInstrumentMetadata?Typed card details (non-null for cards, null for other types)

The SDK maps the backend instrument field default to StoredInstrument.isDefault.

CardInstrumentMetadata

Public data class with typed card presentation data:

PropertyTypeDescription
binString?Card BIN (first digits)
suffixString?Card last digits
networkString?Card network (e.g., "visa", "mastercard")
issuerCountryString?Issuing country code

Migration note: prefer displayName and cardMetadata over parsing the description string. description remains available for backwards compatibility.

Retrieving Stored Instruments

Use Payrails.getStoredInstruments() to retrieve available instruments, then set one on the button:

Kotlin

val instruments = Payrails.getStoredInstruments()
val cardInstruments = Payrails.getStoredInstruments(forType = PaymentMethod.card)

// Set a stored instrument on the card payment button
payButton.setStoredInstrument(cardInstruments.first())

Java

List<StoredInstrument> instruments = Payrails.getStoredInstruments(null);
List<StoredInstrument> cardInstruments = Payrails.getStoredInstruments(PaymentMethod.card);

payButton.setStoredInstrument(cardInstruments.get(0));

Instrument Management APIs

Use Payrails.api(...) to delete or update stored instruments:

// Delete an instrument
val deleteResult = Payrails.api("deleteInstrument", instrumentId)

// Update an instrument
val updateResult = Payrails.api(
    "updateInstrument",
    instrumentId,
    UpdateInstrumentBody(default = true)
)

The return type is InstrumentAPIResponse, a sealed class with delete, update, and save variants. See Card Tokenization for the save variant and the tokenize() methods.

Results and Errors

Payment results are surfaced through delegate interfaces:

Card paymentsPayrailsCardPaymentButtonDelegate:

  • onAuthorizeSuccess — payment succeeded
  • onAuthorizeFailed — payment failed (includes session-expiry outcomes from redirect reconciliation)
  • onThreeDSecureChallenge — 3DS challenge started
  • onPaymentButtonClicked — button was tapped

Google PayPayrailsGooglePayButtonDelegate:

  • onAuthorizeSuccess — payment succeeded
  • onAuthorizeFailed — payment failed
  • onThreeDSecureChallenge — 3DS challenge started
  • onPaymentButtonClicked — button was tapped
  • onGooglePayAvailable — Google Pay is ready on this device

authorizationFailed includes session-expiry outcomes from redirect reconciliation (for example browser abandoned/non-terminal timeout), including cases where automatic onSessionExpired recovery is attempted.

Errors are represented by PayrailsError (e.g., authenticationError, missingData).


What’s Next

Troubleshooting Guide