# SUMIT API Reference This package wraps a small slice of the SUMIT (formerly OfficeGuy) billing API. SUMIT routes card clearing through partner processors — Upay is one of them — and their error codes (e.g. `Upay_30001419`) surface inside SUMIT response bodies, which is why this package redacts them by default. It does **not** ship a transport client — `fetch` is the integrator's responsibility. The helpers here build request bodies, normalize responses, and redact sensitive fields before logging or persistence. ## Official sources - REST portal: - Swagger UI: - Raw OpenAPI 3.1 spec: ## Base URL ```text https://api.sumit.co.il ``` All API calls observed for these flows are `POST` with a JSON body and JSON response. ## Authentication Every server-side request includes a `Credentials` object in the body: ```jsonc { "Credentials": { "CompanyID": 123, "APIKey": "***" } } ``` - `APIKey` is a server-side secret. Never ship it to the browser. - `APIPublicKey` is a separate browser-side key used by SUMIT tokenization to produce a one-shot `SingleUseToken`. - Treat `SingleUseToken` as sensitive and one-use. ## Standard response envelope SUMIT endpoints often return an outer wrapper: ```jsonc { "Status": "Success", "UserErrorMessage": null, "TechnicalErrorDetails": null, "Data": { /* endpoint-specific payload */ } } ``` The outer `Status` is the framework-level response status, not the payment status code. Payment approval codes live on `Data.Payment.Status` or `Payment.Status`. Trigger payloads can flatten `Data` to the top level; the normalizer checks both shapes. ## `POST /billing/payments/charge/` One-off charge against a `SingleUseToken`. Built by `buildOneOffChargePayload`: ```ts { Credentials: { CompanyID, APIKey }, Customer: { ExternalIdentifier: string, SearchMode: 2, Name: string, EmailAddress: string, }, SingleUseToken: string, Items: [{ Item: { Name, Description }, Quantity: number, UnitPrice: number, Currency: 0 | 1 | 2, }], VATIncluded: boolean, OnlyDocument: boolean, AuthoriseOnly?: true, } ``` Response shape matches `/billing/recurring/charge/` (same `Payment.*` envelope). Pass it to `normalizeChargeResponse` — a one-off success surfaces as `eventType: "payment.succeeded"` with no `recurringItemId`. ## `POST /billing/recurring/charge/` Charges a customer and creates/updates a recurring item. Built by `buildRecurringChargePayload`: ```ts { Credentials: { CompanyID, APIKey }, Customer: { ExternalIdentifier: string, SearchMode: 2, Name: string, EmailAddress: string, }, SingleUseToken: string, Items: [{ Item: { Name, Description, Duration_Months }, Quantity: number, UnitPrice: number, Currency: 0 | 1 | 2, Duration_Months: number, Recurrence: number, }], VATIncluded: boolean, OnlyDocument: boolean, AuthoriseOnly?: true, } ``` Notes: - `SearchMode: 2` means lookup by `ExternalIdentifier`. - `Currency`: `0=ILS`, `1=USD`, `2=EUR`. - `VATIncluded` is a product decision. If true, the submitted price is gross/total; if false, SUMIT can add VAT on top. - `AuthoriseOnly: true` is useful for integration sanity checks, but it is not proof of a live recurring subscription. Successful payloads observed in smoke tests include `Payment.ValidPayment === true`, `Payment.Status === "000"`, `Payment.ID`, `CustomerID`, `DocumentID`, and `RecurringCustomerItemIDs[0]`. ## `POST /accounting/documents/create/` Issues a SUMIT accounting document (חשבון עסקה / חשבונית מס / חשבונית מס-קבלה / קבלה) without charging a card. Built by `buildCreateDocumentPayload`: ```ts { Credentials: { CompanyID, APIKey }, Details: { Type: number, // SUMIT document type code; 3 = חשבון עסקה (ProformaInvoice) Customer: { SearchMode: 0 | 1 | 2, // 0 = match by ID (default for this endpoint) Name: string, EmailAddress?: string, Phone?: string, ExternalIdentifier?: string, ID?: string, CompanyNumber?: string, // ת.ז. / ח.פ. Address?: string, City?: string, ZipCode?: string, NoVAT?: boolean, }, SendByEmail?: { EmailAddress, Original, SendAsPaymentRequest }, Language?: number, // Accounting_Typed_Language enum: 0=Hebrew, 1=English, 2=Arabic, 3=Spanish Currency?: "ILS" | "USD" | "EUR", Description?: string, ExternalReference?: string, Date?: string, // ISO date DueDate?: string, IsDraft?: boolean, }, Items: [{ Quantity: number, UnitPrice: number, TotalPrice: number, // defaults to UnitPrice * Quantity VAT?: number, // optional per-line override Item: { Name: string, Description?: string, SKU?: string, ExternalIdentifier?: string, SearchMode: 0 | 1 | 2, }, }], // Payments omitted — SUMIT rejects empty Payments arrays on document creation. VATIncluded: boolean, VATPerItem?: boolean, VATRate?: number, ResponseLanguage?: number, } ``` Notes: - Unlike the charge endpoints, the documents endpoint takes `Currency` as the literal string code (`"ILS"` / `"USD"` / `"EUR"`), not the numeric code. The helper `currencyToSumitString` handles the mapping. - `Payments: []` for a חשבון עסקה — no payment has been collected yet. Use a different document type (e.g. חשבונית מס-קבלה) and a populated `Payments[]` to record an actual payment. - Successful responses surface `Data.DocumentID`, `Data.DocumentNumber`, and (when SUMIT generates one) `Data.DocumentDownloadURL`. Pass the response to `normalizeCreateDocumentResponse` to extract these as `document.created`. - A failed response surfaces as `document.failed` with redacted `userErrorMessage` / `technicalErrorDetails`. ## Related endpoints not wrapped directly | Endpoint | Purpose | | --- | --- | | `POST /billing/recurring/cancel/` | Cancel a recurring item. | | `POST /billing/recurring/update/` | Update a recurring item. | | `POST /billing/recurring/listforcustomer/` | List customer recurring items. | | `POST /billing/payments/list/` | List historical payments. | | `POST /billing/payments/get/` | Fetch one payment. | | `POST /billing/payments/beginredirect/` | Start hosted/redirect checkout. | | `POST /billing/paymentmethods/setforcustomer/` | Save payment method for customer. | | `POST /billing/paymentmethods/getforcustomer/` | Fetch saved payment method. | | `POST /billing/paymentmethods/remove/` | Remove saved payment method. | ## Triggers / webhooks SUMIT Triggers are not Stripe-style. They are configured around folders/views/actions and can send JSON or form-encoded payloads. Common shapes: - JSON charge-like payloads. - `application/x-www-form-urlencoded` with dotted keys, e.g. `Payment.Status=000`. - Indexed form arrays, e.g. `RecurringCustomerItemIDs[0]=444`. - View/card-shaped payloads with `Folder`, `EntityID`, `Type`, and `Properties`. - Form payloads shaped as `json=`. Pass JSON objects or `URLSearchParams` into `normalizeSumitIncomingPayload`. View-shaped triggers still normalize as `eventType: "sumit.trigger.unmapped"` unless the application explicitly authenticates and promotes them to trusted lifecycle events. This package extracts safe reconciliation fields where possible, but app code must decide whether the payload is allowed to mutate billing state. ## Customer SearchMode | Code | Mode | Notes | | --- | --- | --- | | 0 | Automatic | SUMIT chooses heuristically. | | 1 | None | Always create new customer. | | 2 | ExternalIdentifier | Used by this library. | | 3 | Name | Lookup by name. | | 4 | CompanyNumber | Lookup by company number. | | 5 | Phone | Lookup by phone. | | 6 | EmailAddress | Lookup by email. | ## Currency | Code | Currency | | --- | --- | | 0 | ILS | | 1 | USD | | 2 | EUR | `currencyToSumitCode` accepts only ILS/USD/EUR. `currencyFromSumitCode` preserves unknown codes as strings when normalizing provider responses. ## Status codes - `"000"` and `"0"` indicate successful payment. - Any other all-digit `Payment.Status` is treated as failure. - `Payment.ValidPayment === false` is always failure. - Non-numeric statuses are matched against English/Hebrew error keywords and `Upay_*` signals. ## Sensitive-field redaction `redactSumitPayload(value)` walks recursively and replaces sensitive keys with `[REDACTED]`. Covered categories include: - Auth/secrets: `APIKey`, `APIPublicKey`, `SingleUseToken`, generic `Token`, `Authorization`, `Secret`, `Password`. - Card data: `CVV`, `CardMask`, `CardPattern`, `CardToken`, `CardExpiration`, `CreditCard_*`. - Cardholder PII: `CardOwnerName`, `CardOwnerSocialId`, `CitizenID`. - Bank credentials: `DirectDebit_*`. - Other: `AuthNumber`, `EmailAddress`, `Phone`, `ResultRecord`, `DocumentDownloadURL`. `redactSensitiveText(text)` additionally scrubs emails, token/key/card key-value fragments, payment-method last-four labels, UUIDs, long card-like number runs, 9-digit Israeli IDs, and `Upay_*` provider error codes. The redactor is a safety net, not permission to log raw provider payloads. Prefer storing normalized fields plus redacted diagnostics only.