# SDK API Reference This document describes the public API surface exposed by the android sdk, including payment methods, configuration, styling, translations, and event delegates. ## 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` 34 ## Public Artifact * Distributed via [Maven Central](https://central.sonatype.com/) * Maven coordinate: `com.payrails.android:checkout:` * Additional dependency: `com.payrails.android:cse:` ## Getting Started ### Create a Session Use a `Configuration` built from `InitData` plus optional `Options`. **Kotlin** ```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 */ } } ``` To enable automatic SDK-managed recovery when a redirect flow is abandoned or remains non-terminal, pass `onSessionExpired` in `Options.redirectSessionLifecycle`: ```kotlin 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 ```kotlin // Keep the returned session reference from createSession(...) val instruments = Payrails.getStoredInstruments() val cardInstruments = Payrails.getStoredInstruments(forType = PaymentMethod.card) ``` ## 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: Env` — `Env.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: ```kotlin val configuration = Configuration( initData = initData, option = Options(collectMetadata = false) ) ``` ### Request Headers The SDK attaches the following headers to every API request: | Header | Value | Description | | ------------------- | ----------------------------------- | ----------------------------------------------------------------- | | `x-client-version` | SDK version (e.g. `0.1.0-SNAPSHOT`) | Automatically set from the version defined in `gradle.properties` | | `x-client-type` | `android-sdk` | Identifies the SDK platform | | `x-idempotency-key` | UUID | Unique per request, prevents duplicate processing | | `Authorization` | `Bearer ` | 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: ```bash adb shell setprop log.tag.PayrailsSDK DEBUG ``` This persists until the device reboots. After enabling, filter Logcat by the `PayrailsSDK` tag: ```bash adb logcat -s PayrailsSDK:D ``` To disable again: ```bash adb shell setprop log.tag.PayrailsSDK INFO ``` ### Default State | Channel | Default | When active | | -------------------------- | ------------- | ------------------------------------------------------------------- | | In-memory (`LogStore`) | **Always on** | Used by debug viewer; last 500 entries | | Logcat (`PayrailsSDK` tag) | **Off** | Only 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** ```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() ``` 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>?` * `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(...)`. #### Card Form Layout Options `CardFormConfig` also supports label placement and field variant: * `labelPlacement: LabelPlacement` — `FLOATING` (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: FieldVariant` — `OUTLINED` (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: | Property | Type | Default | Description | | ---------------- | ---------------- | ---------------------- | --------------------------------------------------------------- | | `rowSpacing` | `Dp?` | `12.dp` | Vertical spacing between form rows | | `fieldSpacing` | `Dp?` | `12.dp` | Horizontal spacing between fields in a row | | `errorSpacing` | `Dp?` | `4.dp` | Spacing between a field and its error message (always reserved) | | `contentPadding` | `PaddingValues?` | `PaddingValues(16.dp)` | Outer padding of the card form | | `fieldHeight` | `Dp?` | wrap content | Explicit 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`: ```kotlin 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 #### 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. `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: | Property | Type | Default | Description | | ----------------------------- | -------- | --------------------------- | ------------------------------------------------ | | `loadingIndicatorSize` | `Dp?` | `20.dp` | Diameter of the loading spinner | | `loadingIndicatorStrokeWidth` | `Dp?` | `2.dp` | Stroke width of the loading spinner | | `elevation` | `Dp?` | Material3 default | Button elevation (shadow depth) | | `opacity` | `Float?` | `1.0f` | Button opacity (0.0 = transparent, 1.0 = opaque) | | `minHeight` | `Dp?` | Material3 default (`40.dp`) | Minimum button height | These tokens participate in state-variant merging (`disabledStyle`, `loadingStyle`). ### Stored Instrument Card Button Single instrument mapping: ```kotlin val cardForm = Payrails.createCardForm() val button = Payrails.createCardPaymentButton( translations = CardPaymenButtonTranslations("Pay") ) button.setStoredInstrument(instrument) ```
## 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. ```kotlin suspend fun tokenize(options: TokenizeOptions = TokenizeOptions()): SaveInstrumentResponse ``` **Parameters** | Name | Type | Required | Default | Description | | --------- | ----------------- | -------- | ------------------- | ----------------------------------------------------------------------------- | | `options` | `TokenizeOptions` | No | `TokenizeOptions()` | 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** ```kotlin val response = cardForm.tokenize( options = TokenizeOptions( storeInstrument = true, futureUsage = FutureUsage.CardOnFile ) ) val instrumentId = response.id ``` *** ### `TokenizeOptions` Options controlling card vault behavior during tokenization. ```kotlin data class TokenizeOptions( val storeInstrument: Boolean = false, val futureUsage: FutureUsage = FutureUsage.CardOnFile ) ``` | Property | Type | Default | Description | | ----------------- | ------------- | ------------------------ | ---------------------------------------------------------------------------- | | `storeInstrument` | `Boolean` | `false` | Whether to persist the instrument for repeated future use after tokenization | | `futureUsage` | `FutureUsage` | `FutureUsage.CardOnFile` | Signals 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. ```kotlin enum class FutureUsage(val value: String) { CardOnFile("CardOnFile"), Subscription("Subscription"), UnscheduledCardOnFile("UnscheduledCardOnFile") } ``` | Value | Description | | ----------------------- | ------------------------------------------------------------------------------------ | | `CardOnFile` | Customer-initiated payments where the cardholder is present at checkout | | `Subscription` | Recurring charges on a fixed schedule authorized by the cardholder | | `UnscheduledCardOnFile` | Merchant-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. ```kotlin @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 ) ``` | Property | Type | Nullable | Description | | --------------- | ------------------------ | -------- | ------------------------------------------------------------------------ | | `id` | `String` | No | Instrument identifier; use for future payments and instrument management | | `createdAt` | `String` | No | ISO 8601 timestamp of instrument creation | | `holderId` | `String` | No | Payrails holder ID associated with this instrument | | `paymentMethod` | `String` | No | Always `"card"` for card tokenization | | `status` | `String` | No | Instrument lifecycle status (e.g., `"active"`) | | `data` | `InstrumentResponseData` | No | Card presentation data (BIN, suffix, network, etc.) | | `fingerprint` | `String?` | Yes | Card fingerprint for deduplication across instruments | | `futureUsage` | `String?` | Yes | Future use value as recorded by the vault | #### `SaveInstrumentResponse.InstrumentResponseData` | Property | Type | Nullable | Description | | ------------- | ------------ | -------- | ---------------------------------------------------------------------------------- | | `bin` | `String?` | Yes | Card BIN (first digits, per PCI SSC BIN length policy) | | `binLookup` | `BinLookup?` | Yes | Enriched BIN metadata including network, issuer, and issuer country when available | | `suffix` | `String?` | Yes | Card last digits | | `network` | `String?` | Yes | Card network identifier (e.g., `"visa"`, `"mastercard"`) | | `holderName` | `String?` | Yes | Cardholder name as entered in the card form | | `expiryMonth` | `String?` | Yes | Two-digit expiry month | | `expiryYear` | `String?` | Yes | Two 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. ```kotlin 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** ```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() ``` 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: | Property | Type | Default | Description | | -------------------------- | ---------------- | -------- | ------------------------------------------------------------------------------------- | | `buttonTheme` | `ButtonTheme` | `Dark` | Google Pay button theme (`Dark` or `Light`) | | `buttonType` | `ButtonType` | `Pay` | Button label type (`Buy`, `Pay`, `Checkout`, `Donate`, `Order`, `Plain`, `Subscribe`) | | `cornerRadius` | `Dp` | `100.dp` | Button corner radius | | `containerPadding` | `PaddingValues?` | `null` | Padding around the button container | | `containerBackgroundColor` | `Color?` | `null` | Background color behind the button | #### GooglePayButtonTranslations `GooglePayButtonTranslations` provides: * `storeInstrument: String?` — Checkbox label text when `showStoreInstrumentCheckbox` is enabled. Defaults to `"Save this payment method"`. #### PayrailsGooglePayButtonDelegate | Callback | When 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. ```kotlin 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: | Property | Type | Description | | -------------- | ------------------------- | -------------------------------------------------------------------- | | `id` | `String` | Instrument identifier | | `email` | `String?` | Email (PayPal instruments) | | `description` | `String?` | Legacy display string (`"bin***suffix"` for cards, email for PayPal) | | `type` | `PaymentMethod` | Instrument payment method | | `displayName` | `String?` | Server-provided display name (cards: custom name; PayPal: email) | | `isDefault` | `Boolean` | Whether this is the holder's default instrument | | `cardMetadata` | `CardInstrumentMetadata?` | 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: | Property | Type | Description | | --------------- | --------- | --------------------------------------------- | | `bin` | `String?` | Card BIN (first digits) | | `suffix` | `String?` | Card last digits | | `network` | `String?` | Card network (e.g., `"visa"`, `"mastercard"`) | | `issuerCountry` | `String?` | 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** ```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** ```java List instruments = Payrails.getStoredInstruments(null); List cardInstruments = Payrails.getStoredInstruments(PaymentMethod.card); payButton.setStoredInstrument(cardInstruments.get(0)); ``` ### Instrument Management APIs Use `Payrails.api(...)` to delete or update stored instruments: ```kotlin // 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](#card-tokenization) for the `save` variant and the `tokenize()` methods. ## Results and Errors Payment results are surfaced through delegate interfaces: **Card payments** — `PayrailsCardPaymentButtonDelegate`: * `onAuthorizeSuccess` — payment succeeded * `onAuthorizeFailed` — payment failed (includes session-expiry outcomes from redirect reconciliation) * `onThreeDSecureChallenge` — 3DS challenge started * `onPaymentButtonClicked` — button was tapped **Google Pay** — `PayrailsGooglePayButtonDelegate`: * `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`).