Registry block
Dogecoin Payment Button (link to this section)
Installable shadcn registry block that derives BIP44 Dogecoin payment addresses from an account-level public key, then shows a QR code and copyable address in a dialog. Optionally watches the mempool for an incoming payment.
Preview (link to this section)
Install into a Radix UI project.
The block API and registry URL stay the same. Your project's shadcn base decides whether Base UI or Radix UI primitives are installed for button and dialog.
Preview
Radix UI ยท mocked
Uses a fixed example address and a simulated mempool watch that detects a payment after three seconds. No environment variables required.
Live demo (link to this section)
Resolves a payment address from DOGECOIN_XPUB on the server and watches the mempool through the same-origin SSE proxy.
Live demo
API route
Installation (link to this section)
Add the registry block with the shadcn CLI, or copy the component files manually.
pnpm dlx shadcn@latest add https://dogecoin-payment-button.vercel.app/r/dogecoin-payment-button.jsonUsage (link to this section)
Prefer resolving the account public key on the server and passing a resolver into the button.
"use client";
import { DogecoinPaymentButton } from "@/components/dogecoin-payment-button";
import type {
AddressSelectionResult,
DogecoinPaymentResolveFn,
SelectionState,
} from "@/components/dogecoin-payment-button";
const resolvePaymentAddress: DogecoinPaymentResolveFn = async (
state: SelectionState
): Promise<AddressSelectionResult> => {
const response = await fetch("/api/dogecoin/resolve-address", {
body: JSON.stringify(state),
headers: { "Content-Type": "application/json" },
method: "POST",
});
if (!response.ok) {
throw new Error("Unable to resolve a Dogecoin payment address.");
}
return (await response.json()) as AddressSelectionResult;
};
export function DonateButton() {
return (
<DogecoinPaymentButton
amount="5"
mempoolWatch={{ endpoint: "/api/dogecoin/mempool/watch" }}
resolveAddress={resolvePaymentAddress}
/>
);
}
Next.js integration (link to this section)
Copy these Next.js files to wire server-side address resolution and the optional OnlyDoge mempool SSE proxy.
IMPORTANT: Keep DOGECOIN_XPUB and ONLYDOGE_API_TOKEN on the server.
import {
createInitialSelectionState,
resolveDogecoinPaymentAddress,
} from "@/components/dogecoin-payment-button";
import type {
AddressSelectionResult,
SelectionState,
} from "@/components/dogecoin-payment-button";
export const dynamic = "force-dynamic";
const jsonError = (message: string, status: number): Response =>
Response.json({ error: message }, { status });
const isSelectionState = (value: unknown): value is SelectionState => {
if (typeof value !== "object" || value === null) {
return false;
}
const record = value as Record<string, unknown>;
return (
typeof record.startIndex === "number" &&
Number.isInteger(record.startIndex) &&
record.startIndex >= 0 &&
typeof record.uncheckedMode === "boolean"
);
};
const parseSelectionState = (body: unknown): SelectionState | null => {
if (body === null || body === undefined) {
return createInitialSelectionState();
}
if (!isSelectionState(body)) {
return null;
}
return body;
};
/**
* Public payment-address resolver for the live demo.
*
* Intentionally unauthenticated: checkout visitors are anonymous. Kept as a
* Route Handler (not a Server Action) so the public contract is explicit HTTP.
*/
export const POST = async (request: Request): Promise<Response> => {
const xpub = process.env.DOGECOIN_XPUB;
if (!xpub) {
return jsonError(
"DOGECOIN_XPUB is not configured. Add a dedicated account xpub to your environment.",
503
);
}
let body: unknown;
try {
body = await request.json();
} catch {
return jsonError("Request body must be JSON.", 400);
}
const selection = parseSelectionState(body);
if (!selection) {
return jsonError(
"Body must include integer startIndex (>= 0) and boolean uncheckedMode.",
400
);
}
try {
const result: AddressSelectionResult = await resolveDogecoinPaymentAddress({
startIndex: selection.startIndex,
uncheckedMode: selection.uncheckedMode,
xpub,
});
return Response.json(result);
} catch (error: unknown) {
const message =
error instanceof Error
? error.message
: "Unable to resolve a Dogecoin payment address.";
return jsonError(message, 500);
}
};
Notes (link to this section)
Mempool watching is opt-in. Detection means the payment was seen in the mempool, not that it is confirmed. Use a dedicated account public key at m/44'/3'/0'.
Registry URL used by this deployment: https://dogecoin-payment-button.vercel.app/r/dogecoin-payment-button.json