From ac38676645f404007d48f5feb53a61b36a2e97cf Mon Sep 17 00:00:00 2001 From: George Lambert Date: Fri, 11 Sep 2026 15:35:37 -0400 Subject: [PATCH] Add NATS account-balance SoT and statement review for customers, CS, and sales Internal billing now uses verae.billing.* request-reply and pubs. zappier-account-balance tracks prepaid, credits, usage, and payments. Portal, admin, CS, and sales all review the same statement. Independent Forgejo repos stay split via push-module-repos. --- docs/modules/README.md | 15 +++ docs/modules/verae-fleet/supervisor.md | 7 +- docs/modules/zappier-account-balance/books.md | 54 +++++++++++ .../modules/zappier-account-balance/server.md | 53 +++++++++++ .../zappier-account-balance/subjects.md | 36 ++++++++ .../zappier-accounting-export/nats-billing.md | 40 ++++++++ .../zappier-accounting-export/server.md | 46 ++++++++++ .../zappier-customer-service/nats-billing.md | 40 ++++++++ .../zappier-customer-service/server.md | 45 +++++++++ .../zappier-sales-pricing/nats-billing.md | 40 ++++++++ docs/modules/zappier-sales-pricing/server.md | 45 +++++++++ docs/modules/zappier/accounting-export.md | 43 +++++++++ docs/modules/zappier/admin.md | 11 ++- docs/modules/zappier/app.md | 9 +- docs/modules/zappier/billing-nats.md | 54 +++++++++++ docs/modules/zappier/credits.md | 48 ++++++++++ docs/modules/zappier/meter.md | 5 +- docs/modules/zappier/nats-shim.d.md | 41 +++++++++ docs/modules/zappier/portal.md | 7 +- docs/modules/zappier/statement.md | 48 ++++++++++ docs/modules/zappier/upstream.md | 45 +++++++++ docs/sphinx/index.rst | 19 ++++ docs/sphinx/modules/verae-fleet/supervisor.md | 7 +- .../modules/zappier-account-balance/books.md | 54 +++++++++++ .../modules/zappier-account-balance/books.rst | 7 ++ .../modules/zappier-account-balance/server.md | 53 +++++++++++ .../zappier-account-balance/server.rst | 7 ++ .../zappier-account-balance/subjects.md | 36 ++++++++ .../zappier-account-balance/subjects.rst | 7 ++ .../zappier-accounting-export/nats-billing.md | 40 ++++++++ .../nats-billing.rst | 7 ++ .../zappier-accounting-export/server.md | 46 ++++++++++ .../zappier-accounting-export/server.rst | 7 ++ .../zappier-customer-service/nats-billing.md | 40 ++++++++ .../zappier-customer-service/nats-billing.rst | 7 ++ .../zappier-customer-service/server.md | 45 +++++++++ .../zappier-customer-service/server.rst | 7 ++ .../zappier-sales-pricing/nats-billing.md | 40 ++++++++ .../zappier-sales-pricing/nats-billing.rst | 7 ++ .../modules/zappier-sales-pricing/server.md | 45 +++++++++ .../modules/zappier-sales-pricing/server.rst | 7 ++ .../modules/zappier/accounting-export.md | 43 +++++++++ .../modules/zappier/accounting-export.rst | 7 ++ docs/sphinx/modules/zappier/admin.md | 11 ++- docs/sphinx/modules/zappier/app.md | 9 +- docs/sphinx/modules/zappier/billing-nats.md | 54 +++++++++++ docs/sphinx/modules/zappier/billing-nats.rst | 7 ++ docs/sphinx/modules/zappier/credits.md | 48 ++++++++++ docs/sphinx/modules/zappier/credits.rst | 7 ++ docs/sphinx/modules/zappier/meter.md | 5 +- docs/sphinx/modules/zappier/nats-shim.d.md | 41 +++++++++ docs/sphinx/modules/zappier/nats-shim.d.rst | 7 ++ docs/sphinx/modules/zappier/portal.md | 7 +- docs/sphinx/modules/zappier/statement.md | 48 ++++++++++ docs/sphinx/modules/zappier/statement.rst | 7 ++ docs/sphinx/modules/zappier/upstream.md | 45 +++++++++ docs/sphinx/modules/zappier/upstream.rst | 7 ++ docs/sphinx/repos/overview.md | 1 + docs/sphinx/repos/verae-ops.md | 1 + docs/sphinx/repos/zappier-account-balance.md | 31 +++++++ .../sphinx/repos/zappier-accounting-export.md | 21 +++++ docs/sphinx/repos/zappier-customer-service.md | 23 +++++ docs/sphinx/repos/zappier-sales-pricing.md | 19 ++++ docs/sphinx/repos/zappier.md | 5 +- packages/docs-master/modules-and-nats.md | 22 ++++- .../modules/zappier-account-balance/NATS.md | 12 +++ .../zappier-account-balance/SUMMARY.md | 3 + .../modules/zappier-accounting-export/NATS.md | 8 +- .../zappier-accounting-export/SUMMARY.md | 4 +- .../modules/zappier-customer-service/NATS.md | 9 +- .../zappier-customer-service/SUMMARY.md | 6 +- .../modules/zappier-sales-pricing/NATS.md | 8 +- .../modules/zappier-sales-pricing/SUMMARY.md | 4 +- packages/docs-master/modules/zappier/NATS.md | 9 +- packages/overview/01-system.md | 7 +- packages/overview/02-modules-and-repos.md | 1 + packages/overview/03-nats-cluster.md | 7 +- packages/overview/08-diagrams.md | 11 ++- packages/verae-fleet/fleet.json | 7 +- .../verae-fleet/services/account-balance.json | 31 +++++++ .../services/accounting-export.json | 6 +- .../services/customer-service.json | 15 ++- .../verae-fleet/services/sales-pricing.json | 8 +- .../verae-fleet/services/zappier-edge.json | 16 +++- packages/verae-fleet/test/fleet.test.js | 1 + packages/verae-ops/GETTING-STARTED.md | 13 ++- packages/verae-ops/docker-compose.yml | 28 +++++- packages/zappier-account-balance/NATS.md | 3 + packages/zappier-account-balance/README.md | 31 +++++++ .../zappier-account-balance/package-lock.json | 46 ++++++++++ packages/zappier-account-balance/package.json | 14 +++ packages/zappier-account-balance/src/books.js | 89 ++++++++++++++++++ .../zappier-account-balance/src/server.js | 92 +++++++++++++++++++ .../zappier-account-balance/src/subjects.js | 10 ++ .../test/books.test.js | 16 ++++ .../test/health.test.js | 30 ++++++ packages/zappier-accounting-export/README.md | 7 +- .../package-lock.json | 46 ++++++++++ .../zappier-accounting-export/package.json | 3 + .../public/index.html | 51 ++++++++++ .../src/nats-billing.js | 17 ++++ .../zappier-accounting-export/src/server.js | 25 ++++- .../test/health.test.js | 2 + packages/zappier-customer-service/README.md | 8 +- .../package-lock.json | 46 ++++++++++ .../zappier-customer-service/package.json | 3 + .../public/index.html | 67 ++++++++++++++ .../src/nats-billing.js | 20 ++++ .../zappier-customer-service/src/server.js | 81 ++++++++-------- .../test/health.test.js | 38 ++++++++ packages/zappier-sales-pricing/README.md | 7 +- .../zappier-sales-pricing/package-lock.json | 46 ++++++++++ packages/zappier-sales-pricing/package.json | 3 + .../zappier-sales-pricing/public/index.html | 56 +++++++++++ .../zappier-sales-pricing/src/nats-billing.js | 17 ++++ packages/zappier-sales-pricing/src/server.js | 22 +++++ .../zappier-sales-pricing/test/health.test.js | 35 +++++++ packages/zappier/admin/app.js | 45 ++++++++- packages/zappier/admin/index.html | 2 + packages/zappier/package-lock.json | 32 +++++++ packages/zappier/package.json | 1 + packages/zappier/portal/app.js | 32 ++++++- packages/zappier/portal/index.html | 2 + packages/zappier/src/admin.ts | 35 +++++++ packages/zappier/src/app.ts | 25 +++-- packages/zappier/src/billing-nats.ts | 73 +++++++++++++++ packages/zappier/src/meter.ts | 7 +- packages/zappier/src/nats-shim.d.ts | 11 +++ packages/zappier/src/portal.ts | 36 ++++++++ packages/zappier/src/statement.ts | 38 ++++++++ .../tests/health-proxy-credits.test.ts | 12 +++ packages/zappier/tests/portal.test.ts | 6 ++ scripts/build-docs-site.py | 3 + scripts/gen-module-docs.py | 5 + scripts/push-module-repos.sh | 2 + 135 files changed, 3078 insertions(+), 130 deletions(-) create mode 100644 docs/modules/zappier-account-balance/books.md create mode 100644 docs/modules/zappier-account-balance/server.md create mode 100644 docs/modules/zappier-account-balance/subjects.md create mode 100644 docs/modules/zappier-accounting-export/nats-billing.md create mode 100644 docs/modules/zappier-accounting-export/server.md create mode 100644 docs/modules/zappier-customer-service/nats-billing.md create mode 100644 docs/modules/zappier-customer-service/server.md create mode 100644 docs/modules/zappier-sales-pricing/nats-billing.md create mode 100644 docs/modules/zappier-sales-pricing/server.md create mode 100644 docs/modules/zappier/accounting-export.md create mode 100644 docs/modules/zappier/billing-nats.md create mode 100644 docs/modules/zappier/credits.md create mode 100644 docs/modules/zappier/nats-shim.d.md create mode 100644 docs/modules/zappier/statement.md create mode 100644 docs/modules/zappier/upstream.md create mode 100644 docs/sphinx/modules/zappier-account-balance/books.md create mode 100644 docs/sphinx/modules/zappier-account-balance/books.rst create mode 100644 docs/sphinx/modules/zappier-account-balance/server.md create mode 100644 docs/sphinx/modules/zappier-account-balance/server.rst create mode 100644 docs/sphinx/modules/zappier-account-balance/subjects.md create mode 100644 docs/sphinx/modules/zappier-account-balance/subjects.rst create mode 100644 docs/sphinx/modules/zappier-accounting-export/nats-billing.md create mode 100644 docs/sphinx/modules/zappier-accounting-export/nats-billing.rst create mode 100644 docs/sphinx/modules/zappier-accounting-export/server.md create mode 100644 docs/sphinx/modules/zappier-accounting-export/server.rst create mode 100644 docs/sphinx/modules/zappier-customer-service/nats-billing.md create mode 100644 docs/sphinx/modules/zappier-customer-service/nats-billing.rst create mode 100644 docs/sphinx/modules/zappier-customer-service/server.md create mode 100644 docs/sphinx/modules/zappier-customer-service/server.rst create mode 100644 docs/sphinx/modules/zappier-sales-pricing/nats-billing.md create mode 100644 docs/sphinx/modules/zappier-sales-pricing/nats-billing.rst create mode 100644 docs/sphinx/modules/zappier-sales-pricing/server.md create mode 100644 docs/sphinx/modules/zappier-sales-pricing/server.rst create mode 100644 docs/sphinx/modules/zappier/accounting-export.md create mode 100644 docs/sphinx/modules/zappier/accounting-export.rst create mode 100644 docs/sphinx/modules/zappier/billing-nats.md create mode 100644 docs/sphinx/modules/zappier/billing-nats.rst create mode 100644 docs/sphinx/modules/zappier/credits.md create mode 100644 docs/sphinx/modules/zappier/credits.rst create mode 100644 docs/sphinx/modules/zappier/nats-shim.d.md create mode 100644 docs/sphinx/modules/zappier/nats-shim.d.rst create mode 100644 docs/sphinx/modules/zappier/statement.md create mode 100644 docs/sphinx/modules/zappier/statement.rst create mode 100644 docs/sphinx/modules/zappier/upstream.md create mode 100644 docs/sphinx/modules/zappier/upstream.rst create mode 100644 docs/sphinx/repos/zappier-account-balance.md create mode 100644 docs/sphinx/repos/zappier-accounting-export.md create mode 100644 docs/sphinx/repos/zappier-customer-service.md create mode 100644 docs/sphinx/repos/zappier-sales-pricing.md create mode 100644 packages/docs-master/modules/zappier-account-balance/NATS.md create mode 100644 packages/docs-master/modules/zappier-account-balance/SUMMARY.md create mode 100644 packages/verae-fleet/services/account-balance.json create mode 100644 packages/zappier-account-balance/NATS.md create mode 100644 packages/zappier-account-balance/README.md create mode 100644 packages/zappier-account-balance/package-lock.json create mode 100644 packages/zappier-account-balance/package.json create mode 100644 packages/zappier-account-balance/src/books.js create mode 100644 packages/zappier-account-balance/src/server.js create mode 100644 packages/zappier-account-balance/src/subjects.js create mode 100644 packages/zappier-account-balance/test/books.test.js create mode 100644 packages/zappier-account-balance/test/health.test.js create mode 100644 packages/zappier-accounting-export/package-lock.json create mode 100644 packages/zappier-accounting-export/public/index.html create mode 100644 packages/zappier-accounting-export/src/nats-billing.js create mode 100644 packages/zappier-customer-service/package-lock.json create mode 100644 packages/zappier-customer-service/public/index.html create mode 100644 packages/zappier-customer-service/src/nats-billing.js create mode 100644 packages/zappier-sales-pricing/package-lock.json create mode 100644 packages/zappier-sales-pricing/public/index.html create mode 100644 packages/zappier-sales-pricing/src/nats-billing.js create mode 100644 packages/zappier/src/billing-nats.ts create mode 100644 packages/zappier/src/nats-shim.d.ts create mode 100644 packages/zappier/src/statement.ts diff --git a/docs/modules/README.md b/docs/modules/README.md index a04f666..19309bf 100644 --- a/docs/modules/README.md +++ b/docs/modules/README.md @@ -2,6 +2,7 @@ One MD file per source module. +- [zappier/accounting-export](zappier/accounting-export.md) - [zappier/accounts](zappier/accounts.md) - [zappier/admin-users](zappier/admin-users.md) - [zappier/admin](zappier/admin.md) @@ -10,6 +11,8 @@ One MD file per source module. - [zappier/billing/credit](zappier/billing/credit.md) - [zappier/billing/reload](zappier/billing/reload.md) - [zappier/billing/stripe](zappier/billing/stripe.md) +- [zappier/billing-nats](zappier/billing-nats.md) +- [zappier/credits](zappier/credits.md) - [zappier/db/admin-user-repo](zappier/db/admin-user-repo.md) - [zappier/db/billing-repo](zappier/db/billing-repo.md) - [zappier/db/customer-repo](zappier/db/customer-repo.md) @@ -21,10 +24,13 @@ One MD file per source module. - [zappier/invoicing](zappier/invoicing.md) - [zappier/jobs/report-usage](zappier/jobs/report-usage.md) - [zappier/meter](zappier/meter.md) +- [zappier/nats-shim.d](zappier/nats-shim.d.md) - [zappier/paths](zappier/paths.md) - [zappier/portal](zappier/portal.md) - [zappier/pricing](zappier/pricing.md) - [zappier/reports](zappier/reports.md) +- [zappier/statement](zappier/statement.md) +- [zappier/upstream](zappier/upstream.md) - [zappier/usage](zappier/usage.md) - [verae-zapier-middleware/app](verae-zapier-middleware/app.md) - [verae-zapier-middleware/clients/veraeClient](verae-zapier-middleware/clients/veraeClient.md) @@ -111,3 +117,12 @@ One MD file per source module. - [verae-zapier-simulator/pipeline](verae-zapier-simulator/pipeline.md) - [verae-zapier-simulator/server](verae-zapier-simulator/server.md) - [verae-zapier-simulator/trace](verae-zapier-simulator/trace.md) +- [zappier-account-balance/books](zappier-account-balance/books.md) +- [zappier-account-balance/server](zappier-account-balance/server.md) +- [zappier-account-balance/subjects](zappier-account-balance/subjects.md) +- [zappier-customer-service/nats-billing](zappier-customer-service/nats-billing.md) +- [zappier-customer-service/server](zappier-customer-service/server.md) +- [zappier-sales-pricing/nats-billing](zappier-sales-pricing/nats-billing.md) +- [zappier-sales-pricing/server](zappier-sales-pricing/server.md) +- [zappier-accounting-export/nats-billing](zappier-accounting-export/nats-billing.md) +- [zappier-accounting-export/server](zappier-accounting-export/server.md) diff --git a/docs/modules/verae-fleet/supervisor.md b/docs/modules/verae-fleet/supervisor.md index d810429..e778499 100644 --- a/docs/modules/verae-fleet/supervisor.md +++ b/docs/modules/verae-fleet/supervisor.md @@ -2,7 +2,7 @@ **Package:** `verae-fleet` **Source:** `packages/verae-fleet/src/supervisor.js` -**Lines:** 511 +**Lines:** 528 ## What this module is @@ -10,7 +10,7 @@ Implementation module in `verae-fleet`. The tables below are extracted from the ## Exports -`Supervisor` +`spawnLocal`, `Supervisor` ## Types / interfaces / classes @@ -22,6 +22,7 @@ Implementation module in `verae-fleet`. The tables below are extracted from the | Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) | |------|------------|---------------------|---------|-----------------------------| +| `spawnLocal` | `root, spec, env, healthPort` | — | `unknown` | see Call graph | | `waitExit` | `child, ms` | — | `unknown` | see Call graph | | `waitForHealth` | `rec, ms` | — | `unknown` | see Call graph | @@ -70,7 +71,7 @@ Implementation module in `verae-fleet`. The tables below are extracted from the ## Call graph (identifiers invoked) -`floor`, `join`, `dirname`, `fileURLToPath`, `constructor`, `loadFleet`, `mkdirSync`, `isArray`, `machines`, `addMachine`, `upsertMachine`, `saveOverlay`, `log`, `setMachineEnabled`, `find`, `checkMachine`, `sshCheck`, `httpProbe`, `machineOf`, `controlInstance`, `sshHttp`, `postControl`, `toISOString`, `push`, `splice`, `spec`, `nextIndex`, `values`, `filter`, `map`, `has`, `startOne`, `get`, `pickMachine`, `sshSpawnWorker`, `spawn`, `now`, `on`, `set`, `waitForHealth`, `stopInstance`, `sshKillWorker`, `kill`, `waitExit`, `delete`, `reconcile`, `pauseInstance`, `resumeInstance`, `restartInstance`, `markUnhealthy`, `probe`, `isFinite`, `summarizeRtt`, `available`, `keys`, `min`, `startService`, `stopService`, `pauseService`, `resumeService` +`floor`, `join`, `dirname`, `fileURLToPath`, `apps`, `spawnLocal`, `resolve`, `spawn`, `constructor`, `loadFleet`, `mkdirSync`, `isArray`, `machines`, `addMachine`, `upsertMachine`, `saveOverlay`, `log`, `setMachineEnabled`, `find`, `checkMachine`, `sshCheck`, `httpProbe`, `machineOf`, `controlInstance`, `sshHttp`, `postControl`, `toISOString`, `push`, `splice`, `spec`, `nextIndex`, `values`, `filter`, `map`, `has`, `startOne`, `get`, `pickMachine`, `sshSpawnWorker`, `now`, `on`, `set`, `waitForHealth`, `stopInstance`, `sshKillWorker`, `kill`, `waitExit`, `delete`, `reconcile`, `pauseInstance`, `resumeInstance`, `restartInstance`, `markUnhealthy`, `probe`, `isFinite`, `summarizeRtt`, `available`, `keys`, `min`, `startService` Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. diff --git a/docs/modules/zappier-account-balance/books.md b/docs/modules/zappier-account-balance/books.md new file mode 100644 index 0000000..421a2d3 --- /dev/null +++ b/docs/modules/zappier-account-balance/books.md @@ -0,0 +1,54 @@ +# `zappier-account-balance/books` + +**Package:** `zappier-account-balance` +**Source:** `packages/zappier-account-balance/src/books.js` +**Lines:** 90 + +## What this module is + +Implementation module in `zappier-account-balance`. The tables below are extracted from the source (signatures + JSDoc). + +## Exports + +`AccountBooks`, `handle` + +## Types / interfaces / classes + +| Kind | Name | +|------|------| +| class | `AccountBooks` | + +## Functions + +| Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) | +|------|------------|---------------------|---------|-----------------------------| +| `handle` | `subject, payload, books` | — | `unknown` | see Call graph | +| `match` | `rows` | — | `unknown` | see Call graph | + +## Methods (class / object) + +| Name | Parameters | +|------|------------| +| `constructor` | `(none)` | +| `prepaidCents` | `customerId` | +| `adjust` | `{ customerId, cents, reason, agent, kind = 'credit' }` | +| `recordUsage` | `entry` | +| `recordPayment` | `entry` | +| `statement` | `customerId` | + +## What it imports / requires + +_No imports detected._ + +## Call graph (identifiers invoked) + +`constructor`, `prepaidCents`, `adjust`, `trunc`, `now`, `toString`, `random`, `slice`, `toISOString`, `unshift`, `recordUsage`, `recordPayment`, `statement`, `filter`, `match`, `handle`, `endsWith` + +Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. + +## Return values (how to read this) + +- HTTP route handlers return Express `res.json(...)` bodies (see route docs). +- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches). +- Pricing functions return integer **cents** on `Quote.totalCents`. + diff --git a/docs/modules/zappier-account-balance/server.md b/docs/modules/zappier-account-balance/server.md new file mode 100644 index 0000000..6b1858a --- /dev/null +++ b/docs/modules/zappier-account-balance/server.md @@ -0,0 +1,53 @@ +# `zappier-account-balance/server` + +**Package:** `zappier-account-balance` +**Source:** `packages/zappier-account-balance/src/server.js` +**Lines:** 93 + +## What this module is + +Implementation module in `zappier-account-balance`. The tables below are extracted from the source (signatures + JSDoc). + +## Exports + +_No named `export` / `module.exports` keys detected._ + +## Types / interfaces / classes + +_None extracted._ + +## Functions + +| Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) | +|------|------------|---------------------|---------|-----------------------------| +| `startNats` | `(none)` | — | `unknown` | see Call graph | +| `readBody` | `req` | — | `unknown` | see Call graph | +| `reply` | `sub, fn` | — | `unknown` | see Call graph | +| `json` | `code, obj` | — | `unknown` | see Call graph | + +## Methods (class / object) + +| Name | Parameters | +|------|------------| +| `reply` | `nc.subscribe(SUBJECTS.STATEMENT_GET, { queue: SUBJECTS.QUEUE }` | +| `reply` | `nc.subscribe(SUBJECTS.BALANCE_GET, { queue: SUBJECTS.QUEUE }` | +| `reply` | `nc.subscribe(SUBJECTS.BALANCE_ADJUST, { queue: SUBJECTS.QUEUE }` | + +## What it imports / requires + +- `node:http` +- `./books.js` +- `./subjects.js` + +## Call graph (identifiers invoked) + +`startNats`, `import`, `connect`, `split`, `async`, `parse`, `decode`, `fn`, `respond`, `encode`, `stringify`, `reply`, `subscribe`, `handle`, `write`, `readBody`, `on`, `push`, `resolve`, `concat`, `toString`, `createServer`, `writeHead`, `end`, `json`, `match`, `statement`, `adjust`, `listen` + +Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. + +## Return values (how to read this) + +- HTTP route handlers return Express `res.json(...)` bodies (see route docs). +- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches). +- Pricing functions return integer **cents** on `Quote.totalCents`. + diff --git a/docs/modules/zappier-account-balance/subjects.md b/docs/modules/zappier-account-balance/subjects.md new file mode 100644 index 0000000..ebddc14 --- /dev/null +++ b/docs/modules/zappier-account-balance/subjects.md @@ -0,0 +1,36 @@ +# `zappier-account-balance/subjects` + +**Package:** `zappier-account-balance` +**Source:** `packages/zappier-account-balance/src/subjects.js` +**Lines:** 11 + +## What this module is + +Implementation module in `zappier-account-balance`. The tables below are extracted from the source (signatures + JSDoc). + +## Exports + +`SUBJECTS` + +## Types / interfaces / classes + +_None extracted._ + +## Functions + +_No top-level functions extracted._ + +## What it imports / requires + +_No imports detected._ + +## Call graph (identifiers invoked) + +_No local call identifiers extracted._ + +## Return values (how to read this) + +- HTTP route handlers return Express `res.json(...)` bodies (see route docs). +- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches). +- Pricing functions return integer **cents** on `Quote.totalCents`. + diff --git a/docs/modules/zappier-accounting-export/nats-billing.md b/docs/modules/zappier-accounting-export/nats-billing.md new file mode 100644 index 0000000..7c599ff --- /dev/null +++ b/docs/modules/zappier-accounting-export/nats-billing.md @@ -0,0 +1,40 @@ +# `zappier-accounting-export/nats-billing` + +**Package:** `zappier-accounting-export` +**Source:** `packages/zappier-accounting-export/src/nats-billing.js` +**Lines:** 18 + +## What this module is + +Implementation module in `zappier-accounting-export`. The tables below are extracted from the source (signatures + JSDoc). + +## Exports + +`SUBJECTS`, `billingRequest` + +## Types / interfaces / classes + +_None extracted._ + +## Functions + +| Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) | +|------|------------|---------------------|---------|-----------------------------| +| `billingRequest` | `subject, payload` | — | `unknown` | see Call graph | + +## What it imports / requires + +_No imports detected._ + +## Call graph (identifiers invoked) + +`billingRequest`, `import`, `connect`, `split`, `request`, `encode`, `stringify`, `parse`, `decode`, `close` + +Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. + +## Return values (how to read this) + +- HTTP route handlers return Express `res.json(...)` bodies (see route docs). +- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches). +- Pricing functions return integer **cents** on `Quote.totalCents`. + diff --git a/docs/modules/zappier-accounting-export/server.md b/docs/modules/zappier-accounting-export/server.md new file mode 100644 index 0000000..9532ec4 --- /dev/null +++ b/docs/modules/zappier-accounting-export/server.md @@ -0,0 +1,46 @@ +# `zappier-accounting-export/server` + +**Package:** `zappier-accounting-export` +**Source:** `packages/zappier-accounting-export/src/server.js` +**Lines:** 62 + +## What this module is + +Implementation module in `zappier-accounting-export`. The tables below are extracted from the source (signatures + JSDoc). + +## Exports + +_No named `export` / `module.exports` keys detected._ + +## Types / interfaces / classes + +_None extracted._ + +## Functions + +| Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) | +|------|------------|---------------------|---------|-----------------------------| +| `edge` | `pathname` | — | `unknown` | see Call graph | +| `send` | `code, type, body` | — | `unknown` | see Call graph | +| `json` | `code, obj` | — | `unknown` | see Call graph | + +## What it imports / requires + +- `node:fs` +- `node:http` +- `node:path` +- `node:url` +- `./nats-billing.js` + +## Call graph (identifiers invoked) + +`replace`, `join`, `dirname`, `fileURLToPath`, `edge`, `fetch`, `createServer`, `async`, `writeHead`, `end`, `send`, `stringify`, `readFileSync`, `json`, `match`, `billingRequest`, `text`, `get`, `encodeURIComponent`, `listen`, `write` + +Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. + +## Return values (how to read this) + +- HTTP route handlers return Express `res.json(...)` bodies (see route docs). +- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches). +- Pricing functions return integer **cents** on `Quote.totalCents`. + diff --git a/docs/modules/zappier-customer-service/nats-billing.md b/docs/modules/zappier-customer-service/nats-billing.md new file mode 100644 index 0000000..aed55e1 --- /dev/null +++ b/docs/modules/zappier-customer-service/nats-billing.md @@ -0,0 +1,40 @@ +# `zappier-customer-service/nats-billing` + +**Package:** `zappier-customer-service` +**Source:** `packages/zappier-customer-service/src/nats-billing.js` +**Lines:** 21 + +## What this module is + +Implementation module in `zappier-customer-service`. The tables below are extracted from the source (signatures + JSDoc). + +## Exports + +`SUBJECTS`, `billingRequest` + +## Types / interfaces / classes + +_None extracted._ + +## Functions + +| Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) | +|------|------------|---------------------|---------|-----------------------------| +| `billingRequest` | `subject, payload` | — | `unknown` | see Call graph | + +## What it imports / requires + +_No imports detected._ + +## Call graph (identifiers invoked) + +`billingRequest`, `import`, `connect`, `split`, `request`, `encode`, `stringify`, `parse`, `decode`, `close` + +Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. + +## Return values (how to read this) + +- HTTP route handlers return Express `res.json(...)` bodies (see route docs). +- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches). +- Pricing functions return integer **cents** on `Quote.totalCents`. + diff --git a/docs/modules/zappier-customer-service/server.md b/docs/modules/zappier-customer-service/server.md new file mode 100644 index 0000000..76c6218 --- /dev/null +++ b/docs/modules/zappier-customer-service/server.md @@ -0,0 +1,45 @@ +# `zappier-customer-service/server` + +**Package:** `zappier-customer-service` +**Source:** `packages/zappier-customer-service/src/server.js` +**Lines:** 81 + +## What this module is + +Implementation module in `zappier-customer-service`. The tables below are extracted from the source (signatures + JSDoc). + +## Exports + +_No named `export` / `module.exports` keys detected._ + +## Types / interfaces / classes + +_None extracted._ + +## Functions + +| Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) | +|------|------------|---------------------|---------|-----------------------------| +| `statement` | `customerId` | — | `unknown` | see Call graph | +| `json` | `code, obj` | — | `unknown` | see Call graph | + +## What it imports / requires + +- `node:fs` +- `node:http` +- `node:path` +- `node:url` +- `./nats-billing.js` + +## Call graph (identifiers invoked) + +`join`, `dirname`, `fileURLToPath`, `replace`, `statement`, `billingRequest`, `fetch`, `json`, `createServer`, `async`, `writeHead`, `end`, `stringify`, `readFileSync`, `match`, `push`, `parse`, `concat`, `toString`, `get`, `listen`, `write` + +Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. + +## Return values (how to read this) + +- HTTP route handlers return Express `res.json(...)` bodies (see route docs). +- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches). +- Pricing functions return integer **cents** on `Quote.totalCents`. + diff --git a/docs/modules/zappier-sales-pricing/nats-billing.md b/docs/modules/zappier-sales-pricing/nats-billing.md new file mode 100644 index 0000000..dcd029e --- /dev/null +++ b/docs/modules/zappier-sales-pricing/nats-billing.md @@ -0,0 +1,40 @@ +# `zappier-sales-pricing/nats-billing` + +**Package:** `zappier-sales-pricing` +**Source:** `packages/zappier-sales-pricing/src/nats-billing.js` +**Lines:** 18 + +## What this module is + +Implementation module in `zappier-sales-pricing`. The tables below are extracted from the source (signatures + JSDoc). + +## Exports + +`SUBJECTS`, `billingRequest` + +## Types / interfaces / classes + +_None extracted._ + +## Functions + +| Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) | +|------|------------|---------------------|---------|-----------------------------| +| `billingRequest` | `subject, payload` | — | `unknown` | see Call graph | + +## What it imports / requires + +_No imports detected._ + +## Call graph (identifiers invoked) + +`billingRequest`, `import`, `connect`, `split`, `request`, `encode`, `stringify`, `parse`, `decode`, `close` + +Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. + +## Return values (how to read this) + +- HTTP route handlers return Express `res.json(...)` bodies (see route docs). +- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches). +- Pricing functions return integer **cents** on `Quote.totalCents`. + diff --git a/docs/modules/zappier-sales-pricing/server.md b/docs/modules/zappier-sales-pricing/server.md new file mode 100644 index 0000000..4107dab --- /dev/null +++ b/docs/modules/zappier-sales-pricing/server.md @@ -0,0 +1,45 @@ +# `zappier-sales-pricing/server` + +**Package:** `zappier-sales-pricing` +**Source:** `packages/zappier-sales-pricing/src/server.js` +**Lines:** 77 + +## What this module is + +Implementation module in `zappier-sales-pricing`. The tables below are extracted from the source (signatures + JSDoc). + +## Exports + +_No named `export` / `module.exports` keys detected._ + +## Types / interfaces / classes + +_None extracted._ + +## Functions + +| Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) | +|------|------------|---------------------|---------|-----------------------------| +| `edge` | `pathname, { method = 'GET', body } = {}` | — | `unknown` | see Call graph | +| `json` | `code, obj` | — | `unknown` | see Call graph | + +## What it imports / requires + +- `node:fs` +- `node:http` +- `node:path` +- `node:url` +- `./nats-billing.js` + +## Call graph (identifiers invoked) + +`join`, `dirname`, `fileURLToPath`, `replace`, `edge`, `fetch`, `stringify`, `text`, `parse`, `createServer`, `async`, `writeHead`, `end`, `readFileSync`, `json`, `match`, `billingRequest`, `push`, `concat`, `toString`, `listen`, `write` + +Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. + +## Return values (how to read this) + +- HTTP route handlers return Express `res.json(...)` bodies (see route docs). +- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches). +- Pricing functions return integer **cents** on `Quote.totalCents`. + diff --git a/docs/modules/zappier/accounting-export.md b/docs/modules/zappier/accounting-export.md new file mode 100644 index 0000000..897a220 --- /dev/null +++ b/docs/modules/zappier/accounting-export.md @@ -0,0 +1,43 @@ +# `zappier/accounting-export` + +**Package:** `zappier` +**Source:** `packages/zappier/src/accounting-export.ts` +**Lines:** 37 + +## What this module is + +Implementation module in `zappier`. The tables below are extracted from the source (signatures + JSDoc). + +## Exports + +`invoicesToQuickBooksIif`, `invoicesToAccountingCsv` + +## Types / interfaces / classes + +_None extracted._ + +## Functions + +| Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) | +|------|------------|---------------------|---------|-----------------------------| +| `iifDate` | `ms: number` | — | `unknown` | see Call graph | +| `invoicesToQuickBooksIif` | `invoices: Invoice[]` | — | `unknown` | see Call graph | +| | _QuickBooks IIF journal for issued/paid invoices (Accounts Receivable + Sales)._ | | | | +| `invoicesToAccountingCsv` | `invoices: Invoice[]` | — | `unknown` | see Call graph | + +## What it imports / requires + +- `./invoicing` + +## Call graph (identifiers invoked) + +`iifDate`, `getUTCMonth`, `padStart`, `getUTCDate`, `getUTCFullYear`, `invoices`, `invoicesToQuickBooksIif`, `now`, `toFixed`, `push`, `join`, `invoicesToAccountingCsv`, `map` + +Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. + +## Return values (how to read this) + +- HTTP route handlers return Express `res.json(...)` bodies (see route docs). +- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches). +- Pricing functions return integer **cents** on `Quote.totalCents`. + diff --git a/docs/modules/zappier/admin.md b/docs/modules/zappier/admin.md index 455c8b5..445ea51 100644 --- a/docs/modules/zappier/admin.md +++ b/docs/modules/zappier/admin.md @@ -2,7 +2,7 @@ **Package:** `zappier` **Source:** `packages/zappier/src/admin.ts` -**Lines:** 486 +**Lines:** 588 ## What this module is @@ -32,7 +32,8 @@ Implementation module in `zappier`. The tables below are extracted from the sour | `renderInvoiceHtml` | `invoice: Invoice, customerName: string` | — | `unknown` | see Call graph | | `adminRouter` | `store: PricingStore, customers: CustomerRepo, - accounting: AccountingDeps,` | — | `unknown` | see Call graph | + accounting: AccountingDeps, + credits: CreditLedger = new CreditLedger(` | — | `unknown` | see Call graph | ## Methods (class / object) @@ -58,10 +59,14 @@ Implementation module in `zappier`. The tables below are extracted from the sour - `./paths` - `./reports` - `./usage` +- `./credits` +- `./accounting-export` +- `./statement` +- `./billing-nats` ## Call graph (identifiers invoked) -`tokens`, `adminLoginRouter`, `post`, `status`, `json`, `findByUsername`, `verifyPassword`, `randomBytes`, `toString`, `set`, `now`, `adminAuth`, `header`, `next`, `startsWith`, `slice`, `has`, `isValidRule`, `every`, `isValidTier`, `periodWindow`, `split`, `map`, `parseDate`, `trim`, `isNaN`, `getTime`, `escapeHtml`, `replace`, `toFixed`, `renderInvoiceHtml`, `dollars`, `join`, `toISOString`, `toUpperCase`, `adminRouter`, `get`, `getRateCard`, `getTiers`, `put`, `upsertEndpoint`, `delete`, `deleteEndpoint`, `upsertTier`, `deleteTier`, `list`, `randomUUID`, `save`, `find`, `test`, `push`, `listFor`, `filter`, `buildInvoice`, `nextSequence`, `type`, `send`, `untouched`, `flatMap`, `billingRows` +`tokens`, `adminLoginRouter`, `post`, `status`, `json`, `findByUsername`, `verifyPassword`, `randomBytes`, `toString`, `set`, `now`, `adminAuth`, `header`, `next`, `startsWith`, `slice`, `has`, `isValidRule`, `every`, `isValidTier`, `periodWindow`, `split`, `map`, `parseDate`, `trim`, `isNaN`, `getTime`, `escapeHtml`, `replace`, `toFixed`, `renderInvoiceHtml`, `dollars`, `join`, `toISOString`, `toUpperCase`, `adminRouter`, `get`, `getRateCard`, `getTiers`, `put`, `upsertEndpoint`, `delete`, `deleteEndpoint`, `upsertTier`, `deleteTier`, `list`, `randomUUID`, `save`, `find`, `isFinite`, `trunc`, `add`, `natsPublish`, `natsAdjust`, `async`, `natsStatement`, `composeStatement`, `listFor`, `type`, `send` Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. diff --git a/docs/modules/zappier/app.md b/docs/modules/zappier/app.md index 052f90b..6ac6afe 100644 --- a/docs/modules/zappier/app.md +++ b/docs/modules/zappier/app.md @@ -2,7 +2,7 @@ **Package:** `zappier` **Source:** `packages/zappier/src/app.ts` -**Lines:** 257 +**Lines:** 272 ## What this module is @@ -24,6 +24,7 @@ Implementation module in `zappier`. The tables below are extracted from the sour | Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) | |------|------------|---------------------|---------|-----------------------------| | `buildApp` | `deps: AppDeps = {}` | — | `unknown` | see Call graph | +| `onUsage` | `e: { customerId: string; endpointId: string; cents: number }` | — | `unknown` | see Call graph | ## What it imports / requires @@ -44,10 +45,14 @@ Implementation module in `zappier`. The tables below are extracted from the sour - `./invoicing` - `./paths` - `./portal` +- `./upstream` +- `./credits` +- `./statement` +- `./billing-nats` ## Call graph (identifiers invoked) -`join`, `next`, `parse`, `status`, `json`, `buildApp`, `seeded`, `seedAdminUsersFromEnv`, `rateCard`, `getRateCard`, `tiers`, `getTiers`, `express`, `use`, `load`, `setup`, `adminLoginRouter`, `adminAuth`, `adminRouter`, `static`, `async`, `import`, `toDataURL`, `portalRouter`, `apiKeyAuth`, `middleware`, `get`, `meter`, `post`, `toUpperCase`, `toLowerCase`, `createHash`, `update`, `digest`, `randomUUID`, `toISOString`, `set`, `isFinite`, `map`, `unshift`, `filter`, `setUTCDate`, `setUTCHours`, `summaryFor`, `find`, `applyMonthlyCredit` +`join`, `next`, `parse`, `status`, `json`, `buildApp`, `seeded`, `seedAdminUsersFromEnv`, `rateCard`, `getRateCard`, `tiers`, `getTiers`, `natsPublish`, `toISOString`, `express`, `use`, `load`, `setup`, `adminLoginRouter`, `adminAuth`, `adminRouter`, `static`, `async`, `import`, `toDataURL`, `portalRouter`, `get`, `apiKeyAuth`, `middleware`, `meter`, `post`, `toUpperCase`, `proxyVerae`, `toLowerCase`, `createHash`, `update`, `digest`, `randomUUID`, `set`, `isFinite`, `map`, `unshift`, `filter`, `setUTCDate`, `setUTCHours`, `summaryFor`, `find`, `applyMonthlyCredit` Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. diff --git a/docs/modules/zappier/billing-nats.md b/docs/modules/zappier/billing-nats.md new file mode 100644 index 0000000..caf750b --- /dev/null +++ b/docs/modules/zappier/billing-nats.md @@ -0,0 +1,54 @@ +# `zappier/billing-nats` + +**Package:** `zappier` +**Source:** `packages/zappier/src/billing-nats.ts` +**Lines:** 74 + +## What this module is + +Implementation module in `zappier`. The tables below are extracted from the source (signatures + JSDoc). + +## Exports + +`BILLING_SUBJECTS`, `BillingStatement`, `natsStatement`, `natsAdjust`, `natsPublish` + +## Types / interfaces / classes + +| Kind | Name | +|------|------| +| type | `BillingStatement` | +| type | `Nc` | + +## Functions + +| Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) | +|------|------------|---------------------|---------|-----------------------------| +| `nc` | `(none)` | — | `unknown` | see Call graph | +| `encode` | `obj: unknown` | — | `unknown` | see Call graph | +| `decode` | `buf: Uint8Array` | — | `unknown` | see Call graph | +| `natsStatement` | `customerId: string` | — | `unknown` | see Call graph | +| `natsAdjust` | `payload: { + customerId: string; + cents: number; + reason: string; + agent: string; + kind?: string; +}` | — | `unknown` | see Call graph | +| `natsPublish` | `subject: string, payload: unknown` | — | `unknown` | see Call graph | + +## What it imports / requires + +_No imports detected._ + +## Call graph (identifiers invoked) + +`unset`, `nc`, `async`, `import`, `connect`, `split`, `encode`, `stringify`, `decode`, `parse`, `natsStatement`, `request`, `natsAdjust`, `natsPublish`, `then`, `publish` + +Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. + +## Return values (how to read this) + +- HTTP route handlers return Express `res.json(...)` bodies (see route docs). +- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches). +- Pricing functions return integer **cents** on `Quote.totalCents`. + diff --git a/docs/modules/zappier/credits.md b/docs/modules/zappier/credits.md new file mode 100644 index 0000000..6a1b053 --- /dev/null +++ b/docs/modules/zappier/credits.md @@ -0,0 +1,48 @@ +# `zappier/credits` + +**Package:** `zappier` +**Source:** `packages/zappier/src/credits.ts` +**Lines:** 30 + +## What this module is + +Implementation module in `zappier`. The tables below are extracted from the source (signatures + JSDoc). + +## Exports + +`CreditAdjustment`, `CreditLedger` + +## Types / interfaces / classes + +| Kind | Name | +|------|------| +| interface | `CreditAdjustment` | +| class | `CreditLedger` | + +## Functions + +_No top-level functions extracted._ + +## Methods (class / object) + +| Name | Parameters | +|------|------------| +| `add` | `row: Omit & { id?: string; at?: string }` | +| `list` | `customerId?: string` | + +## What it imports / requires + +_No imports detected._ + +## Call graph (identifiers invoked) + +`add`, `now`, `toString`, `toISOString`, `unshift`, `list`, `filter` + +Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. + +## Return values (how to read this) + +- HTTP route handlers return Express `res.json(...)` bodies (see route docs). +- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches). +- Pricing functions return integer **cents** on `Quote.totalCents`. + diff --git a/docs/modules/zappier/meter.md b/docs/modules/zappier/meter.md index 887c0ac..4cc4b79 100644 --- a/docs/modules/zappier/meter.md +++ b/docs/modules/zappier/meter.md @@ -2,7 +2,7 @@ **Package:** `zappier` **Source:** `packages/zappier/src/meter.ts` -**Lines:** 49 +**Lines:** 52 ## What this module is @@ -22,7 +22,8 @@ _None extracted._ |------|------------|---------------------|---------|-----------------------------| | `meter` | `endpointId: string, repo: UsageRepo, - pricing: PricingContext,` | — | `unknown` | see Call graph | + pricing: PricingContext, + onRecord?: (entry: { customerId: string; endpointId: string; cents: number }` | — | `unknown` | see Call graph | ## Methods (class / object) diff --git a/docs/modules/zappier/nats-shim.d.md b/docs/modules/zappier/nats-shim.d.md new file mode 100644 index 0000000..7ea1522 --- /dev/null +++ b/docs/modules/zappier/nats-shim.d.md @@ -0,0 +1,41 @@ +# `zappier/nats-shim.d` + +**Package:** `zappier` +**Source:** `packages/zappier/src/nats-shim.d.ts` +**Lines:** 12 + +## What this module is + +Implementation module in `zappier`. The tables below are extracted from the source (signatures + JSDoc). + +## Exports + +_No named `export` / `module.exports` keys detected._ + +## Types / interfaces / classes + +_None extracted._ + +## Functions + +| Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) | +|------|------------|---------------------|---------|-----------------------------| +| `connect` | `opts: unknown` | — | `unknown` | see Call graph | +| `StringCodec` | `(none)` | — | `unknown` | see Call graph | + +## What it imports / requires + +_No imports detected._ + +## Call graph (identifiers invoked) + +`connect`, `request`, `publish`, `close`, `encode`, `decode` + +Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. + +## Return values (how to read this) + +- HTTP route handlers return Express `res.json(...)` bodies (see route docs). +- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches). +- Pricing functions return integer **cents** on `Quote.totalCents`. + diff --git a/docs/modules/zappier/portal.md b/docs/modules/zappier/portal.md index 3067f61..3a10365 100644 --- a/docs/modules/zappier/portal.md +++ b/docs/modules/zappier/portal.md @@ -2,7 +2,7 @@ **Package:** `zappier` **Source:** `packages/zappier/src/portal.ts` -**Lines:** 285 +**Lines:** 321 ## What this module is @@ -48,10 +48,13 @@ Implementation module in `zappier`. The tables below are extracted from the sour - `./invoicing` - `./pricing` - `./usage` +- `./credits` +- `./statement` +- `./billing-nats` ## Call graph (identifiers invoked) -`now`, `reload`, `customer`, `publicProfile`, `sessionAuth`, `header`, `startsWith`, `slice`, `get`, `list`, `find`, `status`, `json`, `next`, `save`, `portalRouter`, `post`, `trim`, `test`, `hashPassword`, `findByEmail`, `randomBytes`, `toString`, `create`, `verifyPassword`, `verifyTotp`, `use`, `delete`, `setUTCDate`, `setUTCHours`, `summaryFor`, `tiers`, `applyMonthlyCredit`, `rateCard`, `type`, `send`, `renderInvoiceHtml`, `async`, `generateTotpSecret`, `totpUri`, `qr`, `isInteger`, `put` +`now`, `reload`, `customer`, `publicProfile`, `sessionAuth`, `header`, `startsWith`, `slice`, `get`, `list`, `find`, `status`, `json`, `next`, `save`, `portalRouter`, `post`, `trim`, `test`, `hashPassword`, `findByEmail`, `randomBytes`, `toString`, `create`, `verifyPassword`, `verifyTotp`, `use`, `delete`, `setUTCDate`, `setUTCHours`, `summaryFor`, `tiers`, `applyMonthlyCredit`, `rateCard`, `async`, `natsStatement`, `composeStatement`, `listFor`, `type`, `send`, `renderInvoiceHtml`, `generateTotpSecret`, `totpUri`, `qr`, `isInteger`, `natsPublish`, `natsAdjust`, `put` Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. diff --git a/docs/modules/zappier/statement.md b/docs/modules/zappier/statement.md new file mode 100644 index 0000000..c0709a5 --- /dev/null +++ b/docs/modules/zappier/statement.md @@ -0,0 +1,48 @@ +# `zappier/statement` + +**Package:** `zappier` +**Source:** `packages/zappier/src/statement.ts` +**Lines:** 39 + +## What this module is + +Implementation module in `zappier`. The tables below are extracted from the source (signatures + JSDoc). + +## Exports + +`composeStatement` + +## Types / interfaces / classes + +_None extracted._ + +## Functions + +| Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) | +|------|------------|---------------------|---------|-----------------------------| +| `composeStatement` | `args: { + customerId: string; + prepaidCents: number; + credits: CreditAdjustment[]; + usage: UsageEntry[]; + invoices: Invoice[]; +}` | — | `unknown` | see Call graph | + +## What it imports / requires + +- `./credits` +- `./invoicing` +- `./usage` + +## Call graph (identifiers invoked) + +`composeStatement`, `filter`, `map`, `now`, `toISOString`, `slice`, `reverse` + +Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. + +## Return values (how to read this) + +- HTTP route handlers return Express `res.json(...)` bodies (see route docs). +- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches). +- Pricing functions return integer **cents** on `Quote.totalCents`. + diff --git a/docs/modules/zappier/upstream.md b/docs/modules/zappier/upstream.md new file mode 100644 index 0000000..00a5a19 --- /dev/null +++ b/docs/modules/zappier/upstream.md @@ -0,0 +1,45 @@ +# `zappier/upstream` + +**Package:** `zappier` +**Source:** `packages/zappier/src/upstream.ts` +**Lines:** 46 + +## What this module is + +Implementation module in `zappier`. The tables below are extracted from the source (signatures + JSDoc). + +## Exports + +`UpstreamFetch`, `proxyVerae` + +## Types / interfaces / classes + +| Kind | Name | +|------|------| +| type | `UpstreamFetch` | + +## Functions + +| Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) | +|------|------------|---------------------|---------|-----------------------------| +| `proxyVerae` | `req: Request, + res: Response, + pathname: string, + fetchImpl: UpstreamFetch = fetch,` | — | `unknown` | see Call graph | + +## What it imports / requires + +- `express` + +## Call graph (identifiers invoked) + +`proxyVerae`, `replace`, `entries`, `set`, `header`, `toUpperCase`, `stringify`, `fetchImpl`, `toString`, `text`, `parse`, `isArray`, `status`, `json` + +Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. + +## Return values (how to read this) + +- HTTP route handlers return Express `res.json(...)` bodies (see route docs). +- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches). +- Pricing functions return integer **cents** on `Quote.totalCents`. + diff --git a/docs/sphinx/index.rst b/docs/sphinx/index.rst index f446eaf..66d11b7 100644 --- a/docs/sphinx/index.rst +++ b/docs/sphinx/index.rst @@ -24,11 +24,16 @@ Markdown indexes: https://zapier.georgelambert.org/index-md.html repos/zapier-user-docs repos/docs-master repos/verae-nats-process + repos/zappier-account-balance + repos/zappier-customer-service + repos/zappier-sales-pricing + repos/zappier-accounting-export .. toctree:: :maxdepth: 2 :caption: API modules + modules/zappier/accounting-export modules/zappier/accounts modules/zappier/admin-users modules/zappier/admin @@ -37,6 +42,8 @@ Markdown indexes: https://zapier.georgelambert.org/index-md.html modules/zappier/billing/credit modules/zappier/billing/reload modules/zappier/billing/stripe + modules/zappier/billing-nats + modules/zappier/credits modules/zappier/db/admin-user-repo modules/zappier/db/billing-repo modules/zappier/db/customer-repo @@ -48,10 +55,13 @@ Markdown indexes: https://zapier.georgelambert.org/index-md.html modules/zappier/invoicing modules/zappier/jobs/report-usage modules/zappier/meter + modules/zappier/nats-shim.d modules/zappier/paths modules/zappier/portal modules/zappier/pricing modules/zappier/reports + modules/zappier/statement + modules/zappier/upstream modules/zappier/usage modules/verae-zapier-middleware/app modules/verae-zapier-middleware/clients/veraeClient @@ -138,3 +148,12 @@ Markdown indexes: https://zapier.georgelambert.org/index-md.html modules/verae-zapier-simulator/pipeline modules/verae-zapier-simulator/server modules/verae-zapier-simulator/trace + modules/zappier-account-balance/books + modules/zappier-account-balance/server + modules/zappier-account-balance/subjects + modules/zappier-customer-service/nats-billing + modules/zappier-customer-service/server + modules/zappier-sales-pricing/nats-billing + modules/zappier-sales-pricing/server + modules/zappier-accounting-export/nats-billing + modules/zappier-accounting-export/server diff --git a/docs/sphinx/modules/verae-fleet/supervisor.md b/docs/sphinx/modules/verae-fleet/supervisor.md index d810429..e778499 100644 --- a/docs/sphinx/modules/verae-fleet/supervisor.md +++ b/docs/sphinx/modules/verae-fleet/supervisor.md @@ -2,7 +2,7 @@ **Package:** `verae-fleet` **Source:** `packages/verae-fleet/src/supervisor.js` -**Lines:** 511 +**Lines:** 528 ## What this module is @@ -10,7 +10,7 @@ Implementation module in `verae-fleet`. The tables below are extracted from the ## Exports -`Supervisor` +`spawnLocal`, `Supervisor` ## Types / interfaces / classes @@ -22,6 +22,7 @@ Implementation module in `verae-fleet`. The tables below are extracted from the | Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) | |------|------------|---------------------|---------|-----------------------------| +| `spawnLocal` | `root, spec, env, healthPort` | — | `unknown` | see Call graph | | `waitExit` | `child, ms` | — | `unknown` | see Call graph | | `waitForHealth` | `rec, ms` | — | `unknown` | see Call graph | @@ -70,7 +71,7 @@ Implementation module in `verae-fleet`. The tables below are extracted from the ## Call graph (identifiers invoked) -`floor`, `join`, `dirname`, `fileURLToPath`, `constructor`, `loadFleet`, `mkdirSync`, `isArray`, `machines`, `addMachine`, `upsertMachine`, `saveOverlay`, `log`, `setMachineEnabled`, `find`, `checkMachine`, `sshCheck`, `httpProbe`, `machineOf`, `controlInstance`, `sshHttp`, `postControl`, `toISOString`, `push`, `splice`, `spec`, `nextIndex`, `values`, `filter`, `map`, `has`, `startOne`, `get`, `pickMachine`, `sshSpawnWorker`, `spawn`, `now`, `on`, `set`, `waitForHealth`, `stopInstance`, `sshKillWorker`, `kill`, `waitExit`, `delete`, `reconcile`, `pauseInstance`, `resumeInstance`, `restartInstance`, `markUnhealthy`, `probe`, `isFinite`, `summarizeRtt`, `available`, `keys`, `min`, `startService`, `stopService`, `pauseService`, `resumeService` +`floor`, `join`, `dirname`, `fileURLToPath`, `apps`, `spawnLocal`, `resolve`, `spawn`, `constructor`, `loadFleet`, `mkdirSync`, `isArray`, `machines`, `addMachine`, `upsertMachine`, `saveOverlay`, `log`, `setMachineEnabled`, `find`, `checkMachine`, `sshCheck`, `httpProbe`, `machineOf`, `controlInstance`, `sshHttp`, `postControl`, `toISOString`, `push`, `splice`, `spec`, `nextIndex`, `values`, `filter`, `map`, `has`, `startOne`, `get`, `pickMachine`, `sshSpawnWorker`, `now`, `on`, `set`, `waitForHealth`, `stopInstance`, `sshKillWorker`, `kill`, `waitExit`, `delete`, `reconcile`, `pauseInstance`, `resumeInstance`, `restartInstance`, `markUnhealthy`, `probe`, `isFinite`, `summarizeRtt`, `available`, `keys`, `min`, `startService` Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. diff --git a/docs/sphinx/modules/zappier-account-balance/books.md b/docs/sphinx/modules/zappier-account-balance/books.md new file mode 100644 index 0000000..421a2d3 --- /dev/null +++ b/docs/sphinx/modules/zappier-account-balance/books.md @@ -0,0 +1,54 @@ +# `zappier-account-balance/books` + +**Package:** `zappier-account-balance` +**Source:** `packages/zappier-account-balance/src/books.js` +**Lines:** 90 + +## What this module is + +Implementation module in `zappier-account-balance`. The tables below are extracted from the source (signatures + JSDoc). + +## Exports + +`AccountBooks`, `handle` + +## Types / interfaces / classes + +| Kind | Name | +|------|------| +| class | `AccountBooks` | + +## Functions + +| Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) | +|------|------------|---------------------|---------|-----------------------------| +| `handle` | `subject, payload, books` | — | `unknown` | see Call graph | +| `match` | `rows` | — | `unknown` | see Call graph | + +## Methods (class / object) + +| Name | Parameters | +|------|------------| +| `constructor` | `(none)` | +| `prepaidCents` | `customerId` | +| `adjust` | `{ customerId, cents, reason, agent, kind = 'credit' }` | +| `recordUsage` | `entry` | +| `recordPayment` | `entry` | +| `statement` | `customerId` | + +## What it imports / requires + +_No imports detected._ + +## Call graph (identifiers invoked) + +`constructor`, `prepaidCents`, `adjust`, `trunc`, `now`, `toString`, `random`, `slice`, `toISOString`, `unshift`, `recordUsage`, `recordPayment`, `statement`, `filter`, `match`, `handle`, `endsWith` + +Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. + +## Return values (how to read this) + +- HTTP route handlers return Express `res.json(...)` bodies (see route docs). +- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches). +- Pricing functions return integer **cents** on `Quote.totalCents`. + diff --git a/docs/sphinx/modules/zappier-account-balance/books.rst b/docs/sphinx/modules/zappier-account-balance/books.rst new file mode 100644 index 0000000..75b0110 --- /dev/null +++ b/docs/sphinx/modules/zappier-account-balance/books.rst @@ -0,0 +1,7 @@ +zappier-account-balance.books +============================= + +Generated API sheet for ``zappier-account-balance/books``. + +.. include:: books.md + :parser: myst_parser.sphinx_ diff --git a/docs/sphinx/modules/zappier-account-balance/server.md b/docs/sphinx/modules/zappier-account-balance/server.md new file mode 100644 index 0000000..6b1858a --- /dev/null +++ b/docs/sphinx/modules/zappier-account-balance/server.md @@ -0,0 +1,53 @@ +# `zappier-account-balance/server` + +**Package:** `zappier-account-balance` +**Source:** `packages/zappier-account-balance/src/server.js` +**Lines:** 93 + +## What this module is + +Implementation module in `zappier-account-balance`. The tables below are extracted from the source (signatures + JSDoc). + +## Exports + +_No named `export` / `module.exports` keys detected._ + +## Types / interfaces / classes + +_None extracted._ + +## Functions + +| Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) | +|------|------------|---------------------|---------|-----------------------------| +| `startNats` | `(none)` | — | `unknown` | see Call graph | +| `readBody` | `req` | — | `unknown` | see Call graph | +| `reply` | `sub, fn` | — | `unknown` | see Call graph | +| `json` | `code, obj` | — | `unknown` | see Call graph | + +## Methods (class / object) + +| Name | Parameters | +|------|------------| +| `reply` | `nc.subscribe(SUBJECTS.STATEMENT_GET, { queue: SUBJECTS.QUEUE }` | +| `reply` | `nc.subscribe(SUBJECTS.BALANCE_GET, { queue: SUBJECTS.QUEUE }` | +| `reply` | `nc.subscribe(SUBJECTS.BALANCE_ADJUST, { queue: SUBJECTS.QUEUE }` | + +## What it imports / requires + +- `node:http` +- `./books.js` +- `./subjects.js` + +## Call graph (identifiers invoked) + +`startNats`, `import`, `connect`, `split`, `async`, `parse`, `decode`, `fn`, `respond`, `encode`, `stringify`, `reply`, `subscribe`, `handle`, `write`, `readBody`, `on`, `push`, `resolve`, `concat`, `toString`, `createServer`, `writeHead`, `end`, `json`, `match`, `statement`, `adjust`, `listen` + +Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. + +## Return values (how to read this) + +- HTTP route handlers return Express `res.json(...)` bodies (see route docs). +- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches). +- Pricing functions return integer **cents** on `Quote.totalCents`. + diff --git a/docs/sphinx/modules/zappier-account-balance/server.rst b/docs/sphinx/modules/zappier-account-balance/server.rst new file mode 100644 index 0000000..7f7ea49 --- /dev/null +++ b/docs/sphinx/modules/zappier-account-balance/server.rst @@ -0,0 +1,7 @@ +zappier-account-balance.server +============================== + +Generated API sheet for ``zappier-account-balance/server``. + +.. include:: server.md + :parser: myst_parser.sphinx_ diff --git a/docs/sphinx/modules/zappier-account-balance/subjects.md b/docs/sphinx/modules/zappier-account-balance/subjects.md new file mode 100644 index 0000000..ebddc14 --- /dev/null +++ b/docs/sphinx/modules/zappier-account-balance/subjects.md @@ -0,0 +1,36 @@ +# `zappier-account-balance/subjects` + +**Package:** `zappier-account-balance` +**Source:** `packages/zappier-account-balance/src/subjects.js` +**Lines:** 11 + +## What this module is + +Implementation module in `zappier-account-balance`. The tables below are extracted from the source (signatures + JSDoc). + +## Exports + +`SUBJECTS` + +## Types / interfaces / classes + +_None extracted._ + +## Functions + +_No top-level functions extracted._ + +## What it imports / requires + +_No imports detected._ + +## Call graph (identifiers invoked) + +_No local call identifiers extracted._ + +## Return values (how to read this) + +- HTTP route handlers return Express `res.json(...)` bodies (see route docs). +- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches). +- Pricing functions return integer **cents** on `Quote.totalCents`. + diff --git a/docs/sphinx/modules/zappier-account-balance/subjects.rst b/docs/sphinx/modules/zappier-account-balance/subjects.rst new file mode 100644 index 0000000..47bc34d --- /dev/null +++ b/docs/sphinx/modules/zappier-account-balance/subjects.rst @@ -0,0 +1,7 @@ +zappier-account-balance.subjects +================================ + +Generated API sheet for ``zappier-account-balance/subjects``. + +.. include:: subjects.md + :parser: myst_parser.sphinx_ diff --git a/docs/sphinx/modules/zappier-accounting-export/nats-billing.md b/docs/sphinx/modules/zappier-accounting-export/nats-billing.md new file mode 100644 index 0000000..7c599ff --- /dev/null +++ b/docs/sphinx/modules/zappier-accounting-export/nats-billing.md @@ -0,0 +1,40 @@ +# `zappier-accounting-export/nats-billing` + +**Package:** `zappier-accounting-export` +**Source:** `packages/zappier-accounting-export/src/nats-billing.js` +**Lines:** 18 + +## What this module is + +Implementation module in `zappier-accounting-export`. The tables below are extracted from the source (signatures + JSDoc). + +## Exports + +`SUBJECTS`, `billingRequest` + +## Types / interfaces / classes + +_None extracted._ + +## Functions + +| Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) | +|------|------------|---------------------|---------|-----------------------------| +| `billingRequest` | `subject, payload` | — | `unknown` | see Call graph | + +## What it imports / requires + +_No imports detected._ + +## Call graph (identifiers invoked) + +`billingRequest`, `import`, `connect`, `split`, `request`, `encode`, `stringify`, `parse`, `decode`, `close` + +Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. + +## Return values (how to read this) + +- HTTP route handlers return Express `res.json(...)` bodies (see route docs). +- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches). +- Pricing functions return integer **cents** on `Quote.totalCents`. + diff --git a/docs/sphinx/modules/zappier-accounting-export/nats-billing.rst b/docs/sphinx/modules/zappier-accounting-export/nats-billing.rst new file mode 100644 index 0000000..a9cea48 --- /dev/null +++ b/docs/sphinx/modules/zappier-accounting-export/nats-billing.rst @@ -0,0 +1,7 @@ +zappier-accounting-export.nats-billing +====================================== + +Generated API sheet for ``zappier-accounting-export/nats-billing``. + +.. include:: nats-billing.md + :parser: myst_parser.sphinx_ diff --git a/docs/sphinx/modules/zappier-accounting-export/server.md b/docs/sphinx/modules/zappier-accounting-export/server.md new file mode 100644 index 0000000..9532ec4 --- /dev/null +++ b/docs/sphinx/modules/zappier-accounting-export/server.md @@ -0,0 +1,46 @@ +# `zappier-accounting-export/server` + +**Package:** `zappier-accounting-export` +**Source:** `packages/zappier-accounting-export/src/server.js` +**Lines:** 62 + +## What this module is + +Implementation module in `zappier-accounting-export`. The tables below are extracted from the source (signatures + JSDoc). + +## Exports + +_No named `export` / `module.exports` keys detected._ + +## Types / interfaces / classes + +_None extracted._ + +## Functions + +| Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) | +|------|------------|---------------------|---------|-----------------------------| +| `edge` | `pathname` | — | `unknown` | see Call graph | +| `send` | `code, type, body` | — | `unknown` | see Call graph | +| `json` | `code, obj` | — | `unknown` | see Call graph | + +## What it imports / requires + +- `node:fs` +- `node:http` +- `node:path` +- `node:url` +- `./nats-billing.js` + +## Call graph (identifiers invoked) + +`replace`, `join`, `dirname`, `fileURLToPath`, `edge`, `fetch`, `createServer`, `async`, `writeHead`, `end`, `send`, `stringify`, `readFileSync`, `json`, `match`, `billingRequest`, `text`, `get`, `encodeURIComponent`, `listen`, `write` + +Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. + +## Return values (how to read this) + +- HTTP route handlers return Express `res.json(...)` bodies (see route docs). +- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches). +- Pricing functions return integer **cents** on `Quote.totalCents`. + diff --git a/docs/sphinx/modules/zappier-accounting-export/server.rst b/docs/sphinx/modules/zappier-accounting-export/server.rst new file mode 100644 index 0000000..14874a8 --- /dev/null +++ b/docs/sphinx/modules/zappier-accounting-export/server.rst @@ -0,0 +1,7 @@ +zappier-accounting-export.server +================================ + +Generated API sheet for ``zappier-accounting-export/server``. + +.. include:: server.md + :parser: myst_parser.sphinx_ diff --git a/docs/sphinx/modules/zappier-customer-service/nats-billing.md b/docs/sphinx/modules/zappier-customer-service/nats-billing.md new file mode 100644 index 0000000..aed55e1 --- /dev/null +++ b/docs/sphinx/modules/zappier-customer-service/nats-billing.md @@ -0,0 +1,40 @@ +# `zappier-customer-service/nats-billing` + +**Package:** `zappier-customer-service` +**Source:** `packages/zappier-customer-service/src/nats-billing.js` +**Lines:** 21 + +## What this module is + +Implementation module in `zappier-customer-service`. The tables below are extracted from the source (signatures + JSDoc). + +## Exports + +`SUBJECTS`, `billingRequest` + +## Types / interfaces / classes + +_None extracted._ + +## Functions + +| Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) | +|------|------------|---------------------|---------|-----------------------------| +| `billingRequest` | `subject, payload` | — | `unknown` | see Call graph | + +## What it imports / requires + +_No imports detected._ + +## Call graph (identifiers invoked) + +`billingRequest`, `import`, `connect`, `split`, `request`, `encode`, `stringify`, `parse`, `decode`, `close` + +Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. + +## Return values (how to read this) + +- HTTP route handlers return Express `res.json(...)` bodies (see route docs). +- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches). +- Pricing functions return integer **cents** on `Quote.totalCents`. + diff --git a/docs/sphinx/modules/zappier-customer-service/nats-billing.rst b/docs/sphinx/modules/zappier-customer-service/nats-billing.rst new file mode 100644 index 0000000..4c872af --- /dev/null +++ b/docs/sphinx/modules/zappier-customer-service/nats-billing.rst @@ -0,0 +1,7 @@ +zappier-customer-service.nats-billing +===================================== + +Generated API sheet for ``zappier-customer-service/nats-billing``. + +.. include:: nats-billing.md + :parser: myst_parser.sphinx_ diff --git a/docs/sphinx/modules/zappier-customer-service/server.md b/docs/sphinx/modules/zappier-customer-service/server.md new file mode 100644 index 0000000..76c6218 --- /dev/null +++ b/docs/sphinx/modules/zappier-customer-service/server.md @@ -0,0 +1,45 @@ +# `zappier-customer-service/server` + +**Package:** `zappier-customer-service` +**Source:** `packages/zappier-customer-service/src/server.js` +**Lines:** 81 + +## What this module is + +Implementation module in `zappier-customer-service`. The tables below are extracted from the source (signatures + JSDoc). + +## Exports + +_No named `export` / `module.exports` keys detected._ + +## Types / interfaces / classes + +_None extracted._ + +## Functions + +| Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) | +|------|------------|---------------------|---------|-----------------------------| +| `statement` | `customerId` | — | `unknown` | see Call graph | +| `json` | `code, obj` | — | `unknown` | see Call graph | + +## What it imports / requires + +- `node:fs` +- `node:http` +- `node:path` +- `node:url` +- `./nats-billing.js` + +## Call graph (identifiers invoked) + +`join`, `dirname`, `fileURLToPath`, `replace`, `statement`, `billingRequest`, `fetch`, `json`, `createServer`, `async`, `writeHead`, `end`, `stringify`, `readFileSync`, `match`, `push`, `parse`, `concat`, `toString`, `get`, `listen`, `write` + +Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. + +## Return values (how to read this) + +- HTTP route handlers return Express `res.json(...)` bodies (see route docs). +- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches). +- Pricing functions return integer **cents** on `Quote.totalCents`. + diff --git a/docs/sphinx/modules/zappier-customer-service/server.rst b/docs/sphinx/modules/zappier-customer-service/server.rst new file mode 100644 index 0000000..6db6b8f --- /dev/null +++ b/docs/sphinx/modules/zappier-customer-service/server.rst @@ -0,0 +1,7 @@ +zappier-customer-service.server +=============================== + +Generated API sheet for ``zappier-customer-service/server``. + +.. include:: server.md + :parser: myst_parser.sphinx_ diff --git a/docs/sphinx/modules/zappier-sales-pricing/nats-billing.md b/docs/sphinx/modules/zappier-sales-pricing/nats-billing.md new file mode 100644 index 0000000..dcd029e --- /dev/null +++ b/docs/sphinx/modules/zappier-sales-pricing/nats-billing.md @@ -0,0 +1,40 @@ +# `zappier-sales-pricing/nats-billing` + +**Package:** `zappier-sales-pricing` +**Source:** `packages/zappier-sales-pricing/src/nats-billing.js` +**Lines:** 18 + +## What this module is + +Implementation module in `zappier-sales-pricing`. The tables below are extracted from the source (signatures + JSDoc). + +## Exports + +`SUBJECTS`, `billingRequest` + +## Types / interfaces / classes + +_None extracted._ + +## Functions + +| Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) | +|------|------------|---------------------|---------|-----------------------------| +| `billingRequest` | `subject, payload` | — | `unknown` | see Call graph | + +## What it imports / requires + +_No imports detected._ + +## Call graph (identifiers invoked) + +`billingRequest`, `import`, `connect`, `split`, `request`, `encode`, `stringify`, `parse`, `decode`, `close` + +Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. + +## Return values (how to read this) + +- HTTP route handlers return Express `res.json(...)` bodies (see route docs). +- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches). +- Pricing functions return integer **cents** on `Quote.totalCents`. + diff --git a/docs/sphinx/modules/zappier-sales-pricing/nats-billing.rst b/docs/sphinx/modules/zappier-sales-pricing/nats-billing.rst new file mode 100644 index 0000000..2028a42 --- /dev/null +++ b/docs/sphinx/modules/zappier-sales-pricing/nats-billing.rst @@ -0,0 +1,7 @@ +zappier-sales-pricing.nats-billing +================================== + +Generated API sheet for ``zappier-sales-pricing/nats-billing``. + +.. include:: nats-billing.md + :parser: myst_parser.sphinx_ diff --git a/docs/sphinx/modules/zappier-sales-pricing/server.md b/docs/sphinx/modules/zappier-sales-pricing/server.md new file mode 100644 index 0000000..4107dab --- /dev/null +++ b/docs/sphinx/modules/zappier-sales-pricing/server.md @@ -0,0 +1,45 @@ +# `zappier-sales-pricing/server` + +**Package:** `zappier-sales-pricing` +**Source:** `packages/zappier-sales-pricing/src/server.js` +**Lines:** 77 + +## What this module is + +Implementation module in `zappier-sales-pricing`. The tables below are extracted from the source (signatures + JSDoc). + +## Exports + +_No named `export` / `module.exports` keys detected._ + +## Types / interfaces / classes + +_None extracted._ + +## Functions + +| Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) | +|------|------------|---------------------|---------|-----------------------------| +| `edge` | `pathname, { method = 'GET', body } = {}` | — | `unknown` | see Call graph | +| `json` | `code, obj` | — | `unknown` | see Call graph | + +## What it imports / requires + +- `node:fs` +- `node:http` +- `node:path` +- `node:url` +- `./nats-billing.js` + +## Call graph (identifiers invoked) + +`join`, `dirname`, `fileURLToPath`, `replace`, `edge`, `fetch`, `stringify`, `text`, `parse`, `createServer`, `async`, `writeHead`, `end`, `readFileSync`, `json`, `match`, `billingRequest`, `push`, `concat`, `toString`, `listen`, `write` + +Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. + +## Return values (how to read this) + +- HTTP route handlers return Express `res.json(...)` bodies (see route docs). +- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches). +- Pricing functions return integer **cents** on `Quote.totalCents`. + diff --git a/docs/sphinx/modules/zappier-sales-pricing/server.rst b/docs/sphinx/modules/zappier-sales-pricing/server.rst new file mode 100644 index 0000000..31ca462 --- /dev/null +++ b/docs/sphinx/modules/zappier-sales-pricing/server.rst @@ -0,0 +1,7 @@ +zappier-sales-pricing.server +============================ + +Generated API sheet for ``zappier-sales-pricing/server``. + +.. include:: server.md + :parser: myst_parser.sphinx_ diff --git a/docs/sphinx/modules/zappier/accounting-export.md b/docs/sphinx/modules/zappier/accounting-export.md new file mode 100644 index 0000000..897a220 --- /dev/null +++ b/docs/sphinx/modules/zappier/accounting-export.md @@ -0,0 +1,43 @@ +# `zappier/accounting-export` + +**Package:** `zappier` +**Source:** `packages/zappier/src/accounting-export.ts` +**Lines:** 37 + +## What this module is + +Implementation module in `zappier`. The tables below are extracted from the source (signatures + JSDoc). + +## Exports + +`invoicesToQuickBooksIif`, `invoicesToAccountingCsv` + +## Types / interfaces / classes + +_None extracted._ + +## Functions + +| Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) | +|------|------------|---------------------|---------|-----------------------------| +| `iifDate` | `ms: number` | — | `unknown` | see Call graph | +| `invoicesToQuickBooksIif` | `invoices: Invoice[]` | — | `unknown` | see Call graph | +| | _QuickBooks IIF journal for issued/paid invoices (Accounts Receivable + Sales)._ | | | | +| `invoicesToAccountingCsv` | `invoices: Invoice[]` | — | `unknown` | see Call graph | + +## What it imports / requires + +- `./invoicing` + +## Call graph (identifiers invoked) + +`iifDate`, `getUTCMonth`, `padStart`, `getUTCDate`, `getUTCFullYear`, `invoices`, `invoicesToQuickBooksIif`, `now`, `toFixed`, `push`, `join`, `invoicesToAccountingCsv`, `map` + +Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. + +## Return values (how to read this) + +- HTTP route handlers return Express `res.json(...)` bodies (see route docs). +- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches). +- Pricing functions return integer **cents** on `Quote.totalCents`. + diff --git a/docs/sphinx/modules/zappier/accounting-export.rst b/docs/sphinx/modules/zappier/accounting-export.rst new file mode 100644 index 0000000..00741ec --- /dev/null +++ b/docs/sphinx/modules/zappier/accounting-export.rst @@ -0,0 +1,7 @@ +zappier.accounting-export +========================= + +Generated API sheet for ``zappier/accounting-export``. + +.. include:: accounting-export.md + :parser: myst_parser.sphinx_ diff --git a/docs/sphinx/modules/zappier/admin.md b/docs/sphinx/modules/zappier/admin.md index 455c8b5..445ea51 100644 --- a/docs/sphinx/modules/zappier/admin.md +++ b/docs/sphinx/modules/zappier/admin.md @@ -2,7 +2,7 @@ **Package:** `zappier` **Source:** `packages/zappier/src/admin.ts` -**Lines:** 486 +**Lines:** 588 ## What this module is @@ -32,7 +32,8 @@ Implementation module in `zappier`. The tables below are extracted from the sour | `renderInvoiceHtml` | `invoice: Invoice, customerName: string` | — | `unknown` | see Call graph | | `adminRouter` | `store: PricingStore, customers: CustomerRepo, - accounting: AccountingDeps,` | — | `unknown` | see Call graph | + accounting: AccountingDeps, + credits: CreditLedger = new CreditLedger(` | — | `unknown` | see Call graph | ## Methods (class / object) @@ -58,10 +59,14 @@ Implementation module in `zappier`. The tables below are extracted from the sour - `./paths` - `./reports` - `./usage` +- `./credits` +- `./accounting-export` +- `./statement` +- `./billing-nats` ## Call graph (identifiers invoked) -`tokens`, `adminLoginRouter`, `post`, `status`, `json`, `findByUsername`, `verifyPassword`, `randomBytes`, `toString`, `set`, `now`, `adminAuth`, `header`, `next`, `startsWith`, `slice`, `has`, `isValidRule`, `every`, `isValidTier`, `periodWindow`, `split`, `map`, `parseDate`, `trim`, `isNaN`, `getTime`, `escapeHtml`, `replace`, `toFixed`, `renderInvoiceHtml`, `dollars`, `join`, `toISOString`, `toUpperCase`, `adminRouter`, `get`, `getRateCard`, `getTiers`, `put`, `upsertEndpoint`, `delete`, `deleteEndpoint`, `upsertTier`, `deleteTier`, `list`, `randomUUID`, `save`, `find`, `test`, `push`, `listFor`, `filter`, `buildInvoice`, `nextSequence`, `type`, `send`, `untouched`, `flatMap`, `billingRows` +`tokens`, `adminLoginRouter`, `post`, `status`, `json`, `findByUsername`, `verifyPassword`, `randomBytes`, `toString`, `set`, `now`, `adminAuth`, `header`, `next`, `startsWith`, `slice`, `has`, `isValidRule`, `every`, `isValidTier`, `periodWindow`, `split`, `map`, `parseDate`, `trim`, `isNaN`, `getTime`, `escapeHtml`, `replace`, `toFixed`, `renderInvoiceHtml`, `dollars`, `join`, `toISOString`, `toUpperCase`, `adminRouter`, `get`, `getRateCard`, `getTiers`, `put`, `upsertEndpoint`, `delete`, `deleteEndpoint`, `upsertTier`, `deleteTier`, `list`, `randomUUID`, `save`, `find`, `isFinite`, `trunc`, `add`, `natsPublish`, `natsAdjust`, `async`, `natsStatement`, `composeStatement`, `listFor`, `type`, `send` Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. diff --git a/docs/sphinx/modules/zappier/app.md b/docs/sphinx/modules/zappier/app.md index 052f90b..6ac6afe 100644 --- a/docs/sphinx/modules/zappier/app.md +++ b/docs/sphinx/modules/zappier/app.md @@ -2,7 +2,7 @@ **Package:** `zappier` **Source:** `packages/zappier/src/app.ts` -**Lines:** 257 +**Lines:** 272 ## What this module is @@ -24,6 +24,7 @@ Implementation module in `zappier`. The tables below are extracted from the sour | Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) | |------|------------|---------------------|---------|-----------------------------| | `buildApp` | `deps: AppDeps = {}` | — | `unknown` | see Call graph | +| `onUsage` | `e: { customerId: string; endpointId: string; cents: number }` | — | `unknown` | see Call graph | ## What it imports / requires @@ -44,10 +45,14 @@ Implementation module in `zappier`. The tables below are extracted from the sour - `./invoicing` - `./paths` - `./portal` +- `./upstream` +- `./credits` +- `./statement` +- `./billing-nats` ## Call graph (identifiers invoked) -`join`, `next`, `parse`, `status`, `json`, `buildApp`, `seeded`, `seedAdminUsersFromEnv`, `rateCard`, `getRateCard`, `tiers`, `getTiers`, `express`, `use`, `load`, `setup`, `adminLoginRouter`, `adminAuth`, `adminRouter`, `static`, `async`, `import`, `toDataURL`, `portalRouter`, `apiKeyAuth`, `middleware`, `get`, `meter`, `post`, `toUpperCase`, `toLowerCase`, `createHash`, `update`, `digest`, `randomUUID`, `toISOString`, `set`, `isFinite`, `map`, `unshift`, `filter`, `setUTCDate`, `setUTCHours`, `summaryFor`, `find`, `applyMonthlyCredit` +`join`, `next`, `parse`, `status`, `json`, `buildApp`, `seeded`, `seedAdminUsersFromEnv`, `rateCard`, `getRateCard`, `tiers`, `getTiers`, `natsPublish`, `toISOString`, `express`, `use`, `load`, `setup`, `adminLoginRouter`, `adminAuth`, `adminRouter`, `static`, `async`, `import`, `toDataURL`, `portalRouter`, `get`, `apiKeyAuth`, `middleware`, `meter`, `post`, `toUpperCase`, `proxyVerae`, `toLowerCase`, `createHash`, `update`, `digest`, `randomUUID`, `set`, `isFinite`, `map`, `unshift`, `filter`, `setUTCDate`, `setUTCHours`, `summaryFor`, `find`, `applyMonthlyCredit` Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. diff --git a/docs/sphinx/modules/zappier/billing-nats.md b/docs/sphinx/modules/zappier/billing-nats.md new file mode 100644 index 0000000..caf750b --- /dev/null +++ b/docs/sphinx/modules/zappier/billing-nats.md @@ -0,0 +1,54 @@ +# `zappier/billing-nats` + +**Package:** `zappier` +**Source:** `packages/zappier/src/billing-nats.ts` +**Lines:** 74 + +## What this module is + +Implementation module in `zappier`. The tables below are extracted from the source (signatures + JSDoc). + +## Exports + +`BILLING_SUBJECTS`, `BillingStatement`, `natsStatement`, `natsAdjust`, `natsPublish` + +## Types / interfaces / classes + +| Kind | Name | +|------|------| +| type | `BillingStatement` | +| type | `Nc` | + +## Functions + +| Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) | +|------|------------|---------------------|---------|-----------------------------| +| `nc` | `(none)` | — | `unknown` | see Call graph | +| `encode` | `obj: unknown` | — | `unknown` | see Call graph | +| `decode` | `buf: Uint8Array` | — | `unknown` | see Call graph | +| `natsStatement` | `customerId: string` | — | `unknown` | see Call graph | +| `natsAdjust` | `payload: { + customerId: string; + cents: number; + reason: string; + agent: string; + kind?: string; +}` | — | `unknown` | see Call graph | +| `natsPublish` | `subject: string, payload: unknown` | — | `unknown` | see Call graph | + +## What it imports / requires + +_No imports detected._ + +## Call graph (identifiers invoked) + +`unset`, `nc`, `async`, `import`, `connect`, `split`, `encode`, `stringify`, `decode`, `parse`, `natsStatement`, `request`, `natsAdjust`, `natsPublish`, `then`, `publish` + +Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. + +## Return values (how to read this) + +- HTTP route handlers return Express `res.json(...)` bodies (see route docs). +- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches). +- Pricing functions return integer **cents** on `Quote.totalCents`. + diff --git a/docs/sphinx/modules/zappier/billing-nats.rst b/docs/sphinx/modules/zappier/billing-nats.rst new file mode 100644 index 0000000..9fa5588 --- /dev/null +++ b/docs/sphinx/modules/zappier/billing-nats.rst @@ -0,0 +1,7 @@ +zappier.billing-nats +==================== + +Generated API sheet for ``zappier/billing-nats``. + +.. include:: billing-nats.md + :parser: myst_parser.sphinx_ diff --git a/docs/sphinx/modules/zappier/credits.md b/docs/sphinx/modules/zappier/credits.md new file mode 100644 index 0000000..6a1b053 --- /dev/null +++ b/docs/sphinx/modules/zappier/credits.md @@ -0,0 +1,48 @@ +# `zappier/credits` + +**Package:** `zappier` +**Source:** `packages/zappier/src/credits.ts` +**Lines:** 30 + +## What this module is + +Implementation module in `zappier`. The tables below are extracted from the source (signatures + JSDoc). + +## Exports + +`CreditAdjustment`, `CreditLedger` + +## Types / interfaces / classes + +| Kind | Name | +|------|------| +| interface | `CreditAdjustment` | +| class | `CreditLedger` | + +## Functions + +_No top-level functions extracted._ + +## Methods (class / object) + +| Name | Parameters | +|------|------------| +| `add` | `row: Omit & { id?: string; at?: string }` | +| `list` | `customerId?: string` | + +## What it imports / requires + +_No imports detected._ + +## Call graph (identifiers invoked) + +`add`, `now`, `toString`, `toISOString`, `unshift`, `list`, `filter` + +Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. + +## Return values (how to read this) + +- HTTP route handlers return Express `res.json(...)` bodies (see route docs). +- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches). +- Pricing functions return integer **cents** on `Quote.totalCents`. + diff --git a/docs/sphinx/modules/zappier/credits.rst b/docs/sphinx/modules/zappier/credits.rst new file mode 100644 index 0000000..5ee2931 --- /dev/null +++ b/docs/sphinx/modules/zappier/credits.rst @@ -0,0 +1,7 @@ +zappier.credits +=============== + +Generated API sheet for ``zappier/credits``. + +.. include:: credits.md + :parser: myst_parser.sphinx_ diff --git a/docs/sphinx/modules/zappier/meter.md b/docs/sphinx/modules/zappier/meter.md index 887c0ac..4cc4b79 100644 --- a/docs/sphinx/modules/zappier/meter.md +++ b/docs/sphinx/modules/zappier/meter.md @@ -2,7 +2,7 @@ **Package:** `zappier` **Source:** `packages/zappier/src/meter.ts` -**Lines:** 49 +**Lines:** 52 ## What this module is @@ -22,7 +22,8 @@ _None extracted._ |------|------------|---------------------|---------|-----------------------------| | `meter` | `endpointId: string, repo: UsageRepo, - pricing: PricingContext,` | — | `unknown` | see Call graph | + pricing: PricingContext, + onRecord?: (entry: { customerId: string; endpointId: string; cents: number }` | — | `unknown` | see Call graph | ## Methods (class / object) diff --git a/docs/sphinx/modules/zappier/nats-shim.d.md b/docs/sphinx/modules/zappier/nats-shim.d.md new file mode 100644 index 0000000..7ea1522 --- /dev/null +++ b/docs/sphinx/modules/zappier/nats-shim.d.md @@ -0,0 +1,41 @@ +# `zappier/nats-shim.d` + +**Package:** `zappier` +**Source:** `packages/zappier/src/nats-shim.d.ts` +**Lines:** 12 + +## What this module is + +Implementation module in `zappier`. The tables below are extracted from the source (signatures + JSDoc). + +## Exports + +_No named `export` / `module.exports` keys detected._ + +## Types / interfaces / classes + +_None extracted._ + +## Functions + +| Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) | +|------|------------|---------------------|---------|-----------------------------| +| `connect` | `opts: unknown` | — | `unknown` | see Call graph | +| `StringCodec` | `(none)` | — | `unknown` | see Call graph | + +## What it imports / requires + +_No imports detected._ + +## Call graph (identifiers invoked) + +`connect`, `request`, `publish`, `close`, `encode`, `decode` + +Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. + +## Return values (how to read this) + +- HTTP route handlers return Express `res.json(...)` bodies (see route docs). +- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches). +- Pricing functions return integer **cents** on `Quote.totalCents`. + diff --git a/docs/sphinx/modules/zappier/nats-shim.d.rst b/docs/sphinx/modules/zappier/nats-shim.d.rst new file mode 100644 index 0000000..8cb736a --- /dev/null +++ b/docs/sphinx/modules/zappier/nats-shim.d.rst @@ -0,0 +1,7 @@ +zappier.nats-shim.d +=================== + +Generated API sheet for ``zappier/nats-shim.d``. + +.. include:: nats-shim.d.md + :parser: myst_parser.sphinx_ diff --git a/docs/sphinx/modules/zappier/portal.md b/docs/sphinx/modules/zappier/portal.md index 3067f61..3a10365 100644 --- a/docs/sphinx/modules/zappier/portal.md +++ b/docs/sphinx/modules/zappier/portal.md @@ -2,7 +2,7 @@ **Package:** `zappier` **Source:** `packages/zappier/src/portal.ts` -**Lines:** 285 +**Lines:** 321 ## What this module is @@ -48,10 +48,13 @@ Implementation module in `zappier`. The tables below are extracted from the sour - `./invoicing` - `./pricing` - `./usage` +- `./credits` +- `./statement` +- `./billing-nats` ## Call graph (identifiers invoked) -`now`, `reload`, `customer`, `publicProfile`, `sessionAuth`, `header`, `startsWith`, `slice`, `get`, `list`, `find`, `status`, `json`, `next`, `save`, `portalRouter`, `post`, `trim`, `test`, `hashPassword`, `findByEmail`, `randomBytes`, `toString`, `create`, `verifyPassword`, `verifyTotp`, `use`, `delete`, `setUTCDate`, `setUTCHours`, `summaryFor`, `tiers`, `applyMonthlyCredit`, `rateCard`, `type`, `send`, `renderInvoiceHtml`, `async`, `generateTotpSecret`, `totpUri`, `qr`, `isInteger`, `put` +`now`, `reload`, `customer`, `publicProfile`, `sessionAuth`, `header`, `startsWith`, `slice`, `get`, `list`, `find`, `status`, `json`, `next`, `save`, `portalRouter`, `post`, `trim`, `test`, `hashPassword`, `findByEmail`, `randomBytes`, `toString`, `create`, `verifyPassword`, `verifyTotp`, `use`, `delete`, `setUTCDate`, `setUTCHours`, `summaryFor`, `tiers`, `applyMonthlyCredit`, `rateCard`, `async`, `natsStatement`, `composeStatement`, `listFor`, `type`, `send`, `renderInvoiceHtml`, `generateTotpSecret`, `totpUri`, `qr`, `isInteger`, `natsPublish`, `natsAdjust`, `put` Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. diff --git a/docs/sphinx/modules/zappier/statement.md b/docs/sphinx/modules/zappier/statement.md new file mode 100644 index 0000000..c0709a5 --- /dev/null +++ b/docs/sphinx/modules/zappier/statement.md @@ -0,0 +1,48 @@ +# `zappier/statement` + +**Package:** `zappier` +**Source:** `packages/zappier/src/statement.ts` +**Lines:** 39 + +## What this module is + +Implementation module in `zappier`. The tables below are extracted from the source (signatures + JSDoc). + +## Exports + +`composeStatement` + +## Types / interfaces / classes + +_None extracted._ + +## Functions + +| Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) | +|------|------------|---------------------|---------|-----------------------------| +| `composeStatement` | `args: { + customerId: string; + prepaidCents: number; + credits: CreditAdjustment[]; + usage: UsageEntry[]; + invoices: Invoice[]; +}` | — | `unknown` | see Call graph | + +## What it imports / requires + +- `./credits` +- `./invoicing` +- `./usage` + +## Call graph (identifiers invoked) + +`composeStatement`, `filter`, `map`, `now`, `toISOString`, `slice`, `reverse` + +Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. + +## Return values (how to read this) + +- HTTP route handlers return Express `res.json(...)` bodies (see route docs). +- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches). +- Pricing functions return integer **cents** on `Quote.totalCents`. + diff --git a/docs/sphinx/modules/zappier/statement.rst b/docs/sphinx/modules/zappier/statement.rst new file mode 100644 index 0000000..b15ab4a --- /dev/null +++ b/docs/sphinx/modules/zappier/statement.rst @@ -0,0 +1,7 @@ +zappier.statement +================= + +Generated API sheet for ``zappier/statement``. + +.. include:: statement.md + :parser: myst_parser.sphinx_ diff --git a/docs/sphinx/modules/zappier/upstream.md b/docs/sphinx/modules/zappier/upstream.md new file mode 100644 index 0000000..00a5a19 --- /dev/null +++ b/docs/sphinx/modules/zappier/upstream.md @@ -0,0 +1,45 @@ +# `zappier/upstream` + +**Package:** `zappier` +**Source:** `packages/zappier/src/upstream.ts` +**Lines:** 46 + +## What this module is + +Implementation module in `zappier`. The tables below are extracted from the source (signatures + JSDoc). + +## Exports + +`UpstreamFetch`, `proxyVerae` + +## Types / interfaces / classes + +| Kind | Name | +|------|------| +| type | `UpstreamFetch` | + +## Functions + +| Name | Parameters | Param types (JSDoc) | Returns | Calls (same file / helpers) | +|------|------------|---------------------|---------|-----------------------------| +| `proxyVerae` | `req: Request, + res: Response, + pathname: string, + fetchImpl: UpstreamFetch = fetch,` | — | `unknown` | see Call graph | + +## What it imports / requires + +- `express` + +## Call graph (identifiers invoked) + +`proxyVerae`, `replace`, `entries`, `set`, `header`, `toUpperCase`, `stringify`, `fetchImpl`, `toString`, `text`, `parse`, `isArray`, `status`, `json` + +Each identifier is a call site in this file. Follow the import list to see the defining module; open that module’s MD for parameter and return types. + +## Return values (how to read this) + +- HTTP route handlers return Express `res.json(...)` bodies (see route docs). +- Zapier `perform` functions return a **single object** (creates) or an **array** (triggers/searches). +- Pricing functions return integer **cents** on `Quote.totalCents`. + diff --git a/docs/sphinx/modules/zappier/upstream.rst b/docs/sphinx/modules/zappier/upstream.rst new file mode 100644 index 0000000..54a7bb5 --- /dev/null +++ b/docs/sphinx/modules/zappier/upstream.rst @@ -0,0 +1,7 @@ +zappier.upstream +================ + +Generated API sheet for ``zappier/upstream``. + +.. include:: upstream.md + :parser: myst_parser.sphinx_ diff --git a/docs/sphinx/repos/overview.md b/docs/sphinx/repos/overview.md index 7488ca7..0f53d3f 100644 --- a/docs/sphinx/repos/overview.md +++ b/docs/sphinx/repos/overview.md @@ -3,6 +3,7 @@ High-level description of the whole system: what it is, which **independent git repositories** implement it, how it stays up, how it behaves when the local network fails, and how it talks to the **central Verae NATS.IO 3-server cluster**. New work is added by **new address routing**, not by teaching Zapier about NATS. **This repo:** https://git.georgelambert.org/marchon/overview +**Bring online:** https://zapier.georgelambert.org/packages/verae-ops/GETTING-STARTED.pdf **Live catalog (PDF by default):** https://zapier.georgelambert.org/ · [Markdown indexes](https://zapier.georgelambert.org/index-md.html) **Clone:** `ssh://git@git.georgelambert.org:2223/marchon/overview.git` diff --git a/docs/sphinx/repos/verae-ops.md b/docs/sphinx/repos/verae-ops.md index ad9715f..d4895d2 100644 --- a/docs/sphinx/repos/verae-ops.md +++ b/docs/sphinx/repos/verae-ops.md @@ -11,6 +11,7 @@ This is the operations repo. Application code lives in the other independent git ## Reading order +0. **[GETTING-STARTED.md](GETTING-STARTED.md)** — install, first-up, monitor (read this first) 1. [01-dependencies.md](01-dependencies.md) — what must exist, who talks to whom 2. [02-docker.md](02-docker.md) — Compose (NATS 3-node cluster + HTTPS edges) 3. [03-proxmox.md](03-proxmox.md) — LXC / QEMU VMs diff --git a/docs/sphinx/repos/zappier-account-balance.md b/docs/sphinx/repos/zappier-account-balance.md new file mode 100644 index 0000000..0c261d6 --- /dev/null +++ b/docs/sphinx/repos/zappier-account-balance.md @@ -0,0 +1,31 @@ +# zappier-account-balance + +Source of truth for **customer prepaid balances**. Internal traffic is **NATS request-reply**; HTTP is health plus a fallback. + +**Forgejo:** https://git.georgelambert.org/marchon/zappier-account-balance +**Catalog:** https://zapier.georgelambert.org/packages/zappier-account-balance/README.pdf + +Zapier cloud never connects here. zappier-edge, customer-service, sales-pricing, and accounting-export do. + +## Subjects + +| Subject | Kind | Who | +|---------|------|-----| +| `verae.billing.statement.get` | request-reply | portal, CS, sales, admin | +| `verae.billing.balance.get` | request-reply | anyone internal | +| `verae.billing.balance.adjust` | request-reply | CS credits, portal reload | +| `verae.billing.usage.recorded` | pub | zappier-edge meter | +| `verae.billing.payment.recorded` | pub | portal reload, invoice paid | +| `verae.billing.credit.applied` | pub | CS | + +Queue group: `account-balance`. + +## Run + +```bash +NATS_URL=nats://127.0.0.1:4222 PORT=3010 npm start +curl http://127.0.0.1:3010/health +curl http://127.0.0.1:3010/statement/cust_1 +``` + +Statement JSON: `{ customerId, prepaidCents, credits[], usage[], payments[] }`. diff --git a/docs/sphinx/repos/zappier-accounting-export.md b/docs/sphinx/repos/zappier-accounting-export.md new file mode 100644 index 0000000..7a4efe1 --- /dev/null +++ b/docs/sphinx/repos/zappier-accounting-export.md @@ -0,0 +1,21 @@ +# zappier-accounting-export + +Accounting-department export of **invoices, billing, and payment history** to **QuickBooks IIF** and generic CSV (also usable in other ledgers). + +**Forgejo:** https://git.georgelambert.org/marchon/zappier-accounting-export +**Catalog:** https://zapier.georgelambert.org/packages/zappier-accounting-export/README.pdf + +Reads issued/paid invoices from zappier-edge (`ZAPPIER_ADMIN_URL`). Customer statements (credits, balances, usage, payments) come from NATS `verae.billing.statement.get`. + +Staff UI: http://127.0.0.1:3013/ + +```bash +PORT=3013 NATS_URL=nats://127.0.0.1:4222 node src/server.js +curl http://127.0.0.1:3013/review/cust_1 +curl 'http://127.0.0.1:3013/export/quickbooks.iif?period=2026-07' -o zappier.iif +curl 'http://127.0.0.1:3013/export/accounting.csv?period=2026-07' -o zappier.csv +``` + +Import the IIF in QuickBooks Desktop: **File → Utilities → Import → IIF**. The CSV is a flat invoice register for other systems. + +History is the invoice list on zappier-edge (draft / issued / paid) plus CS credit ledger and portal prepaid reloads. diff --git a/docs/sphinx/repos/zappier-customer-service.md b/docs/sphinx/repos/zappier-customer-service.md new file mode 100644 index 0000000..99d9f1f --- /dev/null +++ b/docs/sphinx/repos/zappier-customer-service.md @@ -0,0 +1,23 @@ +# zappier-customer-service + +Department API for **customer-service credit additions** and **review** of credits, balances, usage, and payments. Internally uses **NATS** `verae.billing.*` (account-balance). HTTP fallback to account-balance then zappier-edge. + +**Forgejo:** https://git.georgelambert.org/marchon/zappier-customer-service +**Catalog:** https://zapier.georgelambert.org/packages/zappier-customer-service/README.pdf +**Plugs into:** zappier-edge admin (`x-admin-key`) at `ZAPPIER_ADMIN_URL` (default `http://127.0.0.1:3000`). + +```bash +PORT=3011 ZAPPIER_ADMIN_URL=http://127.0.0.1:3000 node src/server.js +curl -X POST http://127.0.0.1:3011/credits -H 'content-type: application/json' \ + -d '{"customerId":"cust_1","cents":500,"reason":"goodwill","agent":"cs-anna"}' +``` + +| Route | Job | +|-------|-----| +| `GET /` | Staff review UI | +| `GET /health` | `{ ok, role }` | +| `POST /credits` | Add/subtract prepaid cents (NATS `balance.adjust`) | +| `GET /credits?customerId=` | Ledger | +| `GET /review/:customerId` | Full statement (balance, credits, usage, payments) | + +Talks to NATS `verae.billing.*` internally. Never talks to Zapier cloud. diff --git a/docs/sphinx/repos/zappier-sales-pricing.md b/docs/sphinx/repos/zappier-sales-pricing.md new file mode 100644 index 0000000..b3092e8 --- /dev/null +++ b/docs/sphinx/repos/zappier-sales-pricing.md @@ -0,0 +1,19 @@ +# zappier-sales-pricing + +Sales-department API for **per-customer pricing** (tier + `multiplierOverride`). zappier-edge remains the rate-card store. + +**Forgejo:** https://git.georgelambert.org/marchon/zappier-sales-pricing +**Catalog:** https://zapier.georgelambert.org/packages/zappier-sales-pricing/README.pdf + +Internal reviews use NATS `verae.billing.statement.get` (account-balance). + +Staff UI: http://127.0.0.1:3012/ — review credits, balances, usage, payments. + +```bash +PORT=3012 NATS_URL=nats://127.0.0.1:4222 node src/server.js +curl http://127.0.0.1:3012/review/cust_2 +curl http://127.0.0.1:3012/quotes/cust_2 +curl -X PUT http://127.0.0.1:3012/customers/cust_2/pricing \ + -H 'content-type: application/json' \ + -d '{"multiplierOverride":0.4,"tierId":"business"}' +``` diff --git a/docs/sphinx/repos/zappier.md b/docs/sphinx/repos/zappier.md index 8d123a4..a9f0763 100644 --- a/docs/sphinx/repos/zappier.md +++ b/docs/sphinx/repos/zappier.md @@ -5,8 +5,11 @@ monthly credits, a usage ledger, Stripe metered billing, purchase-order invoicing, a company admin console, a self-service customer portal, and a Zapier integration. +This is the **billing and user platform** (originally the standalone `zappier` git tree at `~/zappier`). Forgejo name: **zappier-edge**. It owns signup, TOTP, API keys, rate card, Stripe meter, PO invoices, admin users, and the customer portal. Verae middleware does not replace this. + **Forgejo:** https://git.georgelambert.org/marchon/zappier-edge -**Catalog README:** https://zapier.georgelambert.org/packages/zappier/README.pdf +**Catalog README:** https://zapier.georgelambert.org/packages/zappier/README.pdf +**Bring the whole system online:** https://zapier.georgelambert.org/packages/verae-ops/GETTING-STARTED.pdf ## Documentation (public PDFs) diff --git a/packages/docs-master/modules-and-nats.md b/packages/docs-master/modules-and-nats.md index 850fc28..d0d14a9 100644 --- a/packages/docs-master/modules-and-nats.md +++ b/packages/docs-master/modules-and-nats.md @@ -2,14 +2,19 @@ Master map of every service in the Zapier ↔ middleware ↔ chain ↔ WORM-archive path. -Zapier cloud **never** connects to NATS. Only middleware workers and archives do. +Zapier cloud **never** connects to NATS. Internal services do: middleware workers, archives, account-balance, zappier-edge billing, CS, sales, and accounting. ## Who talks to whom (HTTPS vs NATS) ```text [Zapier Platform app] HTTPS [zappier-edge] HTTPS [middleware-http] - | - NATS JetStream (NS1 loopback :4222) | + | | NATS verae.billing.* | + | v | +[customer portal] [account-balance] NATS JetStream +[CS :3011] queue account-balance (NS1 loopback :4222) +[sales :3012] ^ | +[accounting :3013] | request-reply | + +-------------------------------+ +------------------+------------------+-------------+ | | | [job-poller] [webhook-deliver] [archive-aggregator] @@ -25,7 +30,11 @@ Zapier cloud **never** connects to NATS. Only middleware workers and archives do | Module | Repo (Forgejo) | Runtime | Listens | Sends | |--------|----------------|---------|---------|-------| | zapier-platform-app | `verae-zapier-app` | Zapier cloud | User Zap steps | HTTPS to zappier-edge | -| zappier-edge | `zappier-edge` | Public HTTPS :3000 | Zapier, portal, admin | HTTPS to middleware | +| zappier-edge | `zappier-edge` | Public HTTPS :3000 | Zapier, portal, admin | HTTPS to middleware; NATS billing pub | +| account-balance | `zappier-account-balance` | :3010 + NATS | `verae.billing.*` | statement/adjust replies | +| customer-service | `zappier-customer-service` | :3011 | CS staff HTTP | NATS balance.adjust / statement.get | +| sales-pricing | `zappier-sales-pricing` | :3012 | Sales HTTP | NATS statement.get; HTTPS to edge for multipliers | +| accounting-export | `zappier-accounting-export` | :3013 | Accounting HTTP | reads invoices; NATS statement | | middleware-http | `verae-middleware` | Public HTTPS :3100 | zappier-edge | NATS jobs.watch; HTTP to chain; wait on jobs.events | | job-poller | `verae-job-poller` | Worker | `verae.zapier.jobs.watch` | `verae.zapier.jobs.events`; HTTP GET chain status | | webhook-deliver | `verae-webhook-deliver` | Worker | `verae.zapier.webhooks.deliver` + events | HTTPS POST Zapier REST Hook | @@ -49,6 +58,11 @@ Zapier cloud **never** connects to NATS. Only middleware workers and archives do | `verae.zapier.jobs.events` | JetStream events | `event` (`timestamp.completed\|failed\|timeout`), `tenantId`, `jobId`, `status`, `traceId`, `emittedAt` | | `verae.zapier.webhooks.deliver` | JetStream work queue | `hookId`, `tenantId`, `targetUrl`, `event`, `payload`, `attempt`, `traceId` | | `verae.zapier.usage` | optional | `tenantId`, `action`, `amount`, `at` | +| `verae.billing.statement.get` | request-reply | `customerId` → prepaid, credits, usage, payments | +| `verae.billing.balance.adjust` | request-reply | `customerId`, `cents`, `reason`, `agent` | +| `verae.billing.usage.recorded` | pub | meter events from zappier-edge | +| `verae.billing.payment.recorded` | pub | portal reload / invoice paid | +| `verae.billing.credit.applied` | pub | CS goodwill | | `verae.archive.put` | JetStream | `sha256`, `tenantId`, `kind` (`publicMeta\|privateMeta\|file\|tree`), `record`, `traceId` | | `verae.archive.query` | pub to **all** archives | `correlationId`, `sha256`, `tenantId`, `kinds[]`, `traceId` | | `verae.archive.reply.` | replies | `archiveId`, `sha256`, `records[]`, `traceId` | diff --git a/packages/docs-master/modules/zappier-account-balance/NATS.md b/packages/docs-master/modules/zappier-account-balance/NATS.md new file mode 100644 index 0000000..04ef95f --- /dev/null +++ b/packages/docs-master/modules/zappier-account-balance/NATS.md @@ -0,0 +1,12 @@ +# NATS — zappier-account-balance + +Queue group `account-balance`. This process is the source of truth for prepaid cents. + +| Direction | Address | Kind | Body | +|-----------|---------|------|------| +| IN | `verae.billing.statement.get` | request-reply | `{ customerId }` → statement | +| IN | `verae.billing.balance.get` | request-reply | `{ customerId }` → statement | +| IN | `verae.billing.balance.adjust` | request-reply | `{ customerId, cents, reason, agent, kind }` | +| IN | `verae.billing.usage.recorded` | pub | `{ customerId, endpointId, cents, at }` (debits prepaid) | + +`payment.recorded` and `credit.applied` are fan-out events from publishers; prepaid mutations go through `balance.adjust`. diff --git a/packages/docs-master/modules/zappier-account-balance/SUMMARY.md b/packages/docs-master/modules/zappier-account-balance/SUMMARY.md new file mode 100644 index 0000000..eb73f99 --- /dev/null +++ b/packages/docs-master/modules/zappier-account-balance/SUMMARY.md @@ -0,0 +1,3 @@ +# zappier-account-balance + +NATS source of truth for prepaid balances, credits, usage events, and payments. diff --git a/packages/docs-master/modules/zappier-accounting-export/NATS.md b/packages/docs-master/modules/zappier-accounting-export/NATS.md index 2cac4e8..e19b513 100644 --- a/packages/docs-master/modules/zappier-accounting-export/NATS.md +++ b/packages/docs-master/modules/zappier-accounting-export/NATS.md @@ -1,3 +1,7 @@ -# zappier-accounting-export NATS +# NATS — zappier-accounting-export -None. +| Direction | Address | Kind | +|-----------|---------|------| +| OUT | `verae.billing.statement.get` | request-reply (review) | + +IIF/CSV export remains HTTPS to zappier-edge admin. diff --git a/packages/docs-master/modules/zappier-accounting-export/SUMMARY.md b/packages/docs-master/modules/zappier-accounting-export/SUMMARY.md index 813f130..78be87b 100644 --- a/packages/docs-master/modules/zappier-accounting-export/SUMMARY.md +++ b/packages/docs-master/modules/zappier-accounting-export/SUMMARY.md @@ -1,3 +1,5 @@ # zappier-accounting-export -Exports zappier invoices to QuickBooks IIF and accounting CSV. +Accounting export of invoices to QuickBooks IIF and CSV, plus **review** of customer credits, balances, usage, and payments over NATS. + +Forgejo: https://git.georgelambert.org/marchon/zappier-accounting-export diff --git a/packages/docs-master/modules/zappier-customer-service/NATS.md b/packages/docs-master/modules/zappier-customer-service/NATS.md index 608faa3..14ded3b 100644 --- a/packages/docs-master/modules/zappier-customer-service/NATS.md +++ b/packages/docs-master/modules/zappier-customer-service/NATS.md @@ -1,3 +1,8 @@ -# zappier-customer-service NATS +# NATS — zappier-customer-service -None. HTTPS only to zappier-edge admin. +| Direction | Address | Kind | +|-----------|---------|------| +| OUT | `verae.billing.statement.get` | request-reply (review) | +| OUT | `verae.billing.balance.adjust` | request-reply (credits) | + +HTTP fallback: `ACCOUNT_BALANCE_URL` then zappier-edge admin. Zapier cloud never connects here. diff --git a/packages/docs-master/modules/zappier-customer-service/SUMMARY.md b/packages/docs-master/modules/zappier-customer-service/SUMMARY.md index 5d39fef..2a271cc 100644 --- a/packages/docs-master/modules/zappier-customer-service/SUMMARY.md +++ b/packages/docs-master/modules/zappier-customer-service/SUMMARY.md @@ -1,3 +1,7 @@ # zappier-customer-service -CS department API for prepaid credit additions. Writes `balanceCents` on zappier-edge. +CS department API and UI for prepaid credit additions and **review** of credits, balances, usage, and payments. + +Internal path: NATS `verae.billing.statement.get` / `verae.billing.balance.adjust` → account-balance. HTTP fallback to `:3010` then zappier-edge admin. + +Forgejo: https://git.georgelambert.org/marchon/zappier-customer-service diff --git a/packages/docs-master/modules/zappier-sales-pricing/NATS.md b/packages/docs-master/modules/zappier-sales-pricing/NATS.md index 2d71da6..e8f079e 100644 --- a/packages/docs-master/modules/zappier-sales-pricing/NATS.md +++ b/packages/docs-master/modules/zappier-sales-pricing/NATS.md @@ -1,3 +1,7 @@ -# zappier-sales-pricing NATS +# NATS — zappier-sales-pricing -None. +| Direction | Address | Kind | +|-----------|---------|------| +| OUT | `verae.billing.statement.get` | request-reply (review) | + +Pricing writes remain HTTPS to zappier-edge admin. diff --git a/packages/docs-master/modules/zappier-sales-pricing/SUMMARY.md b/packages/docs-master/modules/zappier-sales-pricing/SUMMARY.md index 6c46037..3efe8fd 100644 --- a/packages/docs-master/modules/zappier-sales-pricing/SUMMARY.md +++ b/packages/docs-master/modules/zappier-sales-pricing/SUMMARY.md @@ -1,3 +1,5 @@ # zappier-sales-pricing -Sales department per-customer tier and multiplier. Writes through zappier-edge `/admin/api/customers`. +Sales department API and UI for per-customer tier/multiplier and **review** of the same NATS statement as CS and the customer portal. + +Forgejo: https://git.georgelambert.org/marchon/zappier-sales-pricing diff --git a/packages/docs-master/modules/zappier/NATS.md b/packages/docs-master/modules/zappier/NATS.md index 81f603f..0573c73 100644 --- a/packages/docs-master/modules/zappier/NATS.md +++ b/packages/docs-master/modules/zappier/NATS.md @@ -1,10 +1,15 @@ # NATS — zappier-edge -This process **does not subscribe or publish** on NATS. +Zapier cloud still never connects to NATS. The edge process **does** publish and request on the private billing bus. | Direction | Address | Peer | Body | |-----------|---------|------|------| | IN HTTPS | `/v1/*` | Zapier Platform app | JSON + `x-api-key` | | OUT HTTPS | middleware `/zapier/v1/*` | verae-middleware | same tenant request | +| OUT NATS | `verae.billing.usage.recorded` | account-balance | meter event | +| OUT NATS | `verae.billing.payment.recorded` | account-balance / others | portal reload | +| OUT NATS | `verae.billing.credit.applied` | account-balance / others | admin/CS credit | +| OUT NATS | `verae.billing.statement.get` | account-balance | portal/admin review | +| OUT NATS | `verae.billing.balance.adjust` | account-balance | prepaid mutation | -NATS is private to middleware workers and archives. +Jobs, archive, and webhooks stay on middleware workers. diff --git a/packages/overview/01-system.md b/packages/overview/01-system.md index 035063a..35089e8 100644 --- a/packages/overview/01-system.md +++ b/packages/overview/01-system.md @@ -11,8 +11,9 @@ Verae Time proves a SHA-256 existed at a given time. Zapier lets customers regis | Plane | What | Network | |-------|------|---------| | Zapier cloud | `verae-zapier-app`, `verae-activate` | HTTPS to zappier-edge | -| Commercial edge | `zappier-edge` portal, admin, `x-api-key`, Stripe meter | Public HTTPS | -| CS / sales / accounting | `zappier-customer-service`, `zappier-sales-pricing`, `zappier-accounting-export` | Loopback/private HTTP into zappier-edge admin | +| Commercial edge | `zappier-edge` portal, admin, `x-api-key`, Stripe meter | Public HTTPS; NATS billing pubs/requests | +| Account balance | `zappier-account-balance` prepaid SoT | NATS `verae.billing.*` + HTTP `:3010` | +| CS / sales / accounting | `zappier-customer-service`, `zappier-sales-pricing`, `zappier-accounting-export` | Private HTTP; NATS statement/adjust | | Middleware HTTP | `/zapier/v1/*` job id + wait | Public HTTPS from edge only | | NATS cluster | JetStream subjects under `verae.*` | Private; loopback or SSH tunnel | | Workers | poller, webhook-deliver, aggregator | NATS + HTTPS to chain or Zapier hooks | @@ -22,6 +23,6 @@ Verae Time proves a SHA-256 existed at a given time. Zapier lets customers regis ## Request in one sentence -A Zap step POSTs to zappier-edge (`x-api-key`); edge meters the call and, when `ZAPPIER_UPSTREAM` is set, forwards timestamp/receipt/hash to middleware `/zapier/v1`. Middleware splits hash vs files, writes the hash (or Merkle root) to the chain, publishes `verae.zapier.jobs.watch`, and either returns `jobId` or waits on `verae.zapier.jobs.events`. Customer-service credits, sales per-customer multipliers, invoices, payment history, and QuickBooks export sit on zappier-edge (department APIs on :3011–:3013). Attached metadata and bulk-summary **leaves** are found later by broadcasting `verae.archive.query` to every archive/tree node. +A Zap step POSTs to zappier-edge (`x-api-key`); edge meters the call and, when `ZAPPIER_UPSTREAM` is set, forwards timestamp/receipt/hash to middleware `/zapier/v1`. Middleware splits hash vs files, writes the hash (or Merkle root) to the chain, publishes `verae.zapier.jobs.watch`, and either returns `jobId` or waits on `verae.zapier.jobs.events`. Meter events, CS credits, and portal reloads go over NATS `verae.billing.*` to **account-balance** (source of truth). Customers review the same statement in the portal; CS (`:3011`) and sales (`:3012`) review it on their department UIs. Attached metadata and bulk-summary **leaves** are found later by broadcasting `verae.archive.query` to every archive/tree node. See [diagrams](08-diagrams.md). diff --git a/packages/overview/02-modules-and-repos.md b/packages/overview/02-modules-and-repos.md index 0074c7d..4be3f26 100644 --- a/packages/overview/02-modules-and-repos.md +++ b/packages/overview/02-modules-and-repos.md @@ -8,6 +8,7 @@ Each runtime piece is its **own git repo** on Forgejo (`git.georgelambert.org`, | **verae-nats-process** | `packages/verae-nats-process` | **Template** for a new addressed NATS process | | **master-zapier-plan-draft** | workspace root | Combined snapshot (`main` and `master`) | | **zappier-edge** | `packages/zappier` | Metered HTTPS, portal, admin, Stripe; **proxies Verae calls to middleware** | +| **zappier-account-balance** | `packages/zappier-account-balance` | NATS source of truth for prepaid balances | | **zappier-customer-service** | `packages/zappier-customer-service` | CS goodwill credits onto prepaid balances | | **zappier-sales-pricing** | `packages/zappier-sales-pricing` | Sales per-customer tier / multiplier | | **zappier-accounting-export** | `packages/zappier-accounting-export` | QuickBooks IIF + accounting CSV | diff --git a/packages/overview/03-nats-cluster.md b/packages/overview/03-nats-cluster.md index 244e5da..fc3042b 100644 --- a/packages/overview/03-nats-cluster.md +++ b/packages/overview/03-nats-cluster.md @@ -4,7 +4,7 @@ All durable messaging for this product is **NATS JetStream**, not Zapier queues ## Cluster -Three `nats-server -js` nodes form the Verae cluster (routes between them, JetStream replication). Clients (middleware, poller, aggregator, WORM, tree nodes, and any new process cloned from `verae-nats-process`) connect with a **cluster URL list**, for example: +Three `nats-server -js` nodes form the Verae cluster (routes between them, JetStream replication). Clients (middleware, poller, aggregator, WORM, tree nodes, account-balance, zappier-edge billing, CS/sales/accounting, and any new process cloned from `verae-nats-process`) connect with a **cluster URL list**, for example: ```text nats://127.0.0.1:4222,nats://127.0.0.1:4223,nats://127.0.0.1:4224 @@ -20,7 +20,7 @@ Today’s NS1 box (`NS1.GEORGELAMBERT.ORG`, `70.88.205.138`) already runs JetStr | Allowed | Forbidden | |---------|-----------| -| verae-middleware, fleet workers, WORM, tree nodes, `verae-nats-process` clones | Zapier cloud, browsers, zappier-edge | +| verae-middleware, fleet workers, WORM, tree nodes, account-balance, zappier-edge billing, CS/sales/accounting, `verae-nats-process` clones | Zapier cloud, customer browsers (they use HTTPS portal/CS/sales UIs) | ## Address families already in use @@ -30,6 +30,9 @@ Today’s NS1 box (`NS1.GEORGELAMBERT.ORG`, `70.88.205.138`) already runs JetStr | `verae.zapier.jobs.events` | events | wait + webhooks | | `verae.zapier.webhooks.deliver` | work queue | HTTPS to Zapier REST Hook | | `verae.zapier.usage` | optional | metering | +| `verae.billing.statement.get` | request-reply | portal, CS, sales, admin | +| `verae.billing.balance.adjust` | request-reply | CS credits, portal reload | +| `verae.billing.usage.recorded` | pub | zappier-edge meter | | `verae.archive.put` | JetStream | splitter / merkle builder | | `verae.archive.query` | **broadcast** (no queue group) | every WORM and tree node | | `verae.archive.reply.` | replies | **only on bloom hit** | diff --git a/packages/overview/08-diagrams.md b/packages/overview/08-diagrams.md index 6b8ad8a..d78e79e 100644 --- a/packages/overview/08-diagrams.md +++ b/packages/overview/08-diagrams.md @@ -9,12 +9,15 @@ SVG files in [`diagrams/`](diagrams/). The same shapes are repeated below in mer ```mermaid flowchart LR Z[Zapier cloud apps] -->|HTTPS x-api-key| E[zappier-edge] - CS[customer-service] --> E - SA[sales-pricing] --> E - AC[accounting-export QuickBooks] --> E + Cust[customer portal] -->|HTTPS statement| E + CS[customer-service] -->|NATS statement/adjust| N[NATS 3-node cluster] + SA[sales-pricing] -->|NATS statement| N + AC[accounting-export] -->|NATS statement| N + E -->|NATS billing| N + BAL[account-balance] -->|reply verae.billing.*| N E -->|HTTPS metered| M[verae-middleware] M -->|HTTPS| C[Verae chain] - M -->|JetStream| N[NATS 3-node cluster] + M -->|JetStream| N N --> P[job-poller] N --> W[webhook-deliver] N --> A[archive-aggregator] diff --git a/packages/verae-fleet/fleet.json b/packages/verae-fleet/fleet.json index 8dd4c7e..942284c 100644 --- a/packages/verae-fleet/fleet.json +++ b/packages/verae-fleet/fleet.json @@ -15,9 +15,10 @@ "services": { "nats": { "min": 1, "max": 1, "keepFloor": false, "enabled": true, "managed": false }, "zappier-edge": { "min": 1, "max": 1, "keepFloor": true, "enabled": true }, - "customer-service": { "min": 0, "max": 1, "keepFloor": false, "enabled": true }, - "sales-pricing": { "min": 0, "max": 1, "keepFloor": false, "enabled": true }, - "accounting-export": { "min": 0, "max": 1, "keepFloor": false, "enabled": true }, + "account-balance": { "min": 1, "max": 1, "keepFloor": true, "enabled": true }, + "customer-service": { "min": 1, "max": 1, "keepFloor": true, "enabled": true }, + "sales-pricing": { "min": 1, "max": 1, "keepFloor": true, "enabled": true }, + "accounting-export": { "min": 1, "max": 1, "keepFloor": true, "enabled": true }, "middleware-http": { "min": 1, "max": 1, "keepFloor": true, "enabled": true }, "job-poller": { "min": 1, "max": 2, "keepFloor": true, "enabled": true }, "webhook-deliver": { "min": 1, "max": 2, "keepFloor": true, "enabled": true }, diff --git a/packages/verae-fleet/services/account-balance.json b/packages/verae-fleet/services/account-balance.json new file mode 100644 index 0000000..d7f6532 --- /dev/null +++ b/packages/verae-fleet/services/account-balance.json @@ -0,0 +1,31 @@ +{ + "id": "account-balance", + "title": "Account balance (NATS billing SoT)", + "kind": "http", + "package": "zappier-account-balance", + "role": "account-balance", + "managed": true, + "runtime": "HTTP :3010 + NATS verae.billing.*", + "health": { "type": "http", "path": "/health", "timeoutMs": 2000 }, + "ports": { "healthBase": 3010 }, + "spawn": { + "cwd": "../zappier-account-balance", + "command": "node", + "args": ["src/server.js"] + }, + "env": { + "PORT": "3010", + "NATS_URL": "nats://127.0.0.1:4222" + }, + "nats": { + "in": [ + "verae.billing.statement.get", + "verae.billing.balance.get", + "verae.billing.balance.adjust", + "verae.billing.usage.recorded", + "verae.billing.payment.recorded", + "verae.billing.credit.applied" + ], + "out": [] + } +} diff --git a/packages/verae-fleet/services/accounting-export.json b/packages/verae-fleet/services/accounting-export.json index 7a01971..e24a5a5 100644 --- a/packages/verae-fleet/services/accounting-export.json +++ b/packages/verae-fleet/services/accounting-export.json @@ -15,7 +15,9 @@ }, "env": { "PORT": "3013", - "ZAPPIER_ADMIN_URL": "http://127.0.0.1:3000" + "ZAPPIER_ADMIN_URL": "http://127.0.0.1:3000", + "ACCOUNT_BALANCE_URL": "http://127.0.0.1:3010", + "NATS_URL": "nats://127.0.0.1:4222" }, - "nats": { "in": [], "out": [] } + "nats": { "in": [], "out": ["verae.billing.statement.get"] } } diff --git a/packages/verae-fleet/services/customer-service.json b/packages/verae-fleet/services/customer-service.json index 991a4a9..623f7ee 100644 --- a/packages/verae-fleet/services/customer-service.json +++ b/packages/verae-fleet/services/customer-service.json @@ -1,11 +1,11 @@ { "id": "customer-service", - "title": "Customer-service credits", + "title": "Customer-service credits and review", "kind": "http", "package": "zappier-customer-service", "role": "customer-service", "managed": true, - "runtime": "HTTP :3011 CS credits → zappier-edge", + "runtime": "HTTP :3011 CS credits + NATS statement review", "health": { "type": "http", "path": "/health", "timeoutMs": 2000 }, "ports": { "healthBase": 3011 }, "spawn": { @@ -15,8 +15,13 @@ }, "env": { "PORT": "3011", - "ZAPPIER_ADMIN_URL": "http://127.0.0.1:3000" + "ZAPPIER_ADMIN_URL": "http://127.0.0.1:3000", + "ACCOUNT_BALANCE_URL": "http://127.0.0.1:3010", + "NATS_URL": "nats://127.0.0.1:4222" }, - "nats": { "in": [], "out": [] }, - "notes": "Department API. Prepaid balances live on zappier-edge." + "nats": { + "in": [], + "out": ["verae.billing.statement.get", "verae.billing.balance.adjust"] + }, + "notes": "CS review and credits. Balances live on account-balance over NATS." } diff --git a/packages/verae-fleet/services/sales-pricing.json b/packages/verae-fleet/services/sales-pricing.json index 359cb18..1865ec7 100644 --- a/packages/verae-fleet/services/sales-pricing.json +++ b/packages/verae-fleet/services/sales-pricing.json @@ -5,7 +5,7 @@ "package": "zappier-sales-pricing", "role": "sales-pricing", "managed": true, - "runtime": "HTTP :3012 quotes → zappier-edge", + "runtime": "HTTP :3012 quotes + NATS statement review", "health": { "type": "http", "path": "/health", "timeoutMs": 2000 }, "ports": { "healthBase": 3012 }, "spawn": { @@ -15,7 +15,9 @@ }, "env": { "PORT": "3012", - "ZAPPIER_ADMIN_URL": "http://127.0.0.1:3000" + "ZAPPIER_ADMIN_URL": "http://127.0.0.1:3000", + "ACCOUNT_BALANCE_URL": "http://127.0.0.1:3010", + "NATS_URL": "nats://127.0.0.1:4222" }, - "nats": { "in": [], "out": [] } + "nats": { "in": [], "out": ["verae.billing.statement.get"] } } diff --git a/packages/verae-fleet/services/zappier-edge.json b/packages/verae-fleet/services/zappier-edge.json index 72d6381..b14b6da 100644 --- a/packages/verae-fleet/services/zappier-edge.json +++ b/packages/verae-fleet/services/zappier-edge.json @@ -18,8 +18,18 @@ "ZAPPIER_UPSTREAM": "http://127.0.0.1:3100", "CS_SERVICE_URL": "http://127.0.0.1:3011", "SALES_PRICING_URL": "http://127.0.0.1:3012", - "ACCOUNTING_EXPORT_URL": "http://127.0.0.1:3013" + "ACCOUNTING_EXPORT_URL": "http://127.0.0.1:3013", + "NATS_URL": "nats://127.0.0.1:4222" }, - "nats": { "in": [], "out": [] }, - "notes": "Zapier x-api-key lands here. HTTPS to middleware only." + "nats": { + "in": [], + "out": [ + "verae.billing.usage.recorded", + "verae.billing.payment.recorded", + "verae.billing.credit.applied", + "verae.billing.statement.get", + "verae.billing.balance.adjust" + ] + }, + "notes": "Zapier x-api-key lands here. HTTPS to middleware. NATS billing to account-balance." } diff --git a/packages/verae-fleet/test/fleet.test.js b/packages/verae-fleet/test/fleet.test.js index 8f343e1..79dbf00 100644 --- a/packages/verae-fleet/test/fleet.test.js +++ b/packages/verae-fleet/test/fleet.test.js @@ -53,6 +53,7 @@ describe('verae-fleet', () => { for (const need of [ 'nats', 'zappier-edge', + 'account-balance', 'customer-service', 'sales-pricing', 'accounting-export', diff --git a/packages/verae-ops/GETTING-STARTED.md b/packages/verae-ops/GETTING-STARTED.md index 5246f95..cb4de0f 100644 --- a/packages/verae-ops/GETTING-STARTED.md +++ b/packages/verae-ops/GETTING-STARTED.md @@ -11,7 +11,8 @@ This is the **top document** for install, first-up, and monitoring. Platform-spe | Piece | Job | Default | |-------|-----|---------| | **zappier-edge** | Billing, API keys, customer portal, admin, Stripe; meters then **proxies** to middleware | `:3000` public HTTPS | -| **customer-service** | CS credit additions | `:3011` | +| **account-balance** | NATS prepaid balances (SoT) | `:3010` | +| **customer-service** | CS credit additions + review | `:3011` | | **sales-pricing** | Per-customer sales quotes | `:3012` | | **accounting-export** | QuickBooks IIF / CSV | `:3013` | | **verae-middleware** | Zapier `/zapier/v1`, chain, NATS | `:3100` | @@ -37,13 +38,19 @@ Check: ```bash curl -fsS http://127.0.0.1:3000/health # zappier-edge (billing + users) +curl -fsS http://127.0.0.1:3010/health # account-balance (NATS SoT) +curl -fsS http://127.0.0.1:3011/health # customer-service +curl -fsS http://127.0.0.1:3012/health # sales-pricing curl -fsS http://127.0.0.1:3100/health # middleware ``` Open: -- Customer portal: http://127.0.0.1:3000/portal — sign up, copy `x-api-key` -- Admin: http://127.0.0.1:3000/admin +- Customer portal: http://127.0.0.1:3000/portal — sign up, copy `x-api-key`, **Statement** tab +- Admin: http://127.0.0.1:3000/admin — **Statement** per customer +- CS review: http://127.0.0.1:3011/ +- Sales review: http://127.0.0.1:3012/ +- Accounting review/export: http://127.0.0.1:3013/ - API docs: http://127.0.0.1:3000/docs Do **not** publish NATS `4222`. diff --git a/packages/verae-ops/docker-compose.yml b/packages/verae-ops/docker-compose.yml index 1c75416..38b50b4 100644 --- a/packages/verae-ops/docker-compose.yml +++ b/packages/verae-ops/docker-compose.yml @@ -50,8 +50,24 @@ services: environment: PORT: "3000" ZAPPIER_UPSTREAM: http://middleware:3100 + NATS_URL: nats://nats1:4222 ports: - "3000:3000" + depends_on: [nats1, account-balance] + + account-balance: + <<: *node + image: node:22-alpine + working_dir: /app + volumes: + - ${MONOREPO:-..}/packages/zappier-account-balance:/app + command: ["node", "src/server.js"] + environment: + PORT: "3010" + NATS_URL: nats://nats1:4222 + ports: + - "3010:3010" + depends_on: [nats1] customer-service: <<: *node @@ -63,9 +79,11 @@ services: environment: PORT: "3011" ZAPPIER_ADMIN_URL: http://zappier-edge:3000 + ACCOUNT_BALANCE_URL: http://account-balance:3010 + NATS_URL: nats://nats1:4222 ports: - "3011:3011" - depends_on: [zappier-edge] + depends_on: [zappier-edge, account-balance, nats1] sales-pricing: <<: *node @@ -77,9 +95,11 @@ services: environment: PORT: "3012" ZAPPIER_ADMIN_URL: http://zappier-edge:3000 + ACCOUNT_BALANCE_URL: http://account-balance:3010 + NATS_URL: nats://nats1:4222 ports: - "3012:3012" - depends_on: [zappier-edge] + depends_on: [zappier-edge, account-balance, nats1] accounting-export: <<: *node @@ -91,9 +111,11 @@ services: environment: PORT: "3013" ZAPPIER_ADMIN_URL: http://zappier-edge:3000 + ACCOUNT_BALANCE_URL: http://account-balance:3010 + NATS_URL: nats://nats1:4222 ports: - "3013:3013" - depends_on: [zappier-edge] + depends_on: [zappier-edge, account-balance, nats1] middleware: <<: *node diff --git a/packages/zappier-account-balance/NATS.md b/packages/zappier-account-balance/NATS.md new file mode 100644 index 0000000..1840a50 --- /dev/null +++ b/packages/zappier-account-balance/NATS.md @@ -0,0 +1,3 @@ +# zappier-account-balance NATS + +See README. Queue `account-balance`. No public bind. diff --git a/packages/zappier-account-balance/README.md b/packages/zappier-account-balance/README.md new file mode 100644 index 0000000..0c261d6 --- /dev/null +++ b/packages/zappier-account-balance/README.md @@ -0,0 +1,31 @@ +# zappier-account-balance + +Source of truth for **customer prepaid balances**. Internal traffic is **NATS request-reply**; HTTP is health plus a fallback. + +**Forgejo:** https://git.georgelambert.org/marchon/zappier-account-balance +**Catalog:** https://zapier.georgelambert.org/packages/zappier-account-balance/README.pdf + +Zapier cloud never connects here. zappier-edge, customer-service, sales-pricing, and accounting-export do. + +## Subjects + +| Subject | Kind | Who | +|---------|------|-----| +| `verae.billing.statement.get` | request-reply | portal, CS, sales, admin | +| `verae.billing.balance.get` | request-reply | anyone internal | +| `verae.billing.balance.adjust` | request-reply | CS credits, portal reload | +| `verae.billing.usage.recorded` | pub | zappier-edge meter | +| `verae.billing.payment.recorded` | pub | portal reload, invoice paid | +| `verae.billing.credit.applied` | pub | CS | + +Queue group: `account-balance`. + +## Run + +```bash +NATS_URL=nats://127.0.0.1:4222 PORT=3010 npm start +curl http://127.0.0.1:3010/health +curl http://127.0.0.1:3010/statement/cust_1 +``` + +Statement JSON: `{ customerId, prepaidCents, credits[], usage[], payments[] }`. diff --git a/packages/zappier-account-balance/package-lock.json b/packages/zappier-account-balance/package-lock.json new file mode 100644 index 0000000..47ff9f4 --- /dev/null +++ b/packages/zappier-account-balance/package-lock.json @@ -0,0 +1,46 @@ +{ + "name": "zappier-account-balance", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "zappier-account-balance", + "version": "0.1.0", + "dependencies": { + "nats": "^2.28.2" + } + }, + "node_modules/nats": { + "version": "2.29.3", + "resolved": "https://registry.npmjs.org/nats/-/nats-2.29.3.tgz", + "integrity": "sha512-tOQCRCwC74DgBTk4pWZ9V45sk4d7peoE2njVprMRCBXrhJ5q5cYM7i6W+Uvw2qUrcfOSnuisrX7bEx3b3Wx4QA==", + "deprecated": "Package moved. Use @nats-io/transport-node from https://github.com/nats-io/nats.js", + "license": "Apache-2.0", + "dependencies": { + "nkeys.js": "1.1.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/nkeys.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/nkeys.js/-/nkeys.js-1.1.0.tgz", + "integrity": "sha512-tB/a0shZL5UZWSwsoeyqfTszONTt4k2YS0tuQioMOD180+MbombYVgzDUYHlx+gejYK6rgf08n/2Df99WY0Sxg==", + "license": "Apache-2.0", + "dependencies": { + "tweetnacl": "1.0.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", + "license": "Unlicense" + } + } +} diff --git a/packages/zappier-account-balance/package.json b/packages/zappier-account-balance/package.json new file mode 100644 index 0000000..4906264 --- /dev/null +++ b/packages/zappier-account-balance/package.json @@ -0,0 +1,14 @@ +{ + "name": "zappier-account-balance", + "version": "0.1.0", + "private": true, + "type": "module", + "description": "NATS source of truth for customer prepaid balances, credits, usage, payments", + "scripts": { + "start": "node src/server.js", + "test": "node --test test/*.test.js" + }, + "dependencies": { + "nats": "^2.28.2" + } +} diff --git a/packages/zappier-account-balance/src/books.js b/packages/zappier-account-balance/src/books.js new file mode 100644 index 0000000..ed4623b --- /dev/null +++ b/packages/zappier-account-balance/src/books.js @@ -0,0 +1,89 @@ +/** + * Source of truth for prepaid balances, CS credits, usage, and payments. + */ +export class AccountBooks { + constructor() { + /** @type {Record} */ + this.prepaid = {}; + this.credits = []; + this.usage = []; + this.payments = []; + } + + prepaidCents(customerId) { + return this.prepaid[customerId] ?? 0; + } + + adjust({ customerId, cents, reason, agent, kind = 'credit' }) { + const delta = Math.trunc(Number(cents) || 0); + const next = this.prepaidCents(customerId) + delta; + this.prepaid[customerId] = next; + const row = { + id: `adj_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`, + customerId, + cents: delta, + reason: reason || kind, + agent: agent || 'system', + kind, + at: new Date().toISOString(), + prepaidCents: next, + }; + if (kind === 'payment' || kind === 'reload') this.payments.unshift(row); + else this.credits.unshift(row); + return row; + } + + recordUsage(entry) { + const cents = Number(entry.cents) || 0; + const customerId = entry.customerId; + const next = this.prepaidCents(customerId) - cents; + this.prepaid[customerId] = next; + const row = { + customerId, + endpointId: entry.endpointId || 'unknown', + cents, + at: entry.at || new Date().toISOString(), + prepaidCents: next, + }; + this.usage.unshift(row); + return row; + } + + recordPayment(entry) { + return this.adjust({ + customerId: entry.customerId, + cents: Number(entry.cents) || 0, + reason: entry.reason || 'payment', + agent: entry.agent || 'payments', + kind: 'payment', + }); + } + + statement(customerId) { + const match = (rows) => rows.filter((r) => r.customerId === customerId).slice(0, 100); + return { + customerId, + prepaidCents: this.prepaidCents(customerId), + credits: match(this.credits), + usage: match(this.usage), + payments: match(this.payments), + }; + } +} + +export function handle(subject, payload, books) { + const p = payload || {}; + if (subject.endsWith('balance.get') || subject.endsWith('statement.get')) { + return books.statement(p.customerId); + } + if (subject.endsWith('balance.adjust') || subject.endsWith('credit.applied')) { + return books.adjust(p); + } + if (subject.endsWith('usage.recorded')) { + return books.recordUsage(p); + } + if (subject.endsWith('payment.recorded')) { + return books.recordPayment(p); + } + throw new Error(`unknown billing subject ${subject}`); +} diff --git a/packages/zappier-account-balance/src/server.js b/packages/zappier-account-balance/src/server.js new file mode 100644 index 0000000..0ec0204 --- /dev/null +++ b/packages/zappier-account-balance/src/server.js @@ -0,0 +1,92 @@ +#!/usr/bin/env node +import http from 'node:http'; +import { AccountBooks, handle } from './books.js'; +import { SUBJECTS } from './subjects.js'; + +const PORT = Number(process.env.PORT || process.env.FLEET_HEALTH_PORT || 3010); +const BIND = process.env.FLEET_HEALTH_BIND || '0.0.0.0'; +const books = new AccountBooks(); +let natsOk = false; + +async function startNats() { + const url = process.env.NATS_URL; + if (!url) return; + const { connect, StringCodec } = await import('nats'); + const nc = await connect({ servers: url.split(','), name: 'zappier-account-balance' }); + const sc = StringCodec(); + const reply = async (sub, fn) => { + for await (const m of sub) { + let payload = {}; + try { + payload = JSON.parse(sc.decode(m.data) || '{}'); + } catch { + payload = {}; + } + const out = fn(payload); + if (m.reply) m.respond(sc.encode(JSON.stringify(out))); + } + }; + reply(nc.subscribe(SUBJECTS.STATEMENT_GET, { queue: SUBJECTS.QUEUE }), (p) => + handle(SUBJECTS.STATEMENT_GET, p, books), + ); + reply(nc.subscribe(SUBJECTS.BALANCE_GET, { queue: SUBJECTS.QUEUE }), (p) => + handle(SUBJECTS.BALANCE_GET, p, books), + ); + reply(nc.subscribe(SUBJECTS.BALANCE_ADJUST, { queue: SUBJECTS.QUEUE }), (p) => + handle(SUBJECTS.BALANCE_ADJUST, p, books), + ); + (async () => { + for await (const m of nc.subscribe(SUBJECTS.USAGE_RECORDED)) { + handle(SUBJECTS.USAGE_RECORDED, JSON.parse(sc.decode(m.data) || '{}'), books); + } + })(); + // payment.recorded / credit.applied are fan-out events. Prepaid mutations + // go through balance.adjust so publishers can both pub and request-reply. + natsOk = true; + process.stdout.write(`account-balance nats ${url}\n`); +} + +function readBody(req) { + return new Promise((resolve) => { + const chunks = []; + req.on('data', (c) => chunks.push(c)); + req.on('end', () => { + try { + resolve(JSON.parse(Buffer.concat(chunks).toString('utf8') || '{}')); + } catch { + resolve({}); + } + }); + }); +} + +const server = http.createServer(async (req, res) => { + const url = new URL(req.url || '/', `http://127.0.0.1:${PORT}`); + const json = (code, obj) => { + res.writeHead(code, { 'content-type': 'application/json' }); + res.end(JSON.stringify(obj)); + }; + try { + if (req.method === 'GET' && url.pathname === '/health') { + return json(200, { ok: true, role: 'zappier-account-balance', nats: natsOk, subjects: SUBJECTS }); + } + const st = url.pathname.match(/^\/statement\/([^/]+)$/); + if (req.method === 'GET' && st) { + return json(200, books.statement(st[1])); + } + if (req.method === 'POST' && url.pathname === '/adjust') { + const body = await readBody(req); + return json(200, books.adjust(body)); + } + json(404, { error: 'not found' }); + } catch (err) { + json(500, { error: err.message }); + } +}); + +server.listen(PORT, BIND, () => { + process.stdout.write(`zappier-account-balance http://${BIND}:${PORT}/\n`); +}); +startNats().catch((err) => { + process.stderr.write(`nats optional: ${err.message}\n`); +}); diff --git a/packages/zappier-account-balance/src/subjects.js b/packages/zappier-account-balance/src/subjects.js new file mode 100644 index 0000000..45e6bb2 --- /dev/null +++ b/packages/zappier-account-balance/src/subjects.js @@ -0,0 +1,10 @@ +/** Internal billing bus. Zapier cloud never subscribes. */ +export const SUBJECTS = { + BALANCE_GET: 'verae.billing.balance.get', + BALANCE_ADJUST: 'verae.billing.balance.adjust', + STATEMENT_GET: 'verae.billing.statement.get', + USAGE_RECORDED: 'verae.billing.usage.recorded', + PAYMENT_RECORDED: 'verae.billing.payment.recorded', + CREDIT_APPLIED: 'verae.billing.credit.applied', + QUEUE: 'account-balance', +}; diff --git a/packages/zappier-account-balance/test/books.test.js b/packages/zappier-account-balance/test/books.test.js new file mode 100644 index 0000000..7d581b6 --- /dev/null +++ b/packages/zappier-account-balance/test/books.test.js @@ -0,0 +1,16 @@ +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { AccountBooks, handle } from '../src/books.js'; +import { SUBJECTS } from '../src/subjects.js'; + +test('adjust credits prepaid and statement lists credits usage payments', () => { + const books = new AccountBooks(); + handle(SUBJECTS.BALANCE_ADJUST, { customerId: 'c1', cents: 500, reason: 'goodwill', agent: 'cs' }, books); + handle(SUBJECTS.USAGE_RECORDED, { customerId: 'c1', endpointId: 'timestamp', cents: 4 }, books); + handle(SUBJECTS.PAYMENT_RECORDED, { customerId: 'c1', cents: 1000, reason: 'reload' }, books); + const st = handle(SUBJECTS.STATEMENT_GET, { customerId: 'c1' }, books); + assert.equal(st.prepaidCents, 1496); + assert.equal(st.credits[0].cents, 500); + assert.equal(st.usage[0].endpointId, 'timestamp'); + assert.equal(st.payments[0].kind, 'payment'); +}); diff --git a/packages/zappier-account-balance/test/health.test.js b/packages/zappier-account-balance/test/health.test.js new file mode 100644 index 0000000..d3b5e06 --- /dev/null +++ b/packages/zappier-account-balance/test/health.test.js @@ -0,0 +1,30 @@ +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { spawn } from 'node:child_process'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const root = path.join(path.dirname(fileURLToPath(import.meta.url)), '..'); + +test('account-balance health and statement', async () => { + const port = 18010; + const child = spawn(process.execPath, ['src/server.js'], { + cwd: root, + env: { ...process.env, PORT: String(port) }, + stdio: ['ignore', 'pipe', 'pipe'], + }); + await new Promise((r) => setTimeout(r, 400)); + try { + const h = await (await fetch(`http://127.0.0.1:${port}/health`)).json(); + assert.equal(h.role, 'zappier-account-balance'); + await fetch(`http://127.0.0.1:${port}/adjust`, { + method: 'POST', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ customerId: 'c1', cents: 200, reason: 'test' }), + }); + const st = await (await fetch(`http://127.0.0.1:${port}/statement/c1`)).json(); + assert.equal(st.prepaidCents, 200); + } finally { + child.kill('SIGTERM'); + } +}); diff --git a/packages/zappier-accounting-export/README.md b/packages/zappier-accounting-export/README.md index ec0439a..7a4efe1 100644 --- a/packages/zappier-accounting-export/README.md +++ b/packages/zappier-accounting-export/README.md @@ -5,10 +5,13 @@ Accounting-department export of **invoices, billing, and payment history** to ** **Forgejo:** https://git.georgelambert.org/marchon/zappier-accounting-export **Catalog:** https://zapier.georgelambert.org/packages/zappier-accounting-export/README.pdf -Reads issued/paid invoices from zappier-edge (`ZAPPIER_ADMIN_URL`). +Reads issued/paid invoices from zappier-edge (`ZAPPIER_ADMIN_URL`). Customer statements (credits, balances, usage, payments) come from NATS `verae.billing.statement.get`. + +Staff UI: http://127.0.0.1:3013/ ```bash -PORT=3013 node src/server.js +PORT=3013 NATS_URL=nats://127.0.0.1:4222 node src/server.js +curl http://127.0.0.1:3013/review/cust_1 curl 'http://127.0.0.1:3013/export/quickbooks.iif?period=2026-07' -o zappier.iif curl 'http://127.0.0.1:3013/export/accounting.csv?period=2026-07' -o zappier.csv ``` diff --git a/packages/zappier-accounting-export/package-lock.json b/packages/zappier-accounting-export/package-lock.json new file mode 100644 index 0000000..bae4794 --- /dev/null +++ b/packages/zappier-accounting-export/package-lock.json @@ -0,0 +1,46 @@ +{ + "name": "zappier-accounting-export", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "zappier-accounting-export", + "version": "0.1.0", + "dependencies": { + "nats": "^2.28.2" + } + }, + "node_modules/nats": { + "version": "2.29.3", + "resolved": "https://registry.npmjs.org/nats/-/nats-2.29.3.tgz", + "integrity": "sha512-tOQCRCwC74DgBTk4pWZ9V45sk4d7peoE2njVprMRCBXrhJ5q5cYM7i6W+Uvw2qUrcfOSnuisrX7bEx3b3Wx4QA==", + "deprecated": "Package moved. Use @nats-io/transport-node from https://github.com/nats-io/nats.js", + "license": "Apache-2.0", + "dependencies": { + "nkeys.js": "1.1.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/nkeys.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/nkeys.js/-/nkeys.js-1.1.0.tgz", + "integrity": "sha512-tB/a0shZL5UZWSwsoeyqfTszONTt4k2YS0tuQioMOD180+MbombYVgzDUYHlx+gejYK6rgf08n/2Df99WY0Sxg==", + "license": "Apache-2.0", + "dependencies": { + "tweetnacl": "1.0.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", + "license": "Unlicense" + } + } +} diff --git a/packages/zappier-accounting-export/package.json b/packages/zappier-accounting-export/package.json index 4165d1c..79041d5 100644 --- a/packages/zappier-accounting-export/package.json +++ b/packages/zappier-accounting-export/package.json @@ -7,5 +7,8 @@ "scripts": { "start": "node src/server.js", "test": "node --test test/*.test.js" + }, + "dependencies": { + "nats": "^2.28.2" } } diff --git a/packages/zappier-accounting-export/public/index.html b/packages/zappier-accounting-export/public/index.html new file mode 100644 index 0000000..d370bbd --- /dev/null +++ b/packages/zappier-accounting-export/public/index.html @@ -0,0 +1,51 @@ + + + + + + Accounting — customer review + + + +

Accounting

+

Review payments, usage, credits, and balances. Export invoices to QuickBooks IIF / CSV.

+
+ + + +

QuickBooks IIF · CSV

+
+
+ + + diff --git a/packages/zappier-accounting-export/src/nats-billing.js b/packages/zappier-accounting-export/src/nats-billing.js new file mode 100644 index 0000000..3634755 --- /dev/null +++ b/packages/zappier-accounting-export/src/nats-billing.js @@ -0,0 +1,17 @@ +export const SUBJECTS = { STATEMENT_GET: 'verae.billing.statement.get' }; + +export async function billingRequest(subject, payload) { + const url = process.env.NATS_URL; + if (!url) return null; + try { + const { connect, StringCodec } = await import('nats'); + const nc = await connect({ servers: url.split(','), name: 'zappier-accounting-export' }); + const sc = StringCodec(); + const m = await nc.request(subject, sc.encode(JSON.stringify(payload)), { timeout: 2000 }); + const out = JSON.parse(sc.decode(m.data) || '{}'); + await nc.close(); + return out; + } catch { + return null; + } +} diff --git a/packages/zappier-accounting-export/src/server.js b/packages/zappier-accounting-export/src/server.js index f19d025..564f9e0 100644 --- a/packages/zappier-accounting-export/src/server.js +++ b/packages/zappier-accounting-export/src/server.js @@ -1,10 +1,16 @@ #!/usr/bin/env node /** Accounting department: QuickBooks IIF + CSV from zappier-edge invoices. */ +import fs from 'node:fs'; import http from 'node:http'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { SUBJECTS, billingRequest } from './nats-billing.js'; const PORT = Number(process.env.PORT || 3013); const EDGE = (process.env.ZAPPIER_ADMIN_URL || 'http://127.0.0.1:3000').replace(/\/$/, ''); const KEY = process.env.ZAPPIER_ADMIN_KEY || 'admin-dev-key'; +const BOOKS = (process.env.ACCOUNT_BALANCE_URL || 'http://127.0.0.1:3010').replace(/\/$/, ''); +const PUBLIC = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'public'); async function edge(pathname) { const r = await fetch(`${EDGE}${pathname}`, { headers: { 'x-admin-key': KEY } }); @@ -17,9 +23,22 @@ const server = http.createServer(async (req, res) => { res.writeHead(code, { 'content-type': type }); res.end(body); }; + const json = (code, obj) => send(code, 'application/json', JSON.stringify(obj)); try { + if (req.method === 'GET' && (url.pathname === '/' || url.pathname === '/index.html')) { + return send(200, 'text/html; charset=utf-8', fs.readFileSync(path.join(PUBLIC, 'index.html'))); + } if (req.method === 'GET' && url.pathname === '/health') { - return send(200, 'application/json', JSON.stringify({ ok: true, role: 'zappier-accounting-export' })); + return json(200, { ok: true, role: 'zappier-accounting-export' }); + } + const review = url.pathname.match(/^\/review\/([^/]+)$/); + if (req.method === 'GET' && review) { + const nats = await billingRequest(SUBJECTS.STATEMENT_GET, { customerId: review[1] }); + if (nats) return json(200, { ...nats, source: 'nats' }); + const r = await fetch(`${BOOKS}/statement/${review[1]}`); + if (r.ok) return json(200, { ...(await r.json()), source: 'account-balance' }); + const e = await edge(`/admin/api/statement/${review[1]}`); + return send(e.status, 'application/json', await e.text()); } const period = url.searchParams.get('period'); const q = period ? `?period=${encodeURIComponent(period)}` : ''; @@ -31,9 +50,9 @@ const server = http.createServer(async (req, res) => { const r = await edge(`/admin/api/exports/accounting.csv${q}`); return send(r.status, 'text/csv', await r.text()); } - send(404, 'application/json', JSON.stringify({ error: 'not found' })); + json(404, { error: 'not found' }); } catch (err) { - send(502, 'application/json', JSON.stringify({ error: err.message })); + json(502, { error: err.message }); } }); diff --git a/packages/zappier-accounting-export/test/health.test.js b/packages/zappier-accounting-export/test/health.test.js index 7d3def8..5355ae4 100644 --- a/packages/zappier-accounting-export/test/health.test.js +++ b/packages/zappier-accounting-export/test/health.test.js @@ -17,6 +17,8 @@ test('accounting-export health', async () => { try { const r = await fetch(`http://127.0.0.1:${port}/health`); assert.equal((await r.json()).role, 'zappier-accounting-export'); + const home = await fetch(`http://127.0.0.1:${port}/`); + assert.equal(home.status, 200); } finally { child.kill('SIGTERM'); } diff --git a/packages/zappier-customer-service/README.md b/packages/zappier-customer-service/README.md index d8ee4e9..99d9f1f 100644 --- a/packages/zappier-customer-service/README.md +++ b/packages/zappier-customer-service/README.md @@ -1,6 +1,6 @@ # zappier-customer-service -Department API for **customer-service credit additions** (goodwill, make-goods). Prepaid balances stay on **zappier-edge**; this service is the CS front door and audit copy. +Department API for **customer-service credit additions** and **review** of credits, balances, usage, and payments. Internally uses **NATS** `verae.billing.*` (account-balance). HTTP fallback to account-balance then zappier-edge. **Forgejo:** https://git.georgelambert.org/marchon/zappier-customer-service **Catalog:** https://zapier.georgelambert.org/packages/zappier-customer-service/README.pdf @@ -14,8 +14,10 @@ curl -X POST http://127.0.0.1:3011/credits -H 'content-type: application/json' \ | Route | Job | |-------|-----| +| `GET /` | Staff review UI | | `GET /health` | `{ ok, role }` | -| `POST /credits` | Add/subtract prepaid cents on the customer | +| `POST /credits` | Add/subtract prepaid cents (NATS `balance.adjust`) | | `GET /credits?customerId=` | Ledger | +| `GET /review/:customerId` | Full statement (balance, credits, usage, payments) | -Does not talk to NATS or Zapier cloud. +Talks to NATS `verae.billing.*` internally. Never talks to Zapier cloud. diff --git a/packages/zappier-customer-service/package-lock.json b/packages/zappier-customer-service/package-lock.json new file mode 100644 index 0000000..d0b6619 --- /dev/null +++ b/packages/zappier-customer-service/package-lock.json @@ -0,0 +1,46 @@ +{ + "name": "zappier-customer-service", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "zappier-customer-service", + "version": "0.1.0", + "dependencies": { + "nats": "^2.28.2" + } + }, + "node_modules/nats": { + "version": "2.29.3", + "resolved": "https://registry.npmjs.org/nats/-/nats-2.29.3.tgz", + "integrity": "sha512-tOQCRCwC74DgBTk4pWZ9V45sk4d7peoE2njVprMRCBXrhJ5q5cYM7i6W+Uvw2qUrcfOSnuisrX7bEx3b3Wx4QA==", + "deprecated": "Package moved. Use @nats-io/transport-node from https://github.com/nats-io/nats.js", + "license": "Apache-2.0", + "dependencies": { + "nkeys.js": "1.1.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/nkeys.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/nkeys.js/-/nkeys.js-1.1.0.tgz", + "integrity": "sha512-tB/a0shZL5UZWSwsoeyqfTszONTt4k2YS0tuQioMOD180+MbombYVgzDUYHlx+gejYK6rgf08n/2Df99WY0Sxg==", + "license": "Apache-2.0", + "dependencies": { + "tweetnacl": "1.0.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", + "license": "Unlicense" + } + } +} diff --git a/packages/zappier-customer-service/package.json b/packages/zappier-customer-service/package.json index 8b7fc1a..c7e23b9 100644 --- a/packages/zappier-customer-service/package.json +++ b/packages/zappier-customer-service/package.json @@ -7,5 +7,8 @@ "scripts": { "start": "node src/server.js", "test": "node --test test/*.test.js" + }, + "dependencies": { + "nats": "^2.28.2" } } diff --git a/packages/zappier-customer-service/public/index.html b/packages/zappier-customer-service/public/index.html new file mode 100644 index 0000000..5402c1f --- /dev/null +++ b/packages/zappier-customer-service/public/index.html @@ -0,0 +1,67 @@ + + + + + + Customer service — account review + + + +

Customer service

+

Review prepaid balance, credits, usage, and payments. Credits go to account-balance over NATS.

+
+ + + +
+
+

Add credit

+ + + + +
+
+ + + diff --git a/packages/zappier-customer-service/src/nats-billing.js b/packages/zappier-customer-service/src/nats-billing.js new file mode 100644 index 0000000..6798fb1 --- /dev/null +++ b/packages/zappier-customer-service/src/nats-billing.js @@ -0,0 +1,20 @@ +export const SUBJECTS = { + STATEMENT_GET: 'verae.billing.statement.get', + BALANCE_ADJUST: 'verae.billing.balance.adjust', +}; + +export async function billingRequest(subject, payload) { + const url = process.env.NATS_URL; + if (!url) return null; + try { + const { connect, StringCodec } = await import('nats'); + const nc = await connect({ servers: url.split(','), name: 'zappier-customer-service' }); + const sc = StringCodec(); + const m = await nc.request(subject, sc.encode(JSON.stringify(payload)), { timeout: 2000 }); + const out = JSON.parse(sc.decode(m.data) || '{}'); + await nc.close(); + return out; + } catch { + return null; + } +} diff --git a/packages/zappier-customer-service/src/server.js b/packages/zappier-customer-service/src/server.js index 301a543..39bd204 100644 --- a/packages/zappier-customer-service/src/server.js +++ b/packages/zappier-customer-service/src/server.js @@ -1,41 +1,27 @@ #!/usr/bin/env node /** - * Customer-service department API: goodwill credits, CS notes. - * Source of truth for balances remains zappier-edge admin. + * CS HTTP front door. Internally prefers NATS account-balance, then HTTP. */ -import http from 'node:http'; import fs from 'node:fs'; +import http from 'node:http'; import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { SUBJECTS, billingRequest } from './nats-billing.js'; + +const PUBLIC = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'public'); const PORT = Number(process.env.PORT || 3011); +const BOOKS = (process.env.ACCOUNT_BALANCE_URL || 'http://127.0.0.1:3010').replace(/\/$/, ''); const EDGE = (process.env.ZAPPIER_ADMIN_URL || 'http://127.0.0.1:3000').replace(/\/$/, ''); const KEY = process.env.ZAPPIER_ADMIN_KEY || 'admin-dev-key'; -const STORE = process.env.CS_STORE || path.join(process.cwd(), 'data', 'credits.json'); -function load() { - try { - return JSON.parse(fs.readFileSync(STORE, 'utf8')); - } catch { - return { credits: [] }; - } -} -function save(data) { - fs.mkdirSync(path.dirname(STORE), { recursive: true }); - fs.writeFileSync(STORE, JSON.stringify(data, null, 2)); -} - -async function edge(pathname, { method = 'GET', body } = {}) { - const r = await fetch(`${EDGE}${pathname}`, { - method, - headers: { 'content-type': 'application/json', 'x-admin-key': KEY }, - body: body ? JSON.stringify(body) : undefined, - }); - const text = await r.text(); - try { - return { status: r.status, body: JSON.parse(text) }; - } catch { - return { status: r.status, body: text }; - } +async function statement(customerId) { + const nats = await billingRequest(SUBJECTS.STATEMENT_GET, { customerId }); + if (nats) return { status: 200, body: { ...nats, source: 'nats' } }; + const r = await fetch(`${BOOKS}/statement/${customerId}`); + if (r.ok) return { status: r.status, body: { ...(await r.json()), source: 'account-balance' } }; + const e = await fetch(`${EDGE}/admin/api/statement/${customerId}`, { headers: { 'x-admin-key': KEY } }); + return { status: e.status, body: { ...(await e.json().catch(() => ({}))), source: 'edge' } }; } const server = http.createServer(async (req, res) => { @@ -45,22 +31,43 @@ const server = http.createServer(async (req, res) => { res.end(JSON.stringify(obj)); }; try { + if (req.method === 'GET' && (url.pathname === '/' || url.pathname === '/index.html')) { + res.writeHead(200, { 'content-type': 'text/html; charset=utf-8' }); + res.end(fs.readFileSync(path.join(PUBLIC, 'index.html'))); + return; + } if (req.method === 'GET' && url.pathname === '/health') { - return json(200, { ok: true, role: 'zappier-customer-service' }); + return json(200, { ok: true, role: 'zappier-customer-service', nats: Boolean(process.env.NATS_URL) }); + } + const review = url.pathname.match(/^\/review\/([^/]+)$/); + if (req.method === 'GET' && review) { + const out = await statement(review[1]); + return json(out.status, out.body); } if (req.method === 'POST' && url.pathname === '/credits') { const chunks = []; for await (const c of req) chunks.push(c); const payload = JSON.parse(Buffer.concat(chunks).toString('utf8') || '{}'); - const forwarded = await edge('/admin/api/credits', { method: 'POST', body: payload }); - const data = load(); - data.credits.unshift({ ...payload, at: new Date().toISOString(), edge: forwarded.status }); - save(data); - return json(forwarded.status, forwarded.body); + const nats = await billingRequest(SUBJECTS.BALANCE_ADJUST, { ...payload, kind: 'credit' }); + if (nats) return json(200, { ...nats, source: 'nats' }); + const r = await fetch(`${BOOKS}/adjust`, { + method: 'POST', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify(payload), + }); + const body = await r.json().catch(() => ({})); + await fetch(`${EDGE}/admin/api/credits`, { + method: 'POST', + headers: { 'content-type': 'application/json', 'x-admin-key': KEY }, + body: JSON.stringify(payload), + }).catch(() => {}); + return json(r.status, body); } if (req.method === 'GET' && url.pathname === '/credits') { - const forwarded = await edge(`/admin/api/credits${url.search}`); - return json(forwarded.status, forwarded.body); + const id = url.searchParams.get('customerId'); + if (!id) return json(400, { error: 'customerId required' }); + const out = await statement(id); + return json(out.status, { credits: out.body.credits || [] }); } json(404, { error: 'not found' }); } catch (err) { @@ -69,5 +76,5 @@ const server = http.createServer(async (req, res) => { }); server.listen(PORT, '0.0.0.0', () => { - process.stdout.write(`zappier-customer-service http://127.0.0.1:${PORT}/\n`); + process.stdout.write(`zappier-customer-service http://0.0.0.0:${PORT}/\n`); }); diff --git a/packages/zappier-customer-service/test/health.test.js b/packages/zappier-customer-service/test/health.test.js index 26586c0..876eb6b 100644 --- a/packages/zappier-customer-service/test/health.test.js +++ b/packages/zappier-customer-service/test/health.test.js @@ -18,7 +18,45 @@ test('customer-service health', async () => { const r = await fetch(`http://127.0.0.1:${port}/health`); const body = await r.json(); assert.equal(body.role, 'zappier-customer-service'); + const home = await fetch(`http://127.0.0.1:${port}/`); + assert.equal(home.status, 200); + assert.match(await home.text(), /Customer service/); } finally { child.kill('SIGTERM'); } }); + +test('customer-service review via account-balance HTTP', async () => { + const booksPort = 18014; + const csPort = 18015; + const books = spawn(process.execPath, ['src/server.js'], { + cwd: path.join(root, '..', 'zappier-account-balance'), + env: { ...process.env, PORT: String(booksPort) }, + stdio: ['ignore', 'pipe', 'pipe'], + }); + const cs = spawn(process.execPath, ['src/server.js'], { + cwd: path.join(root), + env: { + ...process.env, + PORT: String(csPort), + ACCOUNT_BALANCE_URL: `http://127.0.0.1:${booksPort}`, + }, + stdio: ['ignore', 'pipe', 'pipe'], + }); + await new Promise((r) => setTimeout(r, 500)); + try { + const add = await fetch(`http://127.0.0.1:${csPort}/credits`, { + method: 'POST', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ customerId: 'c-review', cents: 700, reason: 'goodwill', agent: 'cs' }), + }); + assert.equal(add.status, 200); + const st = await (await fetch(`http://127.0.0.1:${csPort}/review/c-review`)).json(); + assert.equal(st.prepaidCents, 700); + assert.equal(st.source, 'account-balance'); + assert.equal(st.credits[0].reason, 'goodwill'); + } finally { + cs.kill('SIGTERM'); + books.kill('SIGTERM'); + } +}); diff --git a/packages/zappier-sales-pricing/README.md b/packages/zappier-sales-pricing/README.md index d5c2fbf..b3092e8 100644 --- a/packages/zappier-sales-pricing/README.md +++ b/packages/zappier-sales-pricing/README.md @@ -5,8 +5,13 @@ Sales-department API for **per-customer pricing** (tier + `multiplierOverride`). **Forgejo:** https://git.georgelambert.org/marchon/zappier-sales-pricing **Catalog:** https://zapier.georgelambert.org/packages/zappier-sales-pricing/README.pdf +Internal reviews use NATS `verae.billing.statement.get` (account-balance). + +Staff UI: http://127.0.0.1:3012/ — review credits, balances, usage, payments. + ```bash -PORT=3012 node src/server.js +PORT=3012 NATS_URL=nats://127.0.0.1:4222 node src/server.js +curl http://127.0.0.1:3012/review/cust_2 curl http://127.0.0.1:3012/quotes/cust_2 curl -X PUT http://127.0.0.1:3012/customers/cust_2/pricing \ -H 'content-type: application/json' \ diff --git a/packages/zappier-sales-pricing/package-lock.json b/packages/zappier-sales-pricing/package-lock.json new file mode 100644 index 0000000..68d2b5f --- /dev/null +++ b/packages/zappier-sales-pricing/package-lock.json @@ -0,0 +1,46 @@ +{ + "name": "zappier-sales-pricing", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "zappier-sales-pricing", + "version": "0.1.0", + "dependencies": { + "nats": "^2.28.2" + } + }, + "node_modules/nats": { + "version": "2.29.3", + "resolved": "https://registry.npmjs.org/nats/-/nats-2.29.3.tgz", + "integrity": "sha512-tOQCRCwC74DgBTk4pWZ9V45sk4d7peoE2njVprMRCBXrhJ5q5cYM7i6W+Uvw2qUrcfOSnuisrX7bEx3b3Wx4QA==", + "deprecated": "Package moved. Use @nats-io/transport-node from https://github.com/nats-io/nats.js", + "license": "Apache-2.0", + "dependencies": { + "nkeys.js": "1.1.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/nkeys.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/nkeys.js/-/nkeys.js-1.1.0.tgz", + "integrity": "sha512-tB/a0shZL5UZWSwsoeyqfTszONTt4k2YS0tuQioMOD180+MbombYVgzDUYHlx+gejYK6rgf08n/2Df99WY0Sxg==", + "license": "Apache-2.0", + "dependencies": { + "tweetnacl": "1.0.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", + "license": "Unlicense" + } + } +} diff --git a/packages/zappier-sales-pricing/package.json b/packages/zappier-sales-pricing/package.json index 8fa941f..813317e 100644 --- a/packages/zappier-sales-pricing/package.json +++ b/packages/zappier-sales-pricing/package.json @@ -7,5 +7,8 @@ "scripts": { "start": "node src/server.js", "test": "node --test test/*.test.js" + }, + "dependencies": { + "nats": "^2.28.2" } } diff --git a/packages/zappier-sales-pricing/public/index.html b/packages/zappier-sales-pricing/public/index.html new file mode 100644 index 0000000..3fb996e --- /dev/null +++ b/packages/zappier-sales-pricing/public/index.html @@ -0,0 +1,56 @@ + + + + + + Sales — customer review + + + +

Sales

+

Review customer credits, balances, usage, and payments (NATS account-balance). Pricing writes still go to zappier-edge.

+
+ + + + +
+
+ + + diff --git a/packages/zappier-sales-pricing/src/nats-billing.js b/packages/zappier-sales-pricing/src/nats-billing.js new file mode 100644 index 0000000..efab3b3 --- /dev/null +++ b/packages/zappier-sales-pricing/src/nats-billing.js @@ -0,0 +1,17 @@ +export const SUBJECTS = { STATEMENT_GET: 'verae.billing.statement.get' }; + +export async function billingRequest(subject, payload) { + const url = process.env.NATS_URL; + if (!url) return null; + try { + const { connect, StringCodec } = await import('nats'); + const nc = await connect({ servers: url.split(','), name: 'zappier-sales-pricing' }); + const sc = StringCodec(); + const m = await nc.request(subject, sc.encode(JSON.stringify(payload)), { timeout: 2000 }); + const out = JSON.parse(sc.decode(m.data) || '{}'); + await nc.close(); + return out; + } catch { + return null; + } +} diff --git a/packages/zappier-sales-pricing/src/server.js b/packages/zappier-sales-pricing/src/server.js index eddfb0b..bdb1995 100644 --- a/packages/zappier-sales-pricing/src/server.js +++ b/packages/zappier-sales-pricing/src/server.js @@ -1,10 +1,18 @@ #!/usr/bin/env node /** Sales department: per-customer multiplier / tier. Writes through zappier-edge. */ +import fs from 'node:fs'; import http from 'node:http'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { SUBJECTS, billingRequest } from './nats-billing.js'; + +const PUBLIC = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'public'); const PORT = Number(process.env.PORT || 3012); const EDGE = (process.env.ZAPPIER_ADMIN_URL || 'http://127.0.0.1:3000').replace(/\/$/, ''); const KEY = process.env.ZAPPIER_ADMIN_KEY || 'admin-dev-key'; +const BOOKS = (process.env.ACCOUNT_BALANCE_URL || 'http://127.0.0.1:3010').replace(/\/$/, ''); async function edge(pathname, { method = 'GET', body } = {}) { const r = await fetch(`${EDGE}${pathname}`, { @@ -27,9 +35,23 @@ const server = http.createServer(async (req, res) => { res.end(JSON.stringify(obj)); }; try { + if (req.method === 'GET' && (url.pathname === '/' || url.pathname === '/index.html')) { + res.writeHead(200, { 'content-type': 'text/html; charset=utf-8' }); + res.end(fs.readFileSync(path.join(PUBLIC, 'index.html'))); + return; + } if (req.method === 'GET' && url.pathname === '/health') { return json(200, { ok: true, role: 'zappier-sales-pricing' }); } + const review = url.pathname.match(/^\/review\/([^/]+)$/); + if (req.method === 'GET' && review) { + const nats = await billingRequest(SUBJECTS.STATEMENT_GET, { customerId: review[1] }); + if (nats) return json(200, { ...nats, source: 'nats' }); + const r = await fetch(`${BOOKS}/statement/${review[1]}`); + if (r.ok) return json(200, { ...(await r.json()), source: 'account-balance' }); + const e = await edge(`/admin/api/statement/${review[1]}`); + return json(e.status, e.body); + } const quote = url.pathname.match(/^\/quotes\/([^/]+)$/); if (req.method === 'GET' && quote) { const forwarded = await edge(`/admin/api/sales/quote/${quote[1]}`); diff --git a/packages/zappier-sales-pricing/test/health.test.js b/packages/zappier-sales-pricing/test/health.test.js index 43c2fef..0533933 100644 --- a/packages/zappier-sales-pricing/test/health.test.js +++ b/packages/zappier-sales-pricing/test/health.test.js @@ -17,7 +17,42 @@ test('sales-pricing health', async () => { try { const r = await fetch(`http://127.0.0.1:${port}/health`); assert.equal((await r.json()).role, 'zappier-sales-pricing'); + const home = await fetch(`http://127.0.0.1:${port}/`); + assert.equal(home.status, 200); } finally { child.kill('SIGTERM'); } }); + +test('sales review via account-balance HTTP', async () => { + const booksPort = 18016; + const salesPort = 18017; + const books = spawn(process.execPath, ['src/server.js'], { + cwd: path.join(root, '..', 'zappier-account-balance'), + env: { ...process.env, PORT: String(booksPort) }, + stdio: ['ignore', 'pipe', 'pipe'], + }); + const sales = spawn(process.execPath, ['src/server.js'], { + cwd: root, + env: { + ...process.env, + PORT: String(salesPort), + ACCOUNT_BALANCE_URL: `http://127.0.0.1:${booksPort}`, + }, + stdio: ['ignore', 'pipe', 'pipe'], + }); + await new Promise((r) => setTimeout(r, 500)); + try { + await fetch(`http://127.0.0.1:${booksPort}/adjust`, { + method: 'POST', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ customerId: 'c-sales', cents: 300, reason: 'promo', agent: 'sales' }), + }); + const st = await (await fetch(`http://127.0.0.1:${salesPort}/review/c-sales`)).json(); + assert.equal(st.prepaidCents, 300); + assert.equal(st.source, 'account-balance'); + } finally { + sales.kill('SIGTERM'); + books.kill('SIGTERM'); + } +}); diff --git a/packages/zappier/admin/app.js b/packages/zappier/admin/app.js index cb2b350..a2f9c67 100644 --- a/packages/zappier/admin/app.js +++ b/packages/zappier/admin/app.js @@ -98,6 +98,7 @@ async function load() { renderEndpoints(); renderTiers(); renderCustomers(); + renderStatement(); renderInvoices(); renderReports(); renderSystem(); @@ -277,7 +278,8 @@ function renderCustomers() { - + + `, ) .join(''); @@ -308,6 +310,47 @@ async function saveCustomer(id) { await load(); } +function renderStatement() { + const options = customerOptions(state.customers[0]?.id || ''); + document.getElementById('statement').innerHTML = ` +

Customer statement

+

Credits, prepaid balance, usage, and payments. Prefers NATS account-balance.

+
+
+ + +
+
+
`; +} + +async function reviewCustomer(id) { + if (!id) return; + const st = await api(`/statement/${id}`); + const row = (list, cols) => + (list || []) + .map((r) => `${cols.map((c) => `${r[c] ?? ''}`).join('')}`) + .join('') || `None.`; + const html = ` +

${st.name || id} · prepaid ${fmt(st.prepaidCents || 0)} · source ${st.source || 'local'}

+

Credits

+ ${row(st.credits, ['cents', 'reason', 'agent', 'at'])}
CentsReasonAgentWhen
+

Usage

+ ${row(st.usage, ['endpointId', 'cents', 'at'])}
EndpointCentsWhen
+

Payments

+ ${row(st.payments, ['cents', 'kind', 'reason', 'at'])}
CentsKindReasonWhen
`; + const out = document.getElementById('stmt-out'); + if (out) out.innerHTML = html; + else { + document.getElementById('statement').innerHTML = `

Customer statement

${html}`; + document.querySelectorAll('nav button').forEach((b) => b.classList.toggle('active', b.dataset.tab === 'statement')); + document.querySelectorAll('main section').forEach((s) => (s.hidden = s.id !== 'statement')); + } + const sel = document.getElementById('stmt-customer'); + if (sel) sel.value = id; + say(`Loaded statement for ${id}.`); +} + async function addCustomer() { const name = document.getElementById('new-customer-name').value.trim(); const tierId = document.getElementById('new-customer-tier').value; diff --git a/packages/zappier/admin/index.html b/packages/zappier/admin/index.html index ea45a86..f21be0a 100644 --- a/packages/zappier/admin/index.html +++ b/packages/zappier/admin/index.html @@ -236,6 +236,7 @@ + @@ -248,6 +249,7 @@
+ diff --git a/packages/zappier/package-lock.json b/packages/zappier/package-lock.json index 53ecc97..567b4f0 100644 --- a/packages/zappier/package-lock.json +++ b/packages/zappier/package-lock.json @@ -13,6 +13,7 @@ "dotenv": "^17.4.2", "express": "^4.19.2", "express-openapi-validator": "^5.3.0", + "nats": "^2.29.3", "qrcode": "^1.5.4", "stripe": "^16.0.0", "swagger-ui-express": "^5.0.0", @@ -4397,6 +4398,19 @@ "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", "license": "MIT" }, + "node_modules/nats": { + "version": "2.29.3", + "resolved": "https://registry.npmjs.org/nats/-/nats-2.29.3.tgz", + "integrity": "sha512-tOQCRCwC74DgBTk4pWZ9V45sk4d7peoE2njVprMRCBXrhJ5q5cYM7i6W+Uvw2qUrcfOSnuisrX7bEx3b3Wx4QA==", + "deprecated": "Package moved. Use @nats-io/transport-node from https://github.com/nats-io/nats.js", + "license": "Apache-2.0", + "dependencies": { + "nkeys.js": "1.1.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -4420,6 +4434,18 @@ "dev": true, "license": "MIT" }, + "node_modules/nkeys.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/nkeys.js/-/nkeys.js-1.1.0.tgz", + "integrity": "sha512-tB/a0shZL5UZWSwsoeyqfTszONTt4k2YS0tuQioMOD180+MbombYVgzDUYHlx+gejYK6rgf08n/2Df99WY0Sxg==", + "license": "Apache-2.0", + "dependencies": { + "tweetnacl": "1.0.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/node-abi": { "version": "3.94.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.94.0.tgz", @@ -5821,6 +5847,12 @@ "node": "*" } }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", + "license": "Unlicense" + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", diff --git a/packages/zappier/package.json b/packages/zappier/package.json index b799e3c..5932a26 100644 --- a/packages/zappier/package.json +++ b/packages/zappier/package.json @@ -14,6 +14,7 @@ "dotenv": "^17.4.2", "express": "^4.19.2", "express-openapi-validator": "^5.3.0", + "nats": "^2.29.3", "qrcode": "^1.5.4", "stripe": "^16.0.0", "swagger-ui-express": "^5.0.0", diff --git a/packages/zappier/portal/app.js b/packages/zappier/portal/app.js index e9c0689..c785336 100644 --- a/packages/zappier/portal/app.js +++ b/packages/zappier/portal/app.js @@ -1,4 +1,4 @@ -const state = { me: null, usage: null, invoices: [], pricing: null }; +const state = { me: null, usage: null, invoices: [], pricing: null, statement: null }; const TOKEN_KEY = 'zappier-portal-token'; let authMode = 'login'; // 'login' | 'signup' @@ -118,8 +118,10 @@ async function load() { state.usage = await api('/usage'); state.invoices = (await api('/invoices')).invoices; state.pricing = await api('/pricing'); + state.statement = await api('/statement'); renderDashboard(); renderInvoices(); + renderStatement(); renderBilling(); renderSecurity(); renderDocs(); @@ -200,6 +202,34 @@ async function viewInvoice(id) { window.open(URL.createObjectURL(blob), '_blank'); } +/* ---------------- statement ---------------- */ + +function renderStatement() { + const st = state.statement || { prepaidCents: 0, credits: [], usage: [], payments: [] }; + const row = (list, cols) => + (list || []) + .map((r) => `${cols.map((c) => `${r[c] ?? ''}`).join('')}`) + .join('') || `None yet.`; + document.getElementById('statement').innerHTML = ` +

Statement

+

Prepaid balance, customer-service credits, metered usage, and payments. ${st.source === 'nats' ? 'Live from account-balance over NATS.' : 'From this portal’s ledger.'}

+
+
Prepaid balance
${fmt(st.prepaidCents || 0)}
+
Credits
${st.credits?.length || 0}
+
Usage events
${st.usage?.length || 0}
+
Payments
${st.payments?.length || 0}
+
+

Credits

+ + ${row(st.credits, ['cents', 'reason', 'agent', 'at'])}
CentsReasonAgentWhen
+

Usage

+ + ${row(st.usage, ['endpointId', 'cents', 'at'])}
EndpointCentsWhen
+

Payments

+ + ${row(st.payments, ['cents', 'kind', 'reason', 'at'])}
CentsKindReasonWhen
`; +} + /* ---------------- billing ---------------- */ function renderBilling() { diff --git a/packages/zappier/portal/index.html b/packages/zappier/portal/index.html index d7059c8..5897429 100644 --- a/packages/zappier/portal/index.html +++ b/packages/zappier/portal/index.html @@ -248,6 +248,7 @@