@stewardhq/sdk 0.2.0 → 0.5.0

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 (39) hide show
  1. package/README.md +123 -88
  2. package/dist/_chunks/errors.js +1 -1
  3. package/dist/_chunks/events.d.ts +315 -4
  4. package/dist/_chunks/events.js +1155 -9
  5. package/dist/_chunks/index.d.ts +3473 -90
  6. package/dist/_chunks/locale.d.ts +269 -1
  7. package/dist/_chunks/src.js +842 -24
  8. package/dist/_chunks/validators.d.ts +45 -1
  9. package/dist/_chunks/validators.js +128 -27
  10. package/dist/contract.d.ts +4 -4
  11. package/dist/contract.js +4 -4
  12. package/dist/index.d.ts +436 -22
  13. package/dist/index.js +1659 -26
  14. package/dist/server.d.ts +44 -6
  15. package/dist/server.js +29 -2
  16. package/package.json +4 -10
  17. package/dist/_chunks/steward.d.ts +0 -189
  18. package/dist/_chunks/steward.js +0 -560
  19. package/dist/testing/fixtures/events/LOCK.json +0 -27
  20. package/dist/testing/fixtures/events/account.deleted.json +0 -36
  21. package/dist/testing/fixtures/events/account.state_changed.json +0 -53
  22. package/dist/testing/fixtures/events/account.updated.json +0 -37
  23. package/dist/testing/fixtures/events/checkout.completed.json +0 -36
  24. package/dist/testing/fixtures/events/checkout.expired.json +0 -26
  25. package/dist/testing/fixtures/events/checkout.failed.json +0 -27
  26. package/dist/testing/fixtures/events/invoice.created.json +0 -37
  27. package/dist/testing/fixtures/events/invoice.issued.json +0 -38
  28. package/dist/testing/fixtures/events/invoice.voided.json +0 -39
  29. package/dist/testing/fixtures/events/subscription.activated.json +0 -40
  30. package/dist/testing/fixtures/events/subscription.cancel_scheduled.json +0 -38
  31. package/dist/testing/fixtures/events/subscription.canceled.json +0 -29
  32. package/dist/testing/fixtures/events/subscription.expired.json +0 -27
  33. package/dist/testing/fixtures/events/subscription.payment_failed.json +0 -38
  34. package/dist/testing/fixtures/events/subscription.reactivated.json +0 -36
  35. package/dist/testing/fixtures/events/subscription.renewed.json +0 -39
  36. package/dist/testing/fixtures/events/subscription.suspended.json +0 -36
  37. package/dist/testing/fixtures/events/subscription.terminated.json +0 -28
  38. package/dist/testing.d.ts +0 -626
  39. package/dist/testing.js +0 -3638
@@ -1,4 +1,4 @@
1
- import { A as CodeSchema, B as IntervalSchema, C as BillingProfileSchema, F as EntitlementsSchema, M as DateTimeSchema, O as ActorRefSchema, R as ExternalRefSchema, S as BillingProfileKindSchema, T as SubscriptionStatusSchema, a as EVENT_TYPES, j as CurrencySchema, k as AmountMinorSchema, o as EventIdSchema, v as AccountSchema, y as AccountStateSchema, z as IdSchema } from "./events.js";
1
+ import { A as SubscriptionStatusSchema, B as EmailSettingsPatchSchema, Bt as MeterCreditEntrySchema, C as AccountSchema, Ct as DecimalUnitsStringSchema, D as BillingProfileKindSchema, Fn as CurrencySchema, Gn as IdSchema, Hn as ExternalRefSchema, Ht as MeterDefSchema, I as EMAIL_TEMPLATES, In as DateTimeSchema, Jn as InvoicePaymentStatusSchema, Kn as IntervalSchema, Mn as ActorRefSchema, Nn as AmountMinorSchema, O as BillingProfileSchema, Pn as CodeSchema, Qt as MeteredPriceSchema, V as EmailSettingsSchema, Vt as MeterCreditPropertiesSchema, X as applyEmailSettingsPatch, Zt as MeteredPriceDefSchema, a as EVENT_TYPES, bt as CatalogMeterSchema, cn as plainDecimalOf, dt as CatalogBenefitSchema, en as meterFilterClauseOf, ht as featureValueMatchesType, ln as toMicroUnits, nn as MICRO_UNITS_PER_UNIT, o as EventIdSchema, qn as InvoiceBillingReasonSchema, st as BenefitDefSchema, w as AccountStateSchema, yt as AccountMetersSchema, zn as EntitlementsSchema } from "./events.js";
2
2
  import { z } from "zod";
3
3
  //#region ../contract/src/catalog.ts
4
4
  const FeatureTypeSchema = z.enum([
@@ -11,14 +11,34 @@ const FeatureDefSchema = z.object({
11
11
  type: FeatureTypeSchema,
12
12
  description: z.string().max(500).optional()
13
13
  });
14
+ const PlanRankSchema = z.number().int().nonnegative();
15
+ /** Upper bound of benefits a plan lists. */
16
+ const MAX_PLAN_BENEFITS = 100;
17
+ /** Plan in the catalog sync input. */
14
18
  const PlanDefSchema = z.object({
15
19
  code: CodeSchema,
16
20
  name: z.string().min(1).max(200),
17
- /** Yükseltme/düşürme sırası: büyük olan üst plan. */
18
- rank: z.number().int().nonnegative(),
21
+ /**
22
+ * Upgrade order of the pre-benefits entitlement rule (higher = upper plan). Optional since
23
+ * billing core (M35): default 0; the benefits resolution does not read it.
24
+ */
25
+ rank: PlanRankSchema.default(0),
19
26
  /** Self-servis satın alınabilir mi (false: yalnızca grant ile verilir). */
20
27
  sellable: z.boolean(),
21
- entitlements: EntitlementsSchema
28
+ entitlements: EntitlementsSchema,
29
+ /** Codes of catalog `benefits` the plan includes, in display order (billing core; default empty). */
30
+ benefits: z.array(CodeSchema).max(100).default([])
31
+ });
32
+ /** Plan as returned by `GET /v1/catalog`. */
33
+ const CatalogPlanSchema = z.object({
34
+ code: CodeSchema,
35
+ name: z.string().min(1).max(200),
36
+ /** Stored value (0 when the catalog omitted it); kept for older clients (M35). */
37
+ rank: PlanRankSchema.default(0),
38
+ sellable: z.boolean(),
39
+ entitlements: EntitlementsSchema,
40
+ /** Present only for a product that defines benefits. */
41
+ benefits: z.array(CodeSchema).optional()
22
42
  });
23
43
  const PriceDefSchema = z.object({
24
44
  planCode: CodeSchema,
@@ -31,11 +51,25 @@ const PriceDefSchema = z.object({
31
51
  /** Baz puan: %20 = 2000. */
32
52
  taxRateBps: z.number().int().min(0).max(1e4)
33
53
  });
54
+ /** Upper bound of benefit definitions in one catalog. */
55
+ const MAX_CATALOG_BENEFITS = 200;
56
+ /** Upper bound of meters in one catalog. */
57
+ const MAX_CATALOG_METERS = 100;
58
+ /** Upper bound of metered prices in one catalog. */
59
+ const MAX_CATALOG_METERED_PRICES = 500;
60
+ /** Issue `params.code` of a metered price whose tax terms differ from the plan's base price (M20). */
61
+ const METERED_PRICE_TAX_MISMATCH = "metered_price_tax_mismatch";
34
62
  const CatalogSyncInputSchema = z.object({
35
63
  defaultPlanCode: CodeSchema,
36
64
  features: z.array(FeatureDefSchema),
37
65
  plans: z.array(PlanDefSchema).min(1),
38
- prices: z.array(PriceDefSchema)
66
+ prices: z.array(PriceDefSchema),
67
+ /** Benefit definitions by code (billing core; default empty). */
68
+ benefits: z.record(CodeSchema, BenefitDefSchema).default({}),
69
+ /** Meter definitions by code (metering; default empty). */
70
+ meters: z.record(CodeSchema, MeterDefSchema).default({}),
71
+ /** Metered prices (metering; default empty). */
72
+ meteredPrices: z.array(MeteredPriceDefSchema).default([])
39
73
  }).superRefine((c, ctx) => {
40
74
  const features = new Set(c.features.map((f) => f.key));
41
75
  const plans = new Set(c.plans.map((p) => p.code));
@@ -62,6 +96,78 @@ const CatalogSyncInputSchema = z.object({
62
96
  ],
63
97
  message: "tanımsız feature"
64
98
  });
99
+ const benefitCodes = Object.keys(c.benefits);
100
+ if (benefitCodes.length > 200) ctx.addIssue({
101
+ code: "custom",
102
+ path: ["benefits"],
103
+ message: `at most 200 benefits`
104
+ });
105
+ for (const [i, p] of c.plans.entries()) {
106
+ const listed = /* @__PURE__ */ new Set();
107
+ for (const [j, code] of p.benefits.entries()) {
108
+ if (!Object.hasOwn(c.benefits, code)) ctx.addIssue({
109
+ code: "custom",
110
+ path: [
111
+ "plans",
112
+ i,
113
+ "benefits",
114
+ j
115
+ ],
116
+ message: `undefined benefit: ${code}`
117
+ });
118
+ if (listed.has(code)) ctx.addIssue({
119
+ code: "custom",
120
+ path: [
121
+ "plans",
122
+ i,
123
+ "benefits",
124
+ j
125
+ ],
126
+ message: `duplicate benefit: ${code}`
127
+ });
128
+ listed.add(code);
129
+ }
130
+ }
131
+ const featureTypes = new Map(c.features.map((f) => [f.key, f.type]));
132
+ for (const code of benefitCodes) {
133
+ const benefit = c.benefits[code];
134
+ if (benefit.type === "meter_credit") {
135
+ if (!Object.hasOwn(c.meters, benefit.properties.meter)) ctx.addIssue({
136
+ code: "custom",
137
+ path: [
138
+ "benefits",
139
+ code,
140
+ "properties",
141
+ "meter"
142
+ ],
143
+ message: `undefined meter: ${benefit.properties.meter}`
144
+ });
145
+ continue;
146
+ }
147
+ for (const [key, value] of Object.entries(benefit.properties)) {
148
+ const type = featureTypes.get(key);
149
+ if (type === void 0) ctx.addIssue({
150
+ code: "custom",
151
+ path: [
152
+ "benefits",
153
+ code,
154
+ "properties",
155
+ key
156
+ ],
157
+ message: "undefined feature"
158
+ });
159
+ else if (!featureValueMatchesType(type, value)) ctx.addIssue({
160
+ code: "custom",
161
+ path: [
162
+ "benefits",
163
+ code,
164
+ "properties",
165
+ key
166
+ ],
167
+ message: `value does not match feature type ${type}`
168
+ });
169
+ }
170
+ }
65
171
  const seen = /* @__PURE__ */ new Set();
66
172
  for (const [i, p] of c.prices.entries()) {
67
173
  if (!plans.has(p.planCode)) ctx.addIssue({
@@ -81,6 +187,61 @@ const CatalogSyncInputSchema = z.object({
81
187
  });
82
188
  seen.add(key);
83
189
  }
190
+ if (Object.keys(c.meters).length > 100) ctx.addIssue({
191
+ code: "custom",
192
+ path: ["meters"],
193
+ message: `at most 100 meters`
194
+ });
195
+ if (c.meteredPrices.length > 500) ctx.addIssue({
196
+ code: "custom",
197
+ path: ["meteredPrices"],
198
+ message: `at most 500 metered prices`
199
+ });
200
+ const meteredSeen = /* @__PURE__ */ new Set();
201
+ for (const [i, mp] of c.meteredPrices.entries()) {
202
+ if (!plans.has(mp.planCode)) ctx.addIssue({
203
+ code: "custom",
204
+ path: [
205
+ "meteredPrices",
206
+ i,
207
+ "planCode"
208
+ ],
209
+ message: `undefined plan: ${mp.planCode}`
210
+ });
211
+ if (!Object.hasOwn(c.meters, mp.meter)) ctx.addIssue({
212
+ code: "custom",
213
+ path: [
214
+ "meteredPrices",
215
+ i,
216
+ "meter"
217
+ ],
218
+ message: `undefined meter: ${mp.meter}`
219
+ });
220
+ const key = `${mp.planCode}/${mp.meter}/${mp.currency}`;
221
+ if (meteredSeen.has(key)) ctx.addIssue({
222
+ code: "custom",
223
+ path: ["meteredPrices", i],
224
+ message: `duplicate metered price key: ${key}`
225
+ });
226
+ meteredSeen.add(key);
227
+ if (!plans.has(mp.planCode)) continue;
228
+ const base = c.prices.filter((p) => p.planCode === mp.planCode && p.currency === mp.currency);
229
+ if (base.length === 0) ctx.addIssue({
230
+ code: "custom",
231
+ path: [
232
+ "meteredPrices",
233
+ i,
234
+ "currency"
235
+ ],
236
+ message: `plan ${mp.planCode} has no base price in ${mp.currency}`
237
+ });
238
+ else if (base.some((p) => p.taxInclusive !== mp.taxInclusive || p.taxRateBps !== mp.taxRateBps)) ctx.addIssue({
239
+ code: "custom",
240
+ path: ["meteredPrices", i],
241
+ message: `${METERED_PRICE_TAX_MISMATCH}: taxInclusive and taxRateBps must equal the base prices of plan ${mp.planCode} in ${mp.currency}`,
242
+ params: { code: METERED_PRICE_TAX_MISMATCH }
243
+ });
244
+ }
84
245
  });
85
246
  const PriceSchema = PriceDefSchema.extend({
86
247
  id: IdSchema,
@@ -91,9 +252,15 @@ const CatalogSchema = z.object({
91
252
  product: CodeSchema,
92
253
  defaultPlanCode: CodeSchema,
93
254
  features: z.array(FeatureDefSchema),
94
- plans: z.array(PlanDefSchema),
255
+ plans: z.array(CatalogPlanSchema),
95
256
  /** Varsayılan olarak yalnızca aktif fiyatlar. */
96
- prices: z.array(PriceSchema)
257
+ prices: z.array(PriceSchema),
258
+ /** Active (not archived) benefit definitions by code; present only for a product that defines benefits. */
259
+ benefits: z.record(CodeSchema, CatalogBenefitSchema).optional(),
260
+ /** Active (not archived) meters by code; present only for a product that defines meters (metering). */
261
+ meters: z.record(CodeSchema, CatalogMeterSchema).optional(),
262
+ /** Active metered prices; present only for a product that defines meters (metering). */
263
+ meteredPrices: z.array(MeteredPriceSchema).optional()
97
264
  });
98
265
  const CatalogSyncResultSchema = z.object({
99
266
  /** Aynı girdiyle tekrar senkron: hiçbir şey değişmedi. */
@@ -111,7 +278,21 @@ const CatalogSyncResultSchema = z.object({
111
278
  priceId: IdSchema,
112
279
  provider: z.string(),
113
280
  code: z.string()
114
- }))
281
+ })),
282
+ /** Benefits created, changed or unarchived by this sync. */
283
+ changedBenefits: z.array(CodeSchema).optional(),
284
+ /** Benefits removed from the catalog and archived by this sync. */
285
+ archivedBenefits: z.array(CodeSchema).optional(),
286
+ /** Prices whose amount or tax terms were updated in place (the price id is kept; history in `price_changes`). */
287
+ updatedPrices: z.array(IdSchema).optional(),
288
+ /** Meters created, changed (labels, `lowAt`, display) or unarchived by this sync. */
289
+ changedMeters: z.array(CodeSchema).optional(),
290
+ /** Meters removed from the catalog and archived by this sync (their history stays). */
291
+ archivedMeters: z.array(CodeSchema).optional(),
292
+ createdMeteredPrices: z.array(IdSchema).optional(),
293
+ /** Metered prices whose amount, cap or tax terms were updated in place (the id is kept). */
294
+ updatedMeteredPrices: z.array(IdSchema).optional(),
295
+ deactivatedMeteredPrices: z.array(IdSchema).optional()
115
296
  });
116
297
  /** `POST /v1/admin/accounts/refresh` — ürünün tüm hesaplarının snapshot'ı yeniden hesaplanır. */
117
298
  const AccountsRefreshResultSchema = z.object({
@@ -163,6 +344,43 @@ const BrandingSchema = z.object({
163
344
  accentColor: AccentColorSchema.optional(),
164
345
  supportEmail: SupportEmailSchema.optional()
165
346
  });
347
+ const DEFAULT_PAYMENT_DUE_DAYS = 7;
348
+ const DEFAULT_MIN_INVOICE_MINOR = 1e3;
349
+ const PaymentDueDaysSchema = z.number().int().min(1).max(60);
350
+ const BillingSettingsSchema = z.object({
351
+ paymentDueDays: PaymentDueDaysSchema.default(7),
352
+ minInvoiceMinor: AmountMinorSchema.default(DEFAULT_MIN_INVOICE_MINOR),
353
+ allowMultipleSubscriptions: z.boolean().default(false)
354
+ });
355
+ /** Patch of `settings.billing`: an omitted field is unchanged, `null` restores its default. */
356
+ const BillingSettingsPatchSchema = z.object({
357
+ paymentDueDays: PaymentDueDaysSchema.nullable().optional(),
358
+ minInvoiceMinor: AmountMinorSchema.nullable().optional(),
359
+ allowMultipleSubscriptions: z.boolean().nullable().optional()
360
+ }).strict();
361
+ const DEFAULT_EVENT_RETENTION_DAYS = 120;
362
+ const DEFAULT_LATE_GRACE_MINUTES = 60;
363
+ const METER_CHARGE_MODES = [
364
+ "off",
365
+ "dry_run",
366
+ "live"
367
+ ];
368
+ const MeterChargeModeSchema = z.enum(METER_CHARGE_MODES);
369
+ const DEFAULT_METER_CHARGE_MODE = "dry_run";
370
+ const EventRetentionDaysSchema = z.number().int().min(45).max(400);
371
+ const LateGraceMinutesSchema = z.number().int().min(0).max(1440);
372
+ const EventsSettingsSchema = z.object({ retentionDays: EventRetentionDaysSchema.default(120) });
373
+ const MeteringSettingsSchema = z.object({
374
+ lateGraceMinutes: LateGraceMinutesSchema.default(60),
375
+ chargeMode: MeterChargeModeSchema.default(DEFAULT_METER_CHARGE_MODE)
376
+ });
377
+ /** Patch of `settings.events`: an omitted field is unchanged, `null` restores its default. */
378
+ const EventsSettingsPatchSchema = z.object({ retentionDays: EventRetentionDaysSchema.nullable().optional() }).strict();
379
+ /** Patch of `settings.metering`: an omitted field is unchanged, `null` restores its default. */
380
+ const MeteringSettingsPatchSchema = z.object({
381
+ lateGraceMinutes: LateGraceMinutesSchema.nullable().optional(),
382
+ chargeMode: MeterChargeModeSchema.nullable().optional()
383
+ }).strict();
166
384
  const ProductSettingsSchema = z.object({
167
385
  /** Checkout'ları alan ödeme sağlayıcısı. Operatör ayarıdır (pod CLI); yönetim ucundan yazılmaz. */
168
386
  provider: z.string().min(1).default("iyzico"),
@@ -175,8 +393,28 @@ const ProductSettingsSchema = z.object({
175
393
  branding: BrandingSchema.default({}),
176
394
  /** Açık checkout oturumunun ömrü (sağlayıcının form ömrü daha kısaysa o geçerli). */
177
395
  checkoutTtlMinutes: TtlMinutesSchema.default(30),
178
- portalTtlMinutes: TtlMinutesSchema.default(30)
396
+ portalTtlMinutes: TtlMinutesSchema.default(30),
397
+ /** Billing emails sent by steward (`emails.ts`). */
398
+ emails: EmailSettingsSchema.default({ templates: {} }),
399
+ /** Billing behavior (billing core); absent = defaults (`billingSettingsOf`). */
400
+ billing: BillingSettingsSchema.optional(),
401
+ /** Raw event retention (metering); absent = defaults (`eventsSettingsOf`). */
402
+ events: EventsSettingsSchema.optional(),
403
+ /** Meter periods and overage pricing (metering); absent = defaults (`meteringSettingsOf`). */
404
+ metering: MeteringSettingsSchema.optional()
179
405
  });
406
+ /** `settings.billing` with defaults filled in. */
407
+ function billingSettingsOf(settings) {
408
+ return settings.billing ?? BillingSettingsSchema.parse({});
409
+ }
410
+ /** `settings.events` with defaults filled in. */
411
+ function eventsSettingsOf(settings) {
412
+ return settings.events ?? EventsSettingsSchema.parse({});
413
+ }
414
+ /** `settings.metering` with defaults filled in. */
415
+ function meteringSettingsOf(settings) {
416
+ return settings.metering ?? MeteringSettingsSchema.parse({});
417
+ }
180
418
  const BrandingPatchSchema = z.object({
181
419
  name: BrandingNameSchema.nullable().optional(),
182
420
  logoUrl: LogoUrlSchema.nullable().optional(),
@@ -193,8 +431,21 @@ const ProductSettingsPatchSchema = z.object({
193
431
  termsUrl: WebUrlSchema.nullable().optional(),
194
432
  branding: BrandingPatchSchema.nullable().optional(),
195
433
  checkoutTtlMinutes: TtlMinutesSchema.optional(),
196
- portalTtlMinutes: TtlMinutesSchema.optional()
434
+ portalTtlMinutes: TtlMinutesSchema.optional(),
435
+ emails: EmailSettingsPatchSchema.nullable().optional(),
436
+ /** Merges per field; `billing: null` restores every default. */
437
+ billing: BillingSettingsPatchSchema.nullable().optional(),
438
+ /** Merges per field; `events: null` restores every default. */
439
+ events: EventsSettingsPatchSchema.nullable().optional(),
440
+ /** Merges per field; `metering: null` restores every default. */
441
+ metering: MeteringSettingsPatchSchema.nullable().optional()
197
442
  }).strict();
443
+ /** Settings sections patched field by field (`null` section: every default back). */
444
+ const MERGED_SECTIONS = /* @__PURE__ */ new Set([
445
+ "billing",
446
+ "events",
447
+ "metering"
448
+ ]);
198
449
  /**
199
450
  * Doğrulanmış yamayı ayarlara uygular (saf). Sonuç şemadan geçer; alan kuralları
200
451
  * yamada zaten denetlendiği için yalnızca programlama hatasında fırlatır.
@@ -210,6 +461,15 @@ function applySettingsPatch(current, patch) {
210
461
  else if (fieldValue !== void 0) branding[field] = fieldValue;
211
462
  }
212
463
  next.branding = branding;
464
+ } else if (key === "emails") next.emails = applyEmailSettingsPatch(current.emails, value);
465
+ else if (MERGED_SECTIONS.has(key)) {
466
+ if (value === null) delete next[key];
467
+ else {
468
+ const section = { ...current[key] ?? {} };
469
+ for (const [field, fieldValue] of Object.entries(value)) if (fieldValue === null) delete section[field];
470
+ else if (fieldValue !== void 0) section[field] = fieldValue;
471
+ next[key] = section;
472
+ }
213
473
  } else if (value === null) delete next[key];
214
474
  else next[key] = value;
215
475
  }
@@ -228,6 +488,16 @@ function isReturnUrlAllowed(settings, returnUrl) {
228
488
  //#endregion
229
489
  //#region ../contract/src/checkout.ts
230
490
  const ReturnUrlSchema = z.url({ protocol: /^https?$/ });
491
+ /**
492
+ * Identity number sent to the payment provider (and, when the invoice needs one, used on the
493
+ * invoice) for an INDIVIDUAL buyer (M31): steward never asks for or stores a TCKN; the Turkish
494
+ * "final consumer" placeholder goes instead. A company buyer's VKN comes from the billing profile.
495
+ *
496
+ * It deliberately FAILS the TCKN checksum (`isValidTckn("11111111111") === false`): never run it
497
+ * through a TCKN validator and never show it as a customer's TCKN. Kept here, not in a provider
498
+ * adapter, because another provider may need a different rule.
499
+ */
500
+ const INDIVIDUAL_IDENTITY_PLACEHOLDER = "11111111111";
231
501
  /** Accepted document — content lives in the product, steward keeps only the trace. */
232
502
  const ConsentSchema = z.object({
233
503
  document: z.string().min(1).max(100),
@@ -345,7 +615,7 @@ function originOf(value) {
345
615
  }
346
616
  }
347
617
  /**
348
- * Browser redirect for a checkout outcome — the service and the fake steward share it.
618
+ * Browser redirect for a checkout outcome — used by the service.
349
619
  * Placeholders in `successUrl` (`{CHECKOUT_ID}` `{STATUS}` `{ACCOUNT_REF}` `{LOCALE}`) are
350
620
  * filled with URL-encoded values; nothing is appended, so a URL without placeholders is used
351
621
  * as-is. An encoded value cannot change the origin; it is still checked, and if the origin
@@ -369,7 +639,8 @@ const CHECKOUT_ERRORS = [
369
639
  "return_url_not_allowed",
370
640
  "provider_not_configured",
371
641
  "provider_error",
372
- "account_deleted"
642
+ "account_deleted",
643
+ "meter_already_billed"
373
644
  ];
374
645
  //#endregion
375
646
  //#region ../contract/src/portal.ts
@@ -399,9 +670,11 @@ const SubscriptionSchema = z.object({
399
670
  planCode: CodeSchema,
400
671
  priceId: IdSchema,
401
672
  interval: IntervalSchema,
673
+ /** Amount per period; billing core (M36): the subscription's locked amount. */
402
674
  amountMinor: AmountMinorSchema,
403
675
  currency: CurrencySchema,
404
676
  provider: z.string(),
677
+ /** Legacy provider-managed subscription reference; `""` for a steward-managed subscription (billing core, M30). */
405
678
  providerSubscriptionRef: z.string(),
406
679
  cancelAtPeriodEnd: z.boolean(),
407
680
  cancelAt: DateTimeSchema.nullable(),
@@ -420,6 +693,28 @@ const CancelSubscriptionInputSchema = z.object({
420
693
  /** Sağlayıcıda zaten iptal edildiyse (panelden) sağlayıcı çağrısını atla. */
421
694
  skipProvider: z.boolean().default(false)
422
695
  });
696
+ /**
697
+ * `POST /v1/subscriptions/{id}/change` (billing core MF9): schedule a change for the next period
698
+ * boundary (`AccountState.subscription.scheduledChange`); the next renewal invoice is written from
699
+ * the new terms. `planCode` / `interval` pick another price of the subscription's currency;
700
+ * `price: "current"` re-locks the current amount of the (possibly updated) price and the plan's
701
+ * current metered rates — a metered price added later is attached only this way (M20, M36). At
702
+ * least one of the three. `when` is required so other timings can be added later. A second change
703
+ * replaces the first; asking for the current plan and interval while a change is pending takes it
704
+ * back ("keep my plan"; a move to the current price the operator announced stays).
705
+ * Errors: 404 `subscription_not_found`; 409 `already_subscribed` (another live subscription of the
706
+ * account holds the target plan or is moving to it), `meter_already_billed` (the target prices a
707
+ * meter another live subscription bills; for `price: "current"` only when that is the only
708
+ * difference); 422 `plan_not_found`, `plan_not_sellable`, `subscription_change_invalid` (no such
709
+ * price, nothing changes, the subscription is not live, has a cancellation scheduled or is a legacy
710
+ * provider subscription).
711
+ */
712
+ const SubscriptionChangeInputSchema = z.object({
713
+ planCode: CodeSchema.optional(),
714
+ interval: IntervalSchema.optional(),
715
+ price: z.literal("current").optional(),
716
+ when: z.literal("next_period")
717
+ }).refine((c) => c.planCode !== void 0 || c.interval !== void 0 || c.price !== void 0, { message: "planCode, interval or price is required" });
423
718
  const PaymentSchema = z.object({
424
719
  id: IdSchema,
425
720
  subscriptionId: IdSchema,
@@ -431,19 +726,28 @@ const PaymentSchema = z.object({
431
726
  periodEnd: DateTimeSchema.nullable(),
432
727
  occurredAt: DateTimeSchema
433
728
  });
729
+ /** Upper bound of named benefits in one grant. */
730
+ const MAX_GRANT_BENEFITS = 100;
434
731
  const GrantCreateInputSchema = z.object({
435
732
  planCode: CodeSchema.optional(),
436
- /** Plan değerlerinin üzerine yazılan tek tek haklar. */
733
+ /** Single values written over the merged entitlements (applied last; may lower). */
437
734
  overrides: EntitlementsSchema.default({}),
735
+ /**
736
+ * Catalog benefit codes granted independently of a plan (billing core, M32), e.g. only "sso".
737
+ * An unknown or archived code is rejected with 422 `benefit_not_found`.
738
+ */
739
+ benefitCodes: z.array(CodeSchema).max(100).optional(),
438
740
  reason: z.string().min(3).max(500),
439
741
  startsAt: DateTimeSchema.optional(),
440
742
  endsAt: DateTimeSchema.optional()
441
- }).refine((g) => g.planCode !== void 0 || Object.keys(g.overrides).length > 0, { message: "planCode ya da en az bir override gerekli" });
743
+ }).refine((g) => g.planCode !== void 0 || Object.keys(g.overrides).length > 0 || (g.benefitCodes?.length ?? 0) > 0, { message: "planCode, benefitCodes or at least one override is required" });
442
744
  const GrantSchema = z.object({
443
745
  id: IdSchema,
444
746
  accountRef: ExternalRefSchema,
445
747
  planCode: CodeSchema.nullable(),
446
748
  overrides: EntitlementsSchema,
749
+ /** Named benefits the grant gives (billing core; absent when none or from an older steward). */
750
+ benefitCodes: z.array(CodeSchema).optional(),
447
751
  reason: z.string(),
448
752
  actorRef: ActorRefSchema.nullable(),
449
753
  startsAt: DateTimeSchema,
@@ -468,7 +772,11 @@ const InvoiceLineSchema = z.object({
468
772
  description: z.string(),
469
773
  quantity: z.number().int().positive(),
470
774
  unitAmountMinor: AmountMinorSchema,
471
- amountMinor: AmountMinorSchema
775
+ amountMinor: AmountMinorSchema,
776
+ meter: CodeSchema.optional(),
777
+ units: DecimalUnitsStringSchema.optional(),
778
+ priceAmountMinor: AmountMinorSchema.optional(),
779
+ pricePerUnits: z.number().int().positive().optional()
472
780
  });
473
781
  const InvoiceSchema = z.object({
474
782
  id: IdSchema,
@@ -495,7 +803,12 @@ const InvoiceSchema = z.object({
495
803
  /** Manuel faturanın notu (ör. sözleşme ya da teklif referansı). */
496
804
  note: z.string().nullable().optional(),
497
805
  voidedAt: DateTimeSchema.nullable().optional(),
498
- voidReason: z.string().nullable().optional()
806
+ voidReason: z.string().nullable().optional(),
807
+ billingReason: InvoiceBillingReasonSchema.nullable().optional(),
808
+ paymentStatus: InvoicePaymentStatusSchema.nullable().optional(),
809
+ /** Billing period the invoice covers. */
810
+ periodStart: DateTimeSchema.nullable().optional(),
811
+ periodEnd: DateTimeSchema.nullable().optional()
499
812
  });
500
813
  const InvoiceIssueInputSchema = z.object({
501
814
  number: z.string().min(1).max(100),
@@ -534,6 +847,28 @@ const ManualInvoiceInputSchema = z.object({
534
847
  /** `POST /v1/invoices/{id}/void` — yalnızca manuel fatura; numara serbest kalmaz, belge silinmez. */
535
848
  const InvoiceVoidInputSchema = z.object({ reason: z.string().trim().min(3).max(500) });
536
849
  /**
850
+ * `POST /v1/invoices/{id}/payment-sessions` — opens steward's hosted pay page for an unpaid
851
+ * invoice (the product's own "Pay invoice" button; emails and the portal link to the same page).
852
+ * 409 `invoice_not_payable` when the invoice is not unpaid.
853
+ */
854
+ const InvoicePaymentSessionCreateInputSchema = z.object({
855
+ /** `tr` | `en`; defaults to the account's email locale, else the product's `defaultLocale`. */
856
+ locale: z.enum(SETTINGS_LOCALES).optional(),
857
+ /** Where the page returns after the outcome; its origin must be allowed by product settings. Defaults to `appUrl`. */
858
+ returnUrl: z.url({ protocol: /^https?$/ }).max(2e3).optional()
859
+ });
860
+ /** Pay page session: redirect the customer to `url` (it carries the page key; do not log it). */
861
+ const InvoicePaymentSessionSchema = z.object({
862
+ id: IdSchema,
863
+ url: z.string(),
864
+ expiresAt: DateTimeSchema
865
+ });
866
+ /**
867
+ * `POST /v1/invoices/{id}/waive` — an operator forgives an unpaid invoice (`paymentStatus:
868
+ * "waived"`, e.g. before deleting the account). 409 `invoice_not_waivable` otherwise.
869
+ */
870
+ const InvoiceWaiveInputSchema = z.object({ reason: z.string().trim().min(3).max(500) });
871
+ /**
537
872
  * Vergi dahil tutardan matrah ve vergiyi ayrıştırır (tam sayı, kuruşa
538
873
  * yuvarlama). Toplam her zaman korunur: subtotal + tax === total.
539
874
  */
@@ -638,6 +973,45 @@ const ManualInvoiceResponseSchema = InvoiceSchema.extend(withAccount);
638
973
  const InvoiceVoidResponseSchema = InvoiceSchema.extend(withAccount);
639
974
  /** `GET /v1/checkout-sessions/{id}` — `account` yalnızca oturum `completed` ise. */
640
975
  const CheckoutSessionResponseSchema = CheckoutSessionSchema.extend(withAccount);
976
+ /** `POST /v1/invoices/{id}/waive` (billing core): the waived invoice and the state after it (`openInvoices` shrinks). */
977
+ const InvoiceWaiveResponseSchema = InvoiceSchema.extend(withAccount);
978
+ /** `POST /v1/subscriptions/{id}/change` (billing core MF9): the subscription and the state with `scheduledChange`. */
979
+ const ChangeSubscriptionResponseSchema = SubscriptionSchema.extend(withAccount);
980
+ /** `POST /v1/subscriptions/{id}/uncancel` (billing core MF9): the scheduled cancellation is taken back. */
981
+ const UncancelSubscriptionResponseSchema = SubscriptionSchema.extend(withAccount);
982
+ /**
983
+ * `POST /v1/accounts/{ref}/credits` (metering MF2): the ledger entry written, the meters after it
984
+ * and (K5) the account state after it (a credit may change a meter's standing).
985
+ */
986
+ const CreditGrantResponseSchema = z.object({
987
+ entry: MeterCreditEntrySchema,
988
+ meters: AccountMetersSchema,
989
+ ...withAccount
990
+ });
991
+ /**
992
+ * Known `GET /v1/me` capabilities (M27): what this steward understands beyond the 0.2 contract.
993
+ * `steward catalog push` sends a catalog with `benefits` only when `benefits` is listed (an older
994
+ * steward would silently drop the key); phase 2 adds `events` (`POST /v1/events/ingest`), `meters`
995
+ * (catalog `meters`, `meter_credit` benefits, `GET …/meters`, credits) and `overage` (metered
996
+ * prices, meter charges). A steward lists a capability only once it implements it. Open set.
997
+ */
998
+ const STEWARD_CAPABILITIES = [
999
+ "benefits",
1000
+ "events",
1001
+ "meters",
1002
+ "overage"
1003
+ ];
1004
+ /** `GET /v1/me`: the key's product and scope (setup check). `capabilities` absent from older steward. */
1005
+ const MeResponseSchema = z.object({
1006
+ product: z.string(),
1007
+ keyName: z.string(),
1008
+ scope: z.enum([
1009
+ "product",
1010
+ "admin",
1011
+ "checkout"
1012
+ ]),
1013
+ capabilities: z.array(z.string()).optional()
1014
+ });
641
1015
  /** Değişiklik yapan her istekte zorunlu başlıklar. */
642
1016
  const HEADER_IDEMPOTENCY_KEY = "idempotency-key";
643
1017
  const HEADER_ACTOR_REF = "x-actor-ref";
@@ -685,10 +1059,16 @@ const ERROR_CODES = Object.freeze({
685
1059
  override_type_mismatch: spec(422, "validation"),
686
1060
  /** Grant girdisindeki plan katalogda yok. */
687
1061
  plan_not_found: spec(422, "validation"),
1062
+ /** Grant `benefitCodes`: a code is not an active (not archived) catalog benefit of the product. */
1063
+ benefit_not_found: spec(422, "validation"),
688
1064
  invalid_window: spec(422, "validation"),
689
1065
  already_subscribed: spec(409, "conflict"),
690
1066
  already_canceled: spec(409, "conflict"),
691
- /** `PUT …/plan` `mode: "strict"`: canlı aboneliğin planı hedefi geçiyor; hiçbir şey yazılmadı. */
1067
+ /**
1068
+ * LEGACY — no longer produced (billing core, M35: no plan rank). Steward before the billing core
1069
+ * answered `PUT …/plan` `mode: "strict"` with it when a live subscription's plan outranked the
1070
+ * target. Kept so clients that handle it keep compiling.
1071
+ */
692
1072
  subscription_plan_higher: spec(409, "conflict"),
693
1073
  plan_not_sellable: spec(422, "validation"),
694
1074
  /**
@@ -714,6 +1094,35 @@ const ERROR_CODES = Object.freeze({
714
1094
  invoice_not_voidable: spec(409, "conflict"),
715
1095
  /** Void: ödemeli fatura; iptali iade yolundadır (F4d). */
716
1096
  invoice_has_payment: spec(409, "conflict"),
1097
+ /** `DELETE /v1/accounts/:ref`: the account has an unpaid invoice; pay or waive it first. */
1098
+ invoice_open: spec(409, "conflict"),
1099
+ /** Pay page session: the invoice is not unpaid (paid, waived, uncollectible, or not an invoice-first invoice). */
1100
+ invoice_not_payable: spec(409, "conflict"),
1101
+ /** Waive: the invoice is not unpaid. */
1102
+ invoice_not_waivable: spec(409, "conflict"),
1103
+ /**
1104
+ * `POST /v1/subscriptions/:id/change`: the requested change is not possible — no active price for
1105
+ * the target plan/interval in the subscription's currency, nothing would change, or the
1106
+ * subscription is not live. Plan lookup errors keep their codes (`plan_not_found`, `plan_not_sellable`).
1107
+ */
1108
+ subscription_change_invalid: spec(422, "validation"),
1109
+ /**
1110
+ * `POST /v1/subscriptions/:id/uncancel`: the live subscription has no cancellation scheduled for
1111
+ * the period end (an ended or immediately canceled one is `already_canceled`).
1112
+ */
1113
+ cancel_not_scheduled: spec(409, "conflict"),
1114
+ /** `POST /v1/events/ingest`: more than 1 000 events (or a body over 1 MiB) in one batch; split it. */
1115
+ batch_too_large: spec(422, "validation"),
1116
+ /** `POST /v1/events/ingest`: the unprocessed event backlog is too large; `Retry-After`, keep the batch queued (M11). */
1117
+ events_backlog: spec(503, "unavailable", true),
1118
+ /** Catalog sync: a meter's filter or aggregation changed after its first period opened; use a new meter code (M4). */
1119
+ meter_locked: spec(422, "conflict"),
1120
+ /** Credit grant (or another meter lookup): the code is not an active catalog meter of the product. */
1121
+ meter_not_found: spec(422, "validation"),
1122
+ /** Catalog sync: a metered price's tax terms differ from the plan's base price in that currency (M20). */
1123
+ metered_price_tax_mismatch: spec(422, "validation"),
1124
+ /** A second live subscription would bill the same meter's overage (M20: one billing subscription per meter). */
1125
+ meter_already_billed: spec(409, "conflict"),
717
1126
  internal_error: spec(500, "unavailable", true),
718
1127
  /** `/health`: veritabanına ulaşılamıyor. */
719
1128
  database_unavailable: spec(503, "unavailable", true),
@@ -735,6 +1144,297 @@ function errorCodeSpec(code) {
735
1144
  return isKnownErrorCode(code) ? ERROR_CODES[code] : null;
736
1145
  }
737
1146
  //#endregion
1147
+ //#region ../contract/src/meter-rules.ts
1148
+ const METADATA_PREFIX = "metadata.";
1149
+ function propertyValue(event, property) {
1150
+ if (property === "name") return event.name;
1151
+ const key = property.slice(9);
1152
+ const metadata = event.metadata;
1153
+ return metadata !== void 0 && metadata !== null && Object.hasOwn(metadata, key) ? metadata[key] : void 0;
1154
+ }
1155
+ const isScalar = (value) => typeof value === "string" || typeof value === "number" && Number.isFinite(value) || typeof value === "boolean";
1156
+ /**
1157
+ * Whether an event matches a meter filter (syntax and semantics: `meters.ts`). A condition on an
1158
+ * absent metadata key is false for every operator; `eq` needs the same type; `like` is a
1159
+ * case-sensitive substring. Throws on a filter the schema would reject.
1160
+ */
1161
+ function matchesFilter(event, filter) {
1162
+ if ("and" in filter && Array.isArray(filter.and)) return filter.and.every((f) => matchesFilter(event, f));
1163
+ if ("or" in filter && Array.isArray(filter.or)) return filter.or.some((f) => matchesFilter(event, f));
1164
+ const clause = meterFilterClauseOf(filter);
1165
+ if (clause === null) throw new Error("invalid meter filter condition");
1166
+ const value = propertyValue(event, clause.property);
1167
+ if (!isScalar(value)) return false;
1168
+ const operand = clause.value;
1169
+ switch (clause.operator) {
1170
+ case "eq": return typeof value === typeof operand && value === operand;
1171
+ case "ne": return !(typeof value === typeof operand && value === operand);
1172
+ case "gt": return typeof value === "number" && value > operand;
1173
+ case "gte": return typeof value === "number" && value >= operand;
1174
+ case "lt": return typeof value === "number" && value < operand;
1175
+ case "lte": return typeof value === "number" && value <= operand;
1176
+ case "like": return typeof value === "string" && value.includes(operand);
1177
+ case "not_like": return !(typeof value === "string" && value.includes(operand));
1178
+ }
1179
+ }
1180
+ function contributionOf(event, aggregation) {
1181
+ if (aggregation.kind === "count") return { kind: "count" };
1182
+ const value = propertyValue(event, `${METADATA_PREFIX}${aggregation.key}`);
1183
+ if (aggregation.kind === "unique") {
1184
+ if (typeof value === "string") return {
1185
+ kind: "unique",
1186
+ value: `s:${value}`
1187
+ };
1188
+ if (typeof value === "number" && Number.isFinite(value)) return {
1189
+ kind: "unique",
1190
+ value: `n:${plainDecimalOf(value)}`
1191
+ };
1192
+ if (typeof value === "boolean") return {
1193
+ kind: "unique",
1194
+ value: `b:${value}`
1195
+ };
1196
+ return { kind: "invalid_value" };
1197
+ }
1198
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) return { kind: "invalid_value" };
1199
+ return {
1200
+ kind: aggregation.kind,
1201
+ micro: toMicroUnits(value)
1202
+ };
1203
+ }
1204
+ /**
1205
+ * A meter's value over a set of events (reference computation; the service recomputes a closing
1206
+ * period from raw events with the same result, M8): `count` → matching events, `sum` → total,
1207
+ * `max` → the largest value (0 when none), `unique` → distinct values. `matched` counts events
1208
+ * that passed the filter (invalid ones included), `invalid` those without a usable value.
1209
+ */
1210
+ function consumptionOf(events, meter) {
1211
+ let matched = 0;
1212
+ let invalid = 0;
1213
+ let total = 0n;
1214
+ const distinct = /* @__PURE__ */ new Set();
1215
+ for (const event of events) {
1216
+ if (!matchesFilter(event, meter.filter)) continue;
1217
+ matched += 1;
1218
+ const c = contributionOf(event, meter.aggregation);
1219
+ switch (c.kind) {
1220
+ case "invalid_value":
1221
+ invalid += 1;
1222
+ break;
1223
+ case "count":
1224
+ total += MICRO_UNITS_PER_UNIT;
1225
+ break;
1226
+ case "sum":
1227
+ total += c.micro;
1228
+ break;
1229
+ case "max":
1230
+ if (c.micro > total) total = c.micro;
1231
+ break;
1232
+ case "unique": distinct.add(c.value);
1233
+ }
1234
+ }
1235
+ return {
1236
+ consumedMicro: meter.aggregation.kind === "unique" ? BigInt(distinct.size) * MICRO_UNITS_PER_UNIT : total,
1237
+ matched,
1238
+ invalid
1239
+ };
1240
+ }
1241
+ function nonNegative(name, value) {
1242
+ if (value < 0n) throw new RangeError(`${name} must not be negative`);
1243
+ }
1244
+ /**
1245
+ * The credit rule (M5; decided 2026-09-19): the granted `meter_credit` benefits per meter code.
1246
+ * - Several benefits on one meter sum their units AND their rollover caps (plan + add-on).
1247
+ * - A benefit code granted by several layers counts once (its first counted grant).
1248
+ * - The default plan's credit for a meter counts only when no other layer credits that meter
1249
+ * (`options.defaultPlanCode`): free 50k + team 2M → 2M, free only → 50k.
1250
+ * - Benefits of another type, without a code, or whose properties do not parse are ignored.
1251
+ * The service (`AccountState.meters.includedUnits`, cycle grants) and the SDK (`stateOf`,
1252
+ * `Meters()` fallback) both use this function. Vectors: `credits.json`.
1253
+ */
1254
+ function meterCreditsOf(benefits, options = {}) {
1255
+ const defaultSource = options.defaultPlanCode ? `plan:${options.defaultPlanCode}` : null;
1256
+ const parsed = [];
1257
+ for (const benefit of benefits) {
1258
+ if (benefit.type !== "meter_credit" || typeof benefit.code !== "string") continue;
1259
+ const properties = MeterCreditPropertiesSchema.safeParse(benefit.properties);
1260
+ if (!properties.success) continue;
1261
+ const { meter, units, rolloverCapUnits } = properties.data;
1262
+ parsed.push({
1263
+ code: benefit.code,
1264
+ source: benefit.source ?? null,
1265
+ isDefault: defaultSource !== null && benefit.source === defaultSource,
1266
+ meter,
1267
+ unitsMicro: toMicroUnits(units),
1268
+ rolloverCapMicro: rolloverCapUnits === void 0 ? null : toMicroUnits(rolloverCapUnits)
1269
+ });
1270
+ }
1271
+ const creditedElsewhere = new Set(parsed.filter((b) => !b.isDefault).map((b) => b.meter));
1272
+ const credits = /* @__PURE__ */ new Map();
1273
+ const seen = /* @__PURE__ */ new Set();
1274
+ for (const benefit of parsed) {
1275
+ if (seen.has(benefit.code) || benefit.isDefault && creditedElsewhere.has(benefit.meter)) continue;
1276
+ seen.add(benefit.code);
1277
+ const entry = credits.get(benefit.meter) ?? {
1278
+ unitsMicro: 0n,
1279
+ rolloverCapMicro: null,
1280
+ benefits: []
1281
+ };
1282
+ entry.unitsMicro += benefit.unitsMicro;
1283
+ if (benefit.rolloverCapMicro !== null) entry.rolloverCapMicro = (entry.rolloverCapMicro ?? 0n) + benefit.rolloverCapMicro;
1284
+ entry.benefits.push({
1285
+ code: benefit.code,
1286
+ source: benefit.source,
1287
+ unitsMicro: benefit.unitsMicro,
1288
+ rolloverCapMicro: benefit.rolloverCapMicro
1289
+ });
1290
+ credits.set(benefit.meter, entry);
1291
+ }
1292
+ return credits;
1293
+ }
1294
+ /** `max(0, consumed − credited)` (M21). */
1295
+ function overageOf(input) {
1296
+ const overage = input.consumedMicro - input.creditedMicro;
1297
+ return overage > 0n ? overage : 0n;
1298
+ }
1299
+ /**
1300
+ * Credit standing of a meter (M14): no credit (`creditedMicro` 0) → `ok`; balance
1301
+ * (`credited − consumed`) ≤ 0 → `exhausted`; consumption ≥ `lowAt` × credit → `low` (`lowAt`
1302
+ * null disables `low`); else `ok`. Exact integer comparison (`lowAt` read as micro units).
1303
+ * `billable` does not change the standing — it describes the credit; a billable meter goes
1304
+ * `low`/`exhausted` too and only the gate stays open (M15).
1305
+ */
1306
+ function standingOf(input) {
1307
+ nonNegative("creditedMicro", input.creditedMicro);
1308
+ nonNegative("consumedMicro", input.consumedMicro);
1309
+ if (input.lowAt !== null && !(input.lowAt > 0 && input.lowAt <= 1)) throw new RangeError("lowAt must be in (0, 1]");
1310
+ if (input.creditedMicro === 0n) return "ok";
1311
+ if (input.creditedMicro - input.consumedMicro <= 0n) return "exhausted";
1312
+ if (input.lowAt !== null && input.consumedMicro * 1000000n >= toMicroUnits(input.lowAt) * input.creditedMicro) return "low";
1313
+ return "ok";
1314
+ }
1315
+ /**
1316
+ * Credit carried into the next period (M6): `min(max(closing balance, 0), cap)`; `capMicro`
1317
+ * null (rollover off) → 0. Debt never carries.
1318
+ */
1319
+ function rolloverOf(input) {
1320
+ if (input.capMicro === null) return 0n;
1321
+ nonNegative("capMicro", input.capMicro);
1322
+ const balance = input.closingBalanceMicro > 0n ? input.closingBalanceMicro : 0n;
1323
+ return balance < input.capMicro ? balance : input.capMicro;
1324
+ }
1325
+ function daysInUtcMonth(year, month) {
1326
+ const d = /* @__PURE__ */ new Date(0);
1327
+ d.setUTCFullYear(year, month + 1, 0);
1328
+ return d.getUTCDate();
1329
+ }
1330
+ /**
1331
+ * `from` + `months` months in UTC with the month-end clamp of the service's `addInterval`
1332
+ * (`service/src/periods.ts`, date-fns `addMonths` on `UTCDate`): the day of month is kept, or the
1333
+ * last day of a shorter month (Jan 31 + 1 → Feb 28/29); the time of day is kept.
1334
+ */
1335
+ function addUtcMonths(from, months) {
1336
+ if (!Number.isInteger(months)) throw new RangeError("months must be an integer");
1337
+ const total = from.getUTCMonth() + months;
1338
+ const year = from.getUTCFullYear() + Math.floor(total / 12);
1339
+ const month = (total % 12 + 12) % 12;
1340
+ const out = new Date(from.getTime());
1341
+ out.setUTCFullYear(year, month, Math.min(from.getUTCDate(), daysInUtcMonth(year, month)));
1342
+ return out;
1343
+ }
1344
+ /**
1345
+ * The monthly meter period containing `at` (M7): `[anchor + n months, anchor + (n + 1) months)`,
1346
+ * start inclusive, end exclusive, where every boundary is computed FROM THE ANCHOR (`addUtcMonths`;
1347
+ * Jan 31 → Feb 28 → Mar 31, no drift) — the same grid as the billing cycle's `nextPeriodEnd` for a
1348
+ * monthly subscription. `n` may be negative (`at` before the anchor). UTC throughout.
1349
+ */
1350
+ function periodBoundsOf(anchor, at) {
1351
+ if (Number.isNaN(anchor.getTime()) || Number.isNaN(at.getTime())) throw new RangeError("invalid date");
1352
+ let n = (at.getUTCFullYear() - anchor.getUTCFullYear()) * 12 + (at.getUTCMonth() - anchor.getUTCMonth());
1353
+ while (addUtcMonths(anchor, n).getTime() > at.getTime()) n -= 1;
1354
+ while (addUtcMonths(anchor, n + 1).getTime() <= at.getTime()) n += 1;
1355
+ return {
1356
+ start: addUtcMonths(anchor, n),
1357
+ end: addUtcMonths(anchor, n + 1)
1358
+ };
1359
+ }
1360
+ function nonNegativeInteger(name, value, min = 0) {
1361
+ if (!Number.isSafeInteger(value) || value < min) throw new RangeError(`${name} must be an integer ≥ ${min}`);
1362
+ return BigInt(value);
1363
+ }
1364
+ /** The overage amount before the cap (BigInt, half up) and the cap (null: none); validates the price. */
1365
+ function pricedOverage(input) {
1366
+ const amount = nonNegativeInteger("amountMinor", input.amountMinor);
1367
+ const perUnits = nonNegativeInteger("perUnits", input.perUnits, 1);
1368
+ const cap = input.capMinor === void 0 || input.capMinor === null ? null : nonNegativeInteger("capMinor", input.capMinor);
1369
+ if (input.overageMicro <= 0n) return {
1370
+ minor: 0n,
1371
+ cap
1372
+ };
1373
+ const numerator = input.overageMicro * amount;
1374
+ const denominator = perUnits * MICRO_UNITS_PER_UNIT;
1375
+ let minor = numerator / denominator;
1376
+ if (2n * (numerator % denominator) >= denominator) minor += 1n;
1377
+ return {
1378
+ minor,
1379
+ cap
1380
+ };
1381
+ }
1382
+ /**
1383
+ * Overage amount in minor units (M21): `round_half_up(overage × amountMinor / perUnits)` on micro
1384
+ * units with BigInt — proportional (no block rounding), always whole minor units — then clipped
1385
+ * by `capMinor`. Tax terms are the price's (gross when tax-inclusive).
1386
+ */
1387
+ function rateOverage(input) {
1388
+ const { minor: uncapped, cap } = pricedOverage(input);
1389
+ const minor = cap !== null && uncapped > cap ? cap : uncapped;
1390
+ if (minor > BigInt(Number.MAX_SAFE_INTEGER)) throw new RangeError("overage amount exceeds the safe integer range");
1391
+ return Number(minor);
1392
+ }
1393
+ /**
1394
+ * Whether the period's priced overage reached the rate's `capMinor` (M15: the SDK gate closes on a
1395
+ * billable meter once the cap is reached — further use would be free): there is overage
1396
+ * (`overageMicro` > 0) and its amount before the cap (`rateOverage` without `capMinor`) is ≥ the
1397
+ * cap. No cap → false. Same validation as `rateOverage`.
1398
+ */
1399
+ function overageCapReached(input) {
1400
+ const { minor, cap } = pricedOverage(input);
1401
+ return cap !== null && input.overageMicro > 0n && minor >= cap;
1402
+ }
1403
+ /**
1404
+ * Totals and stored lines of an invoice (or meter charge) with overage lines: the manual invoice
1405
+ * rule (`manualInvoiceTotals`) with every line at quantity 1 — ONE rounding at invoice level
1406
+ * (tax-inclusive: the total is kept and split with `splitInclusiveTax`, the net spread over the
1407
+ * lines by largest remainder; exclusive: tax added on the sum). Stored lines are net and keep
1408
+ * their meter fields (`meter`, `units`, `priceAmountMinor`, `pricePerUnits`).
1409
+ */
1410
+ function meterChargeTotals(lines, terms) {
1411
+ for (const line of lines) nonNegativeInteger("amountMinor", line.amountMinor);
1412
+ const totals = manualInvoiceTotals({
1413
+ lines: lines.map((l) => ({
1414
+ description: l.description,
1415
+ quantity: 1,
1416
+ unitAmountMinor: l.amountMinor
1417
+ })),
1418
+ taxRateBps: terms.taxRateBps,
1419
+ taxInclusive: terms.taxInclusive
1420
+ });
1421
+ return {
1422
+ subtotalMinor: totals.subtotalMinor,
1423
+ taxMinor: totals.taxMinor,
1424
+ totalMinor: totals.totalMinor,
1425
+ lines: totals.lines.map((line, i) => {
1426
+ const source = lines[i];
1427
+ return {
1428
+ ...line,
1429
+ ...source.meter !== void 0 ? { meter: source.meter } : {},
1430
+ ...source.units !== void 0 ? { units: source.units } : {},
1431
+ ...source.priceAmountMinor !== void 0 ? { priceAmountMinor: source.priceAmountMinor } : {},
1432
+ ...source.pricePerUnits !== void 0 ? { pricePerUnits: source.pricePerUnits } : {}
1433
+ };
1434
+ })
1435
+ };
1436
+ }
1437
+ //#endregion
738
1438
  //#region ../contract/src/plan.ts
739
1439
  const PLAN_SET_MODES = ["ensure", "strict"];
740
1440
  const PlanSetModeSchema = z.enum(PLAN_SET_MODES);
@@ -905,7 +1605,69 @@ const DoctorCatalogSchema = z.object({
905
1605
  /** Satışa açık planların (`sellable`) bugün checkout'ta satılabilen aktif fiyatları. */
906
1606
  sellablePrices: z.number().int().nonnegative(),
907
1607
  /** Satışa açık planların satılamayan aktif fiyatları (satışa kapalı planların fiyatları sayılmaz). */
908
- unsellablePrices: z.array(DoctorUnsellablePriceSchema)
1608
+ unsellablePrices: z.array(DoctorUnsellablePriceSchema),
1609
+ /**
1610
+ * Live subscriptions whose locked amount (or tax inclusion) differs from the current price of
1611
+ * their key (M36): they keep paying what they locked until explicitly moved. Absent from older steward.
1612
+ */
1613
+ priceDriftSubscriptions: z.number().int().nonnegative().optional()
1614
+ });
1615
+ /** Billing core facts (M30). Absent from older steward. */
1616
+ const DoctorBillingSchema = z.object({
1617
+ /**
1618
+ * Live subscriptions that still carry a provider subscription reference (created by the removed
1619
+ * iyzico subscription path). Steward never invoices them and the provider may still charge them:
1620
+ * cancel each in the provider panel, then immediately in steward. Should be 0.
1621
+ */
1622
+ legacyProviderSubscriptions: z.number().int().nonnegative() });
1623
+ /**
1624
+ * Event ingest and rollup facts (metering phase 2, M29; only for a product that defines meters).
1625
+ * Absent from older steward. Names and meter codes only: no metadata, no account refs.
1626
+ */
1627
+ const DoctorEventsSchema = z.object({
1628
+ /** Non-archived meters. */
1629
+ meters: z.number().int().nonnegative(),
1630
+ /** Meters whose already received events are still being counted in (`backfilling`). */
1631
+ backfillingMeters: z.array(z.string()),
1632
+ /** This product's events not rolled up yet (counted up to just above the back-pressure limit). */
1633
+ backlogRows: z.number().int().nonnegative(),
1634
+ /** Age of this product's oldest event not rolled up yet, seconds (0 when none). */
1635
+ rollupLagSeconds: z.number().nonnegative(),
1636
+ /** The deployment's backlog is over the limits: `POST /v1/events/ingest` answers `503 events_backlog`. */
1637
+ backlogged: z.boolean(),
1638
+ /** Events of the last 7 days examined for the fields below (newest first, bounded). */
1639
+ sampledEvents: z.number().int().nonnegative(),
1640
+ /** Event names of the sample that no meter selects (top 5 by count). Normal: events are generic (M2). */
1641
+ unmatchedEventNames: z.array(z.object({
1642
+ name: z.string(),
1643
+ events: z.number().int().nonnegative()
1644
+ })),
1645
+ /** Per meter, sampled events it selects that carry no usable value (M4 `invalid_value`). */
1646
+ invalidValues: z.array(z.object({
1647
+ meter: z.string(),
1648
+ events: z.number().int().nonnegative()
1649
+ })),
1650
+ /** Non-archived meters with no usage in their current periods and no selected event in the sample. */
1651
+ metersWithoutEvents: z.array(z.string())
1652
+ });
1653
+ /** Rollup lag above which the doctor warns (the `rollup lag > 60 s` alert, M29). */
1654
+ const DOCTOR_ROLLUP_LAG_WARN_SECONDS = 60;
1655
+ /**
1656
+ * Overage facts (metering MF5, M20/M21): present when the catalog has a metered price or the
1657
+ * product has meter charges. Absent from older steward.
1658
+ */
1659
+ const DoctorOverageSchema = z.object({
1660
+ /** `settings.metering.chargeMode`: `off` | `dry_run` | `live` (open set for newer steward). */
1661
+ chargeMode: z.string(),
1662
+ /** Active metered prices of the catalog. */
1663
+ meteredPrices: z.number().int().nonnegative(),
1664
+ /**
1665
+ * Live subscriptions without a locked rate for a metered price of their plan: a metered price
1666
+ * added after a subscription started is not attached to it (M20), so its overage is not billed.
1667
+ */
1668
+ unratedSubscriptions: z.number().int().nonnegative(),
1669
+ /** Charges below the minimum invoice amount waiting for their subscription's next invoice. */
1670
+ carriedCharges: z.number().int().nonnegative()
909
1671
  });
910
1672
  const DOCTOR_PROVIDER_ENVIRONMENTS = [
911
1673
  "sandbox",
@@ -930,7 +1692,7 @@ const DoctorProviderSchema = z.object({
930
1692
  lastError: z.object({
931
1693
  /** Sağlayıcı hata kodu, ör. `iyzico:timeout`, `iyzico:1001`. */
932
1694
  code: z.string(),
933
- /** `ensure_price` | `create_checkout` | `retrieve_checkout` | `retrieve_subscription` | `cancel_subscription` | `retry_payment`. */
1695
+ /** Provider operation, e.g. `create_payment_checkout`, `retrieve_payment_checkout`, `retrieve_payment` (open set). */
934
1696
  operation: z.string(),
935
1697
  at: DateTimeSchema
936
1698
  }).nullable()
@@ -967,6 +1729,17 @@ const DoctorEndpointSchema = z.object({
967
1729
  lastDelivery: WebhookLastDeliverySchema.nullable(),
968
1730
  diagnosis: z.string()
969
1731
  });
1732
+ /** Billing emails (issue #15). */
1733
+ const DoctorEmailsSchema = z.object({
1734
+ /** The deployment has a mail transport (`STEWARD_SMTP_URL`). */
1735
+ transportConfigured: z.boolean(),
1736
+ /** A sender address is known (`settings.emails.from.address` or the deployment default). */
1737
+ senderConfigured: z.boolean(),
1738
+ /** Templates the product turned off. */
1739
+ disabledTemplates: z.array(z.string()),
1740
+ /** Emails are sent: transport and sender configured, at least one template on. */
1741
+ ready: z.boolean()
1742
+ });
970
1743
  const DoctorReportSchema = z.object({
971
1744
  /** `error` önemde uyarı yok. */
972
1745
  ok: z.boolean(),
@@ -984,8 +1757,28 @@ const DoctorReportSchema = z.object({
984
1757
  dunning: z.object({
985
1758
  /** true: dunning adımları yalnızca iz yazar (askı/sonlandırma yok). */
986
1759
  dryRun: z.boolean() }),
1760
+ /** Absent in reports of steward versions without billing emails. */
1761
+ emails: DoctorEmailsSchema.optional(),
1762
+ /** Absent in reports of steward versions before the billing core. */
1763
+ billing: DoctorBillingSchema.optional(),
1764
+ /** Event ingest and rollup (metering); absent for a product without meters and from older steward. */
1765
+ events: DoctorEventsSchema.optional(),
1766
+ /** Metering MF5; absent without metered prices and from older steward. */
1767
+ overage: DoctorOverageSchema.optional(),
987
1768
  warnings: z.array(DoctorWarningSchema)
988
1769
  });
1770
+ /** Email readiness from settings and the deployment's transport (pure). */
1771
+ function emailReadinessOf(settings, transport) {
1772
+ const templates = settings.emails?.templates ?? {};
1773
+ const disabledTemplates = EMAIL_TEMPLATES.filter((t) => templates[t]?.enabled === false);
1774
+ const senderConfigured = settings.emails?.from?.address !== void 0 || transport.defaultFromConfigured;
1775
+ return {
1776
+ transportConfigured: transport.configured,
1777
+ senderConfigured,
1778
+ disabledTemplates: [...disabledTemplates],
1779
+ ready: transport.configured && senderConfigured && disabledTemplates.length < EMAIL_TEMPLATES.length
1780
+ };
1781
+ }
989
1782
  /** Bekleyen en eski teslimat bundan eskiyse `endpoint_pending_backlog` (K23 alarmıyla aynı eşik). */
990
1783
  const DOCTOR_PENDING_BACKLOG_MS = 36e5;
991
1784
  /** Son denenmiş teslimattan endpoint teşhisi (saf). */
@@ -1044,14 +1837,15 @@ function buildDoctorReport(facts) {
1044
1837
  };
1045
1838
  const { catalog, provider, settings, dunning } = facts;
1046
1839
  if (!catalog.synced) warn("error", "catalog_not_synced", "katalog senkronlanmamış: `PUT /v1/admin/catalog` (steward catalog push)");
1047
- const byReason = (reason) => catalog.unsellablePrices.filter((p) => p.reason === reason);
1048
- const unsynced = byReason("provider_price_missing");
1049
- if (unsynced.length > 0) warn("error", "price_not_synced_with_provider", `${unsynced.length} fiyatın sağlayıcıda karşılığı yok (checkout plan_not_sellable döner): katalog senkronunu yeniden koşun`);
1050
- const otherUnsellable = catalog.unsellablePrices.filter((p) => p.reason !== "provider_price_missing" && p.reason !== "provider_not_configured");
1840
+ const otherUnsellable = catalog.unsellablePrices.filter((p) => p.reason !== "provider_not_configured");
1051
1841
  if (otherUnsellable.length > 0) {
1052
1842
  const reasons = [...new Set(otherUnsellable.map((p) => p.reason))].join(", ");
1053
1843
  warn("warning", "price_unsellable", `${otherUnsellable.length} fiyat satılamaz (${reasons})`);
1054
1844
  }
1845
+ const drift = catalog.priceDriftSubscriptions ?? 0;
1846
+ if (drift > 0) warn("info", "price_drift", `${drift} live subscription(s) keep a locked amount that differs from the current price; moving them to the current price is an explicit operation`);
1847
+ const legacy = facts.billing?.legacyProviderSubscriptions ?? 0;
1848
+ if (legacy > 0) warn("error", "legacy_provider_subscription", `${legacy} live subscription(s) still have a provider subscription: steward never invoices them and the provider may still charge them; cancel each in the provider panel, then immediately in steward`);
1055
1849
  if (!provider.configured) warn("error", "provider_not_configured", `ödeme sağlayıcısı ${provider.id} bu deployment'ta yapılandırılmamış`);
1056
1850
  else if (provider.healthy === false) warn("warning", "provider_unhealthy", `ödeme sağlayıcısı ${provider.id} art arda erişilemedi (yanıtı veren pod'un gözlemi)`);
1057
1851
  const returnOrigins = returnOriginsOf(settings);
@@ -1073,7 +1867,27 @@ function buildDoctorReport(facts) {
1073
1867
  if (endpoint.dead > 0) warn("warning", "endpoint_dead_deliveries", `${endpoint.dead} teslimattan vazgeçildi (dead): etkilenen hesaplar için resync`, endpoint.id);
1074
1868
  if (endpoint.oldestPendingAt !== null && facts.checkedAt.getTime() - Date.parse(endpoint.oldestPendingAt) > 36e5) warn("warning", "endpoint_pending_backlog", `${endpoint.pending} teslimat bekliyor; en eskisi bir saatten eski`, endpoint.id);
1075
1869
  }
1870
+ const emails = facts.emailTransport ? emailReadinessOf(settings, facts.emailTransport) : void 0;
1871
+ if (emails && emails.disabledTemplates.length < EMAIL_TEMPLATES.length) {
1872
+ if (!emails.transportConfigured) warn("warning", "email_not_configured", "email is not configured: steward sends no billing emails (STEWARD_SMTP_URL)");
1873
+ else if (!emails.senderConfigured) warn("warning", "email_sender_missing", "no email sender: set `settings.emails.from.address` or STEWARD_EMAIL_FROM");
1874
+ if (settings.emails?.billingPath === void 0) warn("info", "email_billing_link_missing", "`settings.emails.billingPath` is not set: email buttons open `appUrl`");
1875
+ }
1076
1876
  if (dunning.dryRun) warn("info", "dunning_dry_run", "dunning dry-run: adımlar yalnızca iz yazar, askı ve sonlandırma uygulanmaz");
1877
+ const ev = facts.events;
1878
+ if (ev) {
1879
+ if (ev.backlogged) warn("warning", "events_backlog", "the event backlog is over the limits: ingest answers 503 events_backlog (the SDK keeps batches queued)");
1880
+ if (ev.rollupLagSeconds > 60) warn("warning", "meter_rollup_lag", `the oldest event waiting for the rollup is ${ev.rollupLagSeconds} s old (${ev.backlogRows} waiting): meters lag behind`);
1881
+ for (const { meter, events } of ev.invalidValues) warn("warning", "events_invalid_value", `meter ${meter}: ${events} recent event(s) carry no usable value (sum/max need a finite, non-negative number; unique needs the key) and add nothing`);
1882
+ if (ev.metersWithoutEvents.length > 0) warn("info", "meter_without_events", `no recent event counts in meter(s) ${ev.metersWithoutEvents.join(", ")}: check the event name and filter`);
1883
+ if (ev.unmatchedEventNames.length > 0) warn("info", "events_unmatched", `recent events no meter selects: ${ev.unmatchedEventNames.map((u) => `${u.name} (${u.events})`).join(", ")} — normal when no meter is meant for them`);
1884
+ }
1885
+ const overage = facts.overage;
1886
+ if (overage && overage.meteredPrices > 0) {
1887
+ if (overage.chargeMode === "off") warn("warning", "overage_charge_mode_off", "metering.chargeMode is off: the catalog has metered prices but overage is neither priced nor invoiced");
1888
+ else if (overage.chargeMode === "dry_run") warn("info", "overage_dry_run", "metering.chargeMode is dry_run: overage is priced into meter charges (GET /v1/accounts/:ref/meter-charges) but not invoiced; switch to live after one full cycle");
1889
+ }
1890
+ if (overage && overage.unratedSubscriptions > 0) warn("info", "overage_unrated_subscriptions", `${overage.unratedSubscriptions} live subscription(s) have no locked rate for a metered price of their plan (added after they started, M20): their overage is not billed until they move to the current terms`);
1077
1891
  return {
1078
1892
  ok: !warnings.some((w) => w.severity === "error"),
1079
1893
  checkedAt: facts.checkedAt.toISOString(),
@@ -1087,8 +1901,12 @@ function buildDoctorReport(facts) {
1087
1901
  hosted,
1088
1902
  endpoints,
1089
1903
  dunning,
1904
+ ...emails ? { emails } : {},
1905
+ ...facts.billing ? { billing: facts.billing } : {},
1906
+ ...facts.events ? { events: facts.events } : {},
1907
+ ...overage ? { overage } : {},
1090
1908
  warnings
1091
1909
  };
1092
1910
  }
1093
1911
  //#endregion
1094
- export { InvoiceIssueInputSchema as $, webhookEndpointUrlProblem as A, BrandingSchema as At, AccountDetailSchema as B, applySettingsPatch as Bt, WebhookEndpointListSchema as C, CheckoutSessionSchema as Ct, WebhookLastDeliverySchema as D, ProviderPayloadSchema as Dt, WebhookEndpointStatusSchema as E, MAX_CHECKOUT_CONSENT_DOCS as Et, PlanSetWarningSchema as F, ReturnOriginSchema as Ft, HEADER_ACTOR_REF as G, CatalogSyncInputSchema as Gt, CancelSubscriptionResponseSchema as H, returnOriginsOf as Ht, ResyncResultSchema as I, SETTINGS_LOCALES as It, HEADER_REQUEST_ID as J, FeatureTypeSchema as Jt, HEADER_API_VERSION as K, CatalogSyncResultSchema as Kt, ERROR_CODES as L, SettingsLocaleSchema as Lt, PlanSetInputSchema as M, DEFAULT_PORTAL_TTL_MINUTES as Mt, PlanSetModeSchema as N, ProductSettingsPatchSchema as Nt, WebhookSecretSchema as O, checkoutRedirectUrl as Ot, PlanSetResponseSchema as P, ProductSettingsSchema as Pt, ManualInvoiceResponseSchema as Q, errorCodeSpec as R, TtlMinutesSchema as Rt, WebhookEndpointCreateInputSchema as S, CheckoutSessionCreatedSchema as St, WebhookEndpointSchema as T, ConsentSchema as Tt, CheckoutSessionResponseSchema as U, AccountsRefreshResultSchema as Ut, AccountUpsertResponseSchema as V, isReturnUrlAllowed as Vt, GrantCreateResponseSchema as W, CatalogSchema as Wt, InvoiceIssueResponseSchema as X, PriceDefSchema as Xt, HEADER_SDK as Y, PlanDefSchema as Yt, InvoiceVoidResponseSchema as Z, PriceSchema as Zt, hostedReadinessOf as _, PortalSessionSchema as _t, DOCTOR_SEVERITIES as a, ManualInvoiceInputSchema as at, WEBHOOK_ENDPOINT_STATUSES as b, CheckoutPrefillSchema as bt, DoctorHostedSchema as c, splitInclusiveTax as ct, DoctorSettingsSchema as d, GrantSchema as dt, InvoiceLineSchema as et, DoctorSeveritySchema as f, PaymentSchema as ft, endpointDiagnosisOf as g, PortalSessionCreateInputSchema as gt, buildDoctorReport as h, PORTAL_SESSION_ERRORS as ht, DOCTOR_REQUIRED_LEGAL_LINKS as i, MANUAL_INVOICE_MAX_AMOUNT_MINOR as it, PLAN_SET_MODES as j, DEFAULT_CHECKOUT_TTL_MINUTES as jt, generateWebhookSecret as k, BrandingPatchSchema as kt, DoctorProviderSchema as l, CancelSubscriptionInputSchema as lt, DoctorWarningSchema as m, SubscriptionSchema as mt, DOCTOR_PROVIDER_ENVIRONMENTS as n, InvoiceStatusSchema as nt, DoctorCatalogSchema as o, ManualInvoiceLineInputSchema as ot, DoctorUnsellablePriceSchema as p, StatsSchema as pt, HEADER_IDEMPOTENCY_KEY as q, FeatureDefSchema as qt, DOCTOR_REQUIRED_BRANDING as r, InvoiceVoidInputSchema as rt, DoctorEndpointSchema as s, manualInvoiceTotals as st, DOCTOR_PENDING_BACKLOG_MS as t, InvoiceSchema as tt, DoctorReportSchema as u, GrantCreateInputSchema as ut, DEFAULT_ENDPOINT_EVENT_TYPES as v, CHECKOUT_ERRORS as vt, WebhookEndpointRotateInputSchema as w, CheckoutStatusSchema as wt, WEBHOOK_SECRET_PREFIX as x, CheckoutSessionCreateInputSchema as xt, EventTypeSchema as y, CheckoutConsentInputSchema as yt, isKnownErrorCode as z, WebUrlSchema as zt };
1912
+ export { rolloverOf as $, CheckoutStatusSchema as $t, WebhookEndpointRotateInputSchema as A, TtlMinutesSchema as An, InvoiceWaiveInputSchema as At, PlanSetResponseSchema as B, CatalogSchema as Bn, PaymentSchema as Bt, hostedReadinessOf as C, MeteringSettingsSchema as Cn, InvoiceIssueInputSchema as Ct, WEBHOOK_SECRET_PREFIX as D, ReturnOriginSchema as Dn, InvoiceSchema as Dt, WEBHOOK_ENDPOINT_STATUSES as E, ProductSettingsSchema as En, InvoicePaymentSessionSchema as Et, generateWebhookSecret as F, isReturnUrlAllowed as Fn, splitInclusiveTax as Ft, contributionOf as G, MAX_CATALOG_BENEFITS as Gn, PortalSessionCreateInputSchema as Gt, ResyncResultSchema as H, CatalogSyncResultSchema as Hn, SubscriptionChangeInputSchema as Ht, webhookEndpointUrlProblem as I, meteringSettingsOf as In, CancelSubscriptionInputSchema as It, meterCreditsOf as J, MAX_PLAN_BENEFITS as Jn, CheckoutConsentInputSchema as Jt, matchesFilter as K, MAX_CATALOG_METERED_PRICES as Kn, PortalSessionSchema as Kt, PLAN_SET_MODES as L, returnOriginsOf as Ln, GrantCreateInputSchema as Lt, WebhookEndpointStatusSchema as M, applySettingsPatch as Mn, ManualInvoiceInputSchema as Mt, WebhookLastDeliverySchema as N, billingSettingsOf as Nn, ManualInvoiceLineInputSchema as Nt, WebhookEndpointCreateInputSchema as O, SETTINGS_LOCALES as On, InvoiceStatusSchema as Ot, WebhookSecretSchema as P, eventsSettingsOf as Pn, manualInvoiceTotals as Pt, rateOverage as Q, PriceSchema as Qn, CheckoutSessionSchema as Qt, PlanSetInputSchema as R, AccountsRefreshResultSchema as Rn, GrantSchema as Rt, endpointDiagnosisOf as S, MeteringSettingsPatchSchema as Sn, UncancelSubscriptionResponseSchema as St, EventTypeSchema as T, ProductSettingsPatchSchema as Tn, InvoicePaymentSessionCreateInputSchema as Tt, addUtcMonths as U, FeatureDefSchema as Un, SubscriptionSchema as Ut, PlanSetWarningSchema as V, CatalogSyncInputSchema as Vn, StatsSchema as Vt, consumptionOf as W, FeatureTypeSchema as Wn, PORTAL_SESSION_ERRORS as Wt, overageOf as X, PlanDefSchema as Xn, CheckoutSessionCreateInputSchema as Xt, overageCapReached as Y, METERED_PRICE_TAX_MISMATCH as Yn, CheckoutPrefillSchema as Yt, periodBoundsOf as Z, PriceDefSchema as Zn, CheckoutSessionCreatedSchema as Zt, DoctorSeveritySchema as _, EventsSettingsPatchSchema as _n, InvoiceVoidResponseSchema as _t, DOCTOR_ROLLUP_LAG_WARN_SECONDS as a, BillingSettingsPatchSchema as an, AccountUpsertResponseSchema as at, buildDoctorReport as b, METER_CHARGE_MODES as bn, MeResponseSchema as bt, DoctorCatalogSchema as c, BrandingSchema as cn, CheckoutSessionResponseSchema as ct, DoctorEventsSchema as d, DEFAULT_LATE_GRACE_MINUTES as dn, HEADER_ACTOR_REF as dt, ConsentSchema as en, standingOf as et, DoctorHostedSchema as f, DEFAULT_METER_CHARGE_MODE as fn, HEADER_API_VERSION as ft, DoctorSettingsSchema as g, EventRetentionDaysSchema as gn, InvoiceIssueResponseSchema as gt, DoctorReportSchema as h, DEFAULT_PORTAL_TTL_MINUTES as hn, HEADER_SDK as ht, DOCTOR_REQUIRED_LEGAL_LINKS as i, checkoutRedirectUrl as in, AccountDetailSchema as it, WebhookEndpointSchema as j, WebUrlSchema as jn, MANUAL_INVOICE_MAX_AMOUNT_MINOR as jt, WebhookEndpointListSchema as k, SettingsLocaleSchema as kn, InvoiceVoidInputSchema as kt, DoctorEmailsSchema as l, DEFAULT_CHECKOUT_TTL_MINUTES as ln, CreditGrantResponseSchema as lt, DoctorProviderSchema as m, DEFAULT_PAYMENT_DUE_DAYS as mn, HEADER_REQUEST_ID as mt, DOCTOR_PROVIDER_ENVIRONMENTS as n, MAX_CHECKOUT_CONSENT_DOCS as nn, errorCodeSpec as nt, DOCTOR_SEVERITIES as o, BillingSettingsSchema as on, CancelSubscriptionResponseSchema as ot, DoctorOverageSchema as p, DEFAULT_MIN_INVOICE_MINOR as pn, HEADER_IDEMPOTENCY_KEY as pt, meterChargeTotals as q, MAX_CATALOG_METERS as qn, CHECKOUT_ERRORS as qt, DOCTOR_REQUIRED_BRANDING as r, ProviderPayloadSchema as rn, isKnownErrorCode as rt, DoctorBillingSchema as s, BrandingPatchSchema as sn, ChangeSubscriptionResponseSchema as st, DOCTOR_PENDING_BACKLOG_MS as t, INDIVIDUAL_IDENTITY_PLACEHOLDER as tn, ERROR_CODES as tt, DoctorEndpointSchema as u, DEFAULT_EVENT_RETENTION_DAYS as un, GrantCreateResponseSchema as ut, DoctorUnsellablePriceSchema as v, EventsSettingsSchema as vn, InvoiceWaiveResponseSchema as vt, DEFAULT_ENDPOINT_EVENT_TYPES as w, PaymentDueDaysSchema as wn, InvoiceLineSchema as wt, emailReadinessOf as x, MeterChargeModeSchema as xn, STEWARD_CAPABILITIES as xt, DoctorWarningSchema as y, LateGraceMinutesSchema as yn, ManualInvoiceResponseSchema as yt, PlanSetModeSchema as z, CatalogPlanSchema as zn, MAX_GRANT_BENEFITS as zt };