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

# Flow payment widget

> Add FlowWidget from @dynamic-labs-sdk/react-ui to run a complete Flow payment from a flow ID, or mount a single-method widget for wallet, exchange, or deposit-address payments.

> [!NOTE]
>
> Fireblocks Flow is an enterprise feature. [Contact us](https://www.dynamic.xyz/book-a-call) to enable it for your environment.

`FlowWidget` is a prebuilt React component that runs the payer's side of a [Flow payment](https://www.dynamic.xyz/docs/flow/payments.md). You give it the ID of a flow your server created, and it loads the flow, lets the payer choose how to pay, runs the payment, and shows the result. It calls the same SDK functions described in [Build a custom payment UI](https://www.dynamic.xyz/docs/javascript/reference/flow-ui-guide.md), so use that guide instead when you need screens the widget does not provide.

## Prerequisites

* `@dynamic-labs-sdk/react-ui` installed, `flow.css` imported, and your app wrapped in `DynamicUiProvider` with the `dyn-flow-root` class, as described in [React UI components](https://www.dynamic.xyz/docs/javascript/building-ui/react-ui/overview.md).
* The chains your payers pay from enabled in the Dynamic Console and their chain extensions registered, as described in [Get started with Flow payments](https://www.dynamic.xyz/docs/javascript/reference/flow-getting-started.md#enable-chains-in-the-dynamic-console). Wallet payments need them. Exchange and deposit-address payments do not.
* A server that creates the flow and passes `flow.id` to the payer's page, as described in [Create a flow from your server](https://www.dynamic.xyz/docs/javascript/reference/flow-getting-started.md#create-a-flow-from-your-server).

## Add the widget

Render `FlowWidget` with the flow ID:

```tsx theme={"system"}
import "@dynamic-labs-sdk/react-ui/flow.css";

import { DynamicUiProvider } from "@dynamic-labs-sdk/react-ui";
import { FlowWidget } from "@dynamic-labs-sdk/react-ui/flow";

export const Checkout = ({ flowId }: { flowId: string }) => (
  <DynamicUiProvider className="dyn-flow-root">
    <FlowWidget
      flowId={flowId}
      onPaymentCompleted={(flow) => console.log("Paid", flow.id)}
      onPaymentFailed={(flow) => console.log("Not paid", flow.id)}
    />
  </DynamicUiProvider>
);
```

This example mounts `DynamicUiProvider` next to the widget. If your app already has one at its root with the `dyn-flow-root` class, render `FlowWidget` on its own.

## What the payer sees

The widget moves through these screens on its own:

1. **Loading.** The widget shows a loader while it reads the flow, and an error with a retry button when the flow cannot load.
2. **Payment method.** The payer chooses to pay from a wallet, through an exchange, or by sending funds to a deposit address. When only one method is enabled, the widget skips this screen.
3. **The chosen method.**
   * **Wallet.** The payer connects a wallet, chooses the token to pay with, reviews the quote, and approves the transaction in the wallet. If they decline in the wallet, the widget lets them try again or choose another method.
   * **Exchange.** The payer chooses an exchange, which opens in a new tab so they can buy the payment amount. The widget waits until the exchange confirms the purchase.
   * **Deposit address.** The payer chooses the network and token to send and gives a refund address, and the widget shows the address and the exact amount to send. It then waits for the deposit.
4. **Processing.** The widget shows the payment's progress after the funds are sent.
5. **Outcome.** The widget ends on payment complete, failed, refunded, cancelled, or expired, according to the flow's state.

The payer can go back from a method to the payment method choice. When they go back after opening an exchange or after receiving a deposit address, the widget asks them to confirm first, because a purchase or a deposit they already started can still arrive.

The widget reads the flow's state when it loads, so a payer who reloads the page or reopens the payment link returns to where they were. A flow that already has a quote, or that is waiting for an exchange purchase, resumes on the method that started it, and a flow that has already finished opens on its outcome.

## Handle the outcome

The widget calls one outcome callback with the latest `Flow` object:

| Callback | Called when |
| - | - |
| `onPaymentCompleted` | The payment settles, including a flow that was already paid when the widget opened |
| `onPaymentFailed` | The flow expires, is cancelled, or fails |

Each callback fires at most once while the widget stays mounted. A new mount reports again: reloading the page or reopening the payment link for a finished flow calls the matching callback once more. Make your handlers idempotent by keying them on `flow.id`.

Use the callbacks to update the payer's page, such as to show your own confirmation or move to the next step. Decide whether to fulfill the order on your server, from [webhooks](https://www.dynamic.xyz/docs/flow/webhooks.md) or a server read of the flow, rather than from what the payer's page reports. [Access and data](https://www.dynamic.xyz/docs/flow/security.md#decide-fulfillment-on-your-server) explains why.

Mount one widget per flow. Two widgets for the same flow each report the outcome, so your handlers run twice. Changing `flowId` on a mounted widget starts a new payment for the new flow.

## Configure the widget

Pass `settings` to change what the widget shows. Each field you set replaces the matching value in the flow's `uiConfig`, which your server can set when it creates the flow, as described in [UI configuration](https://www.dynamic.xyz/docs/flow/api.md#ui-configuration). A field you leave out falls back to the flow's value.

```tsx theme={"system"}
import { FlowWidget } from "@dynamic-labs-sdk/react-ui/flow";

<FlowWidget
  flowId={flowId}
  settings={{
    enabledPaymentMethods: ["wallet", "deposit_address"],
    merchantName: "Acme",
    merchantLogoUrl: "https://example.com/logo.svg",
    termsOfServicesUrl: "https://example.com/terms",
    refundAddresses: { EVM: payerEvmAddress },
  }}
/>
```

`payerEvmAddress` is an address your app already holds for the payer.

| Setting | Type | Description |
| - | - | - |
| `enabledPaymentMethods` | `("wallet" \| "exchange" \| "deposit_address")[]` | The payment methods to offer, always shown in the order wallet, exchange, deposit address. With one method, the widget opens straight on it |
| `merchantName` | `string` | Your name, shown on the outcome screens |
| `merchantLogoUrl` | `string` | Your logo, shown on the outcome screens. Only `https:` URLs and inline images are shown |
| `termsOfServicesUrl` | `string` | Your terms of service, which the payer accepts before paying from a wallet |
| `externalPaymentId` | `string` | Your own reference for the payment, shown once it is paid |
| `refundAddresses` | `{ BTC?: string; EVM?: string; SOL?: string; TRON?: string }` | Refund addresses you already hold for the payer, one per chain family. For a deposit-address payment, the widget asks the payer for one only when the family they pay from has no valid address here |

`enabledPaymentMethods` controls only what the widget shows. The API does not enforce it, so a payer can still attach another source with direct requests, and you should not rely on it for a compliance rule. [Payment sources](https://www.dynamic.xyz/docs/flow/payment-sources.md) explains this further.

## Offer one payment method

Each payment method is also available as its own widget, for a page that offers only that method or that builds its own method choice. They take the same `flowId`, `className`, `onPaymentCompleted`, and `onPaymentFailed` props as `FlowWidget`, and they handle loading, processing, outcomes, and reloads the same way.

| Widget | Extra props |
| - | - |
| `FlowPayWithWalletWidget` | `onBack` is shown as Back on the first screen. `onPayAnotherWay` is offered after the payer declines in their wallet, so they can return to your method choice. Without them, neither control appears |
| `FlowPayWithExchangeWidget` | `onBack` is shown as Back on the exchange list. Without it, the list has no Back |
| `FlowDepositAddressWidget` | `onBack` is shown as Back on the deposit-address screen. `refundAddresses` works as in `FlowWidget` settings |

```tsx theme={"system"}
import { FlowPayWithWalletWidget } from "@dynamic-labs-sdk/react-ui/flow";

<FlowPayWithWalletWidget
  flowId={flowId}
  onPaymentCompleted={(flow) => console.log("Paid", flow.id)}
/>;
```

## What the widget does not cover

Build these with the SDK, as described in [Build a custom payment UI](https://www.dynamic.xyz/docs/javascript/reference/flow-ui-guide.md), when your payment page needs them:

* **Cancelling a flow.** The widget has no cancel control. See [Cancel](https://www.dynamic.xyz/docs/javascript/reference/flow-ui-guide.md#cancel).
* **Changing the amount in `deposit` mode.** The widget quotes the amount the flow was created with. See [Deposit mode amount entry](https://www.dynamic.xyz/docs/javascript/reference/flow-ui-guide.md#deposit-mode-amount-entry).

## Change the text

Each Flow screen has its own slot in the `DynamicUiProvider` `text` dictionary, named after the screen, such as `dyn-flow-choose-payment-source-view` for the payment method choice. [Text keys](https://www.dynamic.xyz/docs/javascript/building-ui/react-ui/text-reference.md) lists every Flow slot and key, grouped by screen, with its default.

```tsx theme={"system"}
import { DynamicUiProvider } from "@dynamic-labs-sdk/react-ui";

<DynamicUiProvider
  className="dyn-flow-root"
  text={{
    "dyn-flow-choose-payment-source-view": {
      title: "How would you like to pay?",
    },
  }}
>
  <Checkout flowId={flowId} />
</DynamicUiProvider>
```

## Related

* [React UI components](https://www.dynamic.xyz/docs/javascript/building-ui/react-ui/overview.md): install the package and change its text and styles.
* [Get started with Flow payments](https://www.dynamic.xyz/docs/javascript/reference/flow-getting-started.md): set up chains and create a flow from your server.
* [Build a custom payment UI](https://www.dynamic.xyz/docs/javascript/reference/flow-ui-guide.md): build every payment screen yourself.
* [Payment lifecycle and states](https://www.dynamic.xyz/docs/flow/lifecycle-walkthrough.md): what each flow state means.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.