🔐1auth SDK Docs
Back to all components

PayButton

Component

A 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

PropTypeRequiredDefaultDescription
clientPasskeyProviderClientYes-The SDK client instance
intentOmit<SendIntentOptions, 'username' | 'closeOn'>Yes-Intent parameters (calls, targetChain)
getSignedIntent(params) => Promise<MerchantSignedIntent>No-Backend signing for XSS protection
onSuccess(result: SendIntentResult) => voidNo-Called when payment succeeds
onError(error: Error) => voidNo-Called when payment fails
closeOnCloseOnStatusNo'preconfirmed'When to close the signing dialog
childrenReactNodeNo'Pay with 1auth'Button text
classNamestringNo-Custom CSS class
styleCSSPropertiesNo-Custom inline styles
disabledbooleanNofalseDisabled state
hideIconbooleanNofalseHide 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

  1. First click (new user): Opens the auth modal for sign-in/sign-up
  2. After auth: Stores username in localStorage as 1auth-user
  3. Subsequent clicks: Uses stored username, skips auth
  4. Intent signing: Opens the signing dialog to confirm the transaction
  5. 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 closeOn to control when onSuccess fires relative to transaction finality
  • The fingerprint icon can be hidden for a cleaner look with hideIcon
  • For production, always use getSignedIntent to protect against XSS attacks