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

@aquarian-metals/coin-moebius-bitcoin

v4.5.1

Published

Self-hosted Bitcoin provider for Coin Moebius — direct Bitcoin Core RPC integration. No third-party gateway, no custodial keys, no API tokens. The merchant runs bitcoind and a small indexer; this package supplies the browser provider, the server-side crea

Downloads

995

Readme

@aquarian-metals/coin-moebius-bitcoin

Self-hosted Bitcoin provider for Coin Moebius. No custodial third-party gateway, no API keys to a payment processor, no merchant-tax-form-collecting middleman. The merchant runs bitcoind, this package supplies everything else.

Two entries in one package:

  • @aquarian-metals/coin-moebius-bitcoin — browser entry. Modal-rendering provider that posts to your own checkout endpoint and shows the buyer a fresh Bitcoin address, the exact amount, and a bitcoin: link.
  • @aquarian-metals/coin-moebius-bitcoin/server — server entry. createBitcoinCreator (mints a labeled address per checkout), createBitcoinVerifier (validates indexer webhooks), createBitcoinIndexer (the long-running chain watcher). Never import this from browser code.

Why a self-hosted Bitcoin provider

Custodial gateways (Cryptomus, NOWPayments, BTCPay-as-a-service) ask you to trust them with your money, your KYC, and your geo-availability. Self-hosting Bitcoin gives you:

  • No counterparty. Funds land directly in a wallet you control. No "gateway froze our account" thread on r/Bitcoin ends with your store.
  • No third-party API keys. Nothing on a vendor's dashboard can be revoked from under you.
  • No geo limits. Cryptomus is geo-blocked in the US; NOWPayments is fine but is a custodian. Self-hosted has no political surface.
  • No fee skim. The only fee is the Bitcoin network fee, which the buyer pays.

The trade-off is operational: you (or your VPS) run a Bitcoin node. This package makes the wiring on top of that as small as possible.

Install

npm install @aquarian-metals/coin-moebius-bitcoin

The server entry uses Web Crypto (crypto.subtle) for HMAC and fetch for the node's JSON-RPC — works in Node 18+, Cloudflare Workers, Deno, and Bun. No additional dependency.

Use — browser

import { createBitcoinProvider } from '@aquarian-metals/coin-moebius-bitcoin';
import { createPaymentManager } from '@aquarian-metals/coin-moebius';

const payments = createPaymentManager({
  providers: [
    createBitcoinProvider({
      checkoutEndpoint: '/api/checkout/bitcoin',
      statusEndpoint: '/api/payment-status',
    }),
  ],
});

document.getElementById('buy-with-bitcoin')?.addEventListener('click', () => {
  payments.initiate({ productId: 'ebook-42', amount: 19.99, currency: 'USD' });
});

Use — server (overview)

Three serverless functions / handlers, plus one long-running indexer:

  1. POST /api/checkout/bitcoin — the browser posts here; you call createBitcoinCreator(...) and return its result.
  2. POST /api/payment-webhook — the indexer posts here; you call verifierRegistry.verify(...) and fulfill orders on 'success'.
  3. GET /api/payment-status?paymentId=… — the browser polls here; you return the current PaymentRecord from your store.
  4. The indexer process — see "Running the indexer" below.
// /api/checkout/bitcoin
import { createBitcoinCreator } from '@aquarian-metals/coin-moebius-bitcoin/server';
import { myProductionStore } from './store.js';

const create = createBitcoinCreator({
  rpcUrl: process.env.BITCOIN_RPC_URL,
  wallet: 'shop',
  rpcUser: process.env.BITCOIN_RPC_USER,
  rpcPassword: process.env.BITCOIN_RPC_PASSWORD,
  store: myProductionStore,
  btcPerUnit: async (currency) => {
    // You decide where prices come from — CoinGecko, Kraken ticker,
    // pinned constant. Coin Moebius does not call any oracle.
    return 1 / (await fetchBitcoinPriceFromMyOracle(currency));
  },
});

export default async (req) => {
  const { productId, metadata } = await req.json();
  // Price from your own catalog, never from the request: the browser is the
  // buyer's, and a buyer can send any amount they like.
  const price = await priceOf(productId);
  if (!price) return new Response('Unknown product', { status: 404 });
  return Response.json(await create({ productId, ...price, metadata }));
};
// /api/payment-webhook
import { createVerifierRegistry } from '@aquarian-metals/coin-moebius-server';
import { createBitcoinVerifier } from '@aquarian-metals/coin-moebius-bitcoin/server';

const verifiers = createVerifierRegistry();
verifiers.register('bitcoin', createBitcoinVerifier({ hmacSecret: process.env.BITCOIN_HMAC_SECRET }).verify);

export default async (req) => {
  const result = await verifiers.verify(await req.text(), Object.fromEntries(req.headers));
  if (result?.status === 'success') {
    // Fulfill the order. This handler MUST be idempotent — like every
    // webhook receiver, you may get a duplicate.
  }
  return new Response('', { status: 200 });
};

Running the indexer

The indexer is the long-running process that watches the node's wallet and converts on-chain events into webhook posts. It must run inside the same private network as bitcoind. It never accepts inbound network traffic; it only makes outbound calls to the node's RPC (private network) and to your webhook endpoint (HTTPS).

// indexer.js
import { createBitcoinIndexer } from '@aquarian-metals/coin-moebius-bitcoin/server';
import { myProductionStore } from './store.js';

const indexer = createBitcoinIndexer({
  rpcUrl: process.env.BITCOIN_RPC_URL,
  wallet: 'shop',
  rpcUser: process.env.BITCOIN_RPC_USER,
  rpcPassword: process.env.BITCOIN_RPC_PASSWORD,
  store: myProductionStore,
  webhookUrl: process.env.BITCOIN_WEBHOOK_URL,
  hmacSecret: process.env.BITCOIN_HMAC_SECRET,
  requiredConfirmations: 1,
  pollIntervalMs: 30_000,
});

const stop = indexer.start();
process.on('SIGTERM', stop);
process.on('SIGINT', stop);

Why downtime is cheap

The indexer is catch-up by design. If it's offline for an hour, the next tick queries the node for the missed block range and emits the webhooks then. Buyers see a stale "pending" during the outage; nothing is lost, nothing requires manual reconciliation.

Compared to a hosted gateway, where downtime on the gateway's end means missed events and refund tickets, your operational SLA for self-hosted Bitcoin is "eventually consistent within a few minutes." That's a much easier bar to meet than "five-nines uptime," and it's the property that makes self-hosting realistic for indie merchants who don't run an ops team.

Node setup notes

The minimum the merchant needs running, in production:

# bitcoin.conf
server=1
prune=550
rpcbind=127.0.0.1
rpcallowip=127.0.0.1
rpcuser=shop
rpcpassword=<long random password>
# A watch-only wallet holding only your public descriptor.
bitcoin-cli -named createwallet wallet_name=shop disable_private_keys=true blank=true load_on_startup=true
bitcoin-cli -rpcwallet=shop importdescriptors '[{"desc":"wpkh([…]xpub…/0/*)#…","active":true,"range":[0,999],"timestamp":"now"}]'

Never expose the node's RPC to the public internet. See docs/self-hosted-bitcoin.md for the full walkthrough, including where the descriptor comes from, a regtest recipe for trying it without real money, and troubleshooting.

Related

  • For self-hosted Monero or Zano, see @aquarian-metals/coin-moebius-monero and @aquarian-metals/coin-moebius-zano.
  • For custodial crypto on a hosted gateway, see @aquarian-metals/coin-moebius-nowpayments.
  • For fiat (Stripe), see @aquarian-metals/coin-moebius-stripe.
  • For Goldbacks, cash, check, or barter, see @aquarian-metals/coin-moebius-manual.

See the main README for the full quick-start, and examples/static-site-demo/bitcoin/ for a copy-paste deployment.

License

MIT — see LICENSE.