@softure-ai/billing 0.0.0-stage → 0.1.5
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/LICENSE +21 -0
- package/README.md +699 -2
- package/dist/calendar.d.ts +26 -0
- package/dist/calendar.d.ts.map +1 -0
- package/dist/calendar.js +81 -0
- package/dist/calendar.js.map +1 -0
- package/dist/contract.d.ts +187 -0
- package/dist/contract.d.ts.map +1 -0
- package/dist/contract.js +6 -0
- package/dist/contract.js.map +1 -0
- package/dist/currency-digits.d.ts +3 -0
- package/dist/currency-digits.d.ts.map +1 -0
- package/dist/currency-digits.js +32 -0
- package/dist/currency-digits.js.map +1 -0
- package/dist/entitlement.d.ts +25 -0
- package/dist/entitlement.d.ts.map +1 -0
- package/dist/entitlement.js +75 -0
- package/dist/entitlement.js.map +1 -0
- package/dist/fields.d.ts +25 -0
- package/dist/fields.d.ts.map +1 -0
- package/dist/fields.js +27 -0
- package/dist/fields.js.map +1 -0
- package/dist/index.d.ts +274 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +73 -0
- package/dist/index.js.map +1 -0
- package/dist/invoice.d.ts +29 -0
- package/dist/invoice.d.ts.map +1 -0
- package/dist/invoice.js +35 -0
- package/dist/invoice.js.map +1 -0
- package/dist/mailing/index.d.ts +2 -0
- package/dist/mailing/index.d.ts.map +1 -0
- package/dist/mailing/index.js +4 -0
- package/dist/mailing/index.js.map +1 -0
- package/dist/mailing/reminder-mail.d.ts +50 -0
- package/dist/mailing/reminder-mail.d.ts.map +1 -0
- package/dist/mailing/reminder-mail.js +71 -0
- package/dist/mailing/reminder-mail.js.map +1 -0
- package/dist/manual.d.ts +12 -0
- package/dist/manual.d.ts.map +1 -0
- package/dist/manual.js +22 -0
- package/dist/manual.js.map +1 -0
- package/dist/messages/en.d.ts +176 -0
- package/dist/messages/en.d.ts.map +1 -0
- package/dist/messages/en.js +151 -0
- package/dist/messages/en.js.map +1 -0
- package/dist/messages/index.d.ts +359 -0
- package/dist/messages/index.d.ts.map +1 -0
- package/dist/messages/index.js +14 -0
- package/dist/messages/index.js.map +1 -0
- package/dist/messages/pl.d.ts +3 -0
- package/dist/messages/pl.d.ts.map +1 -0
- package/dist/messages/pl.js +151 -0
- package/dist/messages/pl.js.map +1 -0
- package/dist/next/access.d.ts +16 -0
- package/dist/next/access.d.ts.map +1 -0
- package/dist/next/access.js +24 -0
- package/dist/next/access.js.map +1 -0
- package/dist/next/actions.d.ts +24 -0
- package/dist/next/actions.d.ts.map +1 -0
- package/dist/next/actions.js +158 -0
- package/dist/next/actions.js.map +1 -0
- package/dist/next/context.d.ts +4 -0
- package/dist/next/context.d.ts.map +1 -0
- package/dist/next/context.js +17 -0
- package/dist/next/context.js.map +1 -0
- package/dist/next/current-entitlement.d.ts +18 -0
- package/dist/next/current-entitlement.d.ts.map +1 -0
- package/dist/next/current-entitlement.js +26 -0
- package/dist/next/current-entitlement.js.map +1 -0
- package/dist/next/index.d.ts +8 -0
- package/dist/next/index.d.ts.map +1 -0
- package/dist/next/index.js +12 -0
- package/dist/next/index.js.map +1 -0
- package/dist/next/pages.d.ts +24 -0
- package/dist/next/pages.d.ts.map +1 -0
- package/dist/next/pages.js +166 -0
- package/dist/next/pages.js.map +1 -0
- package/dist/next/pricing.d.ts +12 -0
- package/dist/next/pricing.d.ts.map +1 -0
- package/dist/next/pricing.js +20 -0
- package/dist/next/pricing.js.map +1 -0
- package/dist/next/route.d.ts +11 -0
- package/dist/next/route.d.ts.map +1 -0
- package/dist/next/route.js +56 -0
- package/dist/next/route.js.map +1 -0
- package/dist/options.d.ts +85 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +121 -0
- package/dist/options.js.map +1 -0
- package/dist/payment.d.ts +61 -0
- package/dist/payment.d.ts.map +1 -0
- package/dist/payment.js +12 -0
- package/dist/payment.js.map +1 -0
- package/dist/plans.d.ts +20 -0
- package/dist/plans.d.ts.map +1 -0
- package/dist/plans.js +53 -0
- package/dist/plans.js.map +1 -0
- package/dist/price.d.ts +12 -0
- package/dist/price.d.ts.map +1 -0
- package/dist/price.js +37 -0
- package/dist/price.js.map +1 -0
- package/dist/refund.d.ts +70 -0
- package/dist/refund.d.ts.map +1 -0
- package/dist/refund.js +109 -0
- package/dist/refund.js.map +1 -0
- package/dist/reminder.d.ts +30 -0
- package/dist/reminder.d.ts.map +1 -0
- package/dist/reminder.js +34 -0
- package/dist/reminder.js.map +1 -0
- package/dist/schema.d.ts +1023 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +77 -0
- package/dist/schema.js.map +1 -0
- package/dist/scripts/entitlement-scripts.d.ts +17 -0
- package/dist/scripts/entitlement-scripts.d.ts.map +1 -0
- package/dist/scripts/entitlement-scripts.js +167 -0
- package/dist/scripts/entitlement-scripts.js.map +1 -0
- package/dist/scripts/index.d.ts +3 -0
- package/dist/scripts/index.d.ts.map +1 -0
- package/dist/scripts/index.js +5 -0
- package/dist/scripts/index.js.map +1 -0
- package/dist/scripts/plan-scripts.d.ts +19 -0
- package/dist/scripts/plan-scripts.d.ts.map +1 -0
- package/dist/scripts/plan-scripts.js +106 -0
- package/dist/scripts/plan-scripts.js.map +1 -0
- package/dist/server/entitlements.d.ts +69 -0
- package/dist/server/entitlements.d.ts.map +1 -0
- package/dist/server/entitlements.js +202 -0
- package/dist/server/entitlements.js.map +1 -0
- package/dist/server/grants.d.ts +74 -0
- package/dist/server/grants.d.ts.map +1 -0
- package/dist/server/grants.js +174 -0
- package/dist/server/grants.js.map +1 -0
- package/dist/server/health.d.ts +3 -0
- package/dist/server/health.d.ts.map +1 -0
- package/dist/server/health.js +17 -0
- package/dist/server/health.js.map +1 -0
- package/dist/server/index.d.ts +12 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +14 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/options.d.ts +20 -0
- package/dist/server/options.d.ts.map +1 -0
- package/dist/server/options.js +38 -0
- package/dist/server/options.js.map +1 -0
- package/dist/server/payments.d.ts +140 -0
- package/dist/server/payments.d.ts.map +1 -0
- package/dist/server/payments.js +339 -0
- package/dist/server/payments.js.map +1 -0
- package/dist/server/plans.d.ts +50 -0
- package/dist/server/plans.d.ts.map +1 -0
- package/dist/server/plans.js +129 -0
- package/dist/server/plans.js.map +1 -0
- package/dist/server/privacy.d.ts +77 -0
- package/dist/server/privacy.d.ts.map +1 -0
- package/dist/server/privacy.js +110 -0
- package/dist/server/privacy.js.map +1 -0
- package/dist/server/reminders.d.ts +20 -0
- package/dist/server/reminders.d.ts.map +1 -0
- package/dist/server/reminders.js +85 -0
- package/dist/server/reminders.js.map +1 -0
- package/dist/server/requests.d.ts +80 -0
- package/dist/server/requests.d.ts.map +1 -0
- package/dist/server/requests.js +155 -0
- package/dist/server/requests.js.map +1 -0
- package/dist/server/setup.d.ts +9 -0
- package/dist/server/setup.d.ts.map +1 -0
- package/dist/server/setup.js +34 -0
- package/dist/server/setup.js.map +1 -0
- package/dist/server/take-back.d.ts +80 -0
- package/dist/server/take-back.d.ts.map +1 -0
- package/dist/server/take-back.js +138 -0
- package/dist/server/take-back.js.map +1 -0
- package/dist/server/user-id.d.ts +4 -0
- package/dist/server/user-id.d.ts.map +1 -0
- package/dist/server/user-id.js +11 -0
- package/dist/server/user-id.js.map +1 -0
- package/dist/stripe-currency.d.ts +27 -0
- package/dist/stripe-currency.d.ts.map +1 -0
- package/dist/stripe-currency.js +57 -0
- package/dist/stripe-currency.js.map +1 -0
- package/dist/stripe-webhook.d.ts +101 -0
- package/dist/stripe-webhook.d.ts.map +1 -0
- package/dist/stripe-webhook.js +209 -0
- package/dist/stripe-webhook.js.map +1 -0
- package/dist/stripe.d.ts +25 -0
- package/dist/stripe.d.ts.map +1 -0
- package/dist/stripe.js +116 -0
- package/dist/stripe.js.map +1 -0
- package/dist/ui/access-badge.d.ts +16 -0
- package/dist/ui/access-badge.d.ts.map +1 -0
- package/dist/ui/access-badge.js +43 -0
- package/dist/ui/access-badge.js.map +1 -0
- package/dist/ui/access-notice.d.ts +20 -0
- package/dist/ui/access-notice.d.ts.map +1 -0
- package/dist/ui/access-notice.js +41 -0
- package/dist/ui/access-notice.js.map +1 -0
- package/dist/ui/format.d.ts +11 -0
- package/dist/ui/format.d.ts.map +1 -0
- package/dist/ui/format.js +21 -0
- package/dist/ui/format.js.map +1 -0
- package/dist/ui/grant-form.d.ts +18 -0
- package/dist/ui/grant-form.d.ts.map +1 -0
- package/dist/ui/grant-form.js +23 -0
- package/dist/ui/grant-form.js.map +1 -0
- package/dist/ui/grant-history.d.ts +39 -0
- package/dist/ui/grant-history.d.ts.map +1 -0
- package/dist/ui/grant-history.js +36 -0
- package/dist/ui/grant-history.js.map +1 -0
- package/dist/ui/index.d.ts +9 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +12 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/payment-form.d.ts +23 -0
- package/dist/ui/payment-form.d.ts.map +1 -0
- package/dist/ui/payment-form.js +34 -0
- package/dist/ui/payment-form.js.map +1 -0
- package/dist/ui/payment-requests.d.ts +30 -0
- package/dist/ui/payment-requests.d.ts.map +1 -0
- package/dist/ui/payment-requests.js +31 -0
- package/dist/ui/payment-requests.js.map +1 -0
- package/dist/ui/pricing-tiles.d.ts +22 -0
- package/dist/ui/pricing-tiles.d.ts.map +1 -0
- package/dist/ui/pricing-tiles.js +40 -0
- package/dist/ui/pricing-tiles.js.map +1 -0
- package/migrations/0001_create_entitlements.sql +16 -0
- package/migrations/0002_create_payments.sql +30 -0
- package/migrations/0003_record_payment_grants.sql +25 -0
- package/migrations/0004_create_requests_and_grants.sql +58 -0
- package/migrations/0005_record_refunded_amounts.sql +18 -0
- package/migrations/0006_record_request_handover_and_prices.sql +38 -0
- package/migrations/0007_record_failed_refunds.sql +27 -0
- package/migrations/0008_record_request_handover_claims.sql +9 -0
- package/migrations/0009_record_pending_charge_states.sql +16 -0
- package/module.json +23 -0
- package/package.json +81 -4
- package/src/calendar.ts +90 -0
- package/src/contract.ts +181 -0
- package/src/currency-digits.ts +37 -0
- package/src/entitlement.ts +84 -0
- package/src/fields.ts +37 -0
- package/src/index.ts +163 -0
- package/src/invoice.ts +58 -0
- package/src/mailing/index.ts +11 -0
- package/src/mailing/reminder-mail.ts +108 -0
- package/src/manual.ts +31 -0
- package/src/messages/en.ts +150 -0
- package/src/messages/index.ts +18 -0
- package/src/messages/pl.ts +152 -0
- package/src/next/access.tsx +55 -0
- package/src/next/actions.ts +176 -0
- package/src/next/context.ts +18 -0
- package/src/next/current-entitlement.ts +36 -0
- package/src/next/index.ts +11 -0
- package/src/next/next-modules.d.ts +21 -0
- package/src/next/pages.tsx +267 -0
- package/src/next/pricing.tsx +39 -0
- package/src/next/route.ts +57 -0
- package/src/options.ts +128 -0
- package/src/payment.ts +77 -0
- package/src/plans.ts +63 -0
- package/src/price.ts +50 -0
- package/src/refund.ts +135 -0
- package/src/reminder.ts +52 -0
- package/src/schema.ts +86 -0
- package/src/scripts/entitlement-scripts.ts +188 -0
- package/src/scripts/index.ts +11 -0
- package/src/scripts/plan-scripts.ts +143 -0
- package/src/server/entitlements.ts +227 -0
- package/src/server/grants.ts +227 -0
- package/src/server/health.ts +18 -0
- package/src/server/index.ts +83 -0
- package/src/server/options.ts +52 -0
- package/src/server/payments.ts +434 -0
- package/src/server/plans.ts +143 -0
- package/src/server/privacy.ts +189 -0
- package/src/server/reminders.ts +113 -0
- package/src/server/requests.ts +201 -0
- package/src/server/setup.ts +37 -0
- package/src/server/take-back.ts +190 -0
- package/src/server/user-id.ts +12 -0
- package/src/stripe-currency.ts +65 -0
- package/src/stripe-webhook.ts +278 -0
- package/src/stripe.ts +130 -0
- package/src/ui/access-badge.tsx +64 -0
- package/src/ui/access-notice.tsx +73 -0
- package/src/ui/format.ts +25 -0
- package/src/ui/grant-form.tsx +69 -0
- package/src/ui/grant-history.tsx +127 -0
- package/src/ui/index.ts +25 -0
- package/src/ui/payment-form.tsx +111 -0
- package/src/ui/payment-requests.tsx +126 -0
- package/src/ui/pricing-tiles.tsx +105 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,aAAa,mDAAsB,CAAC;AAEjD,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAOvB,CAAC;AAEH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoBnB,CAAC;AAEH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAW1B,CAAC;AAEF,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAc1B,CAAC;AAEH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAevB,CAAC"}
|
package/dist/schema.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// Drizzle view of the module's tables (migrations/0001_create_entitlements.sql,
|
|
2
|
+
// 0002_create_payments.sql, 0003_record_payment_grants.sql, 0004_create_requests_and_grants.sql,
|
|
3
|
+
// 0005_record_refunded_amounts.sql, 0006_record_request_handover_and_prices.sql,
|
|
4
|
+
// 0007_record_failed_refunds.sql, 0008_record_request_handover_claims.sql and
|
|
5
|
+
// 0009_record_pending_charge_states.sql).
|
|
6
|
+
// The migrations are the source of truth; this file only types the queries.
|
|
7
|
+
import { bigint, boolean, integer, pgSchema, primaryKey, text, timestamp, uuid } from "drizzle-orm/pg-core";
|
|
8
|
+
export const billingSchema = pgSchema("billing");
|
|
9
|
+
export const entitlements = billingSchema.table("entitlements", {
|
|
10
|
+
userId: uuid("user_id").primaryKey(),
|
|
11
|
+
trialEndsAt: timestamp("trial_ends_at", { withTimezone: true }).notNull(),
|
|
12
|
+
paidUntil: timestamp("paid_until", { withTimezone: true }),
|
|
13
|
+
isLifetime: boolean("is_lifetime").notNull().default(false),
|
|
14
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull(),
|
|
15
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull(),
|
|
16
|
+
});
|
|
17
|
+
export const payments = billingSchema.table("payments", {
|
|
18
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
19
|
+
userId: uuid("user_id").notNull(),
|
|
20
|
+
provider: text("provider").notNull(),
|
|
21
|
+
checkoutId: text("checkout_id").notNull(),
|
|
22
|
+
paymentId: text("payment_id"),
|
|
23
|
+
planId: text("plan_id").notNull(),
|
|
24
|
+
amount: bigint("amount", { mode: "number" }).notNull(),
|
|
25
|
+
currency: text("currency").notNull(),
|
|
26
|
+
status: text("status", { enum: ["paid", "refunded"] }).notNull(),
|
|
27
|
+
paidAt: timestamp("paid_at", { withTimezone: true }).notNull(),
|
|
28
|
+
refundedAt: timestamp("refunded_at", { withTimezone: true }),
|
|
29
|
+
grantKind: text("grant_kind", { enum: ["period", "lifetime"] }),
|
|
30
|
+
grantedFrom: timestamp("granted_from", { withTimezone: true }),
|
|
31
|
+
grantedUntil: timestamp("granted_until", { withTimezone: true }),
|
|
32
|
+
refundedAmount: bigint("refunded_amount", { mode: "number" }).notNull().default(0),
|
|
33
|
+
takenBackDays: integer("taken_back_days").notNull().default(0),
|
|
34
|
+
refundsSeenAt: timestamp("refunds_seen_at", { withTimezone: true }),
|
|
35
|
+
pendingRefundedAmount: bigint("pending_refunded_amount", { mode: "number" }),
|
|
36
|
+
pendingRefundsSeenAt: timestamp("pending_refunds_seen_at", { withTimezone: true }),
|
|
37
|
+
});
|
|
38
|
+
export const refundFailures = billingSchema.table("refund_failures", {
|
|
39
|
+
paymentId: uuid("payment_id").notNull(),
|
|
40
|
+
refundId: text("refund_id").notNull(),
|
|
41
|
+
amount: bigint("amount", { mode: "number" }).notNull(),
|
|
42
|
+
refundCreatedAt: timestamp("refund_created_at", { withTimezone: true }).notNull(),
|
|
43
|
+
failedAt: timestamp("failed_at", { withTimezone: true }).notNull(),
|
|
44
|
+
recordedAt: timestamp("recorded_at", { withTimezone: true }).notNull(),
|
|
45
|
+
}, (table) => [primaryKey({ columns: [table.paymentId, table.refundId] })]);
|
|
46
|
+
export const paymentRequests = billingSchema.table("payment_requests", {
|
|
47
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
48
|
+
userId: uuid("user_id").notNull(),
|
|
49
|
+
planId: text("plan_id").notNull(),
|
|
50
|
+
invoiceName: text("invoice_name"),
|
|
51
|
+
invoiceTaxId: text("invoice_tax_id"),
|
|
52
|
+
invoiceAddress: text("invoice_address"),
|
|
53
|
+
status: text("status", { enum: ["open", "granted", "dismissed", "expired"] }).notNull(),
|
|
54
|
+
requestedAt: timestamp("requested_at", { withTimezone: true }).notNull(),
|
|
55
|
+
closedAt: timestamp("closed_at", { withTimezone: true }),
|
|
56
|
+
handedOverAt: timestamp("handed_over_at", { withTimezone: true }),
|
|
57
|
+
handoverClaimedAt: timestamp("handover_claimed_at", { withTimezone: true }),
|
|
58
|
+
amount: bigint("amount", { mode: "number" }),
|
|
59
|
+
currency: text("currency"),
|
|
60
|
+
});
|
|
61
|
+
export const manualGrants = billingSchema.table("manual_grants", {
|
|
62
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
63
|
+
userId: uuid("user_id").notNull(),
|
|
64
|
+
planId: text("plan_id").notNull(),
|
|
65
|
+
requestId: uuid("request_id"),
|
|
66
|
+
grantedBy: uuid("granted_by"),
|
|
67
|
+
grantedAt: timestamp("granted_at", { withTimezone: true }).notNull(),
|
|
68
|
+
grantKind: text("grant_kind", { enum: ["period", "lifetime"] }).notNull(),
|
|
69
|
+
grantedFrom: timestamp("granted_from", { withTimezone: true }),
|
|
70
|
+
grantedUntil: timestamp("granted_until", { withTimezone: true }),
|
|
71
|
+
status: text("status", { enum: ["active", "revoked"] }).notNull(),
|
|
72
|
+
revokedAt: timestamp("revoked_at", { withTimezone: true }),
|
|
73
|
+
revokedBy: uuid("revoked_by"),
|
|
74
|
+
amount: bigint("amount", { mode: "number" }),
|
|
75
|
+
currency: text("currency"),
|
|
76
|
+
});
|
|
77
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type Clock, type SoftureConfig } from "@softure-ai/core";
|
|
2
|
+
import { type OpsScript } from "@softure-ai/ops/scripts";
|
|
3
|
+
export interface ImportEntitlementsScriptArgs {
|
|
4
|
+
readonly file: string;
|
|
5
|
+
}
|
|
6
|
+
export type PinTrialsScriptArgs = Record<string, never>;
|
|
7
|
+
export interface EntitlementScriptOptions {
|
|
8
|
+
/** The time stored as the rows' `created_at` / `updated_at`; the system clock by default. */
|
|
9
|
+
readonly clock?: Clock;
|
|
10
|
+
}
|
|
11
|
+
/** The most rows one import file holds; split a larger one. */
|
|
12
|
+
export declare const MAX_IMPORT_ROWS = 50000;
|
|
13
|
+
/** `import-entitlements --file=…`: records trial ends, paid periods and lifetime access from a JSON file. */
|
|
14
|
+
export declare function createImportEntitlementsScript(config: SoftureConfig, options?: EntitlementScriptOptions): OpsScript<ImportEntitlementsScriptArgs>;
|
|
15
|
+
/** `pin-trials`: writes every derived trial into a row, so a config change moves none of them. */
|
|
16
|
+
export declare function createPinTrialsScript(config: SoftureConfig, options?: EntitlementScriptOptions): OpsScript<PinTrialsScriptArgs>;
|
|
17
|
+
//# sourceMappingURL=entitlement-scripts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entitlement-scripts.d.ts","sourceRoot":"","sources":["../../src/scripts/entitlement-scripts.ts"],"names":[],"mappings":"AAQA,OAAO,EAAmB,KAAK,KAAK,EAAE,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEnF,OAAO,EAAoC,KAAK,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAO3F,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,mBAAmB,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAExD,MAAM,WAAW,wBAAwB;IACvC,6FAA6F;IAC7F,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;CACxB;AAED,+DAA+D;AAC/D,eAAO,MAAM,eAAe,QAAS,CAAC;AAgGtC,6GAA6G;AAC7G,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,GAAE,wBAA6B,GAAG,SAAS,CAAC,4BAA4B,CAAC,CAmCrJ;AAWD,kGAAkG;AAClG,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,GAAE,wBAA6B,GAAG,SAAS,CAAC,mBAAmB,CAAC,CAcnI"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
// The operator's tools for turning billing on for accounts that already exist: safe ops scripts
|
|
2
|
+
// (`@softure-ai/ops/scripts`), dry run by default, `--commit` writes. `import-entitlements` records
|
|
3
|
+
// what another system knew (trial ends, paid periods, lifetime access) from a JSON file through
|
|
4
|
+
// `importEntitlement`; `pin-trials` writes every derived trial into a row before a config change
|
|
5
|
+
// would move it (`pinDerivedTrials`). Reports and refusals carry counts and row numbers, never an
|
|
6
|
+
// email.
|
|
7
|
+
import { readFile } from "node:fs/promises";
|
|
8
|
+
import { users } from "@softure-ai/auth";
|
|
9
|
+
import { ok, systemClock } from "@softure-ai/core";
|
|
10
|
+
import { defineOpsScript, refuseOpsScript } from "@softure-ai/ops/scripts";
|
|
11
|
+
import { count, eq, isNull } from "drizzle-orm";
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
import { entitlements } from "../schema.js";
|
|
14
|
+
import { getEntitlement, importEntitlement, pinDerivedTrials } from "../server/entitlements.js";
|
|
15
|
+
import { findAccountByEmail } from "../server/plans.js";
|
|
16
|
+
/** The most rows one import file holds; split a larger one. */
|
|
17
|
+
export const MAX_IMPORT_ROWS = 50_000;
|
|
18
|
+
/** The most problems a refusal lists. */
|
|
19
|
+
const MAX_LISTED = 10;
|
|
20
|
+
const importArgs = z.strictObject({
|
|
21
|
+
file: z.string().min(1, "--file=<path to a JSON file> is required"),
|
|
22
|
+
});
|
|
23
|
+
const pinArgs = z.strictObject({});
|
|
24
|
+
const instant = z.iso
|
|
25
|
+
.datetime({ offset: true, message: "must be an ISO 8601 date-time with an offset, e.g. 2026-11-01T00:00:00+01:00" })
|
|
26
|
+
.transform((text) => new Date(text));
|
|
27
|
+
const importRow = z
|
|
28
|
+
.strictObject({
|
|
29
|
+
email: z.string().trim().min(1, "is required").max(320),
|
|
30
|
+
trialEndsAt: instant.nullable().optional(),
|
|
31
|
+
paidUntil: instant.nullable().optional(),
|
|
32
|
+
isLifetime: z.boolean().optional(),
|
|
33
|
+
})
|
|
34
|
+
.refine((row) => (row.trialEndsAt ?? row.paidUntil ?? row.isLifetime) != null, "needs trialEndsAt, paidUntil or isLifetime");
|
|
35
|
+
const importFile = z.array(importRow).min(1, "holds no rows").max(MAX_IMPORT_ROWS, `holds more than ${String(MAX_IMPORT_ROWS)} rows; split it`);
|
|
36
|
+
/** "rows 3, 7 and 12" style, at most `MAX_LISTED` of them. */
|
|
37
|
+
function describeRows(rows) {
|
|
38
|
+
const listed = rows.slice(0, MAX_LISTED).map(String);
|
|
39
|
+
const more = rows.length > MAX_LISTED ? ` and ${String(rows.length - MAX_LISTED)} more` : "";
|
|
40
|
+
return `${rows.length === 1 ? "row" : "rows"} ${listed.join(", ")}${more}`;
|
|
41
|
+
}
|
|
42
|
+
/** The file's rows, or why they cannot be imported. Row numbers count from 1. */
|
|
43
|
+
async function readImportFile(path) {
|
|
44
|
+
let text;
|
|
45
|
+
try {
|
|
46
|
+
text = await readFile(path, "utf8");
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
const code = error instanceof Error && "code" in error ? String(error.code) : "unknown error";
|
|
50
|
+
return { ok: false, reason: `cannot read the file "${path}" (${code})` };
|
|
51
|
+
}
|
|
52
|
+
let json;
|
|
53
|
+
try {
|
|
54
|
+
json = JSON.parse(text);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return { ok: false, reason: `the file "${path}" is not valid JSON` };
|
|
58
|
+
}
|
|
59
|
+
const parsed = importFile.safeParse(json);
|
|
60
|
+
if (!parsed.success) {
|
|
61
|
+
const problems = parsed.error.issues.slice(0, MAX_LISTED).map((issue) => {
|
|
62
|
+
const [index, ...field] = issue.path;
|
|
63
|
+
if (typeof index !== "number")
|
|
64
|
+
return `the file ${issue.message}`;
|
|
65
|
+
return `row ${String(index + 1)}${field.length > 0 ? ` ${field.join(".")}` : ""}: ${issue.message}`;
|
|
66
|
+
});
|
|
67
|
+
return { ok: false, reason: `the file must be a JSON array of { email, trialEndsAt?, paidUntil?, isLifetime? }: ${problems.join("; ")}` };
|
|
68
|
+
}
|
|
69
|
+
return { ok: true, rows: parsed.data };
|
|
70
|
+
}
|
|
71
|
+
/** Row numbers of emails seen earlier in the file (compared as auth stores emails). */
|
|
72
|
+
function findRepeatedRows(rows) {
|
|
73
|
+
const seen = new Set();
|
|
74
|
+
const repeated = [];
|
|
75
|
+
rows.forEach((row, index) => {
|
|
76
|
+
const key = row.email.toLowerCase();
|
|
77
|
+
if (seen.has(key))
|
|
78
|
+
repeated.push(index + 1);
|
|
79
|
+
seen.add(key);
|
|
80
|
+
});
|
|
81
|
+
return repeated;
|
|
82
|
+
}
|
|
83
|
+
async function summarize(ctx, userIds) {
|
|
84
|
+
const summary = { accounts: userIds.length, trial: 0, paid: 0, lifetime: 0, readOnly: 0 };
|
|
85
|
+
for (const userId of userIds) {
|
|
86
|
+
const entitlement = await getEntitlement(ctx, userId);
|
|
87
|
+
// The accounts were found under the script's transaction; one erased meanwhile counts as none.
|
|
88
|
+
if (entitlement === null)
|
|
89
|
+
continue;
|
|
90
|
+
if (entitlement.status === "read_only")
|
|
91
|
+
summary.readOnly += 1;
|
|
92
|
+
else if (entitlement.status === "trial")
|
|
93
|
+
summary.trial += 1;
|
|
94
|
+
else if (entitlement.endsAt === null)
|
|
95
|
+
summary.lifetime += 1;
|
|
96
|
+
else
|
|
97
|
+
summary.paid += 1;
|
|
98
|
+
}
|
|
99
|
+
return summary;
|
|
100
|
+
}
|
|
101
|
+
/** `import-entitlements --file=…`: records trial ends, paid periods and lifetime access from a JSON file. */
|
|
102
|
+
export function createImportEntitlementsScript(config, options = {}) {
|
|
103
|
+
const clock = options.clock ?? systemClock;
|
|
104
|
+
return defineOpsScript({
|
|
105
|
+
name: "import-entitlements",
|
|
106
|
+
description: "Records the trial ends, paid periods and lifetime access in a JSON file for the accounts it names by email; never shortens access.",
|
|
107
|
+
usage: ["--file=<path to a JSON array of { email, trialEndsAt?, paidUntil?, isLifetime? }>"],
|
|
108
|
+
args: importArgs,
|
|
109
|
+
run: async (tx, args) => {
|
|
110
|
+
const ctx = { db: tx, clock, config };
|
|
111
|
+
const file = await readImportFile(args.file);
|
|
112
|
+
if (!file.ok)
|
|
113
|
+
return refuseOpsScript(file.reason);
|
|
114
|
+
const repeated = findRepeatedRows(file.rows);
|
|
115
|
+
if (repeated.length > 0)
|
|
116
|
+
return refuseOpsScript(`${describeRows(repeated)} repeat an email named earlier in the file`);
|
|
117
|
+
const userIds = [];
|
|
118
|
+
const unknown = [];
|
|
119
|
+
for (const [index, row] of file.rows.entries()) {
|
|
120
|
+
const account = await findAccountByEmail(ctx, row.email);
|
|
121
|
+
if (account === null)
|
|
122
|
+
unknown.push(index + 1);
|
|
123
|
+
else
|
|
124
|
+
userIds.push(account.id);
|
|
125
|
+
}
|
|
126
|
+
if (unknown.length > 0)
|
|
127
|
+
return refuseOpsScript(`${describeRows(unknown)} name no account; nothing was imported`);
|
|
128
|
+
const before = await summarize(ctx, userIds);
|
|
129
|
+
for (const [index, row] of file.rows.entries()) {
|
|
130
|
+
const userId = userIds[index];
|
|
131
|
+
// One id per row: every row found an account above.
|
|
132
|
+
if (userId === undefined)
|
|
133
|
+
throw new Error(`import-entitlements: row ${String(index + 1)} lost its account`);
|
|
134
|
+
const imported = await importEntitlement(ctx, { userId, trialEndsAt: row.trialEndsAt, paidUntil: row.paidUntil, isLifetime: row.isLifetime });
|
|
135
|
+
// Erased between the lookup and the change's lock.
|
|
136
|
+
if (!imported.ok)
|
|
137
|
+
return refuseOpsScript(`${describeRows([index + 1])} names an account that was deleted meanwhile; nothing was imported`);
|
|
138
|
+
}
|
|
139
|
+
return ok({ before, after: await summarize(ctx, userIds) });
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
async function countAccountsWithoutRow(db) {
|
|
144
|
+
const [row] = await db
|
|
145
|
+
.select({ total: count() })
|
|
146
|
+
.from(users)
|
|
147
|
+
.leftJoin(entitlements, eq(entitlements.userId, users.id))
|
|
148
|
+
.where(isNull(entitlements.userId));
|
|
149
|
+
return row?.total ?? 0;
|
|
150
|
+
}
|
|
151
|
+
/** `pin-trials`: writes every derived trial into a row, so a config change moves none of them. */
|
|
152
|
+
export function createPinTrialsScript(config, options = {}) {
|
|
153
|
+
const clock = options.clock ?? systemClock;
|
|
154
|
+
return defineOpsScript({
|
|
155
|
+
name: "pin-trials",
|
|
156
|
+
description: "Writes the trial every account without an entitlement row is on into a row, before trial.days, trial.startsAt or the time zone changes.",
|
|
157
|
+
usage: [],
|
|
158
|
+
args: pinArgs,
|
|
159
|
+
run: async (tx) => {
|
|
160
|
+
const ctx = { db: tx, clock, config };
|
|
161
|
+
const before = { accountsWithoutRow: await countAccountsWithoutRow(tx) };
|
|
162
|
+
const pinned = await pinDerivedTrials(ctx);
|
|
163
|
+
return ok({ before, after: { accountsWithoutRow: await countAccountsWithoutRow(tx), pinned } });
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=entitlement-scripts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entitlement-scripts.js","sourceRoot":"","sources":["../../src/scripts/entitlement-scripts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { createImportEntitlementsScript, createPinTrialsScript, MAX_IMPORT_ROWS, type EntitlementScriptOptions, type ImportEntitlementsScriptArgs, type PinTrialsScriptArgs, } from "./entitlement-scripts.js";
|
|
2
|
+
export { createGrantPlanScript, createRevokeGrantScript, type GrantPlanScriptArgs, type PlanScriptOptions, type RevokeGrantScriptArgs } from "./plan-scripts.js";
|
|
3
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/scripts/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,8BAA8B,EAC9B,qBAAqB,EACrB,eAAe,EACf,KAAK,wBAAwB,EAC7B,KAAK,4BAA4B,EACjC,KAAK,mBAAmB,GACzB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,KAAK,mBAAmB,EAAE,KAAK,iBAAiB,EAAE,KAAK,qBAAqB,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// `@softure-ai/billing/scripts`: ops scripts that grant a plan, revoke a manual grant, import
|
|
2
|
+
// entitlements and pin derived trials (see the README, "Scripts" and "Existing accounts").
|
|
3
|
+
export { createImportEntitlementsScript, createPinTrialsScript, MAX_IMPORT_ROWS, } from "./entitlement-scripts.js";
|
|
4
|
+
export { createGrantPlanScript, createRevokeGrantScript } from "./plan-scripts.js";
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/scripts/index.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,2FAA2F;AAC3F,OAAO,EACL,8BAA8B,EAC9B,qBAAqB,EACrB,eAAe,GAIhB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,qBAAqB,EAAE,uBAAuB,EAAgF,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type Clock, type SoftureConfig } from "@softure-ai/core";
|
|
2
|
+
import { type OpsScript } from "@softure-ai/ops/scripts";
|
|
3
|
+
export interface GrantPlanScriptArgs {
|
|
4
|
+
readonly email: string;
|
|
5
|
+
readonly plan: string;
|
|
6
|
+
}
|
|
7
|
+
export interface RevokeGrantScriptArgs {
|
|
8
|
+
readonly email: string;
|
|
9
|
+
readonly grant: string;
|
|
10
|
+
}
|
|
11
|
+
export interface PlanScriptOptions {
|
|
12
|
+
/** The time stored as `granted_at` / `revoked_at`; the system clock by default. */
|
|
13
|
+
readonly clock?: Clock;
|
|
14
|
+
}
|
|
15
|
+
/** `grant-plan --email=… --plan=…`: grants one payment of a declared plan by hand and records it. */
|
|
16
|
+
export declare function createGrantPlanScript(config: SoftureConfig, options?: PlanScriptOptions): OpsScript<GrantPlanScriptArgs>;
|
|
17
|
+
/** `revoke-grant --email=… --grant=…`: revokes one active manual grant and takes back what it added. */
|
|
18
|
+
export declare function createRevokeGrantScript(config: SoftureConfig, options?: PlanScriptOptions): OpsScript<RevokeGrantScriptArgs>;
|
|
19
|
+
//# sourceMappingURL=plan-scripts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-scripts.d.ts","sourceRoot":"","sources":["../../src/scripts/plan-scripts.ts"],"names":[],"mappings":"AAKA,OAAO,EAAmB,KAAK,KAAK,EAAE,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEnF,OAAO,EAAoC,KAAK,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAW3F,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC,mFAAmF;IACnF,QAAQ,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC;CACxB;AAuDD,qGAAqG;AACrG,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,GAAE,iBAAsB,GAAG,SAAS,CAAC,mBAAmB,CAAC,CAgC5H;AAED,wGAAwG;AACxG,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,GAAE,iBAAsB,GAAG,SAAS,CAAC,qBAAqB,CAAC,CAoBhI"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// The operator's way to grant a plan and revoke a manual grant without the admin page: safe ops
|
|
2
|
+
// scripts (`@softure-ai/ops/scripts`), dry run by default, `--commit` writes. They write through
|
|
3
|
+
// `grantPlanManually` and `revokeManualGrant`, so a script's grant is in the account's history like
|
|
4
|
+
// the admin page's, and either side can revoke the other's. Reports carry the user id, the
|
|
5
|
+
// entitlement and the active manual grants (with the ids `revoke-grant` takes), never the email.
|
|
6
|
+
import { ok, systemClock } from "@softure-ai/core";
|
|
7
|
+
import { defineOpsScript, refuseOpsScript } from "@softure-ai/ops/scripts";
|
|
8
|
+
import { and, eq } from "drizzle-orm";
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
import { findPlan } from "../plans.js";
|
|
11
|
+
import { manualGrants } from "../schema.js";
|
|
12
|
+
import { getEntitlement } from "../server/entitlements.js";
|
|
13
|
+
import { getAccountHistory, grantPlanManually, revokeManualGrant } from "../server/grants.js";
|
|
14
|
+
import { findAccountByEmail, getBillingPlans } from "../server/plans.js";
|
|
15
|
+
import { isUuid } from "../server/user-id.js";
|
|
16
|
+
const EMAIL = z.string().min(1, "--email=<account email> is required");
|
|
17
|
+
const grantPlanArgs = z.strictObject({
|
|
18
|
+
email: EMAIL,
|
|
19
|
+
plan: z.string().min(1, "--plan=<plan id> is required"),
|
|
20
|
+
});
|
|
21
|
+
const revokeGrantArgs = z.strictObject({
|
|
22
|
+
email: EMAIL,
|
|
23
|
+
grant: z.string().min(1, "--grant=<manual grant id> is required"),
|
|
24
|
+
});
|
|
25
|
+
const NO_ACCOUNT = "no account has this email";
|
|
26
|
+
async function describeAccount(ctx, userId) {
|
|
27
|
+
const history = await getAccountHistory(ctx, userId);
|
|
28
|
+
const grants = history.flatMap((entry) => entry.source === "manual" && entry.status === "active" ? [{ id: entry.id, planId: entry.planId, grantedAt: entry.at, grant: entry.grant }] : []);
|
|
29
|
+
return { userId, access: await getEntitlement(ctx, userId), grants };
|
|
30
|
+
}
|
|
31
|
+
function describeDeclared(config) {
|
|
32
|
+
return getBillingPlans(config)
|
|
33
|
+
.map((plan) => plan.id)
|
|
34
|
+
.join(", ");
|
|
35
|
+
}
|
|
36
|
+
/** Whether `grantId` is an active manual grant of the account (its own select: the history is capped). */
|
|
37
|
+
async function hasActiveGrant(db, userId, grantId) {
|
|
38
|
+
if (!isUuid(grantId))
|
|
39
|
+
return false;
|
|
40
|
+
const [row] = await db
|
|
41
|
+
.select({ id: manualGrants.id })
|
|
42
|
+
.from(manualGrants)
|
|
43
|
+
.where(and(eq(manualGrants.id, grantId), eq(manualGrants.userId, userId), eq(manualGrants.status, "active")));
|
|
44
|
+
return row !== undefined;
|
|
45
|
+
}
|
|
46
|
+
/** `grant-plan --email=… --plan=…`: grants one payment of a declared plan by hand and records it. */
|
|
47
|
+
export function createGrantPlanScript(config, options = {}) {
|
|
48
|
+
const clock = options.clock ?? systemClock;
|
|
49
|
+
return defineOpsScript({
|
|
50
|
+
name: "grant-plan",
|
|
51
|
+
description: "Grants one payment of a plan to the account with the given email, recorded in its billing history.",
|
|
52
|
+
usage: ["--email=<account email>", "--plan=<plan id from billing({ plans })>"],
|
|
53
|
+
args: grantPlanArgs,
|
|
54
|
+
run: async (tx, args) => {
|
|
55
|
+
const ctx = { db: tx, clock, config };
|
|
56
|
+
if (findPlan(getBillingPlans(config), args.plan) === undefined) {
|
|
57
|
+
return refuseOpsScript(`plan "${args.plan}" is not declared (declared: ${describeDeclared(config)})`);
|
|
58
|
+
}
|
|
59
|
+
const account = await findAccountByEmail(ctx, args.email);
|
|
60
|
+
if (account === null)
|
|
61
|
+
return refuseOpsScript(NO_ACCOUNT);
|
|
62
|
+
const before = await describeAccount(ctx, account.id);
|
|
63
|
+
const granted = await grantPlanManually(ctx, { userId: account.id, planId: args.plan, adminId: null });
|
|
64
|
+
if (!granted.ok) {
|
|
65
|
+
switch (granted.error) {
|
|
66
|
+
case "billing.lifetime_active":
|
|
67
|
+
return refuseOpsScript("the account has lifetime access already");
|
|
68
|
+
case "billing.account_unknown":
|
|
69
|
+
// Deleted between the lookup and the grant's lock.
|
|
70
|
+
return refuseOpsScript(NO_ACCOUNT);
|
|
71
|
+
case "billing.plan_unknown":
|
|
72
|
+
case "billing.request_closed":
|
|
73
|
+
// The plan was checked above and no request is given.
|
|
74
|
+
throw new Error(`grant-plan: granting plan "${args.plan}" failed with ${granted.error}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return ok({ before, after: await describeAccount(ctx, account.id) });
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
/** `revoke-grant --email=… --grant=…`: revokes one active manual grant and takes back what it added. */
|
|
82
|
+
export function createRevokeGrantScript(config, options = {}) {
|
|
83
|
+
const clock = options.clock ?? systemClock;
|
|
84
|
+
return defineOpsScript({
|
|
85
|
+
name: "revoke-grant",
|
|
86
|
+
description: "Revokes a manual plan grant of the account with the given email and takes back what it added.",
|
|
87
|
+
usage: ["--email=<account email>", "--grant=<manual grant id, listed by a dry run of either script>"],
|
|
88
|
+
args: revokeGrantArgs,
|
|
89
|
+
run: async (tx, args) => {
|
|
90
|
+
const ctx = { db: tx, clock, config };
|
|
91
|
+
const account = await findAccountByEmail(ctx, args.email);
|
|
92
|
+
if (account === null)
|
|
93
|
+
return refuseOpsScript(NO_ACCOUNT);
|
|
94
|
+
const before = await describeAccount(ctx, account.id);
|
|
95
|
+
const noGrant = `the account has no active manual grant "${args.grant}"`;
|
|
96
|
+
if (!(await hasActiveGrant(tx, account.id, args.grant)))
|
|
97
|
+
return refuseOpsScript(noGrant);
|
|
98
|
+
const revoked = await revokeManualGrant(ctx, { grantId: args.grant, adminId: null });
|
|
99
|
+
// Revoked by the admin page between the check and the revoke's lock.
|
|
100
|
+
if (!revoked.ok)
|
|
101
|
+
return refuseOpsScript(noGrant);
|
|
102
|
+
return ok({ before, after: await describeAccount(ctx, account.id) });
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=plan-scripts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan-scripts.js","sourceRoot":"","sources":["../../src/scripts/plan-scripts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { type Err, type ModuleContext, type Ok } from "@softure-ai/core";
|
|
2
|
+
import type { Queryable } from "@softure-ai/db";
|
|
3
|
+
import type { BillingErrorCode, Entitlement, EntitlementEvent, EntitlementRecord } from "../contract.js";
|
|
4
|
+
export type BillingContext = ModuleContext<Queryable>;
|
|
5
|
+
/** The day number of `trial.startsAt`, or null without one. */
|
|
6
|
+
export declare function getTrialFloorDay(ctx: Pick<BillingContext, "config">): number | null;
|
|
7
|
+
/**
|
|
8
|
+
* The trial an account without a row is on: `trial.days` from the day it was created, or from
|
|
9
|
+
* `trial.startsAt` for an account created before that day.
|
|
10
|
+
*/
|
|
11
|
+
export declare function getDefaultRecord(ctx: Pick<BillingContext, "config">, accountCreatedAt: Date): EntitlementRecord;
|
|
12
|
+
export interface PinEntitlementRowInput {
|
|
13
|
+
readonly userId: string;
|
|
14
|
+
/** The account's `auth.users.created_at`, which its derived trial starts from. */
|
|
15
|
+
readonly accountCreatedAt: Date;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Writes the account's derived trial into a row when it has none (exactly what reads derive), so a
|
|
19
|
+
* check made before the first change has a row to lock: a concurrent change that inserted first is
|
|
20
|
+
* waited for. Returns whether this call inserted the row. The caller holds the account (key share)
|
|
21
|
+
* and runs it in its transaction.
|
|
22
|
+
*/
|
|
23
|
+
export declare function pinEntitlementRow(ctx: Pick<BillingContext, "db" | "config" | "clock">, input: PinEntitlementRowInput): Promise<boolean>;
|
|
24
|
+
/** The account's stored or derived record, or null when no account has this id. */
|
|
25
|
+
export declare function findEntitlementRecord(ctx: Pick<BillingContext, "db" | "config">, userId: string): Promise<EntitlementRecord | null>;
|
|
26
|
+
/** Where the account stands now, or null when no account has this id. Database errors propagate. */
|
|
27
|
+
export declare function getEntitlement(ctx: BillingContext, userId: string): Promise<Entitlement | null>;
|
|
28
|
+
/**
|
|
29
|
+
* The write guard without a framework: `Ok` with the entitlement when the account may write,
|
|
30
|
+
* `billing.read_only` when it may only read, `billing.account_unknown` without an account.
|
|
31
|
+
*/
|
|
32
|
+
export declare function checkWriteAccess(ctx: BillingContext, userId: string): Promise<Ok<Entitlement> | Err<"billing.read_only" | "billing.account_unknown">>;
|
|
33
|
+
/** The event to apply, decided from the account's current record under its lock. */
|
|
34
|
+
export type EntitlementEventResolver = (record: EntitlementRecord, now: Date) => EntitlementEvent;
|
|
35
|
+
/**
|
|
36
|
+
* Applies `event` to the account's entitlement in one transaction and returns where it stands
|
|
37
|
+
* after; a refused event writes nothing. An account without a row gets one holding its derived
|
|
38
|
+
* trial with the event applied, so the trial end never moves when a row appears. A concurrent
|
|
39
|
+
* change that inserted first is waited for and applied on. Database errors propagate.
|
|
40
|
+
*
|
|
41
|
+
* `event` may be a function of the current record (a paid period that starts where access ends,
|
|
42
|
+
* `grantPlan`); it runs under the lock, so two changes at once both count.
|
|
43
|
+
*/
|
|
44
|
+
export declare function changeEntitlement(ctx: BillingContext, userId: string, event: EntitlementEvent | EntitlementEventResolver): Promise<Ok<Entitlement> | Err<BillingErrorCode>>;
|
|
45
|
+
export interface ImportEntitlementInput {
|
|
46
|
+
readonly userId: string;
|
|
47
|
+
/** The trial end the other system knew; omitted or null keeps the account's own. */
|
|
48
|
+
readonly trialEndsAt?: Date | null;
|
|
49
|
+
/** The end of the paid period it knew; omitted or null adds none. */
|
|
50
|
+
readonly paidUntil?: Date | null;
|
|
51
|
+
/** Whether it had lifetime access. */
|
|
52
|
+
readonly isLifetime?: boolean;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Records what another system knew about an account (a trial end, a paid period, lifetime access)
|
|
56
|
+
* through `changeEntitlement`: merged onto the account's current record, each end only moving
|
|
57
|
+
* later, so an import never takes access away and a repeat changes nothing. Not a recorded grant:
|
|
58
|
+
* it is not in the account's history. Database errors propagate.
|
|
59
|
+
*/
|
|
60
|
+
export declare function importEntitlement(ctx: BillingContext, input: ImportEntitlementInput): Promise<Ok<Entitlement> | Err<"billing.account_unknown">>;
|
|
61
|
+
/**
|
|
62
|
+
* Writes the derived trial of every account without a row into a row (exactly what reads derive,
|
|
63
|
+
* `trial.startsAt` included), so a later change of `trial.days`, `trial.startsAt` or the time zone
|
|
64
|
+
* moves no existing trial. A row written meanwhile by a change is kept. Returns how many rows it
|
|
65
|
+
* wrote. Run it in a transaction (the `pin-trials` script does) to pin all or nothing. Database
|
|
66
|
+
* errors propagate.
|
|
67
|
+
*/
|
|
68
|
+
export declare function pinDerivedTrials(ctx: BillingContext): Promise<number>;
|
|
69
|
+
//# sourceMappingURL=entitlements.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entitlements.d.ts","sourceRoot":"","sources":["../../src/server/entitlements.ts"],"names":[],"mappings":"AAKA,OAAO,EAAW,KAAK,GAAG,EAAE,KAAK,aAAa,EAAE,KAAK,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAClF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAGhD,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAMzG,MAAM,MAAM,cAAc,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;AAYtD,+DAA+D;AAC/D,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,cAAc,EAAE,QAAQ,CAAC,GAAG,MAAM,GAAG,IAAI,CAOnF;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,cAAc,EAAE,QAAQ,CAAC,EAAE,gBAAgB,EAAE,IAAI,GAAG,iBAAiB,CAG/G;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,kFAAkF;IAClF,QAAQ,CAAC,gBAAgB,EAAE,IAAI,CAAC;CACjC;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,IAAI,CAAC,cAAc,EAAE,IAAI,GAAG,QAAQ,GAAG,OAAO,CAAC,EAAE,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,OAAO,CAAC,CAQ7I;AAED,mFAAmF;AACnF,wBAAsB,qBAAqB,CAAC,GAAG,EAAE,IAAI,CAAC,cAAc,EAAE,IAAI,GAAG,QAAQ,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAezI;AAED,oGAAoG;AACpG,wBAAsB,cAAc,CAAC,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAGrG;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC,WAAW,CAAC,GAAG,GAAG,CAAC,mBAAmB,GAAG,yBAAyB,CAAC,CAAC,CAI3J;AAYD,oFAAoF;AACpF,MAAM,MAAM,wBAAwB,GAAG,CAAC,MAAM,EAAE,iBAAiB,EAAE,GAAG,EAAE,IAAI,KAAK,gBAAgB,CAAC;AAElG;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,gBAAgB,GAAG,wBAAwB,GACjD,OAAO,CAAC,EAAE,CAAC,WAAW,CAAC,GAAG,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAmClD;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,oFAAoF;IACpF,QAAQ,CAAC,WAAW,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACnC,qEAAqE;IACrE,QAAQ,CAAC,SAAS,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACjC,sCAAsC;IACtC,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,EAAE,CAAC,WAAW,CAAC,GAAG,GAAG,CAAC,yBAAyB,CAAC,CAAC,CAWrJ;AAKD;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAsB3E"}
|