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

# Build a payment page with Ionic Blocks

> The PaymentIntent, browser, and event contracts for a payment page built with Ionic Blocks.

Ionic Blocks collects card details and confirms a PaymentIntent on your page.
Your application owns the amount, customer authorization, payment lifecycle,
and fulfillment policy. This page describes the Ionic interfaces that support
those decisions.

This guide uses a direct merchant's keys. For a platform, see
[Accept payments for connected merchants](/guides/connect-blocks).

## Before you begin

| Value               | Contract                                                                                                                                          |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| Secret key          | Used only on your backend to create and manage intents.                                                                                           |
| Publishable key     | Used by Ionic.js in the browser. It must match the intent's merchant and mode.                                                                    |
| Client secret       | Authorizes browser access to one intent. Provide it only to the customer authorized to use that intent. Keep it out of URLs, logs, and analytics. |
| Payment page origin | Registered in **Developers → Web domains** for the same mode. Scheme, hostname, and port must match.                                              |

Use test keys while building. Subdomains and different ports are separate
origins. [Authentication](/api-reference/authentication) covers keys;
[SDK upgrades](/sdks/upgrading) covers the supported package versions.

## PaymentIntent creation

`paymentIntents.create` creates a new intent. With no payment method or token,
the request sets up a payment for later collection through Blocks.

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

const ionic = new Ionic({ token: process.env.IONIC_SECRET_KEY });
const paymentIntent = await ionic.paymentIntents.create({
  "Idempotency-Key": requestKey,
  amount: 12500,
  currency: "usd",
  capture_method: "automatic",
});
```

`amount` is in minor units: `12500` is \$125.00 for USD. The amount and receiving
merchant must be authorized by your backend. `requestKey` represents
an application-assigned key for this API operation; reuse it with the same body
when repeating that operation. The SDK generates a new key for each call when
one is omitted. See [Idempotency](/api-reference/idempotency).

| Response field  | Meaning                                                          |
| --------------- | ---------------------------------------------------------------- |
| `id`            | Ionic's PaymentIntent identifier.                                |
| `client_secret` | The capability used to create a Blocks instance for this intent. |
| `status`        | The current payment state.                                       |

The creation response includes the client secret. Secret-key retrieval and
webhook payloads omit its value; a reload cannot recover it through those
interfaces. Your application controls authorized access to any retained secret.

The optional `reference` field is your correlation value. Ionic does not enforce
its uniqueness or use it to deduplicate creation. Two create requests with
different idempotency keys can create two intents with the same reference.

## Mount a Payment Block

The browser needs a publishable key and the client secret of the intended
PaymentIntent. The strings below stand for those values from your backend.
The mount target must be an empty element already in the document.

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

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

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

The loader loads Ionic.js from `https://js.ionicfi.com/v1/ionic.js`.
The [script tag](/sdks/browser#script-tag) also exposes the runtime as `Ionic`.

A Blocks instance belongs to one intent and creates one Block. Mounting resolves
when initialization finishes. If the intent is already completed or otherwise
not collectable, the Block displays its state instead of card fields.
`blocks.destroy()` removes the UI and listeners; it does not cancel a payment.

## Confirmation and status

Your application decides when to submit in response to the customer's action.
These methods return results; they do not implement your application's payment
or fulfillment policy.

| Method                                      | Contract                                                                                                                                 |
| ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `ionic.confirmPayment({ blocks })`          | Collects and confirms the PaymentIntent bound to `blocks`. Returns `paymentIntent`, `savedPaymentMethod`, and/or `error` when available. |
| `ionic.retrievePaymentIntent(clientSecret)` | Reads the same intent without submitting card details. Returns `paymentIntent` and/or `error`.                                           |
| `payment.on("change", listener)`            | Reports field state or refreshed intent state. Returns an unsubscribe function.                                                          |
| `payment.on("error", listener)`             | Reports collection or mount errors.                                                                                                      |

### Handle the result

| State or error            | Meaning                                                                     |
| ------------------------- | --------------------------------------------------------------------------- |
| `succeeded`               | The payment completed.                                                      |
| `requires_capture`        | Funds are authorized; capture has not completed.                            |
| `processing`              | The outcome is still pending.                                               |
| `requires_payment_method` | The intent needs usable payment details.                                    |
| `requires_confirmation`   | The intent is ready for confirmation.                                       |
| `requires_action`         | Further authentication is required. Blocks does not run 3DS challenges.     |
| `canceled`                | The intent cannot be completed.                                             |
| `payment_status_unknown`  | The SDK could not verify the result. This does not mean the payment failed. |

A browser result is a display signal. Authoritative payment state is available
to your backend through the [PaymentIntent API](/api-reference/payment-intents)
and [verified events](#payment-events).

## Reloads and retries

Idempotency applies to an API operation, not to your application's reference or
checkout lifecycle. Ionic retains API idempotency keys for 24 hours in their
merchant and mode scope. A repeated key with a changed body is rejected.
[Idempotency](/api-reference/idempotency) documents replay and concurrent requests.

Blocks shares concurrent confirmation calls on the same instance. After an
uncertain response, another confirmation call on that instance reads the intent
and can replay the original confirmation with its original key and body.
That in-memory attempt does not survive a page reload or a new Blocks instance.

A lost response does not establish failure. Creating a replacement intent can
produce another payment. Your application owns recovery across requests, tabs,
and server restarts using the existing intent's state. Ionic does not supply a
payment store, locking scheme, or replacement-payment policy.

## Payment events

A webhook endpoint delivers signed events to your backend. Domain registration
and webhook registration are separate. See [Webhook endpoints](/webhooks/endpoints)
and [Signature verification](/webhooks/signatures).

| Event                           | Meaning                                                                                          |
| ------------------------------- | ------------------------------------------------------------------------------------------------ |
| `payment_intent.succeeded`      | The intent completed successfully.                                                               |
| `payment_intent.payment_failed` | A payment attempt failed; this is not a permanent failure of every future attempt on the intent. |
| `payment_intent.canceled`       | The intent was canceled.                                                                         |

`event.account` identifies the merchant; `event.livemode` identifies the mode;
`event.data.object` contains the PaymentIntent snapshot. An application must
establish the expected merchant, mode, intent, amount, and currency before using
that event as a payment result. A reference alone is not proof of ownership.

Events can be duplicated or arrive out of order. Delivery acknowledgment,
[retry behavior](/webhooks/idempotency-and-retries), and event verification are
Ionic contracts. Storage and fulfillment decisions belong to your application.

## Save the card being entered

A PaymentIntent with a merchant-owned `customer` and
`setup_future_usage: "off_session"` permits optional saving for future payments.
The Block displays an unchecked consent checkbox. `confirmPayment` uses the
customer's selection; leaving it unchecked pays without saving.

A successful charge does not by itself establish that a card was saved.
`savedPaymentMethod` is present when saving was confirmed in the confirmation
response. Retrieval does not return that confirmation-only field.

### Put the consent checkbox anywhere on your page

`saveConsent: "external"` leaves the consent control to your page. In this mode,
`confirmParams.savePaymentMethod` carries the customer's explicit choice.
`false` or omission does not request saving. The default `auto` mode reads the
Block's checkbox and rejects a supplied `savePaymentMethod` parameter.

Ionic retains the submitted consent choice when retrying an uncertain attempt
on the same instance. Your UI must represent the choice that was submitted.
The [Blocks reference](/guides/blocks-reference#confirm) lists the parameters.

For saving without taking a payment, use a [SetupIntent](/guides/save-cards)
with `confirmSetup`. Blocks collects a new card; saved-card selection is owned
by your application.

## Style the Block

`blocks.create` accepts layout, field appearance, styles, and consent placement.
The [Blocks reference](/guides/blocks-reference#options) lists the supported
options, defaults, events, and validation errors. Options are set before mount.

## Content Security Policy

Include the Ionic hosts alongside the sources required by your page:

```text theme={null}
script-src 'self' https://js.ionicfi.com;
connect-src 'self' https://api.ionicfi.com;
frame-src https://embed.ionicfi.com;
```

For tokenization without Blocks confirmation, see
[Advanced card fields](/guides/payment-fields-advanced). For Ionic-managed
checkout submission, see [Embedded checkout](/guides/embedded-checkout).
