dkimkit 0.0.0-stage → 0.6.1

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.
Files changed (128) hide show
  1. package/README.md +153 -2
  2. package/dist/src/cli.d.ts +2 -0
  3. package/dist/src/cli.js +398 -0
  4. package/dist/src/cli.js.map +1 -0
  5. package/dist/src/dns-host.d.ts +10 -0
  6. package/dist/src/dns-host.js +31 -0
  7. package/dist/src/dns-host.js.map +1 -0
  8. package/dist/src/errors.d.ts +17 -0
  9. package/dist/src/errors.js +51 -0
  10. package/dist/src/errors.js.map +1 -0
  11. package/dist/src/hosted/api.d.ts +95 -0
  12. package/dist/src/hosted/api.js +781 -0
  13. package/dist/src/hosted/api.js.map +1 -0
  14. package/dist/src/hosted/auth.d.ts +70 -0
  15. package/dist/src/hosted/auth.js +121 -0
  16. package/dist/src/hosted/auth.js.map +1 -0
  17. package/dist/src/hosted/billing-dodo.d.ts +58 -0
  18. package/dist/src/hosted/billing-dodo.js +139 -0
  19. package/dist/src/hosted/billing-dodo.js.map +1 -0
  20. package/dist/src/hosted/billing-stripe.d.ts +82 -0
  21. package/dist/src/hosted/billing-stripe.js +205 -0
  22. package/dist/src/hosted/billing-stripe.js.map +1 -0
  23. package/dist/src/hosted/billing.d.ts +114 -0
  24. package/dist/src/hosted/billing.js +173 -0
  25. package/dist/src/hosted/billing.js.map +1 -0
  26. package/dist/src/hosted/cli.d.ts +2 -0
  27. package/dist/src/hosted/cli.js +305 -0
  28. package/dist/src/hosted/cli.js.map +1 -0
  29. package/dist/src/hosted/crypto.d.ts +22 -0
  30. package/dist/src/hosted/crypto.js +55 -0
  31. package/dist/src/hosted/crypto.js.map +1 -0
  32. package/dist/src/hosted/index.d.ts +7 -0
  33. package/dist/src/hosted/index.js +11 -0
  34. package/dist/src/hosted/index.js.map +1 -0
  35. package/dist/src/hosted/markdown.d.ts +16 -0
  36. package/dist/src/hosted/markdown.js +168 -0
  37. package/dist/src/hosted/markdown.js.map +1 -0
  38. package/dist/src/hosted/net.d.ts +9 -0
  39. package/dist/src/hosted/net.js +60 -0
  40. package/dist/src/hosted/net.js.map +1 -0
  41. package/dist/src/hosted/postgres-store.d.ts +62 -0
  42. package/dist/src/hosted/postgres-store.js +387 -0
  43. package/dist/src/hosted/postgres-store.js.map +1 -0
  44. package/dist/src/hosted/serialize.d.ts +144 -0
  45. package/dist/src/hosted/serialize.js +126 -0
  46. package/dist/src/hosted/serialize.js.map +1 -0
  47. package/dist/src/hosted/service.d.ts +166 -0
  48. package/dist/src/hosted/service.js +582 -0
  49. package/dist/src/hosted/service.js.map +1 -0
  50. package/dist/src/hosted/site.d.ts +54 -0
  51. package/dist/src/hosted/site.js +472 -0
  52. package/dist/src/hosted/site.js.map +1 -0
  53. package/dist/src/hosted/sqlite-store.d.ts +45 -0
  54. package/dist/src/hosted/sqlite-store.js +351 -0
  55. package/dist/src/hosted/sqlite-store.js.map +1 -0
  56. package/dist/src/hosted/store.d.ts +257 -0
  57. package/dist/src/hosted/store.js +181 -0
  58. package/dist/src/hosted/store.js.map +1 -0
  59. package/dist/src/hosted/widget.d.ts +46 -0
  60. package/dist/src/hosted/widget.js +130 -0
  61. package/dist/src/hosted/widget.js.map +1 -0
  62. package/dist/src/hosted/worker.d.ts +45 -0
  63. package/dist/src/hosted/worker.js +142 -0
  64. package/dist/src/hosted/worker.js.map +1 -0
  65. package/dist/src/http.d.ts +14 -0
  66. package/dist/src/http.js +54 -0
  67. package/dist/src/http.js.map +1 -0
  68. package/dist/src/index.d.ts +19 -0
  69. package/dist/src/index.js +19 -0
  70. package/dist/src/index.js.map +1 -0
  71. package/dist/src/orchestrator.d.ts +70 -0
  72. package/dist/src/orchestrator.js +69 -0
  73. package/dist/src/orchestrator.js.map +1 -0
  74. package/dist/src/propagation.d.ts +30 -0
  75. package/dist/src/propagation.js +76 -0
  76. package/dist/src/propagation.js.map +1 -0
  77. package/dist/src/providers/dns/aws.d.ts +28 -0
  78. package/dist/src/providers/dns/aws.js +103 -0
  79. package/dist/src/providers/dns/aws.js.map +1 -0
  80. package/dist/src/providers/dns/cloudflare.d.ts +26 -0
  81. package/dist/src/providers/dns/cloudflare.js +97 -0
  82. package/dist/src/providers/dns/cloudflare.js.map +1 -0
  83. package/dist/src/providers/dns/manual.d.ts +12 -0
  84. package/dist/src/providers/dns/manual.js +18 -0
  85. package/dist/src/providers/dns/manual.js.map +1 -0
  86. package/dist/src/providers/dns/route53.d.ts +34 -0
  87. package/dist/src/providers/dns/route53.js +176 -0
  88. package/dist/src/providers/dns/route53.js.map +1 -0
  89. package/dist/src/providers/email/factory.d.ts +29 -0
  90. package/dist/src/providers/email/factory.js +67 -0
  91. package/dist/src/providers/email/factory.js.map +1 -0
  92. package/dist/src/providers/email/mailgun.d.ts +36 -0
  93. package/dist/src/providers/email/mailgun.js +141 -0
  94. package/dist/src/providers/email/mailgun.js.map +1 -0
  95. package/dist/src/providers/email/postmark.d.ts +37 -0
  96. package/dist/src/providers/email/postmark.js +121 -0
  97. package/dist/src/providers/email/postmark.js.map +1 -0
  98. package/dist/src/providers/email/sendgrid.d.ts +31 -0
  99. package/dist/src/providers/email/sendgrid.js +109 -0
  100. package/dist/src/providers/email/sendgrid.js.map +1 -0
  101. package/dist/src/providers/email/ses.d.ts +39 -0
  102. package/dist/src/providers/email/ses.js +123 -0
  103. package/dist/src/providers/email/ses.js.map +1 -0
  104. package/dist/src/providers/factory.d.ts +14 -0
  105. package/dist/src/providers/factory.js +32 -0
  106. package/dist/src/providers/factory.js.map +1 -0
  107. package/dist/src/records.d.ts +35 -0
  108. package/dist/src/records.js +109 -0
  109. package/dist/src/records.js.map +1 -0
  110. package/dist/src/server.d.ts +67 -0
  111. package/dist/src/server.js +301 -0
  112. package/dist/src/server.js.map +1 -0
  113. package/dist/src/types.d.ts +75 -0
  114. package/dist/src/types.js +2 -0
  115. package/dist/src/types.js.map +1 -0
  116. package/dist/src/xml.d.ts +6 -0
  117. package/dist/src/xml.js +23 -0
  118. package/dist/src/xml.js.map +1 -0
  119. package/docs/billing.md +43 -0
  120. package/docs/getting-started.md +57 -0
  121. package/docs/hosted-api.md +192 -0
  122. package/docs/self-hosting.md +148 -0
  123. package/docs/widget.md +121 -0
  124. package/package.json +63 -4
  125. package/web/dashboard.html +390 -0
  126. package/web/index.html +697 -0
  127. package/web/widget-frame.html +480 -0
  128. package/web/widget.js +91 -0
@@ -0,0 +1,205 @@
1
+ import { createHmac, timingSafeEqual } from 'node:crypto';
2
+ import { requestText } from '../http.js';
3
+ import { header, WebhookSignatureError } from './billing.js';
4
+ /** Encodes nested params the way Stripe expects: a[b][0][c]=v. */
5
+ export function stripeForm(params) {
6
+ const out = new URLSearchParams();
7
+ const add = (key, value) => {
8
+ if (value === undefined || value === null)
9
+ return;
10
+ if (Array.isArray(value))
11
+ value.forEach((v, i) => add(`${key}[${i}]`, v));
12
+ else if (typeof value === 'object')
13
+ for (const [k, v] of Object.entries(value))
14
+ add(`${key}[${k}]`, v);
15
+ else
16
+ out.append(key, String(value));
17
+ };
18
+ for (const [k, v] of Object.entries(params))
19
+ add(k, v);
20
+ return out.toString();
21
+ }
22
+ export class StripeClient {
23
+ secretKey;
24
+ baseUrl;
25
+ fetchImpl;
26
+ constructor(secretKey, opts = {}) {
27
+ this.secretKey = secretKey;
28
+ this.baseUrl = opts.baseUrl ?? 'https://api.stripe.com';
29
+ this.fetchImpl = opts.fetch ?? fetch;
30
+ }
31
+ async post(path, params, idempotencyKey) {
32
+ const text = await requestText(this.fetchImpl, 'Stripe', this.baseUrl + path, () => ({
33
+ method: 'POST',
34
+ headers: {
35
+ Authorization: `Bearer ${this.secretKey}`,
36
+ 'Content-Type': 'application/x-www-form-urlencoded',
37
+ ...(idempotencyKey ? { 'Idempotency-Key': idempotencyKey } : {}),
38
+ },
39
+ body: stripeForm(params),
40
+ }), (body) => {
41
+ try {
42
+ return JSON.parse(body).error?.message;
43
+ }
44
+ catch {
45
+ return undefined;
46
+ }
47
+ });
48
+ return JSON.parse(text);
49
+ }
50
+ }
51
+ /**
52
+ * Verifies a Stripe-Signature header: `t=<seconds>,v1=<hex>[,v1=…]`, HMAC-SHA256
53
+ * of "<t>.<raw body>" with the endpoint secret. Several v1 values appear while a
54
+ * secret is being rotated; any match is accepted.
55
+ */
56
+ export function verifyStripeSignature(payload, signature, secret, { toleranceSec = 300, nowSec = Math.floor(Date.now() / 1000) } = {}) {
57
+ if (!signature)
58
+ return false;
59
+ const parts = signature.split(',').map((p) => p.trim().split('='));
60
+ const timestamp = Number(parts.find(([k]) => k === 't')?.[1]);
61
+ if (!Number.isInteger(timestamp) || Math.abs(nowSec - timestamp) > toleranceSec)
62
+ return false;
63
+ const expected = createHmac('sha256', secret).update(`${timestamp}.${payload}`, 'utf8').digest();
64
+ return parts
65
+ .filter(([k, v]) => k === 'v1' && /^[0-9a-f]{64}$/i.test(v ?? ''))
66
+ .some(([, v]) => timingSafeEqual(expected, Buffer.from(v, 'hex')));
67
+ }
68
+ function statusOf(stripeStatus) {
69
+ switch (stripeStatus) {
70
+ case 'active':
71
+ case 'trialing':
72
+ return 'active';
73
+ case 'past_due':
74
+ case 'unpaid':
75
+ return 'past_due';
76
+ case 'canceled':
77
+ case 'incomplete_expired':
78
+ return 'free';
79
+ default:
80
+ // incomplete, paused: no change until Stripe settles it.
81
+ return undefined;
82
+ }
83
+ }
84
+ /**
85
+ * Stripe Billing. Expects a graduated, metered price on a `sum` meter whose first
86
+ * tiers are free; usage is reported as increments.
87
+ */
88
+ export class StripeBillingProvider {
89
+ config;
90
+ name = 'stripe';
91
+ stripe;
92
+ meterEventName;
93
+ constructor(config) {
94
+ this.config = config;
95
+ this.stripe = new StripeClient(config.secretKey, { baseUrl: config.baseUrl, fetch: config.fetch });
96
+ this.meterEventName = config.meterEventName ?? 'active_domains';
97
+ }
98
+ async createCustomer(account) {
99
+ // Idempotency key: a double click can't create two customers.
100
+ const customer = await this.stripe.post('/v1/customers', { email: account.email ?? undefined, name: account.name, metadata: { account_id: account.id } }, `customer:${account.id}`);
101
+ return customer.id;
102
+ }
103
+ async checkoutUrl(input) {
104
+ const session = await this.stripe.post('/v1/checkout/sessions', {
105
+ mode: 'subscription',
106
+ customer: input.customerId,
107
+ client_reference_id: input.account.id,
108
+ // Metered prices take no quantity.
109
+ line_items: [{ price: this.config.priceId }],
110
+ subscription_data: { metadata: { account_id: input.account.id } },
111
+ success_url: input.successUrl,
112
+ cancel_url: input.cancelUrl,
113
+ });
114
+ return session.url;
115
+ }
116
+ async portalUrl(customerId, returnUrl) {
117
+ const session = await this.stripe.post('/v1/billing_portal/sessions', { customer: customerId, return_url: returnUrl });
118
+ return session.url;
119
+ }
120
+ parseWebhook(rawBody, headers, nowSec) {
121
+ if (!verifyStripeSignature(rawBody, header(headers, 'stripe-signature'), this.config.webhookSecret, { nowSec })) {
122
+ throw new WebhookSignatureError('Invalid Stripe signature.');
123
+ }
124
+ const event = JSON.parse(rawBody);
125
+ switch (event.type) {
126
+ case 'checkout.session.completed': {
127
+ const session = event.data.object;
128
+ if (session.mode !== 'subscription' || !session.client_reference_id)
129
+ return null;
130
+ return {
131
+ id: event.id,
132
+ occurredAt: event.created,
133
+ accountId: session.client_reference_id,
134
+ customerId: session.customer,
135
+ subscriptionId: session.subscription,
136
+ status: 'active',
137
+ };
138
+ }
139
+ case 'customer.subscription.created':
140
+ case 'customer.subscription.updated':
141
+ case 'customer.subscription.deleted': {
142
+ const sub = event.data.object;
143
+ const item = sub.items?.data?.[0];
144
+ const start = item?.current_period_start ?? sub.current_period_start;
145
+ const end = item?.current_period_end ?? sub.current_period_end;
146
+ return {
147
+ id: event.id,
148
+ occurredAt: event.created,
149
+ accountId: sub.metadata?.account_id,
150
+ customerId: sub.customer,
151
+ subscriptionId: sub.id,
152
+ status: event.type === 'customer.subscription.deleted' ? 'free' : statusOf(sub.status),
153
+ periodStart: start === undefined ? undefined : start * 1000,
154
+ periodEnd: end === undefined ? undefined : end * 1000,
155
+ };
156
+ }
157
+ default:
158
+ return null;
159
+ }
160
+ }
161
+ async reportUsage(input) {
162
+ // The meter sums events, so send the increase.
163
+ await this.stripe.post('/v1/billing/meter_events', {
164
+ event_name: this.meterEventName,
165
+ payload: { stripe_customer_id: input.customerId, value: input.total - input.previous },
166
+ identifier: input.idempotencyKey,
167
+ timestamp: Math.floor(input.nowMs / 1000),
168
+ });
169
+ }
170
+ }
171
+ /**
172
+ * Creates the Stripe objects pay-as-you-go needs: a meter, a product, a graduated
173
+ * metered price (first `freeDomains` free), and optionally the webhook endpoint.
174
+ */
175
+ export async function setupStripe(stripe, opts) {
176
+ const meter = await stripe.post('/v1/billing/meters', {
177
+ display_name: 'Active domains',
178
+ event_name: opts.meterEventName,
179
+ default_aggregation: { formula: 'sum' },
180
+ customer_mapping: { type: 'by_id', event_payload_key: 'stripe_customer_id' },
181
+ value_settings: { event_payload_key: 'value' },
182
+ });
183
+ const product = await stripe.post('/v1/products', { name: 'Email domains', description: 'Authenticated sending domains' });
184
+ const price = await stripe.post('/v1/prices', {
185
+ product: product.id,
186
+ currency: opts.currency,
187
+ nickname: `Pay as you go: first ${opts.freeDomains} domains free`,
188
+ billing_scheme: 'tiered',
189
+ tiers_mode: 'graduated',
190
+ tiers: [
191
+ { up_to: opts.freeDomains, unit_amount: 0 },
192
+ { up_to: 'inf', unit_amount: opts.unitAmount },
193
+ ],
194
+ recurring: { interval: 'month', usage_type: 'metered', meter: meter.id },
195
+ });
196
+ let webhook;
197
+ if (opts.webhookUrl) {
198
+ webhook = await stripe.post('/v1/webhook_endpoints', {
199
+ url: opts.webhookUrl,
200
+ enabled_events: ['checkout.session.completed', 'customer.subscription.created', 'customer.subscription.updated', 'customer.subscription.deleted'],
201
+ });
202
+ }
203
+ return { meterId: meter.id, productId: product.id, priceId: price.id, webhook };
204
+ }
205
+ //# sourceMappingURL=billing-stripe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"billing-stripe.js","sourceRoot":"","sources":["../../../src/hosted/billing-stripe.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,114 @@
1
+ import type { IncomingHttpHeaders } from 'node:http';
2
+ import type { Logger, PlanCap } from './service.js';
3
+ import { type Account, type BillingStatus, type Store } from './store.js';
4
+ export declare class WebhookSignatureError extends Error {
5
+ }
6
+ /** A payment provider's webhook, reduced to what billing acts on. */
7
+ export interface BillingEvent {
8
+ /** Unique per event and stable across the provider's retries, for de-duplication. */
9
+ id: string;
10
+ /** When it happened, seconds. Events older than the last one applied are ignored. */
11
+ occurredAt: number;
12
+ accountId?: string;
13
+ customerId?: string;
14
+ subscriptionId?: string;
15
+ /** The account's new status; undefined means "no change". */
16
+ status?: BillingStatus;
17
+ /** Current billing period, ms; undefined means "unknown from this event". */
18
+ periodStart?: number;
19
+ periodEnd?: number;
20
+ }
21
+ /** What a payment provider (Dodo Payments, Stripe, …) must do for pay-as-you-go billing. */
22
+ export interface BillingProvider {
23
+ readonly name: string;
24
+ /** Creates the provider's customer record for an account; returns its id. */
25
+ createCustomer(account: Account): Promise<string>;
26
+ /** A hosted checkout page that adds a payment method and starts the subscription. */
27
+ checkoutUrl(input: {
28
+ account: Account;
29
+ customerId: string;
30
+ successUrl: string;
31
+ cancelUrl: string;
32
+ }): Promise<string>;
33
+ /** A hosted page where the customer manages payment methods, invoices and cancellation. */
34
+ portalUrl(customerId: string, returnUrl: string): Promise<string>;
35
+ /** Verifies the signature (throws WebhookSignatureError) and normalizes; null for events billing ignores. */
36
+ parseWebhook(rawBody: string, headers: IncomingHttpHeaders, nowSec: number): BillingEvent | null;
37
+ /**
38
+ * Reports that the account has had `total` distinct active domains so far this period;
39
+ * `previous` was the total last reported. Providers send the total or the increase,
40
+ * whichever their meter aggregates. `idempotencyKey` is stable for (account, period, total).
41
+ */
42
+ reportUsage(input: {
43
+ customerId: string;
44
+ total: number;
45
+ previous: number;
46
+ idempotencyKey: string;
47
+ nowMs: number;
48
+ }): Promise<void>;
49
+ }
50
+ export interface BillingConfig {
51
+ provider: BillingProvider;
52
+ /** Active domains included free. Must match the free threshold configured at the provider. Default 3. */
53
+ freeDomains?: number;
54
+ /** Price per active domain beyond the free ones, smallest currency unit. For display; the provider's price is what's charged. Default 200. */
55
+ unitAmount?: number;
56
+ currency?: string;
57
+ }
58
+ export interface BillingEstimate {
59
+ plan: 'free' | 'pay_as_you_go';
60
+ status: BillingStatus;
61
+ /** Inclusive UTC days of the period the estimate covers. */
62
+ periodStart: string;
63
+ periodEnd: string | null;
64
+ activeDomains: number;
65
+ freeDomains: number;
66
+ billableDomains: number;
67
+ unitAmount: number;
68
+ /** Estimated charge so far this period, smallest currency unit. */
69
+ amountDue: number;
70
+ currency: string;
71
+ }
72
+ export interface BillingOptions {
73
+ /** Public base URL of this API, for checkout and portal return links. */
74
+ publicUrl: string;
75
+ now?: () => number;
76
+ log?: Logger;
77
+ }
78
+ /**
79
+ * Pay-as-you-go billing, independent of the payment provider: free accounts may
80
+ * have `freeDomains` domains; with a payment method on file there's no cap and the
81
+ * provider bills distinct active domains per period beyond the free ones.
82
+ */
83
+ export declare class BillingService {
84
+ private readonly store;
85
+ private readonly opts;
86
+ readonly freeDomains: number;
87
+ readonly unitAmount: number;
88
+ readonly currency: string;
89
+ readonly provider: BillingProvider;
90
+ private readonly now;
91
+ private readonly log;
92
+ constructor(store: Store, config: BillingConfig, opts: BillingOptions);
93
+ isPaid(account: Account): boolean;
94
+ /** The free tier's cap, or null once a payment method is on file. Used by DomainService. */
95
+ planCap: (account: Account) => PlanCap | null;
96
+ dashboardUrl(): string;
97
+ estimate(account: Account): Promise<BillingEstimate>;
98
+ checkoutUrl(account: Account): Promise<string>;
99
+ portalUrl(account: Account): Promise<string>;
100
+ private customerFor;
101
+ /**
102
+ * Applies a provider webhook: verified and normalized by the provider, then
103
+ * processed once per event id, ignoring events older than the last one applied.
104
+ */
105
+ handleWebhook(rawBody: string, headers: IncomingHttpHeaders): Promise<'applied' | 'duplicate' | 'ignored'>;
106
+ /**
107
+ * Reports each paying account's distinct active domains this period when it
108
+ * has grown. The stored "reported" total prevents re-sending; the idempotency
109
+ * key covers a retry after a crash between sending and saving.
110
+ */
111
+ reportUsage(): Promise<void>;
112
+ }
113
+ /** First value of a header that may repeat. */
114
+ export declare const header: (headers: IncomingHttpHeaders, name: string) => string | undefined;
@@ -0,0 +1,173 @@
1
+ import { ConfigError } from '../errors.js';
2
+ import { utcDay } from './store.js';
3
+ export class WebhookSignatureError extends Error {
4
+ }
5
+ /**
6
+ * Pay-as-you-go billing, independent of the payment provider: free accounts may
7
+ * have `freeDomains` domains; with a payment method on file there's no cap and the
8
+ * provider bills distinct active domains per period beyond the free ones.
9
+ */
10
+ export class BillingService {
11
+ store;
12
+ opts;
13
+ freeDomains;
14
+ unitAmount;
15
+ currency;
16
+ provider;
17
+ now;
18
+ log;
19
+ constructor(store, config, opts) {
20
+ this.store = store;
21
+ this.opts = opts;
22
+ this.provider = config.provider;
23
+ this.freeDomains = config.freeDomains ?? 3;
24
+ this.unitAmount = config.unitAmount ?? 200;
25
+ this.currency = config.currency ?? 'usd';
26
+ this.now = opts.now ?? Date.now;
27
+ this.log = opts.log ?? (() => { });
28
+ }
29
+ isPaid(account) {
30
+ return account.billingStatus === 'active' || account.billingStatus === 'past_due';
31
+ }
32
+ /** The free tier's cap, or null once a payment method is on file. Used by DomainService. */
33
+ planCap = (account) => {
34
+ if (this.isPaid(account))
35
+ return null;
36
+ const n = this.freeDomains;
37
+ return {
38
+ limit: n,
39
+ message: `The free plan includes ${n} domain${n === 1 ? '' : 's'}. Add a payment method to add more: ${this.dashboardUrl()}`,
40
+ upgradeUrl: this.dashboardUrl(),
41
+ };
42
+ };
43
+ dashboardUrl() {
44
+ return `${this.opts.publicUrl}/dashboard`;
45
+ }
46
+ async estimate(account) {
47
+ const today = utcDay(this.now());
48
+ const paid = this.isPaid(account);
49
+ // Paid accounts: the provider's billing period. Free accounts: the calendar month, for reference.
50
+ const periodStart = paid && account.billingPeriodStart ? utcDay(account.billingPeriodStart) : `${today.slice(0, 7)}-01`;
51
+ const [usage] = await this.store.summarizeUsage({ from: periodStart, to: today, accountId: account.id });
52
+ const activeDomains = usage?.activeDomains ?? 0;
53
+ const billableDomains = paid ? Math.max(0, activeDomains - this.freeDomains) : 0;
54
+ return {
55
+ plan: paid ? 'pay_as_you_go' : 'free',
56
+ status: account.billingStatus,
57
+ periodStart,
58
+ periodEnd: paid && account.billingPeriodEnd ? utcDay(account.billingPeriodEnd) : null,
59
+ activeDomains,
60
+ freeDomains: this.freeDomains,
61
+ billableDomains,
62
+ unitAmount: this.unitAmount,
63
+ amountDue: billableDomains * this.unitAmount,
64
+ currency: this.currency,
65
+ };
66
+ }
67
+ async checkoutUrl(account) {
68
+ if (this.isPaid(account))
69
+ throw new ConfigError('You already have a payment method on file. Use "Manage billing" instead.');
70
+ return this.provider.checkoutUrl({
71
+ account,
72
+ customerId: await this.customerFor(account),
73
+ successUrl: `${this.dashboardUrl()}?billing=return`,
74
+ cancelUrl: `${this.dashboardUrl()}?billing=canceled`,
75
+ });
76
+ }
77
+ async portalUrl(account) {
78
+ if (!account.billingCustomerId)
79
+ throw new ConfigError('Add a payment method first.');
80
+ return this.provider.portalUrl(account.billingCustomerId, this.dashboardUrl());
81
+ }
82
+ async customerFor(account) {
83
+ if (account.billingCustomerId)
84
+ return account.billingCustomerId;
85
+ if (!account.email)
86
+ throw new ConfigError('Billing needs an email address on the account.');
87
+ const id = await this.provider.createCustomer(account);
88
+ await this.store.updateAccount(account.id, { billingCustomerId: id });
89
+ return id;
90
+ }
91
+ /**
92
+ * Applies a provider webhook: verified and normalized by the provider, then
93
+ * processed once per event id, ignoring events older than the last one applied.
94
+ */
95
+ async handleWebhook(rawBody, headers) {
96
+ const event = this.provider.parseWebhook(rawBody, headers, Math.floor(this.now() / 1000));
97
+ if (!event)
98
+ return 'ignored';
99
+ if (!(await this.store.markBillingEventProcessed(`${this.provider.name}:${event.id}`, this.now())))
100
+ return 'duplicate';
101
+ const account = (event.customerId ? await this.store.getAccountByBillingCustomer(event.customerId) : undefined) ??
102
+ (event.accountId ? await this.store.getAccount(event.accountId) : undefined);
103
+ if (!account) {
104
+ this.log('warn', 'billing event for an unknown account', { provider: this.provider.name, event_id: event.id });
105
+ return 'ignored';
106
+ }
107
+ if (account.billingEventAt !== null && event.occurredAt < account.billingEventAt)
108
+ return 'ignored';
109
+ // An event about an old, replaced subscription mustn't change the current one.
110
+ if (event.subscriptionId && account.billingSubscriptionId && event.subscriptionId !== account.billingSubscriptionId && event.status === 'free') {
111
+ return 'ignored';
112
+ }
113
+ const changes = {
114
+ billingCustomerId: event.customerId ?? account.billingCustomerId,
115
+ billingEventAt: event.occurredAt,
116
+ };
117
+ const status = event.status ?? account.billingStatus;
118
+ if (event.status !== undefined)
119
+ changes.billingStatus = event.status;
120
+ if (status === 'free') {
121
+ Object.assign(changes, { billingSubscriptionId: null, billingPeriodStart: null, billingPeriodEnd: null, reportedQuantity: 0 });
122
+ }
123
+ else {
124
+ if (event.subscriptionId)
125
+ changes.billingSubscriptionId = event.subscriptionId;
126
+ if (event.periodStart !== undefined) {
127
+ changes.billingPeriodStart = event.periodStart;
128
+ changes.billingPeriodEnd = event.periodEnd ?? null;
129
+ // A new period is reported from zero.
130
+ if (event.periodStart !== account.billingPeriodStart)
131
+ changes.reportedQuantity = 0;
132
+ }
133
+ }
134
+ await this.store.updateAccount(account.id, changes);
135
+ this.log('info', 'billing updated', { account_id: account.id, status, provider: this.provider.name });
136
+ return 'applied';
137
+ }
138
+ /**
139
+ * Reports each paying account's distinct active domains this period when it
140
+ * has grown. The stored "reported" total prevents re-sending; the idempotency
141
+ * key covers a retry after a crash between sending and saving.
142
+ */
143
+ async reportUsage() {
144
+ const now = this.now();
145
+ for (const account of await this.store.listBillableAccounts()) {
146
+ if (!account.billingPeriodStart || !account.billingCustomerId)
147
+ continue;
148
+ try {
149
+ const [usage] = await this.store.summarizeUsage({ from: utcDay(account.billingPeriodStart), to: utcDay(now), accountId: account.id });
150
+ const total = usage?.activeDomains ?? 0;
151
+ if (total <= account.reportedQuantity)
152
+ continue;
153
+ await this.provider.reportUsage({
154
+ customerId: account.billingCustomerId,
155
+ total,
156
+ previous: account.reportedQuantity,
157
+ idempotencyKey: `${account.id}:${account.billingPeriodStart}:${total}`,
158
+ nowMs: now,
159
+ });
160
+ await this.store.updateAccount(account.id, { reportedQuantity: total });
161
+ }
162
+ catch (err) {
163
+ this.log('error', 'usage report failed', { account_id: account.id, provider: this.provider.name, error: String(err) });
164
+ }
165
+ }
166
+ }
167
+ }
168
+ /** First value of a header that may repeat. */
169
+ export const header = (headers, name) => {
170
+ const v = headers[name];
171
+ return Array.isArray(v) ? v[0] : v;
172
+ };
173
+ //# sourceMappingURL=billing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"billing.js","sourceRoot":"","sources":["../../../src/hosted/billing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};