Skip to content

Credit codes and invites: Premium days via redeem-credits + affiliates (v0.51.0) - #245

Merged
ralyodio merged 1 commit into
mainfrom
feat/credit-codes-invites
Oct 26, 2026
Merged

ralyodio merged 1 commit into
mainfrom
feat/credit-codes-invites

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

Anthony 2026-10-26: "mint him a token i can give him to redeem credits ... add credit code redemptions and invite codes/(affiliate) to all properties". This is nichedb (rollout batch 2).

What a code grants

premium_days: an admin mints a CR-XXXX-XXXX-XXXX-XXXX code worth N days of Premium (1 to 3650, max redemptions, optional expiry). Redeeming it writes a Premium term through grantMembership, the same memberships row a paid CoinPay term writes (price 0, grant_ref = code:<id>), so it stacks after any Premium already held and every Premium check sees it. No parallel flag. Codes are stored as sha256 only.

  • Mint: /admin/credits, POST /api/v1/admin/credits {days,label,max,expires_days}, nichedb credits mint --days 30 --label "..." (admin key). List/revoke on the same surfaces.
  • Redeem: "Have a code?" on /settings and /account/billing, POST /api/v1/credits/redeem {code}, nichedb redeem <code>, MCP redeem_code.

What an invite gives

3 invites per account (INVITE_QUOTA), single use, 30-day expiry, a withdrawn unused one comes back. When the invited person creates an account by spending the emailed sign-in link (that proves the mailbox; an existing account accepting an invite gets nothing), both get 7 days of Premium (INVITE_BONUS_PREMIUM_DAYS), again as memberships rows (invite:<id>:invitee / :inviter).

  • /invites page, GET/POST /api/v1/invites, POST /api/v1/invites/:id/revoke, nichedb invites [create|revoke], MCP list_invites / create_invite.
  • /i/INV-... landing: stores the code in ndb_invite and the signup form posts it onto the login token (login_tokens.invite_code), so the link still counts when opened in another browser. /i/<number> stays the item page (the route only matches INV-).

Attribution, counted once

nichedb's affiliate links stay the existing @profullstack/referrals code (/r/<code>, ndb_ref cookie, 60% commission). On a new account aff.attribute() reports those codes as external: true and they go to users.referred_by exactly as before; only an affiliates-native code would be recorded in aff_attributions. No second cookie.

Schema (migration 0040, additive)

credit_* and aff_* tables (the packages' own DDL, verbatim), memberships.grant_ref + partial unique index (user_id, grant_ref), login_tokens.invite_code. grantMembership gains grantRef and uses a bare on conflict do nothing (payment_id or grant_ref).

Tests

test/credits.test.js runs on a fully migrated PGlite: redeem -> memberships row, already_redeemed/used_up/revoked/invalid, stacking, idempotent grant_ref, invite accept grants both once, quota + refund, self-invite, referral codes external. apps/web/src/routes/credits.test.js covers the routes (JSON vs form, admin-only mint, /i/ vs item ids). Full suite: 2597 pass, 0 fail. Also probed both stores over Bun SQL against a real Postgres 17.

Version bump to 0.51.0 included (15 manifests + CLI VERSION).

🤖 Generated with Claude Code

- Credit codes (@profullstack/redeem-credits 0.2, postgresStore over Bun SQL):
  an admin mints a CR-… code worth N days of Premium (/admin/credits,
  POST /api/v1/admin/credits, `nichedb credits mint`). Redeeming it ("Have a
  code?" on /settings and /account/billing, POST /api/v1/credits/redeem,
  `nichedb redeem`, MCP redeem_code) writes a Premium term through
  grantMembership, the same memberships row a paid term writes.
- Invites (@profullstack/affiliates 0.2): 3 per account, single use, 30 days.
  /invites, /api/v1/invites, `nichedb invites`, MCP list_invites/create_invite.
  /i/INV-… stores the invite in a cookie AND on the emailed link
  (login_tokens.invite_code), so another browser still counts. Accepted only
  when spending the link creates a new account (mailbox proven); then both
  people get 7 days of Premium (INVITE_BONUS_PREMIUM_DAYS).
- Attribution: the ndb_ref cookie still means the existing referrals
  programme. aff.attribute() reports those codes as external and they go to
  users.referred_by as before, so a sign-up is counted once.
- Migration 0040: credit_* and aff_* tables (the packages' own DDL),
  memberships.grant_ref + unique (user_id, grant_ref) so a free grant is
  idempotent, login_tokens.invite_code. grantMembership takes grantRef.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​profullstack/​affiliates@​0.2.07610010090100
Added@​profullstack/​redeem-credits@​0.2.07610010090100

View full report

@ralyodio
ralyodio merged commit fddf9a4 into main Oct 26, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant