node-red-contrib-tdn-sts-gen2 1.1.0
Node-RED nodes for cloud POS transaction and reporting APIs: PKCE OAuth2 token, closed-check pollers, menu and tender reference tables
node-red-contrib-tdn-sts-gen2
Node-RED nodes for working with a cloud POS transaction API:
sts-oidc-token— authenticates against an OpenID Connect provider using the OAuth2 Authorization Code Flow with PKCEsts-gen2-poller— watches enabled locations and revenue centres for newly closed (paid) checks and emits each one exactly oncebi-reference— builds menu item and tender name lookup tables from the reporting API's dimensions endpointsbi-checks-poller— polls the reporting API for closed guest checks, resolving names from those tables
Both pollers emit the same message shape, so one downstream formatter serves either source.
Install
From your Node-RED user directory (typically ~/.node-red):
npm install node-red-contrib-tdn-sts-gen2
Both nodes appear in your palette after install and a restart of Node-RED.
Token node (sts-oidc-token)
The token node performs the full six-step authentication flow on each incoming message:
- Generates a PKCE code verifier + SHA-256 challenge
GET /oidc-provider/v1/oauth2/authorize— captures session cookies- Replays the cookies to
POST /oidc-provider/v1/oauth2/signinwith your API account credentials - Extracts the authorization
codefrom the returnedredirectUrl POST /oidc-provider/v1/oauth2/tokenwith the code + verifier- Caches the token in node context and reuses it until expiry — repeated messages do not re-authenticate while the cached token is valid
Usage
Send a message with a msg.credentials object:
msg.credentials = {
authHost: "https://<your-auth-host>", // OIDC provider host only, no path
clientId: "<client-id>",
username: "<api-account-username>",
password: "<api-account-password>",
orgShortName: "<org-short-name>"
};
return msg;
All five fields are required; the node reports a specific error and status for any missing field.
On success the message passes through with the token attached:
msg.tokenOut = {
access_token: "...",
id_token: "...", // typically used as the Bearer token for API calls
refresh_token: "...",
expires_in: "...",
token_type: "Bearer"
}
Use msg.tokenOut.id_token in the Authorization: Bearer header for
downstream API requests.
Node status
The node shows its exact state in the editor: validating input,
requesting token, cookies stored, token stored, token valid,
token expired, or a specific error state.
Poller node (sts-gen2-poller)
The poller node runs its own non-overlapping poll loop (default every 15 s) and queries the checks endpoint once per enabled (location, revenue centre) pair, capped at a configurable number of parallel requests. It emits only closed checks, exactly once each — checks without payment tenders or line items are held back so no partial receipt is ever dispatched downstream.
It is driven entirely from global context:
global.set("cfg", { apiBase: "https://<api-host>/api", orgShortName: "..." });
global.set("locations", [
{ locRef: "loc1", name: "Site 1", enabled: true,
rvcRefs: [ { rvcRef: 101, name: "Bar", enabled: true } ] }
]);
The work list is rebuilt from locations on every cycle, so enabling or
disabling a location or revenue centre takes effect on the next poll without a
redeploy.
Token handling: wire the token node's output into the poller's input — any
message carrying msg.tokenOut is consumed as a token push. The poller can
also fall back to a configurable global context key. On a 401/403 it drops the
cached token, emits a token-kind error, and keeps polling with backoff.
Outputs (4): status, closed checks, open checks (off by default), errors.
Closed-check messages carry msg.payload (full check), msg.summary (flat
record with location/revenue-centre refs, totals, and tenders), and
msg.topic = locRef/rvcRef for routing.
Input commands (msg.topic or string payload): poll, start, stop,
reset.
Why closed-only polling works: the API's sinceTime filter matches check
creation time, so every check created inside the configurable lookback window
(default 240 min) reappears in each poll until it ages out. Whenever it closes,
the next poll sees the closed status and emits it. Set the lookback longer than
the longest-lived open check.
Reporting API nodes (bi-reference, bi-checks-poller)
The reporting API is an alternative source for closed checks. It differs from the transaction API in two ways that matter:
- its change filter matches modification time, so a check that opens at 09:00 and closes at 10:00 reappears when it closes — no large lookback window is needed
- its check detail lines carry only numbers (menu item number, tender number), never names
bi-reference solves the second point: it loads menu item and tender name
tables per location and stores them in global context, keyed by number, so a
lookup is a direct index. Run it once at startup (menus change rarely).
The dimensions endpoints return no prices, so the poller computes each unit
price from the transaction itself (line total ÷ quantity) and can write those
learned prices back into the table.
bi-checks-poller then polls closed guest checks per enabled location, filters
to enabled revenue centres, resolves names from the tables, and emits each
newly closed check exactly once — normalised to the same msg.summary and
msg.payload.menuItems shape as the transaction poller. Because the API
requires a business date and venues roll over well after midnight, it queries
the last two business dates each cycle by default.
global.set("cfg", {
biBase: "https://<reporting-host>",
apiBase: "https://<api-host>/api",
orgShortName: "..."
});
Notes
- Credentials should come from context or a secure store — never hard-code them in a flow.
- Some providers expire API account passwords on a fixed schedule and lock the account temporarily after repeated failed sign-ins — handle credential rotation accordingly.
- The token is cached per node instance (node context) and re-requested only
after
expires_inelapses.
License
MIT