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

# Save cards for connected merchants

> Mount Ionic's card form on your platform's page to save a card for a connected merchant.

Mount a Payment Block inside an HTML element on your platform's page to collect
and save a customer's card for a connected merchant. Use a SetupIntent to save
the card without taking a payment. The Block displays the card fields and a
consent checkbox.

Your platform uses its own API keys and selects the connected merchant with
the SDK's `account` option. The SetupIntent, customer, and saved card belong to
that merchant. Use the same merchant ID on your server and in the browser.
See [Save a card for later](/guides/save-cards) for consent options and results.

## Before you begin

* Platform keys and an active connection in the intended mode.
* An active receiving merchant with payments enabled and payment activation
  complete. Live requests also require live payment capability. These requirements
  apply to saving cards through Connect even though no payment is taken.
* `setup_intents:write` and `setup_intents:read` on both the key and connection.
* A customer belonging to the selected merchant. Creating that customer also
  requires `customers:write`; see [Connect customers](/guides/customers#customers-on-a-connect-platform).
* The page's origin registered in **Connect → Payment domains** for the same mode.
* `@ionicfi/sdk` 0.4.1 or later and `@ionicfi/js` 0.2.1 or later.

## Create the SetupIntent on your server

The merchant and customer must be selected by your application's authorization
logic. The SDK's `account` option sends `Ionic-Account` for this request:

```ts theme={null}
import { Ionic } from "@ionicfi/sdk";

const ionic = new Ionic({ token: process.env.IONIC_PLATFORM_SECRET_KEY });
const intent = await ionic.setupIntents.create(
  {
    "Idempotency-Key": requestKey,
    currency: "usd",
    customer: "cus_BBBBBBBBBBBBBB",
  },
  { account: "mer_BBBBBBBBBBBBBB" },
);
```

`requestKey` represents an application-assigned key for this operation.
The same operation uses the same key and body within Ionic's
[idempotency window](/api-reference/idempotency).

The create response includes `id` and `client_secret`. Secret-key retrieval
does not return the secret's value. The browser needs the platform publishable
key, the selected merchant ID, and authorized access to that merchant's intent
secret. Client secrets must stay out of URLs, logs, and analytics.

## Mount the card form on your page

Add an empty HTML element where you want the card form to appear:

```html theme={null}
<div id="payment"></div>
```

Run this code in the browser after the element exists. Replace the placeholder
values with your platform's publishable key, the connected merchant ID used on
your server, and the `client_secret` returned when that merchant's SetupIntent
was created.

```ts theme={null}
import { loadIonic } from "@ionicfi/js";

const ionic = await loadIonic("pk_v1_test_...", { account: "mer_BBBBBBBBBBBBBB" });
const blocks = ionic.blocks({ clientSecret: "seti_..._secret_..." });
const payment = blocks.create("payment");
await payment.mount("#payment");
```

The Block type is `"payment"` for both payment collection and card saving. The
SetupIntent client secret selects card saving here. `payment.mount("#payment")`
displays the Block inside `<div id="payment">`; `#payment` is the CSS selector
for that element.

Mounting displays the Block. Saving the card is a separate call to `confirmSetup`.

## Confirm the save and read its status

`ionic.confirmSetup({ blocks })` submits the card details from the mounted
Block for its SetupIntent when your customer agrees to save the card.
`ionic.retrieveSetupIntent(clientSecret)` reads that save request's current
status. Both calls use the connected merchant selected in `loadIonic`.

The merchant is fixed for the client's lifetime. A different merchant requires
a new client and an intent belonging to that merchant. `blocks.destroy()` removes
the UI without canceling a submitted operation.

## Ownership and result

The server retrieval endpoint takes the same `Ionic-Account` selection:

```bash theme={null}
curl "https://api.ionicfi.com/v1/setup_intents/$SETUP_INTENT_ID" \
  -H "Authorization: Bearer $IONIC_PLATFORM_SECRET_KEY" \
  -H "Ionic-Account: $CONNECTED_MERCHANT_ID"
```

| Response field                | Contract                                                                          |
| ----------------------------- | --------------------------------------------------------------------------------- |
| `id`                          | Identifies the intended SetupIntent.                                              |
| `merchant_id`                 | Must match the merchant selected for this save request.                           |
| `customer`                    | Must match the intended customer on that merchant.                                |
| `livemode`                    | Must match the intended mode.                                                     |
| `status` and `payment_method` | A usable saved-card result requires `succeeded` and a non-null payment method ID. |

An application reference can collide across merchants and modes. It does not
establish ownership or authorize access to a saved card or client secret.
A lost response does not imply failure; the existing intent can be retrieved.
[Save a card](/guides/save-cards#saved-card-result) describes each status and
[Blocks](/guides/payment-fields#reloads-and-retries) describes retry limits.

## SetupIntent operations

| Action               | Endpoint                              | SDK method              | Permission            |
| -------------------- | ------------------------------------- | ----------------------- | --------------------- |
| Create               | `POST /v1/setup_intents`              | `setupIntents.create`   | `setup_intents:write` |
| Retrieve             | `GET /v1/setup_intents/{id}`          | `setupIntents.retrieve` | `setup_intents:read`  |
| List                 | `GET /v1/setup_intents`               | `setupIntents.list`     | `setup_intents:read`  |
| Confirm with a token | `POST /v1/setup_intents/{id}/confirm` | `setupIntents.confirm`  | `setup_intents:write` |
| Cancel               | `POST /v1/setup_intents/{id}/cancel`  | `setupIntents.cancel`   | `setup_intents:write` |

Each endpoint accepts `Ionic-Account`; the server SDK accepts `{ account }` as
the second argument. The server confirm endpoint is the tokenization interface
for [advanced card fields](/guides/payment-fields-advanced). Blocks confirms
through `confirmSetup`.

Only an intent in `requires_confirmation` can be canceled. Cancellation does
not revoke an already saved card. A future charge is a separate
[PaymentIntent operation](/api-reference/payment-intents/create-a-payment-intent)
for the same merchant and customer, requiring `payments:write`.

For live-mode setup, see [Going live](/guides/going-live#launching-a-connect-integration).
