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.
🚀 The Seamless Flow
The Atomic Rail SDK is designed to be "invisible." When a user triggers a paid action:
- Extension Detection: On desktop, it looks for
window.ethereum(MetaMask, Coinbase, etc.). - Mobile Deep-Linking: On mobile browsers (Safari/Chrome), it automatically redirects the user to the MetaMask app if no provider is found.
- Automatic Chain Switching: If the user is on the wrong network, it prompts them to switch to the correct one (e.g., Base Sepolia).
- Instant Retry: It intercepts the
402 Payment Requiredresponse, handles the signature, and retries the request automatically.
1. Installation
npm install @atomic-rail/usage-gate viem2. React Integration (Recommended)
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.
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
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.
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.
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:
- Check
eth_chainId. - Call
wallet_switchEthereumChain. - If the chain is missing, call
wallet_addEthereumChainwith 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-requiredheader 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
- Standardize your Buttons: Create a shared
PaymentButtoncomponent (see our Demo folder for an example) that wrapsuseUsageGate. - Environment Sync: Ensure your frontend
chainconfiguration matches the network you've set for that Action in the Registry Dashboard. - Loading States: Always use the
isLoadingstate from the hook to disable buttons and show spinners, as wallet signatures can take several seconds.