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

# Track payment in analytics

> Send payment events to your analytics platform.

Track payment success and failure events in your analytics platform (Google Analytics, Segment, etc.).

## Recipe

<Tabs>
  <Tab title="Web">
    ```javascript theme={"dark"}
    document.addEventListener('primer:payment-success', (event) => {
      const { paymentSummary, paymentMethodType } = event.detail;

      analytics.track('Payment Completed', {
        paymentId: paymentSummary.id,
        orderId: paymentSummary.orderId,
        method: paymentMethodType,
        last4: paymentSummary.paymentMethodData?.last4Digits,
      });
    });

    document.addEventListener('primer:payment-failure', (event) => {
      const { error, paymentMethodType } = event.detail;

      analytics.track('Payment Failed', {
        errorCode: error.code,
        method: paymentMethodType,
        diagnosticsId: error.diagnosticsId,
      });
    });
    ```
  </Tab>

  <Tab title="Android">
    ```kotlin theme={"dark"}
    val state by checkout.state.collectAsStateWithLifecycle()
    LaunchedEffect(state) {
        when (val s = state) {
            is PrimerCheckoutState.Success -> {
                val payment = s.checkoutData.payment
                trackPaymentSuccess(payment.id, payment.orderId)
            }
            is PrimerCheckoutState.Failure -> {
                val error = s.error
                trackPaymentFailure(error.errorCode, error.diagnosticsId)
            }
            else -> Unit
        }
    }

    PrimerCheckoutSheet(checkout = checkout)
    ```
  </Tab>

  <Tab title="iOS">
    ```swift theme={"dark"}
    PrimerCheckout(clientToken: clientToken) { state in
      switch state {
      case let .success(result):
        Analytics.track("payment_success", properties: [
          "payment_id": result.paymentId
        ])
      case let .failure(error):
        Analytics.track("payment_failure", properties: [
          "error_id": error.errorId,
          "diagnostics_id": error.diagnosticsId
        ])
      default:
        break
      }
    }
    ```
  </Tab>
</Tabs>

## How it works

<Tabs>
  <Tab title="Web">
    1. Listen for `primer:payment-success` and `primer:payment-failure` DOM events
    2. Extract relevant data from `event.detail`
    3. Send to your analytics platform with meaningful event names and properties
  </Tab>

  <Tab title="Android">
    1. Observe `checkout.state` with `collectAsStateWithLifecycle()` and react in a `LaunchedEffect`
    2. Map `PrimerCheckoutState` terminal cases to analytics events
    3. Extract relevant data from each state and send to your analytics platform

    | Checkout State | Analytics Event      | Key Properties                 |
    | -------------- | -------------------- | ------------------------------ |
    | `Success`      | `purchase_completed` | `payment_id`, `order_id`       |
    | `Failure`      | `purchase_failed`    | `error_code`, `diagnostics_id` |
  </Tab>

  <Tab title="iOS">
    1. Pass an `onCompletion` closure to `PrimerCheckout` (or to the `.primerCheckoutSession(_:onCompletion:)` modifier when composing views inline). It fires once with the terminal `PrimerCheckoutState`.
    2. Map `PrimerCheckoutState` cases to analytics events
    3. Extract relevant data from each state and send to your analytics platform

    | Checkout State     | Analytics Event      | Key Properties               |
    | ------------------ | -------------------- | ---------------------------- |
    | `.success(result)` | `payment_success`    | `payment_id`                 |
    | `.failure(error)`  | `payment_failure`    | `error_id`, `diagnostics_id` |
    | `.dismissed`       | `checkout_dismissed` | —                            |
  </Tab>
</Tabs>

## Variations

### Google Analytics 4 / Firebase Analytics

<Tabs>
  <Tab title="Web">
    ```javascript theme={"dark"}
    document.addEventListener('primer:payment-success', (event) => {
      const { paymentSummary, paymentMethodType } = event.detail;

      gtag('event', 'purchase', {
        transaction_id: paymentSummary.orderId,
        value: paymentSummary.amount / 100, // Convert from cents
        currency: paymentSummary.currencyCode,
        payment_type: paymentMethodType,
      });
    });
    ```
  </Tab>

  <Tab title="Android">
    Add the Firebase Analytics dependency:

    ```kotlin theme={"dark"}
    // build.gradle.kts
    implementation(platform("com.google.firebase:firebase-bom:33.0.0"))
    implementation("com.google.firebase:firebase-analytics")
    ```

    Create an analytics tracker:

    ```kotlin theme={"dark"}
    class CheckoutAnalytics(
        private val firebaseAnalytics: FirebaseAnalytics,
    ) {
        fun trackPaymentSuccess(paymentId: String, orderId: String?) {
            firebaseAnalytics.logEvent("purchase_completed") {
                param("payment_id", paymentId)
                param("order_id", orderId.orEmpty())
            }
        }

        fun trackPaymentFailure(errorCode: String?, diagnosticsId: String) {
            firebaseAnalytics.logEvent("purchase_failed") {
                param("error_code", errorCode.orEmpty())
                param("diagnostics_id", diagnosticsId)
            }
        }

        fun trackPaymentMethodSelected(methodType: String) {
            firebaseAnalytics.logEvent("payment_method_selected") {
                param("payment_method_type", methodType)
            }
        }
    }
    ```

    Wire it into your checkout:

    ```kotlin theme={"dark"}
    @Composable
    fun CheckoutScreen(
        clientToken: String,
        analytics: CheckoutAnalytics,
    ) {
        val checkout = rememberPrimerCheckoutController(clientToken)
        val state by checkout.state.collectAsStateWithLifecycle()

        LaunchedEffect(state) {
            when (val s = state) {
                is PrimerCheckoutState.Success -> {
                    val payment = s.checkoutData.payment
                    analytics.trackPaymentSuccess(payment.id, payment.orderId)
                }
                is PrimerCheckoutState.Failure -> {
                    analytics.trackPaymentFailure(
                        s.error.errorCode,
                        s.error.diagnosticsId,
                    )
                }
                else -> Unit
            }
        }

        when (state) {
            is PrimerCheckoutState.Loading -> CircularProgressIndicator()
            is PrimerCheckoutState.Ready -> {
                PrimerCheckoutSheet(checkout = checkout)
            }
        }
    }
    ```
  </Tab>

  <Tab title="iOS">
    Add the Firebase Analytics dependency via SPM or CocoaPods, then create a tracker:

    ```swift theme={"dark"}
    class CheckoutAnalytics {
      let firebaseAnalytics: Analytics.Type = Analytics.self

      func trackState(_ state: PrimerCheckoutState) {
        switch state {
        case let .success(result):
          firebaseAnalytics.logEvent("purchase_completed", parameters: [
            "payment_id": result.paymentId,
            "amount": result.amount ?? 0,
            "currency": result.currencyCode ?? ""
          ])
        case let .failure(error):
          firebaseAnalytics.logEvent("purchase_failed", parameters: [
            "error_id": error.errorId,
            "diagnostics_id": error.diagnosticsId
          ])
        default:
          break
        }
      }
    }
    ```

    Wire it into your checkout. `PrimerCheckout` calls `onCompletion` once with the terminal state:

    ```swift theme={"dark"}
    struct CheckoutView: View {
      let clientToken: String
      let analytics = CheckoutAnalytics()

      var body: some View {
        PrimerCheckout(clientToken: clientToken) { state in
          analytics.trackState(state)
        }
      }
    }
    ```
  </Tab>
</Tabs>

### Segment / Reusable analytics wrapper

<Tabs>
  <Tab title="Web">
    ```javascript theme={"dark"}
    document.addEventListener('primer:payment-success', (event) => {
      const { paymentSummary, paymentMethodType } = event.detail;

      analytics.track('Order Completed', {
        order_id: paymentSummary.orderId,
        total: paymentSummary.amount / 100,
        currency: paymentSummary.currencyCode,
        payment_method: paymentMethodType,
      });
    });
    ```
  </Tab>

  <Tab title="Android">
    For apps that use multiple analytics providers, create an interface that decouples checkout tracking from any specific SDK:

    ```kotlin theme={"dark"}
    interface CheckoutTracker {
        fun onPaymentSuccess(paymentId: String, orderId: String?)
        fun onPaymentFailure(errorCode: String?, diagnosticsId: String)
        fun onPaymentMethodSelected(methodType: String)
    }

    class CompositeCheckoutTracker(
        private val trackers: List<CheckoutTracker>,
    ) : CheckoutTracker {
        override fun onPaymentSuccess(paymentId: String, orderId: String?) {
            trackers.forEach { it.onPaymentSuccess(paymentId, orderId) }
        }

        override fun onPaymentFailure(errorCode: String?, diagnosticsId: String) {
            trackers.forEach { it.onPaymentFailure(errorCode, diagnosticsId) }
        }

        override fun onPaymentMethodSelected(methodType: String) {
            trackers.forEach { it.onPaymentMethodSelected(methodType) }
        }
    }
    ```

    Usage:

    ```kotlin theme={"dark"}
    fun handleState(state: PrimerCheckoutState, tracker: CheckoutTracker) {
        when (state) {
            is PrimerCheckoutState.Success -> {
                val payment = state.checkoutData.payment
                tracker.onPaymentSuccess(payment.id, payment.orderId)
            }
            is PrimerCheckoutState.Failure -> {
                tracker.onPaymentFailure(state.error.errorCode, state.error.diagnosticsId)
            }
            else -> Unit
        }
    }
    ```
  </Tab>

  <Tab title="iOS">
    For apps with multiple analytics providers, create a protocol that decouples checkout tracking:

    ```swift theme={"dark"}
    protocol CheckoutTracker {
      func onPaymentSuccess(paymentId: String)
      func onPaymentFailure(errorId: String)
    }

    class CompositeCheckoutTracker: CheckoutTracker {
      let trackers: [CheckoutTracker]

      init(trackers: [CheckoutTracker]) { self.trackers = trackers }

      func onPaymentSuccess(paymentId: String) {
        trackers.forEach { $0.onPaymentSuccess(paymentId: paymentId) }
      }

      func onPaymentFailure(errorId: String) {
        trackers.forEach { $0.onPaymentFailure(errorId: errorId) }
      }
    }
    ```

    Usage:

    ```swift theme={"dark"}
    func handleState(_ state: PrimerCheckoutState, tracker: CheckoutTracker) {
      switch state {
      case let .success(result):
        tracker.onPaymentSuccess(paymentId: result.paymentId)
      case let .failure(error):
        tracker.onPaymentFailure(errorId: error.errorId)
      default:
        break
      }
    }
    ```
  </Tab>
</Tabs>

### Track payment method selection

<Tabs>
  <Tab title="Web">
    ```javascript theme={"dark"}
    document.addEventListener('primer:payment-start', (event) => {
      const { paymentMethodType } = event.detail;

      analytics.track('Payment Method Selected', {
        method: paymentMethodType,
      });
    });
    ```
  </Tab>

  <Tab title="Android">
    <Note>Payment method selection tracking is not available through `PrimerCheckoutState`. The SDK manages payment method selection internally within its UI components.</Note>
  </Tab>

  <Tab title="iOS">
    `PrimerPaymentMethods` injects a `PrimerSelectionSession` into its slots. Observe its `@Published` `state` and track changes to `selectedPaymentMethod` (a `CheckoutPaymentMethod` with `type` and `name`):

    ```swift theme={"dark"}
    struct TrackedPaymentMethods: View {
      @ObservedObject var session: PrimerSelectionSession

      var body: some View {
        PrimerPaymentMethods()
          .onChange(of: session.state.selectedPaymentMethod) { selected in
            guard let selected else { return }
            Analytics.track("payment_method_selected", properties: [
              "type": selected.type,
              "name": selected.name
            ])
          }
      }
    }
    ```
  </Tab>
</Tabs>

## See also

<CardGroup cols={2}>
  <Card title="Log errors for debugging" icon="bug" href="/docs/checkout/primer-checkout/guides-and-recipes/log-errors-debugging">
    Capture and surface errors during development
  </Card>

  <Card title="Events guide" icon="bolt" href="/docs/checkout/primer-checkout/configuration/events">
    Handle payment lifecycle events
  </Card>
</CardGroup>
