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

# First payment

> Understand the default payment flow and what Primer handles for you

You've installed the SDK. You're ready to accept payments.

## The default payment flow

When a customer reaches your checkout, here's what happens:

```mermaid theme={"dark"}
sequenceDiagram
    participant User
    participant App as Your App
    participant SDK as Primer SDK
    participant Primer as Primer API
    participant PSP as Payment Processor

    App->>Primer: Create client session
    Primer-->>App: clientToken
    App->>SDK: Initialize checkout
    SDK->>Primer: Load payment methods
    Primer-->>SDK: Available methods

    User->>SDK: Selects payment method
    User->>SDK: Enters payment details
    SDK->>SDK: Validates input

    User->>SDK: Submits payment
    SDK->>Primer: Submit payment
    Primer->>PSP: Process payment
    PSP-->>Primer: Result
    Primer-->>SDK: Payment result
    SDK-->>App: Success / Failure
```

1. **Your server creates a client session** and returns a `clientToken`
2. **The SDK initializes** and loads available payment methods
3. **Customer selects a method** and enters their details
4. **Customer submits** and the SDK processes the payment
5. **Your app receives the result** via events

## Create a Client Session (Server-Side)

Create a client session on your server using the [Client Session API](/checkout/client-session). This returns a `clientToken` that you pass to your client.

```bash theme={"dark"}
curl -X POST https://api.primer.io/client-session \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "orderId": "order-123",
    "currencyCode": "USD",
    "amount": 1000,
    "order": {
      "lineItems": [{
        "itemId": "item-1",
        "description": "Widget",
        "amount": 1000,
        "quantity": 1
      }]
    }
  }'
```

## Initialize and Show Checkout

<Tabs>
  <Tab title="Web">
    ```html theme={"dark"}
    <primer-checkout client-token="your-client-token"></primer-checkout>
    ```

    Listen for payment results:

    ```javascript theme={"dark"}
    const checkout = document.querySelector('primer-checkout');

    checkout.addEventListener('primer:ready', (event) => {
      const primer = event.detail;

      primer.onPaymentSuccess = ({ payment }) => {
        window.location.href = `/confirmation?order=${payment.orderId}`;
      };

      primer.onPaymentFailure = ({ error }) => {
        console.error('Payment failed:', error.diagnosticsId);
      };
    });
    ```
  </Tab>

  <Tab title="Android">
    ```kotlin theme={"dark"}
    PrimerCheckoutSheet(
        checkout = rememberPrimerCheckoutController(
            clientToken = clientToken,
        )
    )
    ```
  </Tab>

  <Tab title="iOS">
    ```swift theme={"dark"}
    import SwiftUI
    import PrimerSDK

    struct CheckoutView: View {
      let clientToken: String

      var body: some View {
        PrimerCheckout(
          clientToken: clientToken,
          onCompletion: { state in
            switch state {
            case .success(let result):
              // Navigate to confirmation
              print("Payment succeeded: \(result.payment?.id ?? "")")
            case .failure(let error):
              print("Payment failed: \(error.errorId)")
            case .dismissed:
              print("Checkout was dismissed")
            default:
              break
            }
          }
        )
      }
    }
    ```
  </Tab>
</Tabs>

## What Primer handles for you

Out of the box, Primer Checkout manages:

| Concern                  | What Primer Does                                      |
| ------------------------ | ----------------------------------------------------- |
| **Validation**           | Real-time input validation with inline error messages |
| **Error display**        | Payment failures shown automatically in the UI        |
| **Redirects**            | 3DS challenges, bank redirects, and wallet flows      |
| **Loading states**       | Visual feedback during processing                     |
| **Payment method logic** | Each method's specific requirements and flows         |

You don't need to write code for any of these behaviors.

## Listening for results

To react to payment outcomes, listen for payment events:

<Tabs>
  <Tab title="Web">
    ```javascript theme={"dark"}
    const checkout = document.querySelector('primer-checkout');

    checkout.addEventListener('primer:payment-success', (event) => {
      const { payment } = event.detail;
      window.location.href = `/confirmation?order=${payment.orderId}`;
    });

    checkout.addEventListener('primer:payment-failure', (event) => {
      const { error } = event.detail;
      // Error is already displayed in the checkout UI
      console.error('Payment failed:', error.diagnosticsId);
    });
    ```
  </Tab>

  <Tab title="Android">
    ```kotlin theme={"dark"}
    PrimerCheckoutSheet(
        checkout = checkout,
        onEvent = { event ->
            when (event) {
                is PrimerCheckoutEvent.Success -> {
                    val payment = event.checkoutData.payment
                    navController.navigate("confirmation/${payment.id}")
                }
                is PrimerCheckoutEvent.Failure -> {
                    Log.e("Checkout", "Failed: ${event.error.diagnosticsId}")
                }
            }
        },
    )
    ```
  </Tab>

  <Tab title="iOS">
    ```swift theme={"dark"}
    PrimerCheckout(
      clientToken: clientToken,
      onCompletion: { state in
        switch state {
        case .success(let result):
          // Navigate to confirmation screen
          print("Payment succeeded: \(result.payment?.id ?? "")")
        case .failure(let error):
          // Error is already displayed in the checkout UI
          print("Payment failed: \(error.errorId)")
        case .dismissed:
          print("Checkout was dismissed")
        default:
          break
        }
      }
    )
    ```

    | State                     | When it fires                    |
    | ------------------------- | -------------------------------- |
    | `.success(PaymentResult)` | Payment completed successfully   |
    | `.failure(PrimerError)`   | Payment failed or error occurred |
    | `.dismissed`              | User dismissed the checkout      |
  </Tab>
</Tabs>

## Testing in sandbox

Use these test cards to verify your integration:

| Card Number           | Result   |
| --------------------- | -------- |
| `4111 1111 1111 1111` | Success  |
| `4000 0000 0000 0002` | Declined |

Use any future expiry date and any 3-digit CVV.

Try the Web SDK example in your browser:

<a href="https://stackblitz.com/fork/github/primer-io/examples/tree/main/examples/primer-checkout-basic" target="_blank">
  <img src="https://developer.stackblitz.com/img/open_in_stackblitz.svg" alt="Try it in Stackblitz" noZoom />
</a>

## See also

<CardGroup cols={2}>
  <Card title="Styling" icon="paintbrush" href="/checkout/primer-checkout/build-your-ui/styling-customization">
    Match the checkout to your brand
  </Card>

  <Card title="Layout" icon="table-layout" href="/checkout/primer-checkout/build-your-ui/layout-customization">
    Control the structure of checkout components
  </Card>

  <Card title="Core Concepts" icon="lightbulb" href="/checkout/primer-checkout/core-concepts">
    Understand the architecture
  </Card>

  <Card title="Track in Analytics" icon="chart-line" href="/checkout/primer-checkout/guides-and-recipes/track-payment-analytics">
    Send events to your analytics platform
  </Card>
</CardGroup>
