Account self-service: license labels, password change, billing/receipts #4

Closed
opened 2026-07-29 22:33:11 +00:00 by Cordy · 2 comments
Owner

Dashboard quality-of-life for customers.

License labels — customers holding several licenses (e.g. an MSP licensing multiple end-customers) can name each one. PATCH /v1/licenses/{id} {label} (owner-gated: license must belong to the caller's account/org); label shown in the dashboard list. Stored on the record, not in the signed payload (no re-signing).

Password changePOST /v1/account/password {current, new}: verify current (same PBKDF2 path as login), ≥10 chars, re-hash, persist; revoke other sessions. Dashboard gets a small "Account" section.

Billing / receipts — list past payments with receipt links. Server-side proxy (needs PADDLE_API_KEY env — server-side secret, never in the page): GET /v1/billing returns the account's transactions (date, amount, seats, cycle) with Paddle invoice/receipt URLs fetched via the Paddle API. Records already store paddleTxn per license; renewals (refresh issue) should append txn history per record.

Site half: dashboard sections for all three in account.html.

Decision needed at greenlight (Nikola): Paddle API key (sandbox now, live later) as a server env secret on the VPS.

Acceptance: label set + displayed; password change works and old password stops working; billing list shows sandbox transactions with working receipt links.

Dashboard quality-of-life for customers. **License labels** — customers holding several licenses (e.g. an MSP licensing multiple end-customers) can name each one. `PATCH /v1/licenses/{id}` `{label}` (owner-gated: license must belong to the caller's account/org); label shown in the dashboard list. Stored on the record, not in the signed payload (no re-signing). **Password change** — `POST /v1/account/password` `{current, new}`: verify current (same PBKDF2 path as login), ≥10 chars, re-hash, persist; revoke other sessions. Dashboard gets a small "Account" section. **Billing / receipts** — list past payments with receipt links. Server-side proxy (needs `PADDLE_API_KEY` env — server-side secret, never in the page): `GET /v1/billing` returns the account's transactions (date, amount, seats, cycle) with Paddle invoice/receipt URLs fetched via the Paddle API. Records already store `paddleTxn` per license; renewals (refresh issue) should append txn history per record. **Site half:** dashboard sections for all three in `account.html`. **Decision needed at greenlight (Nikola):** Paddle API key (sandbox now, live later) as a server env secret on the VPS. **Acceptance:** label set + displayed; password change works and old password stops working; billing list shows sandbox transactions with working receipt links.
Author
Owner

Partially shipped (PR #7 + site dashboard update):

  • License labels: PATCH /v1/licenses/{id} (owner-gated, 64-char cap, not-found/not-owned indistinguishable), label in the GET /v1/licenses view, inline edit UI on account.html.
  • Password change: POST /v1/account/password (verifies current, min 10 chars, re-salts, revokes all other sessions), form on account.html.

Remaining in this issue: billing/receipts view — blocked on PADDLE_API_KEY being provisioned on the VPS (guide delivered at end of this run). Keeping open for that.

**Partially shipped** (PR #7 + site dashboard update): - ✅ License labels: `PATCH /v1/licenses/{id}` (owner-gated, 64-char cap, not-found/not-owned indistinguishable), label in the `GET /v1/licenses` view, inline edit UI on account.html. - ✅ Password change: `POST /v1/account/password` (verifies current, min 10 chars, re-salts, revokes all other sessions), form on account.html. **Remaining in this issue:** billing/receipts view — blocked on `PADDLE_API_KEY` being provisioned on the VPS (guide delivered at end of this run). Keeping open for that.
Author
Owner

Receipts shipped (PR #9) — issue complete.

  • GET /v1/receipts: org-scoped Paddle transaction history (date, amount, currency, status, invoice number), via the PADDLE_API_KEY now on the VPS (PADDLE_API_URL selects sandbox; live = swap both at launch alongside the client token).
  • GET /v1/receipts/{id}/invoice: authorized invoice-PDF URL proxy — a transaction is visible only if its Paddle customer id appears on one of the org's license records; foreign txns are indistinguishable from not-found.
  • Site: Billing section on account.html — receipt list with per-row "Invoice PDF".

Together with the earlier half (labels, password change), everything in this issue is live. Tested against a fake Paddle server (listing, owned/foreign invoice authorization, unconfigured 503).

Also cleaned up in passing: the one-shot patch workflows had committed the 9 MB build binary to the repo root (go build ./... + git add -A) — removed; future one-shot workflows should build to /tmp.

Receipts shipped (PR #9) — issue complete. - `GET /v1/receipts`: org-scoped Paddle transaction history (date, amount, currency, status, invoice number), via the `PADDLE_API_KEY` now on the VPS (`PADDLE_API_URL` selects sandbox; live = swap both at launch alongside the client token). - `GET /v1/receipts/{id}/invoice`: authorized invoice-PDF URL proxy — a transaction is visible only if its Paddle customer id appears on one of the org's license records; foreign txns are indistinguishable from not-found. - Site: **Billing** section on account.html — receipt list with per-row "Invoice PDF". Together with the earlier half (labels, password change), everything in this issue is live. Tested against a fake Paddle server (listing, owned/foreign invoice authorization, unconfigured 503). Also cleaned up in passing: the one-shot patch workflows had committed the 9 MB build binary to the repo root (`go build ./...` + `git add -A`) — removed; future one-shot workflows should build to `/tmp`.
Cordy closed this issue 2026-07-30 00:52:12 +00:00
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: Cordy/cairn-license-server#4
No description provided.