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

@zelthr/topup

v3.1.1

Published

TrueMoney gift-code redemption and Thai bank-slip verification through the zelthrStudio Open API gateway — no local scanning, no native dependencies.

Readme

@zelthr/topup

A thin, dependency-light Node.js client for the zelthrStudio Open API gateway — TrueMoney gift-code redemption and Thai bank-slip verification, served from https://api.zelthr.rest.

The package does no local work: every call is forwarded to the gateway, which talks to the TrueMoney core and runs the full slip OCR pipeline itself. No image scanning, no QR parsing, no native binaries — just two clean API calls.

Endpoints

Both services share the gateway's single unified verification endpoint (POST /); the gateway auto-detects which service to run from the request body.

| Function | Request body | Purpose | | --- | --- | --- | | truemoney(code, phone, options?) | { gift, phone } | Check / redeem a TrueMoney voucher code | | bank(data, options?) | { img } | Verify a Thai bank transfer slip from an image |

Requirements

  • Node.js >= 20.9

Installation

npm install @zelthr/topup

No install scripts, no native binaries, no model files — installs on any platform in seconds.

Quick Start

TrueMoney voucher redemption

const { truemoney } = require('@zelthr/topup');

// Redeem a gift code to a 10-digit Thai mobile number
// Automatically normalizes '081-234-5678', '+66812345678', ' 081 234 5678 ', etc.
const res = await truemoney('ABCD1234EFGH', '0812345678');

// Gift URLs work too, and you can verify the redeemed amount
const res2 = await truemoney('https://gift.truemoney.com/campaign/?v=XXXX', '+66812345678', { amount: 100 });
// throws AmountMismatchError if the code redeemed a different amount

Bank slip verification

const { bank } = require('@zelthr/topup');
const fs = require('node:fs');

// Pass a Buffer directly from fs.readFileSync:
const res = await bank(fs.readFileSync('slip.jpg'));
// { success: true, amount: 80, ... } — the verified slip result

// Or pass a base64 string / data URI:
const res2 = await bank('data:image/jpeg;base64,...');

// Optional amount verification:
const res3 = await bank(fs.readFileSync('slip.jpg'), { amount: 80 });
// throws AmountMismatchError if the verified slip amount differs

Bare base64 strings, Buffer, Uint8Array, and ArrayBuffer are handled automatically.

API

truemoney(codeOrLink, phone, options?)

Redeems a TrueMoney voucher via the unified endpoint (POST /).

| arg | type | description | | --- | --- | --- | | codeOrLink | string | Gift code (e.g. ABCD1234EFGH) or full gift URL. | | phone | string | Optional. Thai mobile number (08x..., +668x..., 08x-xxx-xxxx); formatting and whitespace are normalized automatically. If omitted, the server's configured wallet number is used. | | options.amount | number | Optional. If set, the redeemed amount is compared against it and a mismatch throws AmountMismatchError (slug === 'amount-mismatch'). | | options.timeoutMs | number | Optional request deadline in milliseconds (default: 30000). | | options.baseUrl | string | Optional base URL override (default: https://api.zelthr.rest). | | options.headers | object | Optional custom headers. |

Under the hood:

POST https://api.zelthr.rest/
{ "gift": "ABCD1234EFGH", "phone": "0812345678" }

Returns the gateway JSON response (the upstream redeem result as-is). A non-existent voucher is a normal 200 with status.code === 'VOUCHER_NOT_FOUND' — HTTP 4xx/5xx means the request itself was rejected or the upstream failed.

bank(data, options?)

Verifies a Thai bank transfer slip via the unified endpoint (POST /). The gateway decodes the QR, extracts the amount and verifies it upstream.

| arg | type | description | | --- | --- | --- | | data | string \| Buffer \| Uint8Array \| ArrayBuffer | Slip image as a Buffer, Uint8Array, ArrayBuffer, base64 string, or data: URI. | | options.amount | number | Optional expected amount (baht). Throws AmountMismatchError if verified slip amount does not match. | | options.timeoutMs | number | Optional request deadline in milliseconds (default: 30000). | | options.baseUrl | string | Optional base URL override (default: https://api.zelthr.rest). | | options.headers | object | Optional custom headers. |

Under the hood:

POST https://api.zelthr.rest/
{ "img": "data:image/jpeg;base64,/9j/4AAQ..." }

Returns the upstream verified-slip result as-is. Throws ValidationError on empty input or oversized payloads, and HttpError on gateway failures (e.g. 400 invalid-image, 422 qr-not-found, 429 rate-limit-exceeded, 502 upstream-error).

Low-level helpers

  • post(url, body?, options?) — shared POST helper used by both clients (JSON body, 30 s deadline, 32 MiB body cap, redirects disabled).
  • TMN_BASE / SLIP_BASE — gateway base URL constants (https://api.zelthr.rest), overridable via env vars below.

Error handling

All errors extend a common base and are exported:

| Error | Meaning | | --- | --- | | TopupError | Base class — catch this for broad handling. | | ValidationError | Invalid caller input (empty code, bad phone, invalid image, ...). | | TimeoutError | Request exceeded its deadline (30 s default). | | HttpError | Non-2xx or transport failure, with .status, .slug, .body. | | AmountMismatchError | Extends HttpError; the code redeemed a different amount than expected (slug === 'amount-mismatch'). | | AmountVerificationError | Extends HttpError; amount verification was requested but no amount could be extracted from the response. |

const { truemoney, AmountMismatchError } = require('@zelthr/topup');

try {
  await truemoney(code, phone, { amount: 100 });
} catch (err) {
  if (err instanceof AmountMismatchError) {
    console.error(`redeemed ${err.body ? JSON.stringify(err.body) : '?'} instead of 100 THB`);
  }
}

Rate limits

The gateway enforces per-IP limits — tmn: 200/min, slip: 100/min — and returns 429 with a Retry-After header when exceeded, surfaced as an HttpError with slug === 'rate-limit-exceeded'.

Environment variables

| var | effect | | --- | --- | | TMN_API_URL | Override the gateway base URL for truemoney() (default https://api.zelthr.rest). | | SLIP_API_URL | Override the gateway base URL for bank() (default https://api.zelthr.rest). |

Security

  • Gift codes, phone numbers and slip images travel in the JSON body over TLS only — redirects are disabled and ambient proxies (HTTP_PROXY/HTTPS_PROXY) are explicitly ignored, so a 307/308 can never re-POST your data to another host.
  • Error messages redact the request URL and cap server-controlled text; oversized error bodies are truncated to a 64 KB preview.

License

MIT