> ## 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.

# Show custom success message

> Display a custom success message or modal after payment completion.

Display a personalized success message or modal to users after their payment completes.

## Recipe

<Tabs>
  <Tab title="Web">
    ```javascript theme={"dark"}
    checkout.addEventListener('primer:ready', (event) => {
      const primer = event.detail;

      primer.onPaymentSuccess = ({ payment, paymentMethodType }) => {
        const message = `Payment of ${payment.amount} completed via ${paymentMethodType}`;
        showSuccessModal(message);
      };
    });
    ```
  </Tab>

  <Tab title="Android">
    ```kotlin theme={"dark"}
    PrimerCheckoutSheet(
        checkout = checkout,
        onEvent = { event ->
            when (event) {
                is PrimerCheckoutEvent.Success -> {
                    val payment = event.checkoutData.payment
                    Log.d("Checkout", "Payment completed: ${payment.id}")

                    navController.navigate("confirmation/${payment.id}") {
                        popUpTo("checkout") { inclusive = true }
                    }
                }
                is PrimerCheckoutEvent.Failure -> {
                    val error = event.error
                    Log.e("Checkout", "Failed: ${error.description}")
                    Log.e("Checkout", "Diagnostics: ${error.diagnosticsId}")
                }
            }
        },
    )
    ```
  </Tab>

  <Tab title="iOS">
    ```swift theme={"dark"}
    struct CheckoutView: View {
      let clientToken: String
      @State private var showSuccess = false

      var body: some View {
        ZStack {
          PrimerCheckout(
            clientToken: clientToken,
            onCompletion: { state in
              if case .success = state {
                withAnimation { showSuccess = true }
              }
            }
          )

          if showSuccess {
            SuccessOverlay()
              .transition(.opacity)
          }
        }
      }
    }
    ```
  </Tab>
</Tabs>

## How it works

<Tabs>
  <Tab title="Web">
    1. Listen for the `primer:ready` event to access the Primer SDK instance
    2. Set the `onPaymentSuccess` callback
    3. Access `payment` details and `paymentMethodType` from the callback parameters
    4. Display your custom UI (modal, toast, inline message, etc.)
  </Tab>

  <Tab title="Android">
    1. Use the `onEvent` callback on `PrimerCheckoutSheet` or `PrimerCheckoutHost`
    2. Handle `PrimerCheckoutEvent.Success` to access `PrimerCheckoutData` with payment details
    3. Navigate to a confirmation screen or update your order UI

    | Property          | Type      | Description           |
    | ----------------- | --------- | --------------------- |
    | `payment.id`      | `String`  | The Primer payment ID |
    | `payment.orderId` | `String?` | Your order ID         |

    <Info>
      When using `PrimerCheckoutSheet`, the SDK shows a default success screen that auto-dismisses after 3 seconds. The `Success` event fires immediately -- you do not need to wait for the dismiss. Navigate as soon as you receive the event.
    </Info>
  </Tab>

  <Tab title="iOS">
    1. Use the `onCompletion` callback on `PrimerCheckout`
    2. Match on `.success` to detect payment completion
    3. Access `result.payment?.id` for the payment ID
    4. Update your SwiftUI state to show a custom success view
  </Tab>
</Tabs>

## Variations

### Custom success with payment details

<Tabs>
  <Tab title="Web">
    ```javascript theme={"dark"}
    primer.onPaymentSuccess = ({ payment }) => {
      document.getElementById('success-container').innerHTML = `
        <h2>Thank you for your order!</h2>
        <p>Order ID: ${payment.orderId}</p>
        <p>Amount: ${formatCurrency(payment.amount, payment.currencyCode)}</p>
      `;
    };
    ```
  </Tab>

  <Tab title="Android">
    Display payment details from the `Success` event:

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

  <Tab title="iOS">
    Display payment details from the success result:

    ```swift theme={"dark"}
    PrimerCheckout(
      clientToken: clientToken,
      onCompletion: { state in
        switch state {
        case .success(let result):
          showSuccessScreen(paymentId: result.payment?.id ?? "")
        case .failure(let error):
          print("Failed: \(error.errorId)")
        default:
          break
        }
      }
    )
    ```
  </Tab>
</Tabs>

### Handle checkout dismissal

<Tabs>
  <Tab title="Web">
    ```javascript theme={"dark"}
    primer.onPaymentSuccess = () => {
      const successEl = document.getElementById('success-message');
      successEl.classList.add('visible');

      // Hide checkout form
      document.querySelector('primer-checkout').style.display = 'none';
    };
    ```
  </Tab>

  <Tab title="Android">
    Use the `onDismiss` callback to detect when the checkout sheet is closed:

    ```kotlin theme={"dark"}
    PrimerCheckoutSheet(
        checkout = checkout,
        onEvent = { event -> /* ... */ },
        onDismiss = {
            navController.popBackStack()
        },
    )
    ```

    <Info>
      `onDismiss` fires after the sheet closes. If you already navigated away in `onEvent`, guard against double navigation.
    </Info>
  </Tab>

  <Tab title="iOS">
    Handle the `.dismissed` case to detect when the user closes the checkout:

    ```swift theme={"dark"}
    PrimerCheckout(
      clientToken: clientToken,
      onCompletion: { state in
        switch state {
        case .success:
          withAnimation { showSuccess = true }
        case .dismissed:
          // User closed the checkout
          break
        default:
          break
        }
      }
    )
    ```
  </Tab>
</Tabs>

## See also

<CardGroup cols={2}>
  <Card title="Redirect after payment" icon="arrow-right" href="/checkout/primer-checkout/guides-and-recipes/redirect-after-payment">
    Navigate to a confirmation page after payment
  </Card>

  <Card title="Error handling" icon="triangle-exclamation" href="/checkout/primer-checkout/build-your-ui/error-handling">
    Handle payment failures and display error messages
  </Card>
</CardGroup>
