@saasicat/nest 0.2.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 (96) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +53 -0
  3. package/dist/admin/index.cjs +1283 -0
  4. package/dist/admin/index.d.cts +225 -0
  5. package/dist/admin/index.d.ts +225 -0
  6. package/dist/admin/index.js +58 -0
  7. package/dist/admin-audit.service-9IqXMlZm.d.cts +30 -0
  8. package/dist/admin-audit.service-9IqXMlZm.d.ts +30 -0
  9. package/dist/admin-manifest.config-DyrQNT7M.d.cts +10 -0
  10. package/dist/admin-manifest.config-DyrQNT7M.d.ts +10 -0
  11. package/dist/aggregation-CJ3qQf92.d.cts +288 -0
  12. package/dist/aggregation-Dvz9e8X2.d.ts +288 -0
  13. package/dist/billing/index.cjs +5205 -0
  14. package/dist/billing/index.d.cts +1326 -0
  15. package/dist/billing/index.d.ts +1326 -0
  16. package/dist/billing/index.js +197 -0
  17. package/dist/catalog/index.cjs +6449 -0
  18. package/dist/catalog/index.d.cts +1073 -0
  19. package/dist/catalog/index.d.ts +1073 -0
  20. package/dist/catalog/index.js +158 -0
  21. package/dist/checkout-offer/index.cjs +807 -0
  22. package/dist/checkout-offer/index.d.cts +120 -0
  23. package/dist/checkout-offer/index.d.ts +120 -0
  24. package/dist/checkout-offer/index.js +20 -0
  25. package/dist/chunk-2FR6ZL7R.js +0 -0
  26. package/dist/chunk-2PNX2QL2.js +347 -0
  27. package/dist/chunk-2VNZYRDV.js +326 -0
  28. package/dist/chunk-7MLGHWKO.js +12 -0
  29. package/dist/chunk-ABB2CRAS.js +31 -0
  30. package/dist/chunk-AJ3EPELS.js +157 -0
  31. package/dist/chunk-E56W4U2P.js +90 -0
  32. package/dist/chunk-HRU3CP57.js +71 -0
  33. package/dist/chunk-L4SUWH5B.js +14 -0
  34. package/dist/chunk-M6CZQYPY.js +314 -0
  35. package/dist/chunk-MDIZUVIK.js +87 -0
  36. package/dist/chunk-MV6LW5UB.js +217 -0
  37. package/dist/chunk-NYLON2VC.js +6 -0
  38. package/dist/chunk-P6MYZMXQ.js +951 -0
  39. package/dist/chunk-Q53N43LQ.js +13 -0
  40. package/dist/chunk-QJVPRD3R.js +573 -0
  41. package/dist/chunk-RIGLQ4BV.js +3367 -0
  42. package/dist/chunk-SABTXESR.js +24 -0
  43. package/dist/chunk-SHUYVCID.js +6 -0
  44. package/dist/chunk-SYEVGPFC.js +686 -0
  45. package/dist/chunk-V7HUZ6UQ.js +5923 -0
  46. package/dist/chunk-WPUVJRIN.js +29 -0
  47. package/dist/chunk-XCZ3TTFK.js +22 -0
  48. package/dist/chunk-XSGU2Y2O.js +1611 -0
  49. package/dist/chunk-YNEW3L63.js +190 -0
  50. package/dist/chunk-YXGHPILG.js +614 -0
  51. package/dist/chunk-ZL4LLE2Z.js +465 -0
  52. package/dist/di-CcNeq9v-.d.cts +16 -0
  53. package/dist/di-CcNeq9v-.d.ts +16 -0
  54. package/dist/discovery/index.cjs +588 -0
  55. package/dist/discovery/index.d.cts +299 -0
  56. package/dist/discovery/index.d.ts +299 -0
  57. package/dist/discovery/index.js +49 -0
  58. package/dist/discovery.scanner-CUYLKlYT.d.cts +59 -0
  59. package/dist/discovery.scanner-CUYLKlYT.d.ts +59 -0
  60. package/dist/entitlement/index.cjs +805 -0
  61. package/dist/entitlement/index.d.cts +60 -0
  62. package/dist/entitlement/index.d.ts +60 -0
  63. package/dist/entitlement/index.js +57 -0
  64. package/dist/index.cjs +15918 -0
  65. package/dist/index.d.cts +85 -0
  66. package/dist/index.d.ts +85 -0
  67. package/dist/index.js +956 -0
  68. package/dist/plan-resolution-CFCoUkrE.d.cts +41 -0
  69. package/dist/plan-resolution-CFCoUkrE.d.ts +41 -0
  70. package/dist/platform/index.cjs +2772 -0
  71. package/dist/platform/index.d.cts +118 -0
  72. package/dist/platform/index.d.ts +118 -0
  73. package/dist/platform/index.js +37 -0
  74. package/dist/promo/index.cjs +995 -0
  75. package/dist/promo/index.d.cts +121 -0
  76. package/dist/promo/index.d.ts +121 -0
  77. package/dist/promo/index.js +58 -0
  78. package/dist/registration/index.cjs +1687 -0
  79. package/dist/registration/index.d.cts +444 -0
  80. package/dist/registration/index.d.ts +444 -0
  81. package/dist/registration/index.js +82 -0
  82. package/dist/saas-platform.module-CgZ2omgg.d.cts +180 -0
  83. package/dist/saas-platform.module-DT9TnCzk.d.ts +180 -0
  84. package/dist/service-DX8KbGXl.d.cts +109 -0
  85. package/dist/service-DX8KbGXl.d.ts +109 -0
  86. package/dist/subscription-contract/index.cjs +471 -0
  87. package/dist/subscription-contract/index.d.cts +17 -0
  88. package/dist/subscription-contract/index.d.ts +17 -0
  89. package/dist/subscription-contract/index.js +21 -0
  90. package/dist/subscription-contract.service--cm47ZJJ.d.cts +39 -0
  91. package/dist/subscription-contract.service--cm47ZJJ.d.ts +39 -0
  92. package/dist/testing/index.cjs +3942 -0
  93. package/dist/testing/index.d.cts +279 -0
  94. package/dist/testing/index.d.ts +279 -0
  95. package/dist/testing/index.js +1195 -0
  96. package/package.json +185 -0
@@ -0,0 +1,444 @@
1
+ import { PendingRegistrationRepository, RegistrationOtpDelivery, UserAccountLookup, SlugAvailabilityCheck, PlanCatalogLookup, PaymentProvider, PaymentEventLog, ActivationOrchestrator, RegistrationAuditLogger, RegistrationResumeTokenSigner, RegistrationResumeDelivery, RegistrationConfiguratorLookup, RegistrationPromoPreview, RegistrationBusinessTypeLookup, PublicSignupPlan, SelectPlanInput, RegistrationAuditContext, SelectPlanResult, StartCheckoutInput, StartCheckoutResult, HandlePaymentEventInput, HandlePaymentEventResult, CleanupResult, StartRegistrationInput, StartRegistrationResult, VerifyRegistrationOtpResult, ResumeRegistrationInput, ResumeRegistrationResult, ConfiguratorCatalog, SaveRegistrationConfigInput, SaveRegistrationConfigResult, ConfiguratorPriceBreakdown, RegistrationConfigSelection } from '@saasicat/types';
2
+ import { CanActivate, ExecutionContext, Type, DynamicModule, ForwardReference, Provider } from '@nestjs/common';
3
+ import { P as ProviderSpec } from '../di-CcNeq9v-.cjs';
4
+ export { CronExpression } from '@nestjs/schedule';
5
+
6
+ /** 6-digit numeric OTP code, uniformly distributed from 100000..999999. */
7
+ declare function generateOtpCode(): string;
8
+ /** SHA-256 hash of an OTP code (hex). OTPs are never stored in plaintext. */
9
+ declare function hashOtpCode(code: string): string;
10
+ /**
11
+ * Constant-time comparison of an OTP hash against a plaintext code.
12
+ * Prevents timing attacks against short numeric codes.
13
+ */
14
+ declare function verifyOtpCode(expectedHash: string, code: string): boolean;
15
+ /**
16
+ * Club/Tenant slug from a plaintext name.
17
+ * Lowercase, ASCII, only a-z0-9 and hyphen.
18
+ * Whitespace and special characters are collapsed to '-'.
19
+ * The result is never empty (fallback `'verein'`).
20
+ */
21
+ declare function slugify(name: string): string;
22
+
23
+ declare const PENDING_REGISTRATION_REPOSITORY_TOKEN: unique symbol;
24
+ declare const REGISTRATION_OTP_DELIVERY_TOKEN: unique symbol;
25
+ declare const USER_ACCOUNT_LOOKUP_TOKEN: unique symbol;
26
+ declare const SLUG_AVAILABILITY_CHECK_TOKEN: unique symbol;
27
+ declare const PASSWORD_HASHER_TOKEN: unique symbol;
28
+ declare const PLAN_CATALOG_LOOKUP_TOKEN: unique symbol;
29
+ declare const PAYMENT_PROVIDER_TOKEN: unique symbol;
30
+ declare const PAYMENT_EVENT_LOG_TOKEN: unique symbol;
31
+ declare const ACTIVATION_ORCHESTRATOR_TOKEN: unique symbol;
32
+ declare const REGISTRATION_AUDIT_LOGGER_TOKEN: unique symbol;
33
+ declare const REGISTRATION_RESUME_TOKEN_SIGNER_TOKEN: unique symbol;
34
+ declare const REGISTRATION_RESUME_DELIVERY_TOKEN: unique symbol;
35
+ declare const REGISTRATION_RESUME_BASE_URL_TOKEN: unique symbol;
36
+ declare const REGISTRATION_CONFIGURATOR_LOOKUP_TOKEN: unique symbol;
37
+ declare const REGISTRATION_PROMO_PREVIEW_TOKEN: unique symbol;
38
+ /** SPEC_V2 §11.1 M5.3 — published BusinessTypeVersion lookup for configurator selection. */
39
+ declare const REGISTRATION_BUSINESS_TYPE_LOOKUP_TOKEN: unique symbol;
40
+ /** Optionally injectable configuration — falls back to default TTLs from saas-platform-types. */
41
+ declare const REGISTRATION_CONFIG_TOKEN: unique symbol;
42
+ interface PasswordHasher {
43
+ hash(plain: string): Promise<string>;
44
+ verify(hash: string, plain: string): Promise<boolean>;
45
+ }
46
+
47
+ /**
48
+ * Orchestrates step 1 (capture registration data) and step 2 (OTP verification)
49
+ * of the multi-step registration flow.
50
+ *
51
+ * Important invariants:
52
+ * - `start()` ALWAYS returns a neutral response (account-enumeration protection).
53
+ * - The OTP is only stored as a SHA-256 hash, never in plaintext.
54
+ * - Passwords are hashed by the injected PasswordHasher (usually Argon2).
55
+ * - On a rate-limit hit the OTP send is silently discarded — no 429 response,
56
+ * otherwise an attacker could infer the existence of a PendingRegistration
57
+ * record from the status code.
58
+ * - OTP failed attempts are counted persistently: at `OTP_VERIFY_MAX_ATTEMPTS`
59
+ * `verifyOtp()` throws `OTP_LOCKED` — even for a subsequently correct code.
60
+ * Only a newly generated OTP (resend, rate-limited separately) unlocks it.
61
+ */
62
+ declare class PendingRegistrationService {
63
+ private readonly repo;
64
+ private readonly otpDelivery;
65
+ private readonly userLookup;
66
+ private readonly slugCheck;
67
+ private readonly passwordHasher;
68
+ private readonly planCatalog;
69
+ private readonly paymentProvider;
70
+ private readonly paymentEventLog;
71
+ private readonly activationOrchestrator;
72
+ private readonly audit;
73
+ private readonly resumeTokenSigner?;
74
+ private readonly resumeDelivery?;
75
+ private readonly resumeBaseUrl?;
76
+ private readonly configuratorLookup?;
77
+ private readonly promoPreview?;
78
+ private readonly businessTypeLookup?;
79
+ private readonly logger;
80
+ constructor(repo: PendingRegistrationRepository, otpDelivery: RegistrationOtpDelivery, userLookup: UserAccountLookup, slugCheck: SlugAvailabilityCheck, passwordHasher: PasswordHasher, planCatalog: PlanCatalogLookup, paymentProvider: PaymentProvider, paymentEventLog: PaymentEventLog, activationOrchestrator: ActivationOrchestrator, audit: RegistrationAuditLogger, resumeTokenSigner?: RegistrationResumeTokenSigner | undefined, resumeDelivery?: RegistrationResumeDelivery | undefined, resumeBaseUrl?: string | undefined, configuratorLookup?: RegistrationConfiguratorLookup | undefined, promoPreview?: RegistrationPromoPreview | undefined, businessTypeLookup?: RegistrationBusinessTypeLookup | undefined);
81
+ private record;
82
+ /** Public signup plan list for step 3 (frontend `usePublicPlans()`). */
83
+ listPublicPlans(): Promise<PublicSignupPlan[]>;
84
+ selectPlan(input: SelectPlanInput, context?: RegistrationAuditContext): Promise<SelectPlanResult>;
85
+ startCheckout(input: StartCheckoutInput, context?: RegistrationAuditContext): Promise<StartCheckoutResult>;
86
+ /**
87
+ * Processes a payment webhook idempotently:
88
+ * 1. `tryClaim` pins the event ID via a @unique INSERT. Duplicates are
89
+ * dropped silently (`ALREADY_PROCESSED`).
90
+ * 2. On status `SUCCEEDED` the PendingRegistration is resolved from the
91
+ * checkout session and the ActivationOrchestrator triggers the final
92
+ * user+tenant+subscription creation in a single transaction.
93
+ * 3. After activation the PendingRegistration is deleted.
94
+ *
95
+ * On errors in step 2/3 the EventLog entry remains — provider retries then
96
+ * run into ALREADY_PROCESSED. Operational repair is done manually (status
97
+ * inspection + cleanup).
98
+ */
99
+ handlePaymentEvent(input: HandlePaymentEventInput, context?: RegistrationAuditContext): Promise<HandlePaymentEventResult>;
100
+ /**
101
+ * Deletes all PendingRegistration records with `expiresAt < now`.
102
+ * Full deletion (no anonymization path) is the simpler option: the schema
103
+ * holds `email`, `firstName`, `lastName`, `passwordHash` as NOT NULL, and
104
+ * the unique email is exactly what needs to become free again after expiry.
105
+ *
106
+ * The batch `limit` guards against memory spikes on large backlogs — the
107
+ * caller (cron) keeps calling until `moreAvailable === false`.
108
+ */
109
+ runCleanup(now: Date, limit?: number): Promise<CleanupResult>;
110
+ start(input: StartRegistrationInput, context?: RegistrationAuditContext): Promise<StartRegistrationResult>;
111
+ verifyOtp(emailRaw: string, code: string, context?: RegistrationAuditContext): Promise<VerifyRegistrationOtpResult>;
112
+ resendOtp(emailRaw: string, context?: RegistrationAuditContext): Promise<StartRegistrationResult>;
113
+ private regenerateOtp;
114
+ private deliverOtp;
115
+ /**
116
+ * Creates a signed resume token (60 min TTL) and sends a magic link to the
117
+ * stored email. Mail send errors are logged but not escalated
118
+ * (account-enumeration protection).
119
+ */
120
+ private sendResumeLink;
121
+ /**
122
+ * Resolves a resume token to the matching onboarding step. Called by the
123
+ * frontend from `/register?resume=<jwt>`. On an invalid token the method
124
+ * throws `RESUME_TOKEN_INVALID`.
125
+ *
126
+ * Also returns a public-safe snapshot of the PendingRegistration so the
127
+ * frontend can prefill completed steps with the existing data (first name /
128
+ * last name, organization name, plan selection ...).
129
+ */
130
+ resumeWithToken(input: ResumeRegistrationInput, context?: RegistrationAuditContext): Promise<ResumeRegistrationResult>;
131
+ /**
132
+ * Signs a short-lived resume token for an existing PendingRegistration —
133
+ * the login endpoint uses this to send the user into the wizard with
134
+ * prefill after successful password verification.
135
+ */
136
+ signResumeToken(pendingRegistrationId: string): Promise<string | null>;
137
+ /** Returns the (app-specific) configurator catalog. */
138
+ getConfiguratorCatalog(): Promise<ConfiguratorCatalog>;
139
+ /**
140
+ * Saves the configurator selection (model + cycle + promo) and returns the
141
+ * full price breakdown.
142
+ *
143
+ * Sets:
144
+ * - `status = PLAN_SELECTED`
145
+ * - `currentStep = 4`
146
+ * - `selectedPlanId = <planId from the chosen model>`
147
+ * - `configJson` / `billingCycle` / `appliedPromoCode`
148
+ * - `expiresAt = now + ONBOARDING_TTL`
149
+ */
150
+ saveConfiguration(input: SaveRegistrationConfigInput, context?: RegistrationAuditContext): Promise<SaveRegistrationConfigResult>;
151
+ /**
152
+ * Validates a promo code against the currently stored configuration and
153
+ * returns a new breakdown. Does NOT modify the pending — for live preview
154
+ * in the frontend. If `null` is passed as `code`, the promo effect is
155
+ * removed.
156
+ */
157
+ previewConfigPromo(pendingRegistrationId: string, code: string | null): Promise<ConfiguratorPriceBreakdown>;
158
+ /**
159
+ * Helper for step 3 resume: the frontend needs the current breakdown when
160
+ * the pending already has a configuration. Pure recomputation against the
161
+ * catalog (without promo validation, since promos should be freshly checked
162
+ * anyway).
163
+ */
164
+ getCurrentBreakdown(pendingRegistrationId: string): Promise<ConfiguratorPriceBreakdown | null>;
165
+ private resolveSlug;
166
+ }
167
+
168
+ /**
169
+ * Daily cleanup cron for expired `PendingRegistration` records.
170
+ *
171
+ * Runs at 04:15 Europe/Berlin (aligned with `trial-expiration.cron` and
172
+ * `PromoCodeExpirer`). Deletes in batches of 500 — the cron
173
+ * keeps running until `moreAvailable` is false (protection against memory
174
+ * spikes on large backlogs).
175
+ *
176
+ * Can be disabled via `RegistrationModule.forRoot({ includeCleanupCron: false })`
177
+ * — e.g. for CLI boots or test setups without `ScheduleModule`.
178
+ */
179
+ declare class RegistrationCleanupCron {
180
+ private readonly service;
181
+ private readonly logger;
182
+ constructor(service: PendingRegistrationService);
183
+ runDaily(): Promise<void>;
184
+ /**
185
+ * Public API: can be triggered manually from an admin endpoint /
186
+ * `/admin/registration/run-cleanup` (same pattern as
187
+ * `PromoCodeExpirer.run` in the consuming apps).
188
+ */
189
+ runCleanup(now?: Date, batchSize?: number): Promise<void>;
190
+ }
191
+
192
+ /**
193
+ * Configurable in-memory rate limiter (per IP, sliding window).
194
+ *
195
+ * Deliberately without external dependencies (`@nestjs/throttler`, Redis) —
196
+ * for the auth routes a single-instance memory bucket is sufficient. For
197
+ * scaled multi-instance deploys this guard should be replaced by a
198
+ * Redis-based counter (same CanActivate contract).
199
+ *
200
+ * The pattern matches `PromoCodeRateLimitGuard`, but is generic: subclasses
201
+ * pass window + limit + bucket map to the base constructor and thus create
202
+ * their own limits per route (login / register / OTP resend / password reset).
203
+ *
204
+ * @example
205
+ * ```ts
206
+ * @Injectable()
207
+ * export class LoginRateLimitGuard extends BaseIpRateLimitGuard {
208
+ * constructor() {
209
+ * super({ windowMs: 15 * 60_000, limit: 10, name: 'login' });
210
+ * }
211
+ * }
212
+ * ```
213
+ */
214
+ declare abstract class BaseIpRateLimitGuard implements CanActivate {
215
+ protected readonly options: {
216
+ /** Sliding window in ms (e.g. 15 * 60_000 for 15 min). */
217
+ windowMs: number;
218
+ /** Max requests per window per IP. */
219
+ limit: number;
220
+ /** Telemetry name (appears in reason + logs). */
221
+ name: string;
222
+ };
223
+ private readonly buckets;
224
+ constructor(options: {
225
+ /** Sliding window in ms (e.g. 15 * 60_000 for 15 min). */
226
+ windowMs: number;
227
+ /** Max requests per window per IP. */
228
+ limit: number;
229
+ /** Telemetry name (appears in reason + logs). */
230
+ name: string;
231
+ });
232
+ canActivate(context: ExecutionContext): boolean;
233
+ private exceeded;
234
+ }
235
+
236
+ interface RegistrationModuleOptions {
237
+ pendingRegistrationRepository: ProviderSpec<PendingRegistrationRepository>;
238
+ otpDelivery: ProviderSpec<RegistrationOtpDelivery>;
239
+ userAccountLookup: ProviderSpec<UserAccountLookup>;
240
+ slugAvailabilityCheck: ProviderSpec<SlugAvailabilityCheck>;
241
+ passwordHasher: ProviderSpec<PasswordHasher>;
242
+ planCatalogLookup: ProviderSpec<PlanCatalogLookup>;
243
+ paymentProvider: ProviderSpec<PaymentProvider>;
244
+ paymentEventLog: ProviderSpec<PaymentEventLog>;
245
+ activationOrchestrator: ProviderSpec<ActivationOrchestrator>;
246
+ auditLogger: ProviderSpec<RegistrationAuditLogger>;
247
+ /**
248
+ * P3.4 (optional): Signs + verifies resume tokens for cases C/D
249
+ * (EMAIL_VERIFIED / PLAN_SELECTED / CHECKOUT_STARTED). If the port
250
+ * is not provided, the service falls back to OTP resend for all
251
+ * statuses.
252
+ */
253
+ resumeTokenSigner?: ProviderSpec<RegistrationResumeTokenSigner>;
254
+ /** P3.4 (optional): Sends the resume mail with a magic link. */
255
+ resumeDelivery?: ProviderSpec<RegistrationResumeDelivery>;
256
+ /** P3.4 (optional): Base URL of the app for the resume link (e.g. `https://app.example.com`). */
257
+ resumeBaseUrl?: string;
258
+ /**
259
+ * Phase A: Provides the app-specific configurator catalog. Without
260
+ * this port, `saveConfiguration()` throws `CONFIGURATOR_NOT_CONFIGURED`.
261
+ */
262
+ configuratorLookup?: ProviderSpec<RegistrationConfiguratorLookup>;
263
+ /**
264
+ * Phase A: Promo-code preview adapter (typically wraps
265
+ * `PromoCodesService.preview()`). Without the adapter the promo code
266
+ * is ignored (snapshot is stored, discount=0).
267
+ */
268
+ promoPreview?: ProviderSpec<RegistrationPromoPreview>;
269
+ /**
270
+ * SPEC_V2 §11.1 M5.3: validates an optionally set
271
+ * `RegistrationConfigSelection.businessTypeVersionId` against the
272
+ * SuperAdmin catalog (published BusinessTypeVersions). Without an adapter
273
+ * the ID is passed through unchecked — fine for apps without a
274
+ * BusinessType catalog.
275
+ */
276
+ businessTypeLookup?: ProviderSpec<RegistrationBusinessTypeLookup>;
277
+ /**
278
+ * Modules that should be visible in the DynamicModule scope — needed so
279
+ * that factory providers with `inject: [...]` can resolve classes from
280
+ * these modules. Analogous to AdminStatsModule.
281
+ */
282
+ imports?: Array<Type<unknown> | DynamicModule | ForwardReference | Promise<DynamicModule>>;
283
+ /**
284
+ * Additional providers registered directly in the DynamicModule scope.
285
+ * Usually the app adapters (Prisma repos, email-service wrapper,
286
+ * Argon2 hasher), so that their `inject: [...]` tokens are resolvable
287
+ * (otherwise UnknownDependenciesException on strict NestJS versions).
288
+ */
289
+ extraProviders?: Provider[];
290
+ /**
291
+ * Default `true`. Registers `RegistrationCleanupCron` (daily 04:15
292
+ * Europe/Berlin). Set to `false` when the consumer has no active
293
+ * `@nestjs/schedule` (e.g. CLI boot, unit tests).
294
+ */
295
+ includeCleanupCron?: boolean;
296
+ /** Register module globally — default `false`. */
297
+ global?: boolean;
298
+ }
299
+ declare class RegistrationModule {
300
+ static forRoot(options: RegistrationModuleOptions): DynamicModule;
301
+ }
302
+
303
+ /**
304
+ * Input for step 1 (registration data) — `POST /auth/register/start`.
305
+ * Collects Tenant and admin data in one step; the final Tenant/User creation
306
+ * only happens after successful payment (step 4).
307
+ */
308
+ declare class RegisterStartDto {
309
+ /** Association/tenant name (required). */
310
+ tenantName: string;
311
+ /** Optional slug. If left empty, it is generated from `tenantName`. */
312
+ tenantSlug?: string;
313
+ /** Salutation of the administrator (optional). */
314
+ salutation?: string;
315
+ firstName: string;
316
+ lastName: string;
317
+ email: string;
318
+ password: string;
319
+ /** Terms / privacy / data processing — mandatory acceptance (must be `true`). */
320
+ termsAccepted: true;
321
+ locale?: 'de' | 'tr';
322
+ }
323
+
324
+ declare class VerifyRegistrationOtpDto {
325
+ email: string;
326
+ otp: string;
327
+ }
328
+
329
+ declare class ResendRegistrationOtpDto {
330
+ email: string;
331
+ }
332
+
333
+ /**
334
+ * Input for step 3 (plan selection) — `POST /auth/register/select-plan`.
335
+ * The frontend passes the PendingRegistration ID (from the verify-otp response
336
+ * or the login onboarding response) and the chosen PlanId.
337
+ */
338
+ declare class SelectRegistrationPlanDto {
339
+ pendingRegistrationId: string;
340
+ planId: string;
341
+ }
342
+
343
+ /**
344
+ * Input for step 4 (start checkout) — `POST /auth/register/start-checkout`.
345
+ * The frontend passes the PendingRegistration ID and the redirect URLs
346
+ * (success / cancel) that the payment provider redirects to after the payment
347
+ * action.
348
+ */
349
+ declare class StartRegistrationCheckoutDto {
350
+ pendingRegistrationId: string;
351
+ successUrl: string;
352
+ cancelUrl: string;
353
+ }
354
+
355
+ /**
356
+ * Webhook contract for `POST /webhooks/payment`.
357
+ *
358
+ * Provider-agnostic: the Stripe adapter (phase 2.4) maps Stripe's
359
+ * `checkout.session.completed` → `{ eventId, sessionId, status: 'SUCCEEDED' }`,
360
+ * `payment_intent.payment_failed` → `{ status: 'FAILED' }`, etc.
361
+ *
362
+ * In production the signature verification (Stripe `Stripe-Signature`
363
+ * header) should run BEFORE the DTO validation — see StripeWebhookGuard in
364
+ * the app module.
365
+ */
366
+ declare class PaymentWebhookDto {
367
+ eventId: string;
368
+ sessionId?: string;
369
+ provider: string;
370
+ status: 'SUCCEEDED' | 'FAILED';
371
+ payload?: Record<string, unknown>;
372
+ }
373
+
374
+ /**
375
+ * Input for `POST /auth/register/continue`. The frontend reads the
376
+ * `?resume=<jwt>` query from the magic link in the resume email and passes
377
+ * it to this endpoint.
378
+ */
379
+ declare class ContinueRegistrationDto {
380
+ token: string;
381
+ }
382
+
383
+ /**
384
+ * Configurator selection payload for `POST /auth/register/save-config`.
385
+ *
386
+ * Intentionally kept flat — model validation happens in the service against
387
+ * the Catalog. The DTO only checks structural types.
388
+ */
389
+ declare class SaveRegistrationConfigSelectionDto {
390
+ modelId: string;
391
+ billingCycle: 'MONTHLY' | 'YEARLY';
392
+ appliedPromoCode?: string | null;
393
+ /**
394
+ * SPEC_V2 §11.1 M5.3 — optional business-type choice. UUID of a
395
+ * **published** BusinessTypeVersion. The service validates existence +
396
+ * live status against `RegistrationBusinessTypeLookup`. `null` = no
397
+ * choice (legacy Plan-only path).
398
+ */
399
+ businessTypeVersionId?: string | null;
400
+ /**
401
+ * METAMODELL §17a — Bundle consistency. ID of the `CheckoutOffer` that was
402
+ * created on the pricing page (`<app>/register?offer=<id>`). Passed
403
+ * through unchanged in `configJson`; the ActivationOrchestrator freezes it
404
+ * as `packageSnapshot` when the subscription is created and sets the offer
405
+ * to `consumed`. `null` = direct registration without a website offer.
406
+ */
407
+ offerId?: string | null;
408
+ }
409
+ declare class SaveRegistrationConfigDto {
410
+ pendingRegistrationId: string;
411
+ selection: SaveRegistrationConfigSelectionDto;
412
+ }
413
+
414
+ /**
415
+ * Input for `POST /auth/register/preview-promo`. If `code` is empty, the
416
+ * effect of a previously applied code is removed (preview without promo).
417
+ * Does NOT change the stored pending — only the preview calculation.
418
+ */
419
+ declare class PreviewRegistrationPromoDto {
420
+ pendingRegistrationId: string;
421
+ code?: string;
422
+ }
423
+
424
+ interface PromoEvaluation {
425
+ /** Discount in EUR on the subtotal (gross or net depending on catalog convention; here on net). */
426
+ discountAmount: number;
427
+ /** Percentage for UI display. */
428
+ percent: number;
429
+ label: string;
430
+ }
431
+ /**
432
+ * Validates the selection against the catalog and returns the complete price
433
+ * breakdown. On invalid inputs (e.g. unknown model) it throws an `Error` — the
434
+ * service translates that into a BadRequestException.
435
+ */
436
+ declare function computeBreakdown(catalog: ConfiguratorCatalog, selection: RegistrationConfigSelection, promo?: PromoEvaluation): ConfiguratorPriceBreakdown;
437
+ /** Pull a model from the catalog; throws if unknown. */
438
+ declare function resolveModel(catalog: ConfiguratorCatalog, modelId: string): {
439
+ planId: string;
440
+ monthlyNet: number;
441
+ yearlyNet: number;
442
+ };
443
+
444
+ export { ACTIVATION_ORCHESTRATOR_TOKEN, BaseIpRateLimitGuard, ContinueRegistrationDto, PASSWORD_HASHER_TOKEN, PAYMENT_EVENT_LOG_TOKEN, PAYMENT_PROVIDER_TOKEN, PENDING_REGISTRATION_REPOSITORY_TOKEN, PLAN_CATALOG_LOOKUP_TOKEN, type PasswordHasher, PaymentWebhookDto, PendingRegistrationService, PreviewRegistrationPromoDto, type PromoEvaluation, REGISTRATION_AUDIT_LOGGER_TOKEN, REGISTRATION_BUSINESS_TYPE_LOOKUP_TOKEN, REGISTRATION_CONFIGURATOR_LOOKUP_TOKEN, REGISTRATION_CONFIG_TOKEN, REGISTRATION_OTP_DELIVERY_TOKEN, REGISTRATION_PROMO_PREVIEW_TOKEN, REGISTRATION_RESUME_BASE_URL_TOKEN, REGISTRATION_RESUME_DELIVERY_TOKEN, REGISTRATION_RESUME_TOKEN_SIGNER_TOKEN, RegisterStartDto, RegistrationCleanupCron, RegistrationModule, type RegistrationModuleOptions, ResendRegistrationOtpDto, SLUG_AVAILABILITY_CHECK_TOKEN, SaveRegistrationConfigDto, SaveRegistrationConfigSelectionDto, SelectRegistrationPlanDto, StartRegistrationCheckoutDto, USER_ACCOUNT_LOOKUP_TOKEN, VerifyRegistrationOtpDto, computeBreakdown, generateOtpCode, hashOtpCode, resolveModel, slugify, verifyOtpCode };