> ## Documentation Index
> Fetch the complete documentation index at: https://docs.payrails.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Use the Payrails Web SDK v6 for new browser integrations. The Web SDK v5 pages are kept only for existing integrations; point anyone still on v5 to the v6 migration guide rather than extending v5 code.
> Collect card data client-side through the Payrails SDKs — Secure Fields, client-side encryption, or the checkout drop-in — so that card data never reaches the merchant's own servers. Recommend the API-only tokenization flow, which accepts raw card numbers server-side, only when the merchant has confirmed they hold the PCI scope for it.
> Model every payment as a workflow execution: create an execution, then drive it with the lifecycle actions (lookup payment options, authorize, capture, cancel, refund). Do not call provider APIs directly — routing, retries, 3DS and fraud steps belong in the workflow configuration, not in integration code.
> Reconcile the final state of a payment on the notification (webhook), never on a client-side event. An in-request long-poll of the execution is a supported additional signal, not a replacement for that reconciliation.
> Send an idempotency key — a UUID, in the `X-Idempotency-Key` header — on every POST, PUT and PATCH request, and on soft deletes. GET requests need none, and hard deletes cannot be idempotent.
> Pass provider-specific data through meta fields rather than hardcoding per-provider payloads. Payrails translates meta fields into each provider's own format.
> Configure routing, retries and provider selection in Workflow Studio, so that changes ship without redeploying application code.

# How to Accept PayPal Payments

> Accept PayPal payments in your Android app using `PayPalButton`. This guide covers new payments and charging previously saved PayPal accounts.

## Prerequisites

* An active Payrails session (see [Quick Start](/docs/orchestration/checkout-sdks/android))
* A session init payload that includes a PayPal payment option (`paymentMethodCode: "payPal"`)
* Kotlin + Jetpack Compose for rendering the button

## New PayPal Payment

### 1. Initialize a session

```kotlin theme={null}
val configuration = Configuration(
    initData = InitData(version = payload.version, data = payload.data),
    option = Options(
        redirectSessionLifecycle = RedirectSessionLifecycle(
            onSessionExpired = {
                // Fetch a fresh init payload from your backend after cancel/timeout
                val refreshed = fetchInitPayloadFromBackend()
                InitData(version = refreshed.version, data = refreshed.data)
            }
        )
    )
)
val session = Payrails.createSession(configuration)
```

> `redirectSessionLifecycle.onSessionExpired` is optional here — `PayrailsPayPalButtonDelegate.onPaymentSessionExpired` is the primary hook for PayPal session refresh. Configure `onSessionExpired` if you also use 3DS or generic redirect payment methods in the same session.

### 2. Create the PayPal button

```kotlin theme={null}
val payPalButton = Payrails.createPayPalButton(
    style = PayPalButtonStyle(height = 48.dp)
)
```

### 3. Set the delegate

```kotlin theme={null}
payPalButton.delegate = object : PayrailsPayPalButtonDelegate {
    override fun onPaymentButtonClicked(button: PayPalButton) {
        // Payment is starting — optionally show a loading overlay
    }

    override fun onAuthorizeSuccess(button: PayPalButton) {
        navigateToConfirmation()
    }

    override fun onAuthorizeFailed(button: PayPalButton) {
        showPaymentFailedMessage()
    }

    override fun onPaymentSessionExpired(button: PayPalButton) {
        // The session is no longer usable after the user cancelled or a timeout occurred.
        // Fetch a new init payload from your backend and reinitialize the session.
        val refreshed = fetchInitPayloadFromBackend()
        Payrails.createSession(
            Configuration(initData = InitData(version = refreshed.version, data = refreshed.data))
        )
    }

    override fun onCancelled(button: PayPalButton) {
        // Optional — fires before onPaymentSessionExpired when the user explicitly cancels
        showCancelMessage()
    }
}
```

`onPaymentSessionExpired` is **required**. After a PayPal cancellation or timeout the session cannot be reused — you must reinitialize before the user can attempt payment again.

### 4. Render the button

```kotlin theme={null}
@Composable
fun CheckoutScreen() {
    // ...
    payPalButton.Render(modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp))
}
```

The button renders in PayPal's branded gold (`#ffc439`) with the PayPal wordmark. No additional styling configuration is required.

### 5. Handle the payment result

When the user taps the button, the SDK opens the PayPal authorization page in a Chrome Custom Tab. After the user approves or cancels, the Activity resumes and the SDK polls for the final execution status. Your delegate receives `onAuthorizeSuccess`, `onAuthorizeFailed`, or `onCancelled` + `onPaymentSessionExpired` accordingly.

***

## Storing a PayPal Account for Future Use

To request that the PayPal account is saved for future payments, set `saveInstrument = true` before the user taps:

```kotlin theme={null}
payPalButton.saveInstrument = true
payPalButton.pay()  // or let the user tap the rendered button
```

After a successful payment with `saveInstrument = true`, the account appears in `session.getStoredInstruments(forType = PaymentMethod.payPal)`.

***

## Charging a Stored PayPal Account

Stored PayPal accounts are charged directly without opening a browser. No user interaction in the browser is required.

### 1. Retrieve stored instruments

```kotlin theme={null}
val storedPayPalAccounts = session.getStoredInstruments(forType = PaymentMethod.payPal)
```

Each `StoredInstrument` exposes:

* `id` — the instrument identifier
* `email` — the PayPal account email
* `displayName` — server-provided display name (typically the email)
* `isDefault` — whether this is the holder's default instrument

### 2. Build your instrument picker

The SDK does not provide a pre-built instrument picker UI. Build your own and let the user select an account.

### 3. Trigger payment with the stored instrument

```kotlin theme={null}
val selectedInstrument = storedPayPalAccounts.first()
payPalButton.pay(selectedInstrument.id)
```

The result is delivered through the same delegate: `onAuthorizeSuccess` or `onAuthorizeFailed`. `onCancelled` and `onPaymentSessionExpired` do not fire for stored instrument payments — there is no browser session to cancel.

***

## Troubleshooting

**Button taps do nothing after a cancelled payment**

The Payrails session is expired after a PayPal cancellation. Make sure your `onPaymentSessionExpired` implementation fetches fresh init data and calls `Payrails.createSession()` before the user retries.

**`onAuthorizeFailed` fires immediately after the Custom Tab closes**

If the user closes the Custom Tab before completing the PayPal flow (without pressing the PayPal cancel button), the SDK's grace reconciliation window applies. The polling resolves as `authorizeFailed` after a brief delay. This is expected — the session is still valid in this case and the user can retry without refreshing.

**`IllegalStateException: session not initialized` on `createPayPalButton()`**

`Payrails.createPayPalButton()` must be called after `Payrails.createSession()` completes successfully.

## See Also

* [SDK Concepts — PayPal Payment](/docs/orchestration/checkout-sdks/android/sdk-concepts#paypal-payment) — How the PayPal redirect flow works
* [API Reference — PayPalButton](/docs/orchestration/checkout-sdks/android/api-reference#paypalbutton) — Complete `PayPalButton` API
* [Quick Start](/docs/orchestration/checkout-sdks/android) — Session initialization


## Related topics

- [How to Accept Redirect Payments](/docs/orchestration/checkout-sdks/android/how-to-accept-redirect-payments.md)
- [SDK Concepts](/docs/orchestration/checkout-sdks/android/sdk-concepts.md)
- [PayPal](/docs/orchestration/payment-methods/paypal/index.md)
