Backstage pass · Sample event
Tap below to launch the embedded Flow widget and watch the lifecycle in the code panel on the right.
The buyer pays with any token from any wallet. The merchant's vault receives the configured stablecoin every time — Fireblocks Flow provides the swap, settlement, and webhook infrastructure[†].
Tap below to launch the embedded Flow widget and watch the lifecycle in the code panel on the right.
Your application should make clear to end-users that asset conversion and cross-chain routing are executed by independent third-party providers. Users keep full control of their assets and must explicitly sign each transfer. On-chain transactions are final and cannot be reversed.
By continuing, you agree to our Terms of Service and Privacy Policy.
Flow runs on mainnet networks only. Use a sandbox environment id with real mainnet addresses for development.
Server-side. Defines the merchant-facing settlement asset(s) and destination wallet(s) — uses your environment-scoped API token. Reuse a single Flow across many buyer transactions when the merchant config is shared (e.g. fixed-amount deposits). For ecommerce checkouts where each cart has a different amount or line items, create one Flow per checkout instead.
// Runs on your server — one-time per Flow config.
const res = await fetch(
`https://api.dynamic.xyz/v0/environments/${process.env.ENV_ID}/checkouts`,
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.DYNAMIC_API_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
mode: "payment",
settlementConfig: {
strategy: "preferred_order",
settlements: [
{
chainName: "EVM",
chainId: "8453",
symbol: "USDC",
tokenAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
tokenDecimals: 6,
},
],
},
destinationConfig: {
destinations: [
{
chainName: "EVM",
type: "address",
identifier: "<destination_address>",
},
],
},
}),
},
);
const { id: flowId } = await res.json();Per buyer. Opens a transaction against the Flow id from Step 01 and returns a session token used to authenticate the rest of the buyer's lifecycle.
import { addEvmExtension } from "@dynamic-labs-sdk/evm";
import { createCheckoutTransaction } from "@dynamic-labs-sdk/client";
addEvmExtension();
// Open a transaction against the Flow id returned in Step 01. Returns
// both the transaction and a one-time sessionToken — store the latter.
const { transaction, sessionToken } = await createCheckoutTransaction({
checkoutId: flowId,
amount: "5.00",
currency: "USD",
});Declare the wallet the buyer is paying from. Dynamic runs risk and sanctions screening here; a 403 means the source is blocked.
import {
attachCheckoutTransactionSource,
getActiveNetworkData,
getPrimaryWalletAccount,
} from "@dynamic-labs-sdk/client";
const wallet = getPrimaryWalletAccount()!;
const { networkData } = await getActiveNetworkData({ walletAccount: wallet });
await attachCheckoutTransactionSource({
transactionId: transaction.id,
fromAddress: wallet.address,
fromChainId: String(networkData.networkId),
fromChainName: wallet.chain,
});
// 403 → source blocked by risk/sanctions screening.Quote the cross-chain swap from the buyer's chosen token to the settlement asset. Quotes expire in 60 seconds — re-quote if the buyer takes longer.
import {
getBalances,
getCheckoutTransactionQuote,
} from "@dynamic-labs-sdk/client";
// First non-zero balance — production would let the user pick which
// token to pay with.
const [fromToken] = await getBalances({ walletAccount: wallet });
const quoted = await getCheckoutTransactionQuote({
transactionId: transaction.id,
fromTokenAddress: fromToken.address,
slippage: 0.005, // 0.5%
});
// quoted.quote.expiresAt — re-quote if the user takes longer than 60s.Three things happen here: Dynamic prepares the signing payload, the buyer signs it in their wallet, and your client notifies Dynamic that the chain tx is broadcast. The SDK collapses all three into one helper; the REST path keeps them separate so you can plug in your own wallet client.
import { submitCheckoutTransaction } from "@dynamic-labs-sdk/client";
// One helper handles all three sub-stages:
// 1. prepare → fetch the signing payload
// 2. sign → wallet popup appears here
// 3. broadcast → notify Dynamic of the on-chain txHash
await submitCheckoutTransaction({
transactionId: transaction.id,
walletAccount: wallet,
});We poll the transaction endpoint every ~3 seconds so the settlement lifecycle stays visible in the demo. Production should subscribe to the checkout.transaction.settlement.updated webhook instead — push-driven, no idle polling. Either way, wait for settlementState to reach completed or failed.
import {
getCheckoutTransaction,
isTerminalState,
} from "@dynamic-labs-sdk/client";
// Poll every 3s — or subscribe via the lifecycle webhook.
let tx = await getCheckoutTransaction({ transactionId: transaction.id });
while (!isTerminalState(tx)) {
await new Promise((r) => setTimeout(r, 3000));
tx = await getCheckoutTransaction({ transactionId: transaction.id });
}
// tx.settlementState === "completed" | "failed"Dynamic does not control the swap, bridge, or routing protocols used to convert and deliver assets. Rates and fees are sourced from third-party providers and may change between quote and execution.
Cross-chain transfers carry risk — including slippage, partial fills, and failed conversions. On-chain transactions are final and cannot be reversed.
These materials are not investment, financial, legal, or tax advice. You are responsible for evaluation at your own discretion. Please review Dynamic's terms and conditions for full details on acceptable use.