@softure-ai/billing 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,15 @@
1
+ # Changelog
2
+
3
+ Newest first. Each version lists what changed for an app that uses `@softure-ai/billing`. When an app has run a version in
4
+ production, the version gets a line `verified in: <app>@<commit>` ([docs/05](../../docs/05-adoption-playbook.md),
5
+ "Definition of done"). Versions before the first one below are described in their GitHub Releases (`billing@x.y.z`).
6
+
7
+ ## 0.1.7
8
+
9
+ - `sendAccessReminders` stops at a refused mail account or a spent quota (mailing's `halted`, counted in
10
+ `retryLater`) and skips a reminder whose send was interrupted long ago (mailing's `uncertain`, counted in `skipped`).
11
+
12
+ ## 0.1.6
13
+
14
+ - `module.json` names `mailing` and `ops` as optional dependencies.
15
+ - Adapters and commands use the configured database handle; `@softure-ai/ui` is a peer dependency.
package/README.md CHANGED
@@ -1,10 +1,6 @@
1
1
  # @softure-ai/billing
2
2
 
3
- **Status:** wave 3 · entitlements and the write guard (MO-1); plans, pricing tiles, the payment page
4
- and the manual adapter (MO-2); Stripe Checkout with verified webhooks and refunds (MO-3); stored
5
- invoice requests, recorded manual grants with revoke and an account history (FU-9); reminder mail
6
- before and after access ends (FU-6) · depends on: core, db, ui, security, auth (mailing optional, for
7
- `@softure-ai/billing/mailing`)
3
+ **Depends on:** core, db, ui, security, auth (mailing optional, for `@softure-ai/billing/mailing`).
8
4
 
9
5
  Decides whether an account may still write: a trial every account starts with, paid access (dated
10
6
  or lifetime) and a read-only state once both end. It replaces FIRE_TRACKER's access logic
package/dist/index.js CHANGED
@@ -25,8 +25,8 @@ export const BILLING_RATE_LIMIT_BUCKETS = {
25
25
  export const billing = defineModule({
26
26
  manifest: {
27
27
  id: MODULE_ID,
28
- version: "0.1.5",
29
- dependsOn: { security: "^0.1.0", auth: "^0.1.0" },
28
+ version: "0.1.7",
29
+ dependsOn: { security: "^0.1.0", auth: "^0.1.0", mailing: "^0.1.0?", ops: "^0.1.0?" },
30
30
  dbSchema: "billing",
31
31
  tables: ["entitlements", "payments", "payment_requests", "manual_grants"],
32
32
  env: [
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -28,11 +28,17 @@ export interface AccessReminderSummary {
28
28
  readonly due: number;
29
29
  /** Mails sent now. */
30
30
  readonly sent: number;
31
- /** Mails an earlier run sent (or refused), or another run is sending right now. */
31
+ /**
32
+ * Mails an earlier run sent (or refused), another run is sending right now, or whose send was interrupted
33
+ * so long ago that it may have gone out (mailing's `uncertain`).
34
+ */
32
35
  readonly skipped: number;
33
36
  /** Mails refused for good (an invalid address, the provider rejected it). */
34
37
  readonly rejected: number;
35
- /** Mails the provider could not take now; the next run sends them. */
38
+ /**
39
+ * Mails the provider could not take now; the next run sends them. A refused account or a spent quota stops the
40
+ * run there: the mails after it are not counted and go out on the next run.
41
+ */
36
42
  readonly retryLater: number;
37
43
  }
38
44
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"reminder-mail.d.ts","sourceRoot":"","sources":["../../src/mailing/reminder-mail.ts"],"names":[],"mappings":"AAIA,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAA0B,KAAK,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEjF,OAAO,EAAuB,KAAK,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AAG9F,gGAAgG;AAChG,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAE7C,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC,iFAAiF;IACjF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,uCAAuC;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,wEAAwE;AACxE,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,YAAY,EAAE,SAAS,GAAG,MAAM,CAAC,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpG,MAAM,WAAW,0BAA2B,SAAQ,0BAA0B;IAC5E,kGAAkG;IAClG,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,yCAAyC;IACzC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD;AAED,MAAM,WAAW,qBAAqB;IACpC,wCAAwC;IACxC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,sBAAsB;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,mFAAmF;IACnF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,6EAA6E;IAC7E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,sEAAsE;IACtE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAeD;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,eAAe,EAAE,KAAK,EAAE,uBAAuB,GAAG,kBAAkB,CAStH;AAMD;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,aAAa,CAAC,SAAS,CAAC,EAAE,OAAO,GAAE,0BAA+B,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAqBjJ"}
1
+ {"version":3,"file":"reminder-mail.d.ts","sourceRoot":"","sources":["../../src/mailing/reminder-mail.ts"],"names":[],"mappings":"AAIA,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAA0B,KAAK,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEjF,OAAO,EAAuB,KAAK,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AAG9F,gGAAgG;AAChG,eAAO,MAAM,yBAAyB,MAAM,CAAC;AAE7C,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC,iFAAiF;IACjF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,uCAAuC;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,wEAAwE;AACxE,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,YAAY,EAAE,SAAS,GAAG,MAAM,CAAC,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpG,MAAM,WAAW,0BAA2B,SAAQ,0BAA0B;IAC5E,kGAAkG;IAClG,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,yCAAyC;IACzC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD;AAED,MAAM,WAAW,qBAAqB;IACpC,wCAAwC;IACxC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,sBAAsB;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,6EAA6E;IAC7E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAeD;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,eAAe,EAAE,KAAK,EAAE,uBAAuB,GAAG,kBAAkB,CAStH;AAMD;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,aAAa,CAAC,SAAS,CAAC,EAAE,OAAO,GAAE,0BAA+B,GAAG,OAAO,CAAC,qBAAqB,CAAC,CA0BjJ"}
@@ -53,10 +53,15 @@ export async function sendAccessReminders(ctx, options = {}) {
53
53
  const lastDay = formatLastDay(reminder.endsAt, ctx.config.locale, ctx.config.timezone);
54
54
  const mail = renderAccessReminderMail(messages, { kind: reminder.kind, lastDay, link });
55
55
  const outcome = await deliverOnce(ctx, { scope: getAccessReminderScope(reminder.kind, reminder.userId, reminder.endsAt), mail: { to: reminder.email, ...mail } });
56
- if (outcome.status === "done" || outcome.status === "in-flight") {
56
+ if (outcome.status === "done" || outcome.status === "in-flight" || outcome.status === "uncertain") {
57
57
  counts.skipped += 1;
58
58
  continue;
59
59
  }
60
+ if (outcome.status === "halted") {
61
+ // The account cannot send (refused key, spent quota): every other mail would fail the same way.
62
+ counts.retryLater += 1;
63
+ break;
64
+ }
60
65
  if (outcome.status === "sent")
61
66
  counts.sent += 1;
62
67
  else if (outcome.status === "rejected")
@@ -1 +1 @@
1
- {"version":3,"file":"reminder-mail.js","sourceRoot":"","sources":["../../src/mailing/reminder-mail.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"reminder-mail.js","sourceRoot":"","sources":["../../src/mailing/reminder-mail.ts"],"names":[],"mappings":"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"}
@@ -3,7 +3,7 @@
3
3
  // undeclared `adminRole`) fails on the first one.
4
4
  import { systemClock } from "@softure-ai/core";
5
5
  import { getSoftureConfig } from "@softure-ai/core/next";
6
- import { getSharedDatabase } from "@softure-ai/db";
6
+ import { getConfiguredDatabase } from "@softure-ai/db";
7
7
  import { assertAdminRoleDeclared } from "../server/setup.js";
8
8
  export async function getBillingContext(config = getSoftureConfig()) {
9
9
  assertAdminRoleDeclared(config);
@@ -11,7 +11,7 @@ export async function getBillingContext(config = getSoftureConfig()) {
11
11
  // Unreachable for a validated config: the module has a database schema.
12
12
  throw new Error("@softure-ai/billing: softure.config.ts has no database; billing needs one");
13
13
  }
14
- const { db } = await getSharedDatabase(config.database.url);
14
+ const { db } = await getConfiguredDatabase(config.database);
15
15
  return { db, clock: systemClock, config };
16
16
  }
17
17
  //# sourceMappingURL=context.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/next/context.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,6FAA6F;AAC7F,kDAAkD;AAClD,OAAO,EAAE,WAAW,EAAsB,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEnD,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAE7D,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,SAAwB,gBAAgB,EAAE;IAChF,uBAAuB,CAAC,MAAM,CAAC,CAAC;IAChC,IAAI,MAAM,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;QAC7B,wEAAwE;QACxE,MAAM,IAAI,KAAK,CAAC,2EAA2E,CAAC,CAAC;IAC/F,CAAC;IACD,MAAM,EAAE,EAAE,EAAE,GAAG,MAAM,iBAAiB,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC5D,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC;AAC5C,CAAC"}
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/next/context.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,6FAA6F;AAC7F,kDAAkD;AAClD,OAAO,EAAE,WAAW,EAAsB,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAE7D,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,SAAwB,gBAAgB,EAAE;IAChF,uBAAuB,CAAC,MAAM,CAAC,CAAC;IAChC,IAAI,MAAM,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;QAC7B,wEAAwE;QACxE,MAAM,IAAI,KAAK,CAAC,2EAA2E,CAAC,CAAC;IAC/F,CAAC;IACD,MAAM,EAAE,EAAE,EAAE,GAAG,MAAM,qBAAqB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC5D,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC;AAC5C,CAAC"}
package/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "billing",
3
- "version": "0.1.5",
4
- "dependsOn": { "security": "^0.1.0", "auth": "^0.1.0" },
3
+ "version": "0.1.7",
4
+ "dependsOn": { "security": "^0.1.0", "auth": "^0.1.0", "mailing": "^0.1.0?", "ops": "^0.1.0?" },
5
5
  "dbSchema": "billing",
6
6
  "tables": ["entitlements", "payments", "payment_requests", "manual_grants"],
7
7
  "env": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softure-ai/billing",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "SOFTURE AI billing module: trial, paid and read-only entitlements from a pure state machine, billing.entitlements apart from auth.users, a write guard for server actions, access badge and notice components, a privacy contributor, reminder mail before access ends over @softure-ai/mailing, manual invoice requests handed to the owner once and expired after a configurable age, grant-plan and revoke-grant ops scripts, and import-entitlements and pin-trials ops scripts for accounts that exist before billing is turned on.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -15,6 +15,7 @@
15
15
  },
16
16
  "files": [
17
17
  "dist",
18
+ "CHANGELOG.md",
18
19
  "src",
19
20
  "!src/**/*.test.ts",
20
21
  "!src/**/*.test.tsx",
@@ -66,15 +67,18 @@
66
67
  "@softure-ai/db": "^0.1.0",
67
68
  "@softure-ai/ops": "^0.1.0",
68
69
  "@softure-ai/security": "^0.1.0",
69
- "@softure-ai/ui": "^0.1.0",
70
70
  "zod": "^4.6.5"
71
71
  },
72
72
  "peerDependencies": {
73
73
  "@softure-ai/mailing": "^0.1.0",
74
+ "@softure-ai/ui": "^0.1.0",
74
75
  "drizzle-orm": "^0.45.2",
75
76
  "next": "^16.0.0",
76
77
  "react": "^19.0.0"
77
78
  },
79
+ "devDependencies": {
80
+ "@softure-ai/ui": "^0.1.7"
81
+ },
78
82
  "peerDependenciesMeta": {
79
83
  "@softure-ai/mailing": {
80
84
  "optional": true
package/src/index.ts CHANGED
@@ -28,8 +28,8 @@ export const BILLING_RATE_LIMIT_BUCKETS = {
28
28
  export const billing = defineModule({
29
29
  manifest: {
30
30
  id: MODULE_ID,
31
- version: "0.1.5",
32
- dependsOn: { security: "^0.1.0", auth: "^0.1.0" },
31
+ version: "0.1.7",
32
+ dependsOn: { security: "^0.1.0", auth: "^0.1.0", mailing: "^0.1.0?", ops: "^0.1.0?" },
33
33
  dbSchema: "billing",
34
34
  tables: ["entitlements", "payments", "payment_requests", "manual_grants"],
35
35
  env: [
@@ -38,11 +38,17 @@ export interface AccessReminderSummary {
38
38
  readonly due: number;
39
39
  /** Mails sent now. */
40
40
  readonly sent: number;
41
- /** Mails an earlier run sent (or refused), or another run is sending right now. */
41
+ /**
42
+ * Mails an earlier run sent (or refused), another run is sending right now, or whose send was interrupted
43
+ * so long ago that it may have gone out (mailing's `uncertain`).
44
+ */
42
45
  readonly skipped: number;
43
46
  /** Mails refused for good (an invalid address, the provider rejected it). */
44
47
  readonly rejected: number;
45
- /** Mails the provider could not take now; the next run sends them. */
48
+ /**
49
+ * Mails the provider could not take now; the next run sends them. A refused account or a spent quota stops the
50
+ * run there: the mails after it are not counted and go out on the next run.
51
+ */
46
52
  readonly retryLater: number;
47
53
  }
48
54
 
@@ -95,10 +101,15 @@ export async function sendAccessReminders(ctx: ModuleContext<Queryable>, options
95
101
  const lastDay = formatLastDay(reminder.endsAt, ctx.config.locale, ctx.config.timezone);
96
102
  const mail = renderAccessReminderMail(messages, { kind: reminder.kind, lastDay, link });
97
103
  const outcome = await deliverOnce(ctx, { scope: getAccessReminderScope(reminder.kind, reminder.userId, reminder.endsAt), mail: { to: reminder.email, ...mail } });
98
- if (outcome.status === "done" || outcome.status === "in-flight") {
104
+ if (outcome.status === "done" || outcome.status === "in-flight" || outcome.status === "uncertain") {
99
105
  counts.skipped += 1;
100
106
  continue;
101
107
  }
108
+ if (outcome.status === "halted") {
109
+ // The account cannot send (refused key, spent quota): every other mail would fail the same way.
110
+ counts.retryLater += 1;
111
+ break;
112
+ }
102
113
  if (outcome.status === "sent") counts.sent += 1;
103
114
  else if (outcome.status === "rejected") counts.rejected += 1;
104
115
  else counts.retryLater += 1;
@@ -3,7 +3,7 @@
3
3
  // undeclared `adminRole`) fails on the first one.
4
4
  import { systemClock, type SoftureConfig } from "@softure-ai/core";
5
5
  import { getSoftureConfig } from "@softure-ai/core/next";
6
- import { getSharedDatabase } from "@softure-ai/db";
6
+ import { getConfiguredDatabase } from "@softure-ai/db";
7
7
  import type { BillingContext } from "../server/entitlements.js";
8
8
  import { assertAdminRoleDeclared } from "../server/setup.js";
9
9
 
@@ -13,6 +13,6 @@ export async function getBillingContext(config: SoftureConfig = getSoftureConfig
13
13
  // Unreachable for a validated config: the module has a database schema.
14
14
  throw new Error("@softure-ai/billing: softure.config.ts has no database; billing needs one");
15
15
  }
16
- const { db } = await getSharedDatabase(config.database.url);
16
+ const { db } = await getConfiguredDatabase(config.database);
17
17
  return { db, clock: systemClock, config };
18
18
  }