@stableyard/widget
v0.2.0
Published
Drop-in payment widget for accepting stablecoins
Maintainers
Readme
@stableyard/widget
Drop-in widget for accepting stablecoin payments. Users pay with any token from any chain — you receive USDC instantly.
Built by Stableyard — powered by Gasyard.
Two Ways to Integrate
| Version | Best For | Features | |---------|----------|----------| | React | React/Next.js apps | Full wallet connection, token selection, real-time balances | | Standalone | Any website (HTML/JS) | No framework needed, QR code payments, address-based flow |
React Integration
Full-featured widget with wallet connection, multi-chain support, and real-time token balances.
Installation
npm install @stableyard/widgetPeer Dependencies:
npm install react react-dom @tanstack/react-query wagmi viem @privy-io/react-authFor Solana support:
npm install @solana/web3.js @solana/spl-token @solana/kit @solana-program/system @solana-program/token @solana-program/memoQuick Start
import { StableyardProvider, StableyardWidget } from '@stableyard/widget'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { WagmiProvider, createConfig, http } from 'wagmi'
import { base, arbitrum, polygon } from 'wagmi/chains'
import { PrivyProvider } from '@privy-io/react-auth'
const queryClient = new QueryClient()
const wagmiConfig = createConfig({
chains: [base, arbitrum, polygon],
transports: {
[base.id]: http(),
[arbitrum.id]: http(),
[polygon.id]: http(),
},
})
function App() {
return (
<PrivyProvider appId="your-privy-app-id">
<WagmiProvider config={wagmiConfig}>
<QueryClientProvider client={queryClient}>
<StableyardProvider apiKey="your-api-key">
<StableyardWidget
to="[email protected]"
amount={29.99}
onSuccess={(payment) => console.log('Paid!', payment)}
/>
</StableyardProvider>
</QueryClientProvider>
</WagmiProvider>
</PrivyProvider>
)
}Provider Props
<StableyardProvider
apiKey="your-api-key" // Required
apiUrl="https://api.stableyard.fi" // Optional: custom API endpoint
theme="light" // 'light' | 'dark' | 'auto'
accentColor="#0066FF" // Primary brand color
>
<App />
</StableyardProvider>Widget Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| to | string | required | Recipient identity or wallet address |
| amount | number | - | Amount in USD (omit for user-entered) |
| buttonText | string | "Pay" | Custom button text |
| buttonVariant | 'primary' \| 'secondary' \| 'outline' \| 'ghost' | 'primary' | Button style |
| buttonSize | 'sm' \| 'md' \| 'lg' | 'md' | Button size |
| disabled | boolean | false | Disable the button |
| renderInline | boolean | false | Render inline instead of modal |
| containerStyle | CSSProperties | - | Style for inline container |
| onSuccess | (payment) => void | - | Called when payment succeeds |
| onError | (error) => void | - | Called on error |
| onClose | () => void | - | Called when modal closes |
| onStatusChange | (status) => void | - | Called on status change |
| onTransactionSubmitted | (hash, chainId) => void | - | Called when TX is signed |
| onTransactionConfirmed | (hash, chainId) => void | - | Called when TX confirms |
Inline Mode
Embed the widget directly in your page:
<StableyardWidget
to="merchant@example"
amount={25}
renderInline={true}
containerStyle={{ maxWidth: 420 }}
/>Theming
Customize with the accentColor prop:
<StableyardProvider
apiKey="your-key"
accentColor="#8B5CF6" // Your brand color
theme="light"
>
<App />
</StableyardProvider>Status Callbacks
Track the payment lifecycle:
<StableyardWidget
to="merchant@example"
amount={10}
onStatusChange={(status) => {
// 'idle' | 'intro' | 'connecting' | 'loading_balances' |
// 'selecting_token' | 'loading_quote' | 'reviewing' |
// 'signing' | 'confirming' | 'processing' | 'success' | 'error'
console.log('Status:', status)
}}
onTransactionSubmitted={(hash, chainId) => {
console.log('TX signed:', hash)
}}
onSuccess={(payment) => {
console.log('Payment complete:', payment.id)
}}
/>Standalone Integration (Vanilla JS)
For non-React websites. Uses QR code / address-based payments — no wallet connection required on your site.
Option 1: Script Tag
<!DOCTYPE html>
<html>
<head>
<title>Checkout</title>
</head>
<body>
<h1>Complete Your Purchase</h1>
<p>Total: $29.99</p>
<button id="pay-button">Pay with Crypto</button>
<!-- Include the standalone bundle -->
<script src="https://unpkg.com/@stableyard/widget/dist/standalone.js"></script>
<script>
// Initialize with your API key
Stableyard.init({
apiKey: 'your-api-key',
theme: 'light', // optional: 'light' | 'dark'
accentColor: '#0066FF' // optional: brand color
})
// Open payment modal on button click
document.getElementById('pay-button').addEventListener('click', function() {
Stableyard.pay({
to: 'merchant@stableyard',
amount: 29.99,
onSuccess: function(payment) {
alert('Payment successful!')
console.log('Payment:', payment)
// Redirect to success page, update UI, etc.
},
onError: function(error) {
console.error('Payment failed:', error)
},
onClose: function() {
console.log('Modal closed')
}
})
})
</script>
</body>
</html>Option 2: ES Module Import
<script type="module">
import { init, pay } from 'https://unpkg.com/@stableyard/widget/dist/standalone.js'
init({ apiKey: 'your-api-key' })
document.getElementById('pay-button').onclick = () => {
pay({
to: 'merchant@stableyard',
amount: 29.99,
onSuccess: (payment) => console.log('Paid!', payment)
})
}
</script>Option 3: Self-Hosted
Download and host the standalone bundle yourself:
# Download from npm
npm pack @stableyard/widget
tar -xzf stableyard-widget-*.tgz
cp package/dist/standalone.js ./public/js/<script src="/js/standalone.js"></script>Standalone API
Stableyard.init(options)
Initialize the widget. Call once on page load.
Stableyard.init({
apiKey: 'your-api-key', // Required
theme: 'light', // Optional: 'light' | 'dark' | 'auto'
accentColor: '#0066FF', // Optional: hex color
})Stableyard.pay(options)
Open the payment modal.
Stableyard.pay({
to: 'merchant@stableyard', // Required: recipient
amount: 29.99, // Optional: USD amount (user can enter if omitted)
onSuccess: (payment) => {}, // Called on successful payment
onError: (error) => {}, // Called on error
onClose: () => {}, // Called when modal closes
})Payment Object
The onSuccess callback receives a payment object:
{
quoteId: 'quote_abc123',
status: 'completed',
inputTxHash: '0x...',
outputTxHash: '0x...',
from: {
chainId: 137,
token: 'USDC',
amount: '29.99'
},
to: {
identity: 'merchant@stableyard',
chainId: 1,
token: 'USDC',
amount: '29.99'
}
}Standalone Flow
The standalone version uses an address-based payment flow:
- User clicks your pay button
- Modal opens showing amount and network selection
- User selects their preferred network (Ethereum, Base, Polygon, etc.)
- A deposit address and QR code are displayed
- User sends payment from their wallet or exchange
- Payment is detected and confirmed
onSuccesscallback fires
This flow works without any wallet connection on your website — users can pay from any wallet, exchange, or app that supports sending crypto.
Supported Chains
| Chain | Tokens | |-------|--------| | Ethereum | USDC, USDT | | Base | USDC, USDT | | Arbitrum | USDC, USDT | | Polygon | USDC, USDT | | Optimism | USDC, USDT | | BSC | USDC, USDT | | Solana | USDC |
React Hooks
For building custom payment UIs:
useWalletConnection
import { useWalletConnection } from '@stableyard/widget'
const {
isConnected,
isReady,
evmAddress,
solanaAddress,
login,
logout
} = useWalletConnection()useBalances
import { useBalances } from '@stableyard/widget'
const { balances, isLoading, error } = useBalances({
address: '0x...',
enabled: true,
})useQuote
import { useQuote } from '@stableyard/widget'
const { getQuote, isLoading, error } = useQuote()
const quote = await getQuote({
amount: 2500000, // in token decimals
destinationPaymentAddress: 'merchant@stableyard',
sourceChainId: 137,
sourceChainToken: 'USDC',
})TypeScript
All types are exported:
import type {
StableyardWidgetProps,
StableyardProviderProps,
Payment,
TokenBalance,
QuoteResponseData,
WidgetPaymentStatus,
} from '@stableyard/widget'License
MIT
