@softure-ai/billing 0.1.6 → 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 +5 -0
- package/dist/index.js +1 -1
- package/dist/mailing/reminder-mail.d.ts +8 -2
- package/dist/mailing/reminder-mail.d.ts.map +1 -1
- package/dist/mailing/reminder-mail.js +6 -1
- package/dist/mailing/reminder-mail.js.map +1 -1
- package/module.json +1 -1
- package/package.json +1 -1
- package/src/index.ts +1 -1
- package/src/mailing/reminder-mail.ts +14 -3
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,11 @@ Newest first. Each version lists what changed for an app that uses `@softure-ai/
|
|
|
4
4
|
production, the version gets a line `verified in: <app>@<commit>` ([docs/05](../../docs/05-adoption-playbook.md),
|
|
5
5
|
"Definition of done"). Versions before the first one below are described in their GitHub Releases (`billing@x.y.z`).
|
|
6
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
|
+
|
|
7
12
|
## 0.1.6
|
|
8
13
|
|
|
9
14
|
- `module.json` names `mailing` and `ops` as optional dependencies.
|
package/dist/index.js
CHANGED
|
@@ -25,7 +25,7 @@ export const BILLING_RATE_LIMIT_BUCKETS = {
|
|
|
25
25
|
export const billing = defineModule({
|
|
26
26
|
manifest: {
|
|
27
27
|
id: MODULE_ID,
|
|
28
|
-
version: "0.1.
|
|
28
|
+
version: "0.1.7",
|
|
29
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"],
|
|
@@ -28,11 +28,17 @@ export interface AccessReminderSummary {
|
|
|
28
28
|
readonly due: number;
|
|
29
29
|
/** Mails sent now. */
|
|
30
30
|
readonly sent: number;
|
|
31
|
-
/**
|
|
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
|
-
/**
|
|
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
|
|
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":"AAAA,gGAAgG;AAChG,oGAAoG;AACpG,oGAAoG;AACpG,yFAAyF;AACzF,OAAO,EAAE,aAAa,EAAsB,MAAM,kBAAkB,CAAC;AAGrE,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,EAAE,sBAAsB,EAA2B,MAAM,gBAAgB,CAAC;AACjF,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAAmC,MAAM,wBAAwB,CAAC;AAC9F,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,gGAAgG;AAChG,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAG,CAAC;
|
|
1
|
+
{"version":3,"file":"reminder-mail.js","sourceRoot":"","sources":["../../src/mailing/reminder-mail.ts"],"names":[],"mappings":"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"}
|
package/module.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@softure-ai/billing",
|
|
3
|
-
"version": "0.1.
|
|
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",
|
package/src/index.ts
CHANGED
|
@@ -28,7 +28,7 @@ export const BILLING_RATE_LIMIT_BUCKETS = {
|
|
|
28
28
|
export const billing = defineModule({
|
|
29
29
|
manifest: {
|
|
30
30
|
id: MODULE_ID,
|
|
31
|
-
version: "0.1.
|
|
31
|
+
version: "0.1.7",
|
|
32
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"],
|
|
@@ -38,11 +38,17 @@ export interface AccessReminderSummary {
|
|
|
38
38
|
readonly due: number;
|
|
39
39
|
/** Mails sent now. */
|
|
40
40
|
readonly sent: number;
|
|
41
|
-
/**
|
|
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
|
-
/**
|
|
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;
|