Client-Side Integration

This guide explains how to integrate Atomic Rail payments into your frontend. We provide a high-level SDK that automates wallet orchestration, mobile deep-linking, and payment signatures.

Last updated: December 27, 2025

🚀 The Seamless Flow

The Atomic Rail SDK is designed to be "invisible." When a user triggers a paid action:

  1. Extension Detection: On desktop, it looks for window.ethereum (MetaMask, Coinbase, etc.).
  2. Mobile Deep-Linking: On mobile browsers (Safari/Chrome), it automatically redirects the user to the MetaMask app if no provider is found.
  3. Automatic Chain Switching: If the user is on the wrong network, it prompts them to switch to the correct one (e.g., Base Sepolia).
  4. Instant Retry: It intercepts the 402 Payment Required response, handles the signature, and retries the request automatically.

1. Installation

bash
npm install @atomic-rail/usage-gate viem

The easiest way to use the SDK is via the useUsageGate hook. It works out-of-the-box with injected wallets (MetaMask, Coinbase Wallet, etc.) and handles mobile deep-linking automatically.

tsx
import { useUsageGate } from "@atomic-rail/usage-gate/client";
import { baseSepolia } from "viem/chains";

export default function MyComponent() {
const { gatedFetch, isLoading } = useUsageGate({
chain: baseSepolia,
// preferredWallet: "coinbase" // Optional: defaults to "metamask"
});

const handleAction = async () => {
const res = await gatedFetch("/api/premium-feature");
// ...
};
}

3. Advanced Integration (RainbowKit, wagmi, Web3Modal)

If you are already using a connection library like RainbowKit, wagmi, or AppKit (Web3Modal), you can plug their provider directly into our SDK. This lets the other library handle the connection UI while we handle the gated payments.

Example with wagmi / RainbowKit

tsx
import { useUsageGate } from "@atomic-rail/usage-gate/client";
import { useWalletClient } from "wagmi";
import { baseSepolia } from "viem/chains";

export function RainbowKitGatedAction() {
const { data: walletClient } = useWalletClient();

const { gatedFetch } = useUsageGate({
chain: baseSepolia,
// Pass the provider from wagmi to make the SDK wallet-agnostic
provider: walletClient?.transport,
});

const handleAction = async () => {
const res = await gatedFetch("/api/premium-feature");
// ...
};
}

Example with custom "Connect Wallet" Modal

If you want to handle the "No Wallet" state yourself (e.g. by opening a custom modal instead of redirecting to MetaMask), use the onNoWallet callback.

tsx
const { gatedFetch } = useUsageGate({
chain: baseSepolia,
onNoWallet: () => {
// Open your own connection modal here
myModal.open();
},
});

4. Generic / Framework Agnostic Integration

If you aren't using React, you can still use the core logic provided by the @x402/fetch and @x402/evm packages which our SDK is built upon.

Vanilla JavaScript / Vue / Svelte

For non-React frameworks, you can set up a reusable paidFetch instance.

typescript
import { x402Client, wrapFetchWithPayment } from "@x402/fetch";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { createWalletClient, custom } from "viem";

export async function setupPaidFetch(chain) {
// 1. Initialize x402
const x402 = new x402Client();

// 2. Connect Wallet
const [address] = await window.ethereum.request({
method: "eth_requestAccounts",
});
const walletClient = createWalletClient({
account: address,
chain,
transport: custom(window.ethereum),
});

// 3. Register EVM Payment Scheme
registerExactEvmScheme(x402, {
signer: {
address,
signTypedData: (m) =>
walletClient.signTypedData({ account: address, ...m }),
},
});

// 4. Return the wrapped fetch
return wrapFetchWithPayment(fetch, x402);
}

// Usage in any framework:
const paidFetch = await setupPaidFetch(baseSepolia);
const res = await paidFetch("/api/my-paid-route");

4. Key Features

📱 Mobile Support

Our React hook automatically handles the "Browser -> App" transition. If a user clicks a payment button in Safari, they are redirected to: https://metamask.app.link/dapp/your-site.com/current-page

Once inside the MetaMask browser, the window.ethereum provider is detected and the payment proceeds normally.

⛓️ Automatic Chain Switching

The SDK ensures the user is on the correct network before prompting for payment. If you configure chain: baseSepolia, the SDK will:

  1. Check eth_chainId.
  2. Call wallet_switchEthereumChain.
  3. If the chain is missing, call wallet_addEthereumChain with the correct RPC and explorer details.

💰 Error Handling

The SDK includes built-in logic to detect and handle common crypto errors:

  • Insufficient Funds: Automatically parses the payment-required header and shows a friendly alert with the required amount and a faucet link.
  • User Cancellation: Detects when a user rejects a signature request.
  • Connection Pending: Alerts the user if MetaMask already has a pending request.

💡 Best Practices

  1. Standardize your Buttons: Create a shared PaymentButton component (see our Demo folder for an example) that wraps useUsageGate.
  2. Environment Sync: Ensure your frontend chain configuration matches the network you've set for that Action in the Registry Dashboard.
  3. Loading States: Always use the isLoading state from the hook to disable buttons and show spinners, as wallet signatures can take several seconds.