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

snap-payments-sdk

v1.0.0

Published

Official TypeScript SDK for the SNAP payments API — Somalia's developer-first payment infrastructure.

Readme

snap-payments-sdk

Official TypeScript SDK for the SNAP payments API. Isomorphic — runs in Node 18+, modern browsers, Bun, Deno, and edge runtimes.

Package name note. The @snap npm scope is owned by Snap Inc., so we publish as the unscoped snap-payments-sdk. If we later claim a SNAP-owned scope (e.g. @snap-payments) the unscoped name stays available as a compat alias.

Install

npm install snap-payments-sdk
# or
bun add snap-payments-sdk

Quick start

import { Snap } from "snap-payments-sdk";

const client = new Snap(process.env.SNAP_SECRET_KEY!);

const payment = await client.payments.create({
  amount: 1500, // $15.00 in cents
  provider: "evcPlus",
  customer: { phone: "+252615123456" },
});

console.log(payment.id, payment.status);

Surface

Core

client.payments.create(params, options?)
client.payments.retrieve(id, options?)

client.refunds.create(params, options?)
client.refunds.retrieve(id, options?)

client.checkout.sessions.create(params, options?)

client.webhooks.verify({ payload, header, secret, tolerance?, now? })

Billing (v0.2.0)

client.invoices.create(params, options?)
client.invoices.retrieve(id, options?)
client.invoices.list(params?, options?)
client.invoices.send(id, options?)
client.invoices.void(id, options?)

client.prices.create(params, options?)
client.prices.list({ productId?, limit? }, options?)
client.prices.archive(id, options?)

client.subscriptions.create(params, options?)
client.subscriptions.cancel(id, { atPeriodEnd? }, options?)
client.subscriptions.resume(id, options?)

client.payouts.create(params, options?)
client.payouts.retry(id, options?)
client.payouts.cancel(id, options?)

client.portalSessions.create(params, options?)

Platform (v0.3.0)

client.connectedAccounts.create(params, options?)
client.connectedAccounts.list({ status?, limit? }, options?)

client.transfers.create(params, options?)
client.transfers.list({ destination?, status?, limit? }, options?)

client.revenue.summary({ rangeStart, rangeEnd, scope? }, options?)
client.revenue.listSchedules(params?, options?)
client.revenue.retrieveSchedule(id, options?)

client.reports.get({ name, rangeStart, rangeEnd, format? }, options?)

client.radar.createRule(params, options?)
client.radar.listRules({ active? }, options?)
client.radar.archiveRule(id, options?)
client.radar.listEvaluations({ action?, limit? }, options?)
client.radar.approveEvaluation(id, options?)
client.radar.rejectEvaluation(id, options?)

client.payoutMethods.create(params, options?)
client.payoutMethods.delete(id, options?)

All return values are fully typed against the wire format the API returns. Field names are snake_case to match the JSON payload — no silent renaming.

End-to-end: subscribe a new customer

A minimal flow that covers most SaaS onboarding paths — create a recurring price, start a subscription, mint a customer-portal link for the confirmation email, and hand back a session URL the browser can redirect to.

import { Snap } from "snap-payments-sdk";

const client = new Snap(process.env.SNAP_SECRET_KEY!);

// 1. One-time setup: a product + a monthly price. Skip on repeat.
const price = await client.prices.create({
  productId: "prod_starter",
  unitAmount: 2900,          // $29.00
  interval: "month",
  nickname: "Starter monthly",
});

// 2. Start the subscription. Server generates the first invoice
//    and holds it in `incomplete` until the checkout succeeds.
const subscription = await client.subscriptions.create({
  customerId: "cus_123",
  priceId: price.id,
});

// 3. Hosted checkout for the first invoice. `successUrl` gets a
//    session_id query param so you can verify the return.
const invoiceUrl = subscription.latest_invoice_id
  ? (await client.invoices.retrieve(subscription.latest_invoice_id))
      .hosted_invoice_url
  : null;

// 4. Customer-portal link for the receipt email. Expires in 24h.
const portal = await client.portalSessions.create({
  customerId: "cus_123",
  returnUrl: "https://app.example.com/billing",
  expiresInSeconds: 60 * 60 * 24,
});

return { checkoutUrl: invoiceUrl, portalUrl: portal.url };

Any of these calls throws a typed SnapApiError on 4xx and auto- retries transient 5xx / 429 responses with exponential backoff.

Authentication & idempotency

const client = new Snap({
  apiKey: process.env.SNAP_SECRET_KEY!,
  baseUrl: process.env.SNAP_API_URL!, // or omit and export SNAP_API_URL
});

The API host is not baked in — you must either pass baseUrl explicitly or export SNAP_API_URL in the process environment. If neither is set the constructor throws, so a typo can't silently route live-mode calls at the wrong environment.

Mutating calls (POST/PUT) auto-generate an Idempotency-Key (UUID v4) when you don't pass one. To pin your own — recommended for any call that might be retried by your caller — pass it via the per-request options:

await client.payments.create(
  { amount: 100, provider: "mock" },
  { idempotencyKey: `order_${orderId}` },
);

The API replays the same response for the same key + same body for the merchant's idempotency TTL window. A different body under the same key throws IdempotencyConflictError.

Errors

import {
  Snap,
  SnapApiError,
  AuthenticationError,
  NotFoundError,
  IdempotencyConflictError,
} from "snap-payments-sdk";

try {
  await client.payments.retrieve("pay_missing");
} catch (err) {
  if (err instanceof NotFoundError) {
    // 404 — handle missing payment
  } else if (err instanceof AuthenticationError) {
    // 401 — rotate or check the API key
  } else if (err instanceof SnapApiError) {
    // any other API-side failure — `err.code`, `err.requestId`, etc.
  } else {
    // network failure, timeout, or non-JSON 5xx — SnapConnectionError
    throw err;
  }
}

| Class | Wire codes | | --------------------------- | --------------------------------------------------------------------- | | AuthenticationError | authentication_failed | | PermissionDeniedError | permission_denied, merchant_not_approved, merchant_suspended | | InvalidRequestError | validation_error, idempotency_key_missing, unsupported_provider | | IdempotencyConflictError | idempotency_conflict | | NotFoundError | not_found | | ProviderError | provider_offline, provider_error | | ServerError | internal_error | | SnapConnectionError | network failures, timeouts, malformed responses |

Every SnapApiError carries code, type, statusCode, requestId, and the raw response body for log forwarding.

Webhooks

import { Snap, WebhookVerificationError } from "snap-payments-sdk";

const client = new Snap(process.env.SNAP_SECRET_KEY!);

app.post("/webhooks", express.raw({ type: "application/json" }), async (req, res) => {
  try {
    const event = await client.webhooks.verify({
      payload: req.body.toString("utf8"),
      header: req.header("X-SNAP-Signature")!,
      secret: process.env.SNAP_WEBHOOK_SECRET!,
    });
    // event.type → "payment.succeeded" | "payment.failed" | ...
    // event.data.object → Payment / Refund / CheckoutSession
    res.sendStatus(204);
  } catch (err) {
    if (err instanceof WebhookVerificationError) res.sendStatus(400);
    else throw err;
  }
});

The verifier checks both the HMAC-SHA256 MAC over ${t}.${payload} and that the timestamp is within tolerance seconds of the current time (default 300). Pass tolerance: 0 to disable the timestamp check entirely (useful for offline replay tooling).

Per-request options

Every resource method accepts a second options argument:

await client.payments.create(params, {
  idempotencyKey: "order_42",  // override the auto-generated key
  timeoutMs: 10_000,           // abort if the request takes longer
  signal: controller.signal,   // pair with your own AbortController
});

Configuration

new Snap({
  apiKey: "sk_live_...",
  baseUrl: "https://api.snap.example",          // or set SNAP_API_URL — no baked-in default
  fetch: customFetch,                           // override globalThis.fetch
  appInfo: { name: "my-store", version: "2.1" } // appended to User-Agent
});

License

MIT