npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@canopypay/react-checkout

v0.3.0

Published

Native React checkout using the same Canopy checkout UI and payment APIs as the hosted widget. It renders in your page without an iframe or Next.js runtime. Requires React and React DOM 18.3+ or 19, a browser bundler with dynamic import support, and your

Downloads

1,080

Readme

@canopypay/react-checkout

Native React checkout using the same Canopy checkout UI and payment APIs as the hosted widget. It renders in your page without an iframe or Next.js runtime. Requires React and React DOM 18.3+ or 19, a browser bundler with dynamic import support, and your exact frontend origin registered against your Canopy merchant account.

0.3.0

Adds useCheckout, useCheckoutStatus, and typed onReady, onDepositDetected, onPaid, and onError callbacks. Existing client / intentId component integrations keep working. The hooks share lifecycle and widget event state; they add no payment polling or wallet prompts.

0.2.0

This release decodes the versioned checkout status contract (canopy.checkout.status v1). It requires a Canopy server deploy that serves the contract; any deploy that includes it will do. The public component API is unchanged — the minor bump reflects the decoded payload, not your integration.

0.1.x keeps working for one release: the server serves a superset with the legacy identity field still aliased at the top level. Upgrade before that compatibility window closes.

Integration

Create the payment intent on your backend as usual. Keep secret merchant keys there. Pass its ID and your publishable merchant key to your client component:

"use client";
import { useMemo } from "react";
import {
  CanopyCheckout,
  createCheckoutClient,
  preloadCheckout,
} from "@canopypay/react-checkout";
import "@canopypay/react-checkout/styles.css";

export function Checkout({
  intentId,
  merchant,
}: {
  intentId: string | null;
  merchant: string;
}) {
  const client = useMemo(() => createCheckoutClient({ merchant }), [merchant]);
  return (
    <CanopyCheckout
      client={client}
      intentId={intentId}
      features={{ manualTransfer: true, walletPayment: true }}
      onEvent={(event) => {
        if (event.type === "paid") {
          // Refresh your order from your backend. Webhooks remain authoritative.
        }
      }}
    />
  );
}

Import the CSS once in your app's global CSS entry if your framework requires that. Styles are scoped to .canopy-checkout; theme defaults to light and theme="dark" opts into dark mode. The host still controls the surrounding layout and can override styles.

Lifecycle hooks and sibling status

Create one flow with useCheckout, pass its checkoutProps to one widget, and pass the opaque checkout handle to sibling UI:

"use client";
import { useMemo } from "react";
import {
  CanopyCheckout,
  createCheckoutClient,
  useCheckout,
  useCheckoutStatus,
  type CheckoutHandle,
} from "@canopypay/react-checkout";
import "@canopypay/react-checkout/styles.css";

function PaymentStatus({ checkout }: { checkout: CheckoutHandle }) {
  const status = useCheckoutStatus(checkout);
  return (
    <p aria-live="polite">
      {status.paid
        ? "Payment update received. Checking your order…"
        : status.depositDetected
          ? "Deposit detected"
          : status.ready
            ? "Ready to deposit"
            : status.preparing
              ? "Preparing checkout…"
              : "Checkout waiting"}
    </p>
  );
}

export function Checkout({
  merchant,
  intentId,
  refreshOrder,
}: {
  merchant: string;
  intentId: string | null;
  refreshOrder: () => void;
}) {
  const client = useMemo(() => createCheckoutClient({ merchant }), [merchant]);
  const flow = useCheckout({ client, intentId, onPaid: refreshOrder });
  return (
    <section>
      <CanopyCheckout {...flow.checkoutProps} />
      <PaymentStatus checkout={flow.checkout} />
      {flow.fatalError && (
        <button
          onClick={() => {
            void flow.retry().catch(() => {});
          }}
        >
          Retry checkout
        </button>
      )}
      <button onClick={flow.reset}>Pause checkout</button>
      <button
        onClick={() => {
          void flow.prepare().catch(() => {});
        }}
      >
        Prepare or resume checkout
      </button>
    </section>
  );
}

useCheckout itself makes no request during render or server rendering. Mounting its widget automatically prepares a non-null intent. Before mounting, await flow.prepare() warms preparation without discovering wallets or submitting a payment. A null intent leaves preparation idle. Keep client stable with useMemo and create the intent on your backend.

| Value or method | Meaning | | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | preparing / prepared | Preparation is pending / verified preparation has completed. Neither means the widget is usable. | | shellReady | The widget shell reached its first animation frame. | | ready | The widget reported that checkout is usable. | | depositDetected / paid | The widget has emitted that UI notification in this flow. | | error | Most recent error, including nonfatal rejected theme variables. | | fatalError | checkout_unavailable or invalid_features; theme rejection does not set it. | | prepare() | Prepare this intent, sharing the client's cached request; also resume a reset flow. Rejects with a generic checkout error on failure. | | reset() | Clear this flow's UI state and unmount its payment widget until prepare(), retry(), or a different client/intent resumes it. Does not cancel an order or payment, revoke a session, or clear the client's preparation cache. | | retry() | Clear this intent's preparation cache, clear lifecycle errors, and restart this intent. Recovers a failed runtime load or widget boundary. Other intents' cached preparations remain available. Rejects if preparation fails. |

prepare() and retry() finish preparation, not widget readiness; watch ready or onReady for usability. Reset, retry, client/intent changes and unmount discard stale completions and event callbacks. A request already sent can finish in the background, but cannot update an obsolete flow. Retained controls become inert after the owning flow unmounts.

useCheckoutStatus(checkout) only subscribes to the same in-memory snapshot. It performs no fetch and starts no timer. Live deposit and payment updates require the mounted widget; this is not a headless payment controller. Render one CanopyCheckout per handle. The handle and hook snapshots expose no bearer token, prepared response, or transport.

All convenience callbacks receive the corresponding typed CheckoutEvent. They work on both useCheckout and CanopyCheckout alongside onEvent. Callbacks run after the state update; replacing them does not restart checkout, and a throwing callback does not fail checkout or prevent the other callbacks. ready and shell_ready fire once per flow generation; later deposit/payment notifications continue to forward. Avoid registering the same side effect in both hook and component callbacks.

Use either checkout or the legacy client / intentId props on a component; TypeScript rejects combining them. paid remains a UI notification: backend order state and webhooks are authoritative for fulfilment.

Theme options

Use the same theme and variables options as the inline SDK:

<CanopyCheckout
  client={client}
  intentId={intentId}
  theme="dark" // "light", "dark", or "auto" (follows the system)
  variables={{
    accent: "#17803d",
    background: "#080808",
    surface: "#141414",
    text: "#f0ede8",
    muted: "#999999",
    border: "#333333",
    radius: "12px",
    font: "Inter, sans-serif",
  }}
/>

The variables apply immediately to both skeletons and ready checkout, stay scoped to this instance, and update when props change. theme defaults to light for existing React integrations; use auto explicitly to follow system changes. style remains available for layout and explicit overrides and takes precedence over variables.

The inline SDK's variable validation and contrast rules also apply here. Rejected entries are ignored and emit a nonfatal error event with code and variable; valid entries continue to apply. These theme errors do not make checkout unavailable. Exported CanopyThemeOption, CanopyThemeVariable, and CanopyThemeVariables types match the inline SDK.

Open immediately

Mount and show the component as soon as the customer chooses to pay. intentId can be null while your backend creates the order; the skeleton renders and the runtime preloads without starting a preparation request. Set the ID when it arrives. Method selection survives this transition.

Keep the checkout container visible while loading. shell_ready fires at the first animation frame after mounting; it signals that the shell is mounted, not that the host has made it visible or payment is usable. Do not hide the checkout behind the ready event—that event waits for verified payment data.

Prepare before the click

Call preloadCheckout().catch(() => {}) when the cart opens or the payment button becomes likely to be used. It loads the manual checkout runtime without wallet discovery. As soon as your backend returns the intent ID, call client.prepare(intentId).catch(() => {}) on the same client instance you pass to the component. These independent operations can run together.

Preparation shares pending work and reuses a successful result for 45 seconds in browser memory. No bearer is written to cookies, URLs, local storage or events. client.clear() forgets all cached preparations; client.clear(intentId) forgets only that intent. Keep the client stable across renders, scoped to a browser checkout flow; do not share one global client between server-rendered users.

Preparation establishes a checkout session but does not create an order or submit payment. Native rendering removes the second document's navigation/hydration and extension reinjection opportunity. It still needs Canopy's verified intent/customer readiness and API response; it does not guarantee an instant cold checkout. Wallet extensions can still run in the merchant page independently of Canopy.

Features and events

  • Both manualTransfer and walletPayment default to enabled. Either can be disabled; both disabled or unknown feature names produce invalid_features. Future methods can add named flags without changing the component shape.
  • Wallet modules and discovery start when wallet payment is selected. With wallet-only features, it is selected immediately. Selecting a method does not request wallet permission.
  • An intent’s required price remains fixed in the wallet amount field and respects the rail minimum, matching the hosted widget. Unpriced deposits remain editable.
  • ready fires once when the checkout is usable. This is a mount-to-usable event, unlike the iframe SDK's shell-ready event.
  • paid is a UI notification, not proof of settlement; use server-side order state and webhooks.
  • error carries invalid_features or checkout_unavailable, without credentials or server error text.

canopyOrigin defaults to https://www.canopypay.io; set it only for your Canopy deployment or localhost development. CSP must permit connect-src and img-src to that origin, plus the wallet/RPC origins needed for enabled methods. Requests use explicit bearer authorization and omit ambient cookies. The server checks the registered origin, merchant key, and session ownership/expiry/revocation, then uses the existing payment handlers.

Timing and rollout

Axiom checkout.native.startup reports dur_ms (mount to usable), server_result_wait_ms, widget_load_ms (parallel module wait), and server_prepare_ms from preparation. These waits overlap; do not add them. Trace headers correlate preparation, subsequent APIs and browser timing. Reports contain timings only and are best effort. Version 0.1.2 adds shell_ready_ms, intent_wait_ms, and prepare_*_ms measurements for fetch/headers/body, application handler duration, and browser resource timing where available. Handler duration excludes platform work before invocation; resource detail may be absent in some browsers. These durations overlap and are not additive.

Deploy the accompanying /api/checkout/* server routes before publishing/installing this package. 0.1.1 adds a preparing view with interactive payment-method selection. A choice made while preparation is pending is retained when the verified checkout becomes usable; no wallet prompt or payment action is available before then. Existing @canopypay/[email protected] integrations continue using the hosted iframe and do not need a package release for the backend optimizations. Switching to native checkout requires changing the merchant integration.

From the repository root:

npm run react-sdk:build
# Browser fixture uses mocked payment responses; never submits a payment:
CANOPY_PLAYWRIGHT_MODULE=/path/to/playwright \
CANOPY_CHROME_EXECUTABLE=/path/to/chrome \
node scripts/check-native-checkout-browser.cjs