@billmyagent/payments-react
v2.1.2
Published
React hook + button for paying x402-gated APIs (viem signer)
Readme
@billmyagent/payments-react
React components and hooks for the x402 payment protocol.
A thin, ergonomic wrapper around @billmyagent/payments-core that gives
React apps a way to call x402-gated URLs and automatically pay the HTTP 402
challenge when one is returned (the core client signs an EIP-3009
transferWithAuthorization with your wallet and the facilitator settles it
on-chain):
<PaymentButton>— drop-in button that calls a URL and pays the 402, with built-in loading and error statesusePayment()— hook for calling an x402-gated URL imperatively and tracking the request lifecycle
Install
npm install @billmyagent/payments-react @billmyagent/payments-corePeer dependencies: React >=18.0.0, react-dom >=18.0.0. Tested against React 19.
Quick start
First build a PaymentClient with a signer. In a browser, pass a viem wallet
client backed by the user's wallet (e.g. MetaMask); on a server/agent, build one
from a private key with createSigner (never ship a real key to a browser).
Drop-in button
import { PaymentButton, PaymentClient, createSigner } from '@billmyagent/payments-react';
// Browser: const signer = createWalletClient({ account, transport: custom(window.ethereum) });
const signer = await createSigner('base', process.env.PRIVATE_KEY!);
const client = new PaymentClient({ signer });
export function ReadArticle() {
return (
<PaymentButton
client={client}
url="https://api.example.com/premium-article"
onSuccess={(data) => console.log('unlocked:', data)}
onError={(err) => console.error(err)}
>
Read article
</PaymentButton>
);
}Imperative hook
import { usePayment } from '@billmyagent/payments-react';
function Premium({ client }) {
const { pay, data, loading, error, paid } = usePayment(client);
return (
<>
<button disabled={loading} onClick={() => pay('https://api.example.com/premium')}>
{loading ? 'Loading…' : 'Load premium'}
</button>
{error && <p>Error: {error.message}</p>}
{data && <p>Loaded{paid ? ' (paid)' : ''}: {JSON.stringify(data)}</p>}
</>
);
}API
<PaymentButton> props
| prop | type | required | notes |
|---|---|---|---|
| client | PaymentClient | yes | instance created with a signer so 402s can be paid |
| url | string | yes | the x402-gated URL to call (and pay, if challenged) |
| requestConfig | AxiosRequestConfig | no | method, headers, body, params |
| onSuccess | (data: unknown) => void | no | response body once the call (and any payment) succeeds |
| onError | (error: Error) => void | no | |
| className | string | no | applied to outer container |
| children | ReactNode | no | button label; defaults to "Pay" |
usePayment(client)
Returns { data, loading, error, paid, pay, reset }.
pay(url, config?)— call an x402-gated URL; if it responds 402 and the client has a signer, the payment is made and the request retried. Resolves with the body.paid—truewhen the most recent call required and completed a payment.reset()— cleardata,error, andpaid.
For merchant account operations (payouts, fees, invoices) and the payment REST
API, use the PaymentClient methods directly — see
@billmyagent/payments-core.
Supported networks
EVM only, exact scheme: Base, Ethereum, Polygon.
Development
# from sdk/react/
npm install
npm run type-check
npm test
npm run buildCI runs all of the above on every PR (.github/workflows/static-analysis.yml).
License
Apache 2.0 — see LICENSE.
