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

sumit-api

v0.3.1

Published

TypeScript helpers for SUMIT (formerly OfficeGuy) recurring charges and trigger webhooks. Includes redaction for upstream Upay clearer error codes.

Readme

sumit-api

npm types license zero deps

Pure TypeScript helpers for SUMIT (formerly OfficeGuy) recurring billing and trigger webhooks. SUMIT routes card clearing through partner processors such as Upay, and their error codes surface in SUMIT responses — this package handles SUMIT's request/response shapes and redacts those upstream codes before they hit your logs. Zero runtime dependencies.

Companion package: sumit-react<SumitCheckout /> plus Next.js charge and webhook route helpers.


Contents


Why this package

SUMIT (formerly OfficeGuy) does not publish a typed SDK for their billing APIs, and their trigger webhooks ship in three different content shapes. SUMIT also delegates the actual card clearing to partner processors (Upay is one — others exist), so processor-level error codes (e.g. Upay_30001419) appear unredacted inside SUMIT's response bodies. This package gives you a small, opinionated surface that is safe to drop into any backend:

  • Build /billing/recurring/charge/ request payloads with strict types.
  • Normalize successful and failed charge responses into a single discriminated union.
  • Parse SUMIT Trigger / Webhook payloads — JSON, application/x-www-form-urlencoded, and SUMIT's json=… envelope.
  • Redact API keys, tokens, card data, emails, and other sensitive fields before logging.

See docs/API_REFERENCE.md for a deeper summary of the SUMIT endpoints, response envelopes, trigger shapes, and redaction rules this package targets.


Install

pnpm add sumit-api
# or
npm install sumit-api
# or
yarn add sumit-api

The package has no runtime dependencies.


Build a one-off charge payload

import { buildOneOffChargePayload } from "sumit-api";

const payload = buildOneOffChargePayload({
  companyId: 123,
  apiKey: process.env.SUMIT_API_KEY!,
  customer: {
    externalIdentifier: "org_123",
    name: "Acme Ltd",
    emailAddress: "[email protected]",
  },
  singleUseToken: "[single-use-token-from-client]",
  item: {
    name: "Setup fee",
    description: "One-time onboarding charge",
    unitPrice: 49,
    currency: "USD",
  },
});

POST this body to https://api.sumit.co.il/billing/payments/charge/. The response uses the same shape recurring charges return, so normalizeChargeResponse handles both — a one-off success surfaces as eventType: "payment.succeeded" (no recurringItemId).


Build a recurring-charge payload

import { buildRecurringChargePayload } from "sumit-api";

const payload = buildRecurringChargePayload({
  companyId: 123,
  apiKey: process.env.SUMIT_API_KEY!,
  customer: {
    externalIdentifier: "org_123",
    name: "Acme Ltd",
    emailAddress: "[email protected]",
  },
  singleUseToken: "[single-use-token-from-client]",
  item: {
    name: "Pro Plan",
    description: "Pro subscription — monthly",
    unitPrice: 19,
    currency: "USD",
    durationMonths: 1,
  },
});

Build a create-document payload

Issue a SUMIT accounting document (חשבון עסקה / Transaction Invoice) without charging a card — useful when a proposal/quote is accepted and you want to hand the customer a pre-payment invoice. POST the body to https://api.sumit.co.il/accounting/documents/create/.

import { buildCreateDocumentPayload, SUMIT_DOCUMENT_TYPE } from "sumit-api";

const payload = buildCreateDocumentPayload({
  companyId: 123,
  apiKey: process.env.SUMIT_API_KEY!,
  documentType: SUMIT_DOCUMENT_TYPE.ProformaInvoice, // 3 — חשבון עסקה
  customer: {
    externalIdentifier: "client_42",
    name: "Acme Ltd",
    emailAddress: "[email protected]",
    taxId: "514999000", // ת.ז. / ח.פ. — mapped to CompanyNumber
  },
  items: [
    { name: "Logo design", description: "Includes 3 revisions", unitPrice: 1500, quantity: 1 },
    { name: "Development hours", unitPrice: 300, quantity: 8 },
  ],
  currency: "ILS",
  vatIncluded: false, // unit prices are net; SUMIT adds VAT
  language: "he",
  sendByEmail: { emailAddress: "[email protected]" }, // optional
});

SUMIT_DOCUMENT_TYPE covers SUMIT's Accounting_Typed_DocumentType enum — Invoice (0, חשבונית מס), InvoiceAndReceipt (1, חשבונית מס-קבלה), Receipt (2, קבלה), ProformaInvoice (3, חשבון עסקה), PriceQuotation (12, הצעת מחיר), credit/expense variants, and more. Pass any numeric code directly via documentType if needed.

language accepts either a SUMIT_LANGUAGE numeric code or the shorthand strings "he"/"en"/"ar"/"es" (and their full English names) — the helper converts to the numeric enum SUMIT requires. Unknown strings are dropped silently.

customer.searchMode is derived automatically when omitted: SUMIT id 1, ExternalIdentifier 2, otherwise 0 (create new). Pass an explicit value to override.


Normalize a charge response

normalizeChargeResponse handles both one-off and recurring response shapes — a recurring.charged event is surfaced only when SUMIT returns a RecurringCustomerItemIDs[*]. (normalizeRecurringChargeResponse remains exported as an alias.)

import { normalizeChargeResponse } from "sumit-api";

const event = normalizeChargeResponse(sumitResponse);

if (event.ok && event.eventType === "recurring.charged") {
  // Save event.customerId, event.recurringItemId, event.paymentId, event.documentId, ...
}

if (event.ok && event.eventType === "payment.succeeded") {
  // One-off charge succeeded — store event.paymentId and event.documentId.
}

if (event.ok === false) {
  // Don't activate the subscription / mark the order paid. Store event.diagnostic safely.
}

A successful SUMIT charge response typically includes:

| Field | Meaning | | -------------------------------- | ---------------------------------------- | | Payment.ValidPayment === true | Provider considers the charge valid | | Payment.Status === "000" | Provider success status code | | RecurringCustomerItemIDs[0] | The created recurring-item ID | | CustomerID | SUMIT customer record ID | | DocumentID | Issued invoice / receipt ID |


Normalize a create-document response

import { normalizeCreateDocumentResponse } from "sumit-api";

const event = normalizeCreateDocumentResponse(sumitResponse);

if (event.ok && event.eventType === "document.created") {
  // Persist event.documentId / event.documentNumber / event.documentDownloadUrl.
}

if (event.eventType === "document.failed") {
  // event.userErrorMessage is safe to display; event.technicalErrorDetails is redacted.
}

A successful create-document response surfaces:

| Field | Source | | --------------------- | -------------------------------------------------------- | | documentId | Data.DocumentID / DocumentID | | documentNumber | Data.DocumentNumber / Data.Document.Number | | documentDownloadUrl | Data.DocumentDownloadURL / Data.Document.DownloadURL | | customerId | Data.CustomerID / Data.Customer.ID |


Normalize a SUMIT trigger / webhook payload

import { normalizeSumitIncomingPayload } from "sumit-api";

const normalized = normalizeSumitIncomingPayload(payloadOrUrlSearchParams);

if (normalized.eventType === "sumit.trigger.unmapped") {
  // Store the sanitized raw event for later mapping.
}

SUMIT Trigger webhooks are often view / card based and may not include a fixed provider event schema — this package does not assume Stripe-style lifecycle events.

For SUMIT view-shaped trigger payloads with top-level Folder, EntityID, Type, and Properties, normalization extracts these safe reconciliation fields when present:

| Normalized field | Source | | ---------------- | --------------------------------- | | paymentId | EntityID | | customerId | Properties.Property_3[0].ID | | documentId | Properties.Property_5[0].ID | | amount | Properties.Billing_Amount[0] | | status | Type | | occurredAt | Properties.Property_2[0] |

These events still normalize as sumit.trigger.unmapped until your application explicitly authenticates and maps them to a trusted billing lifecycle event.


Safety

Never log or persist raw SUMIT payloads. Use redactSumitPayload, or persist only the safe normalized fields.

redactSumitPayload walks the value tree and masks sensitive data via two complementary mechanisms:

| Mechanism | Catches | | --- | --- | | Key-based (SENSITIVE_KEY_PATTERN) | API keys, public keys, single-use tokens, card mask/pattern/token/expiration, citizen ID, card-owner name and social ID, Authorization, secrets, passwords, CVV, email addresses, phone, document download URLs, full CreditCard_* and DirectDebit_* subtrees. | | Text-based (redactSensitiveText) | Embedded emails, "Credit Card (1234)" patterns, token=… / apikey=… key-value strings, Upay_* references, UUIDs, 12–19 digit card-like numbers in free text, citizen IDs in keyword context (citizen, ת.ז, מ.ז). |

Form payloads parsed by normalizeSumitIncomingPayload reject prototype-pollution keys (__proto__, constructor, prototype) before assembling the nested object — see src/index.ts.


Development

pnpm install
pnpm test         # vitest run
pnpm typecheck    # tsc --noEmit
pnpm build        # tsc → dist/

The build emits ESM with .d.ts declarations to dist/. Source lives in src/.


License

MIT