Bind each customer to a Verae userId for hop tracing
Some checks are pending
offline / test (push) Waiting to run
Some checks are pending
offline / test (push) Waiting to run
Signup registers/binds a Verae central user and stores veraeUserId. Public access stays the zappier API key. Chain JWTs stay server-side behind tokenRef. Authz, billing, and jobs.watch carry veraeUserId.
This commit is contained in:
parent
1b199ca4d4
commit
345aeeead9
79 changed files with 703 additions and 95 deletions
|
|
@ -5,16 +5,23 @@ export class AccountBooks {
|
|||
constructor() {
|
||||
/** @type {Record<string, number>} */
|
||||
this.prepaid = {};
|
||||
/** @type {Record<string, string>} */
|
||||
this.veraeUserIds = {};
|
||||
this.credits = [];
|
||||
this.usage = [];
|
||||
this.payments = [];
|
||||
}
|
||||
|
||||
remember(customerId, veraeUserId) {
|
||||
if (customerId && veraeUserId) this.veraeUserIds[customerId] = veraeUserId;
|
||||
}
|
||||
|
||||
prepaidCents(customerId) {
|
||||
return this.prepaid[customerId] ?? 0;
|
||||
}
|
||||
|
||||
adjust({ customerId, cents, reason, agent, kind = 'credit' }) {
|
||||
adjust({ customerId, cents, reason, agent, kind = 'credit', veraeUserId }) {
|
||||
this.remember(customerId, veraeUserId);
|
||||
const delta = Math.trunc(Number(cents) || 0);
|
||||
const next = this.prepaidCents(customerId) + delta;
|
||||
this.prepaid[customerId] = next;
|
||||
|
|
@ -34,6 +41,7 @@ export class AccountBooks {
|
|||
}
|
||||
|
||||
recordUsage(entry) {
|
||||
this.remember(entry.customerId, entry.veraeUserId);
|
||||
const cents = Number(entry.cents) || 0;
|
||||
const customerId = entry.customerId;
|
||||
const next = this.prepaidCents(customerId) - cents;
|
||||
|
|
@ -63,6 +71,7 @@ export class AccountBooks {
|
|||
const match = (rows) => rows.filter((r) => r.customerId === customerId).slice(0, 100);
|
||||
return {
|
||||
customerId,
|
||||
veraeUserId: this.veraeUserIds[customerId],
|
||||
prepaidCents: this.prepaidCents(customerId),
|
||||
credits: match(this.credits),
|
||||
usage: match(this.usage),
|
||||
|
|
@ -74,6 +83,7 @@ export class AccountBooks {
|
|||
export function handle(subject, payload, books) {
|
||||
const p = payload || {};
|
||||
if (subject.endsWith('balance.get') || subject.endsWith('statement.get')) {
|
||||
books.remember(p.customerId, p.veraeUserId);
|
||||
return books.statement(p.customerId);
|
||||
}
|
||||
if (subject.endsWith('balance.adjust') || subject.endsWith('credit.applied')) {
|
||||
|
|
|
|||
|
|
@ -5,11 +5,16 @@ 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.BALANCE_ADJUST,
|
||||
{ customerId: 'c1', veraeUserId: 'vu_abc', 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.veraeUserId, 'vu_abc');
|
||||
assert.equal(st.credits[0].cents, 500);
|
||||
assert.equal(st.usage[0].endpointId, 'timestamp');
|
||||
assert.equal(st.payments[0].kind, 'payment');
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue