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

# Stripe Payments

> Track a Stripe PaymentIntent from confirmation through charge, funds availability, and payout

## Overview

A Stripe payment resolves in stages that are hours or days apart. The PaymentIntent succeeds in seconds, the resulting charge produces a balance transaction that becomes available later, and the payout that moves the money to a bank account arrives later still. Instrumenting only the API call that creates the intent captures the first moment and none of the rest.

Mirador's Stripe hint ties a PaymentIntent to a trace by its ID. The backend `stripehint` processor polls Stripe and emits the settlement lifecycle onto that same trace:

* `StripeHintAdded` → `StripeTrackingStarted`
* `StripePaymentSucceeded` (charge and balance transaction resolved)
* `StripeFundsAvailable` (gross, fee, and net, with the availability date)
* `StripePayoutPaid` (the payout that settled the funds)
* `StripeTrackingFinished` (terminal)

You record the hint once. The lifecycle is filled in for you.

<Note>
  Stripe hints are emitted over OpenTelemetry only. Unlike the Web3 hints, there is no Web SDK method — no `Web3Plugin`, no `trace.payment.*` namespace. Emit the reserved span event described below.
</Note>

## Recording a Payment Hint

Add the `mirador.payment.stripe.intenthint` event to the active span once Stripe has returned a PaymentIntent ID.

<CodeGroup>
  ```typescript TypeScript theme={null}
  span.addEvent('mirador.payment.stripe.intenthint', {
    'stripe.payment_intent_id': paymentIntent.id,
  });
  ```

  ```go Go theme={null}
  span.AddEvent("mirador.payment.stripe.intenthint", trace.WithAttributes(
      attribute.String("stripe.payment_intent_id", paymentIntent.ID),
  ))
  ```

  ```python Python theme={null}
  span.add_event("mirador.payment.stripe.intenthint", {
      "stripe.payment_intent_id": payment_intent.id,
  })
  ```
</CodeGroup>

The PaymentIntent ID is the only attribute Mirador needs. Amounts, currency, charge IDs, fees, and payout details are resolved server-side from Stripe's API.

### Attributes

| Attribute                  | Type   | Required | Description                                                         |
| -------------------------- | ------ | -------- | ------------------------------------------------------------------- |
| `stripe.payment_intent_id` | string | Yes      | The PaymentIntent ID (`pi_…`)                                       |
| `stripe.account_id`        | string | No       | A Stripe Connect connected account (`acct_…`) to route API calls to |

No chain attribute applies. Stripe is not a blockchain hint, so `chain.id` and `chain.name` do not affect tracking — like any extra attribute, they are preserved on the event as context.

### Connected Accounts

On a Stripe Connect platform, pass the connected account that owns the payment so Mirador queries the right account.

```typescript theme={null}
span.addEvent('mirador.payment.stripe.intenthint', {
  'stripe.payment_intent_id': paymentIntent.id,
  'stripe.account_id': connectedAccountId,
});
```

Omit `stripe.account_id` when observing payments on your own account.

## Lifecycle Events

The hint opens a synthetic `stripehint` span on the trace, and the lifecycle nests inside it.

| Event                    | Emitted when                              | Carries                                                    |
| ------------------------ | ----------------------------------------- | ---------------------------------------------------------- |
| `StripeHintAdded`        | The hint is accepted                      | PaymentIntent ID, optional account ID                      |
| `StripeTrackingStarted`  | The processor begins polling Stripe       | PaymentIntent ID                                           |
| `StripePaymentSucceeded` | The intent reaches a resolved state       | Payment status, amount, charge ID, balance transaction ID  |
| `StripeFundsAvailable`   | The balance transaction becomes available | Gross, fee, net, availability date                         |
| `StripePayoutPaid`       | The settling payout is paid               | Payout ID, amount, status, arrival date, automatic flag    |
| `StripeTrackingFinished` | The lifecycle reaches a terminal state    | Terminal status, PaymentIntent, charge, payout, net amount |

<Tip>
  Monetary values use Stripe's native representation: an integer in the currency's minor unit (cents for USD) plus a lowercase ISO currency code. This mirrors Stripe's own `amount`/`currency` pair exactly, so no precision is lost.
</Tip>

## Terminal Statuses

`StripeTrackingFinished` closes the lifecycle with one of:

| Status           | Meaning                                           |
| ---------------- | ------------------------------------------------- |
| `SUCCESS`        | Payment succeeded and funds were paid out         |
| `PAYMENT_FAILED` | The PaymentIntent failed or was canceled          |
| `REFUNDED`       | The charge was refunded                           |
| `DISPUTED`       | The charge was disputed or charged back           |
| `PAYOUT_FAILED`  | The payout to the bank account failed             |
| `TIMEOUT`        | Tracking gave up before reaching a terminal phase |
| `NOT_FOUND`      | The PaymentIntent was never found in Stripe       |

A payment that settles normally but has not yet paid out is still in flight, not failed. `TIMEOUT` means Mirador stopped watching, not that Stripe failed.

## End-to-End Example

```typescript theme={null}
import { trace } from '@opentelemetry/api';

const tracer = trace.getTracer('checkout');

await tracer.startActiveSpan('checkout-flow', async (span) => {
  try {
    // 1. Create the PaymentIntent
    const paymentIntent = await stripe.paymentIntents.create({
      amount: 4999,
      currency: 'usd',
    });

    // 2. Tie it to the trace — only after Stripe returns the ID
    span.addEvent('mirador.payment.stripe.intenthint', {
      'stripe.payment_intent_id': paymentIntent.id,
    });

    // 3. Confirm the payment. Nothing further is needed Mirador-side — the
    //    stripehint processor emits the charge, funds-available, and payout
    //    events onto this trace as Stripe reports them.
    await confirmPayment(paymentIntent);
  } finally {
    span.end();
  }
});
```

## Combining With Other Hints

Stripe hints compose freely with the Web3 hints. A flow that charges a card and then settles onchain can carry both, and each resolves independently onto the same trace.

```typescript theme={null}
span.addEvent('mirador.payment.stripe.intenthint', {
  'stripe.payment_intent_id': paymentIntent.id,
});

span.addEvent('mirador.web3.evm.txhint', {
  'tx.hash': settlementTx.hash,
  'chain.id': 8453,
});
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Enrichment Hints" icon="wand-magic-sparkles" href="/opentelemetry/enrichment-hints">
    The full reserved-event catalog and attribute contract
  </Card>

  <Card title="Market Rates" icon="chart-line" href="/concepts/market-rates">
    Resolve asset spot rates and USD notionals
  </Card>

  <Card title="Traces over OTLP" icon="route" href="/opentelemetry/traces">
    Exporter configuration and span mapping
  </Card>

  <Card title="Automation Rules" icon="bolt" href="/automations/rules">
    Alert on payment failures and disputes
  </Card>
</CardGroup>
