Headless React components and helpers for accepting stable tokens on Solana with built-in verification. USDC remains the default.
This package is the reusable payment component layer. It does not depend on the hosted Payflow dashboard or request-page product.
npm install @futurekode/stablepay-reactimport {
StablePayProvider,
StablePayButton,
usePaymentVerification,
} from "@futurekode/stablepay-react";
export function Checkout() {
const { status, verify } = usePaymentVerification();
return (
<StablePayProvider to="YOUR_WALLET_ADDRESS">
<StablePayButton
amount={0.1}
reference="order-123"
metadata={{ requestId: "req_123", customerEmail: "alice@example.com" }}
onSuccess={async (payload) => {
console.log(payload.metadata?.requestId);
await verify(payload);
}}
>
<button>
{status === "verifying"
? "Verifying..."
: status === "pending"
? "Confirming..."
: status === "confirmed"
? "Paid"
: "Pay 0.1 USDC"}
</button>
</StablePayButton>
</StablePayProvider>
);
}StablePayButton is the clearer alias for the payment button component. StablePay is still exported for compatibility.
import { TOKENS, getTokenConfig } from "@futurekode/stablepay-react";
const usdc = TOKENS.USDC;
const usdt = getTokenConfig("USDT");Use usePaymentPreflight to check wallet readiness before asking the user to sign.
import {
TOKENS,
StablePayButton,
StablePayProvider,
usePaymentPreflight,
} from "@futurekode/stablepay-react";
export function Checkout() {
const { check, loading, result } = usePaymentPreflight({
amount: 24,
to: "YOUR_WALLET_ADDRESS",
token: TOKENS.USDC,
});
return (
<StablePayProvider to="YOUR_WALLET_ADDRESS">
<button onClick={() => void check()}>
{loading ? "Checking..." : "Check wallet readiness"}
</button>
{result && <p>{result.message}</p>}
<StablePayButton amount={24} reference="invoice-1842" token={TOKENS.USDC}>
<button disabled={!result?.ok}>Pay 24.00 USDC</button>
</StablePayButton>
</StablePayProvider>
);
}runPaymentPreflight is also exported for non-hook usage.
Set createRecipientTokenAccount if you want the payment transaction to create the recipient associated token account when it does not exist yet.
import { StablePayButton, TOKENS } from "@futurekode/stablepay-react";
<StablePayButton
amount={24}
reference="invoice-1842"
token={TOKENS.USDC}
createRecipientTokenAccount
>
<button>Pay 24.00 USDC</button>
</StablePayButton>;import { normalizeStablePayError, TOKENS } from "@futurekode/stablepay-react";
try {
// payment flow
} catch (error) {
const normalized = normalizeStablePayError(error, TOKENS.USDC);
console.log(normalized.code, normalized.message);
}useStablePay exposes status, reset(), and lifecycle events for checkout-style flows.
import { TOKENS, useStablePay } from "@futurekode/stablepay-react";
export function CheckoutButton() {
const { pay, status, error, reset } = useStablePay();
return (
<>
<button
onClick={() =>
void pay({
amount: 24,
to: "YOUR_WALLET_ADDRESS",
reference: "invoice-1842",
token: TOKENS.USDC,
})
}
>
{status === "awaiting_wallet"
? "Connect wallet..."
: status === "preparing"
? "Preparing..."
: status === "submitting"
? "Opening wallet..."
: status === "confirming"
? "Confirming..."
: status === "confirmed"
? "Paid"
: "Pay 24.00 USDC"}
</button>
{error ? <p>{error.message}</p> : null}
{status === "failed" ? <button onClick={reset}>Try again</button> : null}
</>
);
}StablePayButton— clearer alias for the payment button componentuseStablePay— hook with payment status, reset, and lifecycle eventsusePaymentVerification— verify a submitted paymentusePaymentPreflight— wallet and payment readiness checksrunPaymentPreflight— non-hook preflight helperverifyPayment— verify a transaction against expected payment detailswaitForPaymentConfirmation— wait until a payment is confirmednormalizeStablePayError— map raw errors into developer-friendly messagesStableTokenConfig— token model for mint/decimals-aware integrations
StablePayButton accepts an optional token prop. If omitted, the package uses USDC_TOKEN_CONFIG.
import { StablePayButton, TOKENS } from "@futurekode/stablepay-react";
<StablePayButton amount={0.1} reference="order-123" token={TOKENS.USDC}>
<button>Pay 0.1 USDC</button>
</StablePayButton>;StablePay is still exported as a compatibility name for the same component. USDC compatibility helpers such as buildUsdcTransfer, parseUsdcPaymentFromTransaction, and getUsdcTokenAccountForWallet are also still exported, and USDC remains the default token when no token is provided.
StablePayButton accepts an optional metadata prop for app-side context.
<StablePayButton
amount={0.1}
reference="order-123"
metadata={{ requestId: "req_123", customerEmail: "alice@example.com" }}
onSuccess={(payload) => {
console.log(payload.metadata?.requestId);
}}
>
<button>Pay 0.1 USDC</button>
</StablePayButton>metadata is:
- returned in the
onSuccesspayload - useful for request IDs, customer context, or analytics source data
- not sent on-chain
- not persisted anywhere by the package itself