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

# Payments

> Accept payments from Blackbird members in FLY. Modeled on Stripe Payment Intents.

Flynet Payment Intents let approved partners accept payments
from Blackbird members in FLY, on behalf of third-party merchants.
The model is closely analogous to Stripe Payment Intents: you
create an intent, the member confirms it, and FLY transfers from
the member's wallet to the merchant.

## The merchant ID is your third credential

Every `POST /payment_intents` requires a `flynet_merchant_id`, a UUID identifying the merchant receiving funds. You receive it in your onboarding email if you applied for payments access. It's scoped to your `client_id` and your environment; staging and production merchant IDs differ.

If `flynet_merchant_id` is missing, wrong, or scoped to a different partner, `create` returns:

```json theme={null}
{
  "error": {
    "type": "invalid_request_error",
    "code": "resource_not_found",
    "message": "Flynet merchant not found.",
    "param": null
  }
}
```

The merchant ID is not a secret on its own (it's an addressing field), but it's how the API knows which payee to route to. Store it alongside `client_id` and `API_KEY` in your env config; don't hard-code it.

## The primitive

A **PaymentIntent** moves FLY from a member's wallet to a
merchant's wallet. Each intent has:

* A `flynet_merchant_id` - the merchant receiving the payment
* A `customer_user_id` - the Blackbird member paying
* An `amount` in FLY
* A `status` that derives from event timestamps

## Status lifecycle

```text theme={null}
                  confirm
pending -----------------------> paid
   |                              |
   | cancel                       | refund
   v                              v
canceled                       refunded

(expires_at elapses before confirm -> expired)
```

| Status     | When                                       |
| ---------- | ------------------------------------------ |
| `pending`  | Intent created, not yet confirmed          |
| `paid`     | Confirmed; FLY has transferred             |
| `canceled` | Canceled before confirm                    |
| `refunded` | Paid then refunded; FLY returned to member |
| `expired`  | `expires_at` elapsed without confirm       |

<Note>
  **Tasting note** - `status` is computed at read time from the
  timestamps on the intent. You do not transition states directly;
  you call `cancel`, `confirm`, or `refund` and the status follows.
</Note>

## v1 capabilities

| Capability                     | v1 status                       |
| ------------------------------ | ------------------------------- |
| FLY-to-FLY transfers           | Live                            |
| Idempotent create              | Live                            |
| Cancel pending intents         | Live                            |
| Refund paid intents, full only | Live                            |
| Receipt emails on confirm      | Live                            |
| Card-funded payments           | Coming soon                     |
| Partial refunds                | Coming soon                     |
| Webhook delivery               | Coming soon; poll status for v1 |
| Hosted checkout page           | Coming soon                     |

<Warning>
  **Chef's warning** - v1 is FLY-only. If the member does not already
  hold enough FLY, `confirm` returns 400 `payment0030`. You cannot
  card-fund or auto-load FLY in v1.
</Warning>

## Idempotency

Network calls fail. Retries are normal. Without idempotency, retries on `POST /payment_intents` could create duplicate charges. Flynet uses idempotency keys to make retries safe.

Every Payment Intent create requires an `idempotency_key` in the request body. The key is unique per `(flynet_merchant_id, idempotency_key)`.

| Scenario                  | Response                       |
| ------------------------- | ------------------------------ |
| First create with key `X` | `201 Created`, new intent      |
| Replay with same key `X`  | `200 OK`, same intent returned |
| New create with key `Y`   | `201 Created`, new intent      |

```js theme={null}
async function createPaymentIntent(orderId) {
  const response = await fetch(`${API_BASE_URL}/payment_intents`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${ACCESS_TOKEN}`,
    },
    body: JSON.stringify({
      flynet_merchant_id: process.env.MERCHANT_ID,
      customer_user_id: userId,
      amount: { value: "1000000000000000000", currency: "FLY" },
      idempotency_key: orderId,
      description: `Order ${orderId}`,
    }),
  });
  return response.json();
}
```

<Tip>
  **From the kitchen** - Use your own order ID as the idempotency key. Network retries on the same order naturally return the same Payment Intent without creating a duplicate.
</Tip>

Idempotency on `confirm`, `cancel`, and `refund` is state-based: re-confirming a paid intent or re-canceling a canceled intent returns the same intent without side effects. No separate key needed.

## Money

All amounts use the [Money](/concepts/money-and-tokens) wire shape:
`{ value, currency }` with `value` as a stringified integer in FLY wei.

## Receipts

On successful `confirm`, the customer receives a receipt email from
`flynet@blackbird.xyz`.

## Auth

All Payment Intent routes require an **OAuth access token**. API keys
are not accepted on payment routes.

## Endpoints at a glance

| Method | Path                            | Purpose                  |
| ------ | ------------------------------- | ------------------------ |
| POST   | `/payment_intents`              | Create                   |
| GET    | `/payment_intents/{id}`         | Retrieve                 |
| GET    | `/payment_intents`              | List                     |
| POST   | `/payment_intents/{id}/cancel`  | Cancel pending           |
| POST   | `/payment_intents/{id}/confirm` | Confirm and transfer FLY |
| POST   | `/payment_intents/{id}/refund`  | Refund a paid intent     |

See the [API Reference](/api-reference/payments/create) and the
[first payment recipe](/recipes/mains/first-payment).

## What's coming

Card-funded confirm, partner webhooks, partial refunds, and a hosted
checkout surface are on the roadmap.
