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

# Build a payment deep link

> Build a custom extension that turns a Fireblocks Flow deposit into a deep link or QR code for a mobile wallet app.

This guide is for teams that operate their own mobile wallet app and want a dedicated [Fireblocks Flow](/docs/overview/fireblocks-flow) integration with the Dynamic JavaScript SDK, without using WalletConnect. Instead of connecting the wallet inside the SDK and signing through `submitFlowTransaction`, you create a Flow with `sourceType: 'deposit_address'` and pass the payment details to your wallet app through a deep link or QR code. The wallet app controls the send UX, and Flow monitors the deposit.

## Why build a custom payment deep link

A custom deep link lets the wallet app own the payment experience:

* **No SDK wallet connection** — the app running the SDK never has to connect to the user's wallet.
* **Fewer hops** — the connected-wallet path requires the user to connect the wallet and then sign a transaction, which means switching to the wallet twice. A deep link lets the user confirm and send in the wallet app once.
* **Pre-filled, correct details** — the deep link can carry the exact deposit address, amount, asset, and chain, so the wallet app can build a dedicated confirmation screen instead of asking the user to copy and paste values.
* **Less room for error** — with a raw deposit address, the user must send the exact correct amount. Any mismatch typically triggers a refund. A deep link removes most manual entry and reduces that risk.
* **One code path for QR and deep links** — the same URL can be rendered as a QR code on desktop, opened as a deep link on mobile web, or invoked directly from a mobile native app.

## When to use a payment deep link

Use `sourceType: 'deposit_address'` and a custom deep link when:

* You control the wallet app and want to keep the send UX inside it.
* The app running the SDK should not connect to the user's wallet.
* You want one implementation that covers desktop QR codes, mobile web deep links, and mobile native deep links.
* You want to reduce the chance of the user sending the wrong amount, asset, or address.

For the connected-wallet alternative — where the SDK attaches `sourceType: 'wallet'` and signs inside `submitFlowTransaction` through `executeSwapTransaction` — see [Add swap capabilities](/docs/javascript/reference/creating-extensions/wallet-provider/add-swap-capabilities).

## How this fits into Flow

`attachFlowSource` supports three source types: `wallet`, `exchange`, and `deposit_address`.

* `sourceType: 'wallet'` — the SDK connects a wallet and signs. This is the connected-wallet path covered in [Add swap capabilities](/docs/javascript/reference/creating-extensions/wallet-provider/add-swap-capabilities).
* `sourceType: 'deposit_address'` — Flow generates a unique deposit address and amount. Your extension turns that address and amount into a deep link or QR code. The wallet app sends the funds, and Flow detects the deposit and advances `executionState` to `source_confirmed`.

The deposit-address path does not require a `WalletProvider` and does not call `submitFlowTransaction`.

## Connected wallet vs deposit deep link

<CardGroup cols={2}>
  <Card title="Connected wallet" icon="wallet">
    `sourceType: 'wallet'`

    **SDK role**

    * Attaches a wallet source.
    * Calls `submitFlowTransaction`.
    * Signs through `executeSwapTransaction`.

    **Wallet app role**

    * Provides a `WalletProvider` with signing.

    **UX**

    * Two hops: connect the wallet, then sign the transaction.

    **Best for**

    * Tightly integrated wallets that can expose a `WalletProvider`.
  </Card>

  <Card title="Deposit deep link" icon="qrcode">
    `sourceType: 'deposit_address'`

    **SDK role**

    * Attaches a deposit source.
    * Calls `getFlowQuote`.
    * Builds the deep link or QR code.

    **Wallet app role**

    * Receives the deep link, pre-fills the send screen, and sends the exact amount.

    **UX**

    * One confirmation inside the wallet app.

    **Best for**

    * Wallet apps that want to own the send UX or cannot expose a `WalletProvider`.
  </Card>
</CardGroup>

## Build the deep link

Create a single extension function that takes a `Flow` and builds a custom-app deep link. The example uses `mywallet://deposit`, but replace the scheme with your own wallet's URL scheme.

```typescript theme={"system"}
import { getDefaultClient } from "@dynamic-labs-sdk/client";
import type { DynamicClient, Flow } from "@dynamic-labs-sdk/client";
import { getCore } from "@dynamic-labs-sdk/client/core";

const APP_SCHEME = "mywallet";

export const createMyWalletDepositUrl = ({
  flow,
  returnUrl,
}: {
  flow: Flow;
  returnUrl?: string;
}): URL => {
  const url = new URL(`${APP_SCHEME}://deposit`);

  if (!flow.depositAddress) {
    throw new Error("Flow did not return a deposit address");
  }

  url.searchParams.set("flowId", flow.id);
  url.searchParams.set("depositAddress", flow.depositAddress);
  url.searchParams.set("rawAmount", flow.quote.fromAmount);

  if (flow.fromToken) {
    url.searchParams.set("fromToken", flow.fromToken);
  }

  if (flow.fromChainId) {
    url.searchParams.set("fromChainId", flow.fromChainId);
  }

  if (flow.fromChainName) {
    url.searchParams.set("fromChainName", flow.fromChainName);
  }

  if (flow.memo) {
    url.searchParams.set("memo", JSON.stringify(flow.memo));
  }

  if (returnUrl) {
    url.searchParams.set("returnUrl", returnUrl);
  }

  // Add any other fields your wallet app needs (for example, the token contract address).

  return url;
};

export const openMyWalletDeposit = (
  { flow, returnUrl }: { flow: Flow; returnUrl?: string },
  client: DynamicClient = getDefaultClient()
): Promise<void> =>
  getCore(client).openDeeplink(
    createMyWalletDepositUrl({ flow, returnUrl }).toString()
  );
```

Important details:

* `flow.depositAddress` is the address the wallet must send funds to.
* `flow.quote.fromAmount` is the exact source amount in base units; do not fall back to `flow.amount`, which is the destination amount.
* `fromToken`, `fromChainId`, and `fromChainName` describe the asset and chain the user is paying from.
* The query parameter names and the custom scheme are up to you — this is just an example contract.

## Use the deep link in a Flow

A consumer app creates a Flow, attaches a deposit-address source, gets a quote, then opens the deep link or shows a QR code:

```typescript theme={"system"}
import {
  attachFlowSource,
  getFlowQuote,
} from "@dynamic-labs-sdk/client";
import { createMyWalletDepositUrl, openMyWalletDeposit } from "my-wallet-extension";

const flowId = "flow-abc123";

await attachFlowSource({
  flowId,
  sourceType: "deposit_address",
  fromChainId: "1",
  fromChainName: "EVM",
  // Optional: where to return funds if the transfer fails.
  refundAddress: "0x...",
});

const flow = await getFlowQuote({
  flowId,
  fromTokenAddress: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
});

// On mobile native or mobile web, open the wallet app directly.
await openMyWalletDeposit({
  flow,
  returnUrl: "myapp://deposit-complete",
});

// On desktop, encode the same URL as a QR code.
const qrData = createMyWalletDepositUrl({ flow }).toString();
```

## QR codes vs deep links

* **Desktop**: show a QR code with the deposit deep-link URL. The user scans it with their phone camera or wallet app.
* **Mobile web**: call `openDeeplink` or render a button that links to the deep-link URL. A phone cannot scan a QR code that it is already displaying, so a QR code is not the primary UX on mobile web.
* **Mobile native**: call `openDeeplink` directly. If the wallet app is not installed, fall back to the QR code or an app-store prompt.

See [Open deep links](/docs/javascript/reference/creating-extensions/services/open-deep-links) for the platform behavior and React Native configuration.

## Development cost

Building the deep link is only half of the integration. The wallet app must also:

* Register and handle the custom URL scheme (for example, `mywallet://deposit`).
* Parse the query parameters.
* Pre-fill the send screen with `depositAddress`, `rawAmount`, and the correct asset and chain.
* Validate that the amount, asset, and chain match the flow before signing.
* Optionally handle `returnUrl` to send the user back to the app that started the flow.

The Dynamic SDK creates the deep link and opens it; the wallet app must implement everything that happens after the user taps or scans it.

## After the payment

The user sends the exact asset and amount to `depositAddress` from the wallet app. Fireblocks Flow monitors the address and advances `executionState` from `quoted` to `source_confirmed`. Poll `getFlow` to track progress; there is no `submitFlowTransaction` call for this path.

## Related

* [Add swap capabilities](/docs/javascript/reference/creating-extensions/wallet-provider/add-swap-capabilities) — the connected-wallet signing path
* [Open deep links](/docs/javascript/reference/creating-extensions/services/open-deep-links)
* [attachFlowSource](/docs/javascript/reference/client/attach-flow-source)
* [getFlowQuote](/docs/javascript/reference/client/get-flow-quote)
* [getFlow](/docs/javascript/reference/client/get-flow)
