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

@gobtcpay/pos-api-sdk

v0.1.1

Published

GoMining POS API SDK

Readme

@gobtcpay/pos-api-sdk

Client for the GoBTC Pay POS API — accept Bitcoin payments from a point-of-sale terminal: create a payment, show the QR code, and track it until it settles on-chain.

Installation

npm install @gobtcpay/pos-api-sdk

Runs on Node >= 18 and in browsers — it uses the isomorphic Web Crypto API, no node:crypto dependency.

⚠️ The apiKey is a per-terminal secret. Only embed it in a trusted environment (server, or a controlled POS device). Do not ship it in a public web page.

Quick start

import { GoBTCPay } from "@gobtcpay/pos-api-sdk";

// Initialize once. The client signs every request for you with a fresh
// HMAC signature + timestamp — there is no token to manage or refresh.
const btcPay = new GoBTCPay({
  apiKey: process.env.POS_API_KEY!,
  posTerminalId: process.env.POS_TERMINAL_ID!,
});

// Create a payment and show the QR to the customer.
const payment = await btcPay.createPayment({
  amount: 10,
  currency: "USD",
  description: "Order #1024",
});
console.log(payment.paymentId, payment.qrString);

// Poll for the outcome.
const latest = await btcPay.getPayment({ paymentId: payment.paymentId });
console.log(latest.status); // initiated | paid | cleared | expired | canceled | failed

CommonJS works too:

const { GoBTCPay } = require("@gobtcpay/pos-api-sdk");

Usage by environment

The package is isomorphic (ESM + CJS, Web Crypto) and runs on a server, in a bundled browser app, and in a plain HTML page.

⚠️ Where to put the apiKey. It is a per-terminal secret used to sign requests. Keep it server-side, or on a controlled POS device. Do not ship it in a public website. For browser usage the API must also allow your origin via CORS — otherwise route calls through your own backend.

Server (Node.js / Bun / Deno)

ESM:

import { GoBTCPay } from "@gobtcpay/pos-api-sdk";

const btcPay = new GoBTCPay({
  apiKey: process.env.POS_API_KEY,
  posTerminalId: process.env.POS_TERMINAL_ID,
});

const payment = await btcPay.createPayment({ amount: 10, currency: "USD" });
console.log(payment.qrString);

CommonJS:

const { GoBTCPay } = require("@gobtcpay/pos-api-sdk");

A common pattern is to keep the secret on your server and expose thin endpoints (POST /payments, GET /payments/:id) that the browser calls.

Browser (with a bundler — Vite, webpack, Next.js, …)

Install and import like any package; the bundler picks the ESM build:

import { GoBTCPay } from "@gobtcpay/pos-api-sdk";

const btcPay = new GoBTCPay({ apiKey, posTerminalId });

const payment = await btcPay.createPayment({ amount: 10, currency: "USD" });

// Watch for the result and update the UI live.
const watcher = btcPay.watchPayment({ paymentId: payment.paymentId });
watcher.onChange((s) => (statusEl.textContent = s.status));
watcher.onPaid(() => showPaidScreen());

Plain HTML (no build step)

Load the ESM build straight from a CDN inside <script type="module">:

<!doctype html>
<html>
  <body>
    <pre id="out">creating…</pre>
    <script type="module">
      import { GoBTCPay } from "https://esm.sh/@gobtcpay/pos-api-sdk";

      const btcPay = new GoBTCPay({
        apiKey: "YOUR_TERMINAL_KEY", // test/controlled device only — this is a secret
        posTerminalId: "YOUR_TERMINAL_ID",
        // baseUrl: "https://pay.dev.gobtcpay.com/public/api/v1.1", // staging
      });

      const out = document.getElementById("out");
      try {
        const payment = await btcPay.createPayment({ amount: 1, currency: "USD" });
        out.textContent = `paymentId: ${payment.paymentId}\nqr: ${payment.qrString}`;

        const watcher = btcPay.watchPayment({ paymentId: payment.paymentId });
        watcher.onChange((s) => (out.textContent += `\nstatus: ${s.status}`));
      } catch (err) {
        out.textContent = "error: " + err.message;
      }
    </script>
  </body>
</html>

The browser must be allowed to reach the API origin (CORS). If you hit Failed to fetch / a CORS error, proxy the requests through your backend and point baseUrl at that proxy.

Configuration

| Option | Required | Default | Description | | --------------- | -------- | ---------------------------------------------------- | ---------------------------------------------------- | | apiKey | yes | — | Per-terminal secret used to sign every request. | | posTerminalId | no | — | Default terminal for createPayment. | | baseUrl | no | https://api.gobtcpay.com/public/api/v1.1 | Override for staging/dev environments. | | timeoutMs | no | 30000 | Per-request timeout. | | fetch | no | global fetch | Custom fetch implementation. |

Methods

btcPay.createPayment({ amount, currency, description?, ttlSeconds?, externalId?, posTerminalId? });
btcPay.getPayment({ paymentId });
btcPay.watchPayment({ paymentId, intervalMs?, timeoutMs?, until?, immediate?, stopOnError? });

Auto-polling

watchPayment() polls getPayment on an interval until the payment reaches a final status (cleared / expired / canceled / failed). It returns a PaymentPoller with a live state object, subscriptions, and a done() promise. It starts automatically. The interval defaults to and is clamped to a minimum of 3 seconds.

const watcher = btcPay.watchPayment({ paymentId, intervalMs: 3000 });

// Live state, readable at any time:
watcher.state; // { status, payment, error, attempts, isPolling, isFinal, ... }

// Subscriptions (each returns an unsubscribe function):
watcher.onChange((state) => render(state.status)); // any state change
watcher.onUpdate((payment) => {});                 // every successful poll
watcher.onPaid((payment) => {});                   // customer paid (not yet on-chain)
watcher.onSettled((payment) => {});                // reached a final status
watcher.onError((err) => {});                      // a poll failed

// Or just await the outcome:
const finalPayment = await watcher.done();

watcher.stop(); // stop early

paid vs cleared: paid means the customer has paid (the moment a cashier usually cares about); cleared is the later on-chain confirmation. By default the poller keeps running until a final status (cleared / …) and done() resolves there. To stop as soon as the customer pays, pass until: ["paid"] — or just react to onPaid while polling continues.

Webhooks

Register a webhook URL in the merchant dashboard to receive payment.status.updated events instead of (or in addition to) polling. The handler verifies the X-GoBTCPay-Signature header, enforces a replay window, and de-duplicates on eventId.

const webhooks = btcPay.webhooks({ signingSecret: process.env.POS_WEBHOOK_SECRET! });

webhooks.on("payment.status.updated", (event) => {
  // event.data has the same shape as getPayment()
  console.log(event.data.paymentId, event.data.status);
});

// Express example — pass the RAW request body, not a re-serialized object.
app.post("/webhooks/gobtcpay", express.raw({ type: "*/*" }), async (req, res) => {
  try {
    await webhooks.handle(req.body.toString("utf8"), req.header("X-GoBTCPay-Signature"));
    res.sendStatus(200); // any 2xx acknowledges; non-2xx is retried
  } catch (err) {
    res.sendStatus(400);
  }
});

handle() returns the parsed event, or null if it was a duplicate. Use constructEvent() if you only want to verify + parse without dispatching.

Errors

All errors extend GoBTCPayError:

  • GoBTCPayApiError — the API returned an error envelope or non-2xx status (.httpStatus, .body).
  • WebhookSignatureError — a webhook signature failed verification.

Development

npm install
npm run build      # dist/ (ESM + CJS + .d.ts)
npm run dev        # rebuild on change
npm run typecheck

Versioning

The API version is pinned inside the SDK (currently v1.1, exported as API_VERSION) — you don't put it in a URL. The SDK always speaks the API version it was built and tested against, and the package version tracks it via semver:

| API version | SDK version | | ----------- | ----------- | | v1.x | 0.x / 1.x | | v2 (breaking) | 2.x |

  • Non-breaking API changes (v1.1 → v1.2) ship as a minor SDK release.
  • A breaking API version (v2) ships as a new major SDK release that points at the new base URL internally.

Need a different version or environment? Override baseUrl (the escape hatch):

new GoBTCPay({ apiKey, baseUrl: "https://api.gobtcpay.com/public/api/v1.1" });

Testing

Two layers, both run on Vitest.

Unit tests — pure logic, no network. They gate every MR and run in CI.

npm test              # run once
npm run test:watch    # watch mode
npm run test:coverage # with a coverage report (thresholds enforced)

They cover request signing (HMAC cross-checked against an independent implementation), the HTTP envelope transport, webhook verification (signature / replay window / de-dup), the payment poller lifecycle (fake timers), and the client surface.

Integration tests — exercise the library end-to-end against a live POS API on the test contour. They are opt-in and self-skip unless credentials are present, so npm test and CI stay green without secrets.

cp .env.example .env   # fill in test-contour values, then:
POS_API_KEY=... POS_TERMINAL_ID=... npm run test:integration

See .env.example for all variables. Use a test-contour terminal secret only — never a production key.

License

MIT — a permissive open-source license. You may freely use, copy, modify, merge, publish, distribute, sublicense, and sell the software, including in closed-source and commercial products. The only condition is that the copyright notice and the license text are kept in copies. The software is provided "as is", without warranty and with no liability on the authors.

See LICENSE for the full text.