PayButton
ComponentA one-click payment button that handles authentication and intent signing automatically.
import { PayButton } from '@1auth/sdk'Overview
The PayButton component provides a complete payment flow in a single button. It automatically handles:
- User authentication (if not already signed in)
- Storing user credentials in localStorage
- Signing and submitting payment intents
- Visual feedback for processing and success states
Live Demo
Connects to passkey app at https://passkey.1auth.box
Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
client | PasskeyProviderClient | Yes | - | The SDK client instance |
intent | Omit<SendIntentOptions, 'username' | 'closeOn'> | Yes | - | Intent parameters (calls, targetChain) |
getSignedIntent | (params) => Promise<MerchantSignedIntent> | No | - | Backend signing for XSS protection |
onSuccess | (result: SendIntentResult) => void | No | - | Called when payment succeeds |
onError | (error: Error) => void | No | - | Called when payment fails |
closeOn | CloseOnStatus | No | 'preconfirmed' | When to close the signing dialog |
children | ReactNode | No | 'Pay with 1auth' | Button text |
className | string | No | - | Custom CSS class |
style | CSSProperties | No | - | Custom inline styles |
disabled | boolean | No | false | Disabled state |
hideIcon | boolean | No | false | Hide the fingerprint icon |
Basic Usage (Recommended)
Use getSignedIntent to sign intents on your backend. This prevents malicious scripts from modifying transaction data.
import { PayButton } from "@1auth/sdk/react";
import { PasskeyProviderClient } from "@1auth/sdk";
const client = new PasskeyProviderClient({
providerUrl: "https://passkey.1auth.box",
clientId: "my-app",
});
function Checkout() {
const getSignedIntent = async ({ username, targetChain, calls, tokenRequests }) => {
const res = await fetch("/api/sign-intent", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username, targetChain, calls, tokenRequests }),
});
return res.json();
};
return (
<PayButton
client={client}
intent={{
targetChain: 8453, // Base
calls: [{
to: '0x...',
data: '0x...',
label: 'Purchase NFT',
sublabel: '$50.00',
}],
}}
getSignedIntent={getSignedIntent}
onSuccess={(result) => {
console.log('Payment complete:', result.transactionHash);
}}
onError={(error) => {
console.error('Payment failed:', error.message);
}}
>
Pay $50.00
</PayButton>
);
}Create the backend endpoint with one line using the SDK:
// app/api/sign-intent/route.ts
import { createSignIntentHandler } from "@1auth/sdk/server";
export const POST = createSignIntentHandler({
merchantId: process.env.MERCHANT_ID!,
privateKey: process.env.MERCHANT_PRIVATE_KEY!,
});Register as a merchant at developer.1auth.box to get your credentials.
See the Merchant Authentication guide for full details.
Coming Soon: Signed intents will support sponsorship flags, allowing merchants to sponsor transaction fees for their users.
First-Party Apps (No Signing)
For first-party apps whose origin is in ALLOWED_ORIGINS, you can skip backend signing:
<PayButton
client={client}
intent={{
targetChain: 8453,
calls: [{
to: '0x...',
data: '0x...',
label: 'Purchase NFT',
sublabel: '$50.00',
}],
}}
onSuccess={(result) => console.log('Done:', result.transactionHash)}
/>Note: Without getSignedIntent, the passkey service validates the request origin. If your domain isn't registered, the request will be rejected.
Custom Styling
The button can be customized with className and style props:
<PayButton
client={client}
intent={intent}
getSignedIntent={getSignedIntent}
className="my-custom-button"
style={{ backgroundColor: '#6366f1' }}
>
Complete Purchase
</PayButton>How It Works
- First click (new user): Opens the auth modal for sign-in/sign-up
- After auth: Stores username in localStorage as
1auth-user - Subsequent clicks: Uses stored username, skips auth
- Intent signing: Opens the signing dialog to confirm the transaction
- Success: Button shows checkmark and "Paid" text
Testing
For testing on Base Sepolia, you'll need testnet USDC. Get some from the Circle Faucet.
Notes
- The button auto-detects returning users via localStorage
- Use
closeOnto control whenonSuccessfires relative to transaction finality - The fingerprint icon can be hidden for a cleaner look with
hideIcon - For production, always use
getSignedIntentto protect against XSS attacks