@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,1687 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/registration/index.ts
22
+ var registration_exports = {};
23
+ __export(registration_exports, {
24
+ ACTIVATION_ORCHESTRATOR_TOKEN: () => ACTIVATION_ORCHESTRATOR_TOKEN,
25
+ BaseIpRateLimitGuard: () => BaseIpRateLimitGuard,
26
+ ContinueRegistrationDto: () => ContinueRegistrationDto,
27
+ CronExpression: () => import_schedule.CronExpression,
28
+ PASSWORD_HASHER_TOKEN: () => PASSWORD_HASHER_TOKEN,
29
+ PAYMENT_EVENT_LOG_TOKEN: () => PAYMENT_EVENT_LOG_TOKEN,
30
+ PAYMENT_PROVIDER_TOKEN: () => PAYMENT_PROVIDER_TOKEN,
31
+ PENDING_REGISTRATION_REPOSITORY_TOKEN: () => PENDING_REGISTRATION_REPOSITORY_TOKEN,
32
+ PLAN_CATALOG_LOOKUP_TOKEN: () => PLAN_CATALOG_LOOKUP_TOKEN,
33
+ PaymentWebhookDto: () => PaymentWebhookDto,
34
+ PendingRegistrationService: () => PendingRegistrationService,
35
+ PreviewRegistrationPromoDto: () => PreviewRegistrationPromoDto,
36
+ REGISTRATION_AUDIT_LOGGER_TOKEN: () => REGISTRATION_AUDIT_LOGGER_TOKEN,
37
+ REGISTRATION_BUSINESS_TYPE_LOOKUP_TOKEN: () => REGISTRATION_BUSINESS_TYPE_LOOKUP_TOKEN,
38
+ REGISTRATION_CONFIGURATOR_LOOKUP_TOKEN: () => REGISTRATION_CONFIGURATOR_LOOKUP_TOKEN,
39
+ REGISTRATION_CONFIG_TOKEN: () => REGISTRATION_CONFIG_TOKEN,
40
+ REGISTRATION_OTP_DELIVERY_TOKEN: () => REGISTRATION_OTP_DELIVERY_TOKEN,
41
+ REGISTRATION_PROMO_PREVIEW_TOKEN: () => REGISTRATION_PROMO_PREVIEW_TOKEN,
42
+ REGISTRATION_RESUME_BASE_URL_TOKEN: () => REGISTRATION_RESUME_BASE_URL_TOKEN,
43
+ REGISTRATION_RESUME_DELIVERY_TOKEN: () => REGISTRATION_RESUME_DELIVERY_TOKEN,
44
+ REGISTRATION_RESUME_TOKEN_SIGNER_TOKEN: () => REGISTRATION_RESUME_TOKEN_SIGNER_TOKEN,
45
+ RegisterStartDto: () => RegisterStartDto,
46
+ RegistrationCleanupCron: () => RegistrationCleanupCron,
47
+ RegistrationModule: () => RegistrationModule,
48
+ ResendRegistrationOtpDto: () => ResendRegistrationOtpDto,
49
+ SLUG_AVAILABILITY_CHECK_TOKEN: () => SLUG_AVAILABILITY_CHECK_TOKEN,
50
+ SaveRegistrationConfigDto: () => SaveRegistrationConfigDto,
51
+ SaveRegistrationConfigSelectionDto: () => SaveRegistrationConfigSelectionDto,
52
+ SelectRegistrationPlanDto: () => SelectRegistrationPlanDto,
53
+ StartRegistrationCheckoutDto: () => StartRegistrationCheckoutDto,
54
+ USER_ACCOUNT_LOOKUP_TOKEN: () => USER_ACCOUNT_LOOKUP_TOKEN,
55
+ VerifyRegistrationOtpDto: () => VerifyRegistrationOtpDto,
56
+ computeBreakdown: () => computeBreakdown,
57
+ generateOtpCode: () => generateOtpCode,
58
+ hashOtpCode: () => hashOtpCode,
59
+ resolveModel: () => resolveModel,
60
+ slugify: () => slugify,
61
+ verifyOtpCode: () => verifyOtpCode
62
+ });
63
+ module.exports = __toCommonJS(registration_exports);
64
+
65
+ // src/registration/helpers.ts
66
+ var import_node_crypto = require("crypto");
67
+ function generateOtpCode() {
68
+ return String((0, import_node_crypto.randomInt)(1e5, 1e6));
69
+ }
70
+ __name(generateOtpCode, "generateOtpCode");
71
+ function hashOtpCode(code) {
72
+ return (0, import_node_crypto.createHash)("sha256").update(code).digest("hex");
73
+ }
74
+ __name(hashOtpCode, "hashOtpCode");
75
+ function verifyOtpCode(expectedHash, code) {
76
+ const actual = hashOtpCode(code);
77
+ if (actual.length !== expectedHash.length) {
78
+ return false;
79
+ }
80
+ try {
81
+ return (0, import_node_crypto.timingSafeEqual)(Buffer.from(actual, "hex"), Buffer.from(expectedHash, "hex"));
82
+ } catch {
83
+ return false;
84
+ }
85
+ }
86
+ __name(verifyOtpCode, "verifyOtpCode");
87
+ function slugify(name) {
88
+ const transliterated = name.toLowerCase().replace(/ß/g, "ss").replace(/ä/g, "ae").replace(/ö/g, "oe").replace(/ü/g, "ue").normalize("NFKD").replace(/[̀-ͯ]/g, "");
89
+ const slug = transliterated.replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
90
+ return slug || "verein";
91
+ }
92
+ __name(slugify, "slugify");
93
+
94
+ // src/registration/tokens.ts
95
+ var PENDING_REGISTRATION_REPOSITORY_TOKEN = /* @__PURE__ */ Symbol("PENDING_REGISTRATION_REPOSITORY");
96
+ var REGISTRATION_OTP_DELIVERY_TOKEN = /* @__PURE__ */ Symbol("REGISTRATION_OTP_DELIVERY");
97
+ var USER_ACCOUNT_LOOKUP_TOKEN = /* @__PURE__ */ Symbol("USER_ACCOUNT_LOOKUP");
98
+ var SLUG_AVAILABILITY_CHECK_TOKEN = /* @__PURE__ */ Symbol("SLUG_AVAILABILITY_CHECK");
99
+ var PASSWORD_HASHER_TOKEN = /* @__PURE__ */ Symbol("PASSWORD_HASHER");
100
+ var PLAN_CATALOG_LOOKUP_TOKEN = /* @__PURE__ */ Symbol("PLAN_CATALOG_LOOKUP");
101
+ var PAYMENT_PROVIDER_TOKEN = /* @__PURE__ */ Symbol("PAYMENT_PROVIDER");
102
+ var PAYMENT_EVENT_LOG_TOKEN = /* @__PURE__ */ Symbol("PAYMENT_EVENT_LOG");
103
+ var ACTIVATION_ORCHESTRATOR_TOKEN = /* @__PURE__ */ Symbol("ACTIVATION_ORCHESTRATOR");
104
+ var REGISTRATION_AUDIT_LOGGER_TOKEN = /* @__PURE__ */ Symbol("REGISTRATION_AUDIT_LOGGER");
105
+ var REGISTRATION_RESUME_TOKEN_SIGNER_TOKEN = /* @__PURE__ */ Symbol("REGISTRATION_RESUME_TOKEN_SIGNER");
106
+ var REGISTRATION_RESUME_DELIVERY_TOKEN = /* @__PURE__ */ Symbol("REGISTRATION_RESUME_DELIVERY");
107
+ var REGISTRATION_RESUME_BASE_URL_TOKEN = /* @__PURE__ */ Symbol("REGISTRATION_RESUME_BASE_URL");
108
+ var REGISTRATION_CONFIGURATOR_LOOKUP_TOKEN = /* @__PURE__ */ Symbol("REGISTRATION_CONFIGURATOR_LOOKUP");
109
+ var REGISTRATION_PROMO_PREVIEW_TOKEN = /* @__PURE__ */ Symbol("REGISTRATION_PROMO_PREVIEW");
110
+ var REGISTRATION_BUSINESS_TYPE_LOOKUP_TOKEN = /* @__PURE__ */ Symbol("REGISTRATION_BUSINESS_TYPE_LOOKUP");
111
+ var REGISTRATION_CONFIG_TOKEN = /* @__PURE__ */ Symbol("REGISTRATION_CONFIG");
112
+
113
+ // src/registration/pending-registration.service.ts
114
+ var import_common = require("@nestjs/common");
115
+ var import_types = require("@saasicat/types");
116
+ var import_types2 = require("@saasicat/types");
117
+
118
+ // src/registration/pricing.ts
119
+ function round2(value) {
120
+ return Math.round(value * 100) / 100;
121
+ }
122
+ __name(round2, "round2");
123
+ function computeBreakdown(catalog, selection, promo) {
124
+ const model = catalog.models.find((m) => m.id === selection.modelId);
125
+ if (!model) {
126
+ throw new Error(`Unknown model: ${selection.modelId}`);
127
+ }
128
+ const modelMonthlyNet = round2(model.monthlyNet);
129
+ const effectiveQuotas = {
130
+ ...model.quotaBase
131
+ };
132
+ const subtotalMonthlyNet = modelMonthlyNet;
133
+ const factor = selection.billingCycle === "YEARLY" ? catalog.cycleDiscount : 1;
134
+ const subtotalNet = round2(subtotalMonthlyNet * factor);
135
+ const discountAmount = promo ? Math.min(round2(promo.discountAmount), subtotalNet) : 0;
136
+ const totalNet = Math.max(0, round2(subtotalNet - discountAmount));
137
+ const totalGross = round2(totalNet * (1 + catalog.vatRate / 100));
138
+ const yearlySavings = selection.billingCycle === "YEARLY" ? round2(subtotalMonthlyNet * 12 - subtotalMonthlyNet * catalog.cycleDiscount) : 0;
139
+ return {
140
+ cycle: selection.billingCycle,
141
+ effectiveQuotas,
142
+ modelMonthlyNet,
143
+ subtotalMonthlyNet,
144
+ subtotalNet,
145
+ discountAmount,
146
+ totalNet,
147
+ vatRate: catalog.vatRate,
148
+ totalGross,
149
+ yearlySavings,
150
+ appliedPromo: promo ? {
151
+ code: "",
152
+ label: promo.label,
153
+ percent: promo.percent
154
+ } : void 0
155
+ };
156
+ }
157
+ __name(computeBreakdown, "computeBreakdown");
158
+ function resolveModel(catalog, modelId) {
159
+ const model = catalog.models.find((m) => m.id === modelId);
160
+ if (!model) throw new Error(`Unknown model: ${modelId}`);
161
+ return {
162
+ planId: model.planId,
163
+ monthlyNet: model.monthlyNet,
164
+ yearlyNet: model.yearlyNet
165
+ };
166
+ }
167
+ __name(resolveModel, "resolveModel");
168
+
169
+ // src/registration/pending-registration.service.ts
170
+ function _ts_decorate(decorators, target, key, desc) {
171
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
172
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
173
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
174
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
175
+ }
176
+ __name(_ts_decorate, "_ts_decorate");
177
+ function _ts_metadata(k, v) {
178
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
179
+ }
180
+ __name(_ts_metadata, "_ts_metadata");
181
+ function _ts_param(paramIndex, decorator) {
182
+ return function(target, key) {
183
+ decorator(target, key, paramIndex);
184
+ };
185
+ }
186
+ __name(_ts_param, "_ts_param");
187
+ var PendingRegistrationService = class _PendingRegistrationService {
188
+ static {
189
+ __name(this, "PendingRegistrationService");
190
+ }
191
+ repo;
192
+ otpDelivery;
193
+ userLookup;
194
+ slugCheck;
195
+ passwordHasher;
196
+ planCatalog;
197
+ paymentProvider;
198
+ paymentEventLog;
199
+ activationOrchestrator;
200
+ audit;
201
+ resumeTokenSigner;
202
+ resumeDelivery;
203
+ resumeBaseUrl;
204
+ configuratorLookup;
205
+ promoPreview;
206
+ businessTypeLookup;
207
+ logger = new import_common.Logger(_PendingRegistrationService.name);
208
+ constructor(repo, otpDelivery, userLookup, slugCheck, passwordHasher, planCatalog, paymentProvider, paymentEventLog, activationOrchestrator, audit, resumeTokenSigner, resumeDelivery, resumeBaseUrl, configuratorLookup, promoPreview, businessTypeLookup) {
209
+ this.repo = repo;
210
+ this.otpDelivery = otpDelivery;
211
+ this.userLookup = userLookup;
212
+ this.slugCheck = slugCheck;
213
+ this.passwordHasher = passwordHasher;
214
+ this.planCatalog = planCatalog;
215
+ this.paymentProvider = paymentProvider;
216
+ this.paymentEventLog = paymentEventLog;
217
+ this.activationOrchestrator = activationOrchestrator;
218
+ this.audit = audit;
219
+ this.resumeTokenSigner = resumeTokenSigner;
220
+ this.resumeDelivery = resumeDelivery;
221
+ this.resumeBaseUrl = resumeBaseUrl;
222
+ this.configuratorLookup = configuratorLookup;
223
+ this.promoPreview = promoPreview;
224
+ this.businessTypeLookup = businessTypeLookup;
225
+ }
226
+ async record(eventType, pendingRegistrationId, context, metadata) {
227
+ try {
228
+ await this.audit.log({
229
+ eventType,
230
+ pendingRegistrationId,
231
+ context,
232
+ metadata
233
+ });
234
+ } catch (error) {
235
+ const message = error instanceof Error ? error.message : String(error);
236
+ this.logger.warn(`Audit-Log fehlgeschlagen (${eventType}): ${message}`);
237
+ }
238
+ }
239
+ /** Public signup plan list for step 3 (frontend `usePublicPlans()`). */
240
+ async listPublicPlans() {
241
+ return this.planCatalog.listPublicSignupPlans();
242
+ }
243
+ async selectPlan(input, context) {
244
+ const pending = await this.repo.findById(input.pendingRegistrationId);
245
+ if (!pending) {
246
+ throw new import_common.NotFoundException({
247
+ code: "PENDING_REGISTRATION_NOT_FOUND"
248
+ });
249
+ }
250
+ if (pending.expiresAt.getTime() < Date.now()) {
251
+ throw new import_common.BadRequestException({
252
+ code: "PENDING_REGISTRATION_EXPIRED"
253
+ });
254
+ }
255
+ if (pending.status !== "EMAIL_VERIFIED" && pending.status !== "PLAN_SELECTED" && pending.status !== "CHECKOUT_STARTED") {
256
+ throw new import_common.BadRequestException({
257
+ code: "INVALID_REGISTRATION_STATE"
258
+ });
259
+ }
260
+ const plan = await this.planCatalog.findPublicSignupPlan(input.planId);
261
+ if (!plan) {
262
+ throw new import_common.BadRequestException({
263
+ code: "PLAN_NOT_AVAILABLE"
264
+ });
265
+ }
266
+ const now = /* @__PURE__ */ new Date();
267
+ const expiresAt = new Date(now.getTime() + import_types2.PENDING_ONBOARDING_TTL_DAYS * 24 * 60 * 60 * 1e3);
268
+ const updated = await this.repo.update(pending.id, {
269
+ status: "PLAN_SELECTED",
270
+ currentStep: 4,
271
+ selectedPlanId: plan.id,
272
+ expiresAt
273
+ });
274
+ await this.record("PLAN_SELECTED", updated.id, context, {
275
+ planId: plan.id
276
+ });
277
+ return {
278
+ pendingRegistrationId: updated.id,
279
+ status: updated.status,
280
+ nextStep: import_types2.REGISTRATION_STEP_BY_STATUS[updated.status],
281
+ selectedPlanId: updated.selectedPlanId ?? plan.id
282
+ };
283
+ }
284
+ async startCheckout(input, context) {
285
+ const pending = await this.repo.findById(input.pendingRegistrationId);
286
+ if (!pending) {
287
+ throw new import_common.NotFoundException({
288
+ code: "PENDING_REGISTRATION_NOT_FOUND"
289
+ });
290
+ }
291
+ if (pending.expiresAt.getTime() < Date.now()) {
292
+ throw new import_common.BadRequestException({
293
+ code: "PENDING_REGISTRATION_EXPIRED"
294
+ });
295
+ }
296
+ if (pending.status !== "PLAN_SELECTED" && pending.status !== "CHECKOUT_STARTED") {
297
+ throw new import_common.BadRequestException({
298
+ code: "PLAN_NOT_SELECTED"
299
+ });
300
+ }
301
+ if (!pending.selectedPlanId) {
302
+ throw new import_common.BadRequestException({
303
+ code: "PLAN_NOT_SELECTED"
304
+ });
305
+ }
306
+ const plan = await this.planCatalog.findPublicSignupPlan(pending.selectedPlanId);
307
+ if (!plan) {
308
+ throw new import_common.BadRequestException({
309
+ code: "PLAN_NOT_AVAILABLE"
310
+ });
311
+ }
312
+ const session = await this.paymentProvider.createCheckoutSession({
313
+ pendingRegistrationId: pending.id,
314
+ planId: pending.selectedPlanId,
315
+ email: pending.email,
316
+ successUrl: input.successUrl,
317
+ cancelUrl: input.cancelUrl
318
+ });
319
+ const now = /* @__PURE__ */ new Date();
320
+ const expiresAt = new Date(now.getTime() + import_types2.PENDING_CHECKOUT_TTL_DAYS * 24 * 60 * 60 * 1e3);
321
+ const updated = await this.repo.update(pending.id, {
322
+ status: "CHECKOUT_STARTED",
323
+ currentStep: 4,
324
+ checkoutSessionId: session.sessionId,
325
+ checkoutStartedAt: now,
326
+ expiresAt
327
+ });
328
+ await this.record("CHECKOUT_STARTED", updated.id, context, {
329
+ sessionId: session.sessionId,
330
+ provider: session.provider
331
+ });
332
+ return {
333
+ pendingRegistrationId: updated.id,
334
+ status: updated.status,
335
+ nextStep: import_types2.REGISTRATION_STEP_BY_STATUS[updated.status],
336
+ checkoutSessionId: session.sessionId,
337
+ checkoutUrl: session.checkoutUrl
338
+ };
339
+ }
340
+ /**
341
+ * Processes a payment webhook idempotently:
342
+ * 1. `tryClaim` pins the event ID via a @unique INSERT. Duplicates are
343
+ * dropped silently (`ALREADY_PROCESSED`).
344
+ * 2. On status `SUCCEEDED` the PendingRegistration is resolved from the
345
+ * checkout session and the ActivationOrchestrator triggers the final
346
+ * user+tenant+subscription creation in a single transaction.
347
+ * 3. After activation the PendingRegistration is deleted.
348
+ *
349
+ * On errors in step 2/3 the EventLog entry remains — provider retries then
350
+ * run into ALREADY_PROCESSED. Operational repair is done manually (status
351
+ * inspection + cleanup).
352
+ */
353
+ async handlePaymentEvent(input, context) {
354
+ const claimed = await this.paymentEventLog.tryClaim(input.eventId, {
355
+ provider: input.provider,
356
+ sessionId: input.sessionId,
357
+ status: input.status,
358
+ rawPayload: input.rawPayload
359
+ });
360
+ if (!claimed) {
361
+ this.logger.warn(`Idempotenz: PaymentEvent ${input.eventId} bereits verarbeitet \u2014 Duplikat verworfen.`);
362
+ await this.record("PAYMENT_DUPLICATE_IGNORED", null, context, {
363
+ eventId: input.eventId,
364
+ sessionId: input.sessionId
365
+ });
366
+ return {
367
+ activated: false,
368
+ reason: "ALREADY_PROCESSED"
369
+ };
370
+ }
371
+ if (input.status !== "SUCCEEDED") {
372
+ await this.record("PAYMENT_FAILED", null, context, {
373
+ eventId: input.eventId,
374
+ sessionId: input.sessionId
375
+ });
376
+ return {
377
+ activated: false,
378
+ reason: "PAYMENT_NOT_SUCCEEDED"
379
+ };
380
+ }
381
+ if (!input.sessionId) {
382
+ return {
383
+ activated: false,
384
+ reason: "MISSING_SESSION_ID"
385
+ };
386
+ }
387
+ const pending = await this.repo.findByCheckoutSession(input.sessionId);
388
+ if (!pending) {
389
+ return {
390
+ activated: false,
391
+ reason: "PENDING_REGISTRATION_NOT_FOUND"
392
+ };
393
+ }
394
+ if (pending.status !== "CHECKOUT_STARTED" && pending.status !== "PLAN_SELECTED") {
395
+ return {
396
+ activated: false,
397
+ reason: "INVALID_STATE"
398
+ };
399
+ }
400
+ await this.record("PAYMENT_RECEIVED", pending.id, context, {
401
+ eventId: input.eventId,
402
+ sessionId: input.sessionId
403
+ });
404
+ const result = await this.activationOrchestrator.activate(pending);
405
+ await this.repo.delete(pending.id);
406
+ await this.record("ACTIVATION_COMPLETED", pending.id, context, {
407
+ userId: result.userId,
408
+ tenantId: result.tenantId,
409
+ subscriptionId: result.subscriptionId
410
+ });
411
+ this.logger.log(`Aktivierung erfolgreich: pending=${pending.id} \u2192 user=${result.userId} tenant=${result.tenantId} subscription=${result.subscriptionId}`);
412
+ return {
413
+ activated: true,
414
+ result
415
+ };
416
+ }
417
+ /**
418
+ * Deletes all PendingRegistration records with `expiresAt < now`.
419
+ * Full deletion (no anonymization path) is the simpler option: the schema
420
+ * holds `email`, `firstName`, `lastName`, `passwordHash` as NOT NULL, and
421
+ * the unique email is exactly what needs to become free again after expiry.
422
+ *
423
+ * The batch `limit` guards against memory spikes on large backlogs — the
424
+ * caller (cron) keeps calling until `moreAvailable === false`.
425
+ */
426
+ async runCleanup(now, limit = 500) {
427
+ if (limit <= 0) {
428
+ return {
429
+ deleted: 0,
430
+ moreAvailable: false
431
+ };
432
+ }
433
+ const expired = await this.repo.findExpired(now, limit);
434
+ for (const pending of expired) {
435
+ try {
436
+ await this.repo.delete(pending.id);
437
+ } catch (error) {
438
+ const message = error instanceof Error ? error.message : String(error);
439
+ this.logger.warn(`Cleanup: PendingRegistration ${pending.id} konnte nicht geloescht werden (${message}).`);
440
+ }
441
+ }
442
+ if (expired.length > 0) {
443
+ this.logger.log(`Cleanup: ${expired.length} abgelaufene PendingRegistrations geloescht.`);
444
+ }
445
+ return {
446
+ deleted: expired.length,
447
+ moreAvailable: expired.length >= limit
448
+ };
449
+ }
450
+ async start(input, context) {
451
+ const email = normalizeEmail(input.email);
452
+ if (await this.userLookup.hasActiveUser(email)) {
453
+ this.logger.log(`register/start fuer aktiven Account ignoriert (${email}).`);
454
+ await this.record("REGISTRATION_NEUTRAL_ACTIVE_USER", null, context);
455
+ return {
456
+ neutral: true
457
+ };
458
+ }
459
+ const existing = await this.repo.findByEmail(email);
460
+ if (existing) {
461
+ if (existing.expiresAt.getTime() < Date.now()) {
462
+ await this.repo.delete(existing.id);
463
+ await this.record("REGISTRATION_NEUTRAL_EXPIRED", existing.id, context);
464
+ } else if (existing.status === "PENDING_EMAIL_VERIFICATION") {
465
+ await this.regenerateOtp(existing);
466
+ await this.record("REGISTRATION_NEUTRAL_REPLAY", existing.id, context, {
467
+ status: existing.status
468
+ });
469
+ return {
470
+ neutral: true
471
+ };
472
+ } else {
473
+ if (this.resumeTokenSigner && this.resumeDelivery) {
474
+ await this.sendResumeLink(existing);
475
+ } else {
476
+ await this.regenerateOtp(existing);
477
+ }
478
+ await this.record("REGISTRATION_NEUTRAL_REPLAY", existing.id, context, {
479
+ status: existing.status
480
+ });
481
+ return {
482
+ neutral: true
483
+ };
484
+ }
485
+ }
486
+ const slug = await this.resolveSlug(input.tenantName, input.tenantSlug);
487
+ const passwordHash = await this.passwordHasher.hash(input.password);
488
+ const code = generateOtpCode();
489
+ const otpHash = hashOtpCode(code);
490
+ const now = /* @__PURE__ */ new Date();
491
+ const otpExpiresAt = new Date(now.getTime() + import_types2.OTP_TTL_MINUTES * 60 * 1e3);
492
+ const expiresAt = new Date(now.getTime() + import_types2.PENDING_EMAIL_TTL_HOURS * 60 * 60 * 1e3);
493
+ const pending = await this.repo.create({
494
+ tenantName: input.tenantName.trim(),
495
+ tenantSlug: slug,
496
+ salutation: nullable(input.salutation),
497
+ firstName: input.firstName.trim(),
498
+ lastName: input.lastName.trim(),
499
+ email,
500
+ passwordHash,
501
+ locale: input.locale ?? "de",
502
+ otpHash,
503
+ otpExpiresAt,
504
+ expiresAt
505
+ });
506
+ await this.deliverOtp(pending, code);
507
+ await this.record("REGISTRATION_STARTED", pending.id, context);
508
+ return {
509
+ neutral: true
510
+ };
511
+ }
512
+ async verifyOtp(emailRaw, code, context) {
513
+ const email = normalizeEmail(emailRaw);
514
+ const pending = await this.repo.findByEmail(email);
515
+ if (!pending) {
516
+ await this.record("OTP_VERIFY_FAILED", null, context, {
517
+ reason: "unknown_email"
518
+ });
519
+ throw new import_common.BadRequestException({
520
+ code: "OTP_INVALID"
521
+ });
522
+ }
523
+ if (pending.status !== "PENDING_EMAIL_VERIFICATION") {
524
+ return {
525
+ status: pending.status,
526
+ nextStep: import_types2.REGISTRATION_STEP_BY_STATUS[pending.status],
527
+ pendingRegistrationId: pending.id
528
+ };
529
+ }
530
+ if (!pending.otpHash || !pending.otpExpiresAt) {
531
+ await this.record("OTP_VERIFY_FAILED", pending.id, context, {
532
+ reason: "no_otp_set"
533
+ });
534
+ throw new import_common.BadRequestException({
535
+ code: "OTP_INVALID"
536
+ });
537
+ }
538
+ if (pending.otpExpiresAt.getTime() < Date.now()) {
539
+ await this.record("OTP_VERIFY_FAILED", pending.id, context, {
540
+ reason: "expired"
541
+ });
542
+ throw new import_common.BadRequestException({
543
+ code: "OTP_EXPIRED"
544
+ });
545
+ }
546
+ const maxAttempts = resolveOtpVerifyMaxAttempts();
547
+ if (pending.otpAttemptCount >= maxAttempts) {
548
+ await this.record("OTP_VERIFY_FAILED", pending.id, context, {
549
+ reason: "locked"
550
+ });
551
+ throw new import_common.BadRequestException({
552
+ code: "OTP_LOCKED"
553
+ });
554
+ }
555
+ const attemptCount = await this.repo.incrementOtpAttemptCount(pending.id);
556
+ if (attemptCount > maxAttempts) {
557
+ await this.record("OTP_VERIFY_FAILED", pending.id, context, {
558
+ reason: "locked"
559
+ });
560
+ throw new import_common.BadRequestException({
561
+ code: "OTP_LOCKED"
562
+ });
563
+ }
564
+ if (!verifyOtpCode(pending.otpHash, code)) {
565
+ await this.record("OTP_VERIFY_FAILED", pending.id, context, {
566
+ reason: "wrong_code"
567
+ });
568
+ throw new import_common.BadRequestException({
569
+ code: "OTP_INVALID"
570
+ });
571
+ }
572
+ const now = /* @__PURE__ */ new Date();
573
+ const updated = await this.repo.update(pending.id, {
574
+ status: "EMAIL_VERIFIED",
575
+ currentStep: 3,
576
+ emailVerifiedAt: now,
577
+ otpHash: null,
578
+ otpExpiresAt: null,
579
+ expiresAt: new Date(now.getTime() + import_types2.PENDING_ONBOARDING_TTL_DAYS * 24 * 60 * 60 * 1e3)
580
+ });
581
+ await this.record("OTP_VERIFIED", updated.id, context);
582
+ return {
583
+ status: updated.status,
584
+ nextStep: import_types2.REGISTRATION_STEP_BY_STATUS[updated.status],
585
+ pendingRegistrationId: updated.id
586
+ };
587
+ }
588
+ async resendOtp(emailRaw, context) {
589
+ const email = normalizeEmail(emailRaw);
590
+ const pending = await this.repo.findByEmail(email);
591
+ if (!pending) {
592
+ return {
593
+ neutral: true
594
+ };
595
+ }
596
+ if (pending.status !== "PENDING_EMAIL_VERIFICATION") {
597
+ return {
598
+ neutral: true
599
+ };
600
+ }
601
+ await this.regenerateOtp(pending);
602
+ await this.record("OTP_RESEND_REQUESTED", pending.id, context);
603
+ return {
604
+ neutral: true
605
+ };
606
+ }
607
+ async regenerateOtp(pending) {
608
+ const now = /* @__PURE__ */ new Date();
609
+ const windowMs = import_types2.OTP_RATE_LIMIT_WINDOW_MINUTES * 60 * 1e3;
610
+ const withinWindow = pending.lastOtpSentAt !== null && now.getTime() - pending.lastOtpSentAt.getTime() < windowMs;
611
+ if (withinWindow && pending.otpSendCount >= import_types2.OTP_RATE_LIMIT_MAX_SENDS) {
612
+ this.logger.warn(`OTP-Rate-Limit erreicht fuer ${pending.email} \u2014 Send wird still verworfen.`);
613
+ return;
614
+ }
615
+ const code = generateOtpCode();
616
+ const otpHash = hashOtpCode(code);
617
+ const otpExpiresAt = new Date(now.getTime() + import_types2.OTP_TTL_MINUTES * 60 * 1e3);
618
+ const otpSendCount = withinWindow ? pending.otpSendCount + 1 : 1;
619
+ await this.repo.update(pending.id, {
620
+ otpHash,
621
+ otpExpiresAt,
622
+ otpSendCount,
623
+ lastOtpSentAt: now,
624
+ // New code = fresh attempt budget. Overall throughput stays capped
625
+ // by the send rate limit above.
626
+ otpAttemptCount: 0
627
+ });
628
+ await this.deliverOtp(pending, code);
629
+ }
630
+ async deliverOtp(pending, code) {
631
+ try {
632
+ await this.otpDelivery.sendVerificationOtp({
633
+ to: pending.email,
634
+ code,
635
+ firstName: pending.firstName,
636
+ locale: pending.locale
637
+ });
638
+ } catch (error) {
639
+ const message = error instanceof Error ? error.message : String(error);
640
+ this.logger.error(`OTP-Mail an ${pending.email} fehlgeschlagen: ${message}`);
641
+ }
642
+ }
643
+ /**
644
+ * Creates a signed resume token (60 min TTL) and sends a magic link to the
645
+ * stored email. Mail send errors are logged but not escalated
646
+ * (account-enumeration protection).
647
+ */
648
+ async sendResumeLink(pending) {
649
+ if (!this.resumeTokenSigner || !this.resumeDelivery) {
650
+ return;
651
+ }
652
+ const token = await this.resumeTokenSigner.sign({
653
+ pendingRegistrationId: pending.id,
654
+ ttlMinutes: import_types.REGISTRATION_RESUME_TTL_MINUTES
655
+ });
656
+ const base = (this.resumeBaseUrl ?? "http://localhost").replace(/\/+$/, "");
657
+ const url = `${base}/login?resume=${encodeURIComponent(token)}`;
658
+ try {
659
+ await this.resumeDelivery.sendResumeEmail({
660
+ to: pending.email,
661
+ firstName: pending.firstName,
662
+ locale: pending.locale,
663
+ resumeUrl: url
664
+ });
665
+ } catch (error) {
666
+ const message = error instanceof Error ? error.message : String(error);
667
+ this.logger.error(`Resume-Mail an ${pending.email} fehlgeschlagen: ${message}`);
668
+ }
669
+ }
670
+ /**
671
+ * Resolves a resume token to the matching onboarding step. Called by the
672
+ * frontend from `/register?resume=<jwt>`. On an invalid token the method
673
+ * throws `RESUME_TOKEN_INVALID`.
674
+ *
675
+ * Also returns a public-safe snapshot of the PendingRegistration so the
676
+ * frontend can prefill completed steps with the existing data (first name /
677
+ * last name, organization name, plan selection ...).
678
+ */
679
+ async resumeWithToken(input, context) {
680
+ if (!this.resumeTokenSigner) {
681
+ throw new import_common.BadRequestException({
682
+ code: "RESUME_NOT_CONFIGURED"
683
+ });
684
+ }
685
+ let decoded;
686
+ try {
687
+ decoded = await this.resumeTokenSigner.verify(input.token);
688
+ } catch {
689
+ throw new import_common.BadRequestException({
690
+ code: "RESUME_TOKEN_INVALID"
691
+ });
692
+ }
693
+ const pending = await this.repo.findById(decoded.pendingRegistrationId);
694
+ if (!pending) {
695
+ throw new import_common.BadRequestException({
696
+ code: "RESUME_TOKEN_INVALID"
697
+ });
698
+ }
699
+ if (pending.expiresAt.getTime() < Date.now()) {
700
+ throw new import_common.BadRequestException({
701
+ code: "PENDING_REGISTRATION_EXPIRED"
702
+ });
703
+ }
704
+ await this.record("REGISTRATION_NEUTRAL_REPLAY", pending.id, context, {
705
+ via: "resume_token",
706
+ status: pending.status
707
+ });
708
+ return {
709
+ pendingRegistrationId: pending.id,
710
+ status: pending.status,
711
+ nextStep: import_types2.REGISTRATION_STEP_BY_STATUS[pending.status],
712
+ snapshot: toSnapshot(pending)
713
+ };
714
+ }
715
+ /**
716
+ * Signs a short-lived resume token for an existing PendingRegistration —
717
+ * the login endpoint uses this to send the user into the wizard with
718
+ * prefill after successful password verification.
719
+ */
720
+ async signResumeToken(pendingRegistrationId) {
721
+ if (!this.resumeTokenSigner) return null;
722
+ return this.resumeTokenSigner.sign({
723
+ pendingRegistrationId
724
+ });
725
+ }
726
+ /* ─── Configurator (Step 3) ────────────────────────────────────────── */
727
+ /** Returns the (app-specific) configurator catalog. */
728
+ async getConfiguratorCatalog() {
729
+ if (!this.configuratorLookup) {
730
+ throw new import_common.BadRequestException({
731
+ code: "CONFIGURATOR_NOT_CONFIGURED"
732
+ });
733
+ }
734
+ return this.configuratorLookup.getCatalog();
735
+ }
736
+ /**
737
+ * Saves the configurator selection (model + cycle + promo) and returns the
738
+ * full price breakdown.
739
+ *
740
+ * Sets:
741
+ * - `status = PLAN_SELECTED`
742
+ * - `currentStep = 4`
743
+ * - `selectedPlanId = <planId from the chosen model>`
744
+ * - `configJson` / `billingCycle` / `appliedPromoCode`
745
+ * - `expiresAt = now + ONBOARDING_TTL`
746
+ */
747
+ async saveConfiguration(input, context) {
748
+ if (!this.configuratorLookup) {
749
+ throw new import_common.BadRequestException({
750
+ code: "CONFIGURATOR_NOT_CONFIGURED"
751
+ });
752
+ }
753
+ const pending = await this.repo.findById(input.pendingRegistrationId);
754
+ if (!pending) {
755
+ throw new import_common.NotFoundException({
756
+ code: "PENDING_REGISTRATION_NOT_FOUND"
757
+ });
758
+ }
759
+ if (pending.expiresAt.getTime() < Date.now()) {
760
+ throw new import_common.BadRequestException({
761
+ code: "PENDING_REGISTRATION_EXPIRED"
762
+ });
763
+ }
764
+ if (pending.status !== "EMAIL_VERIFIED" && pending.status !== "PLAN_SELECTED" && pending.status !== "CHECKOUT_STARTED") {
765
+ throw new import_common.BadRequestException({
766
+ code: "INVALID_REGISTRATION_STATE"
767
+ });
768
+ }
769
+ const catalog = await this.configuratorLookup.getCatalog();
770
+ const model = catalog.models.find((m) => m.id === input.selection.modelId);
771
+ if (!model) {
772
+ throw new import_common.BadRequestException({
773
+ code: "MODEL_NOT_AVAILABLE"
774
+ });
775
+ }
776
+ if (input.selection.businessTypeVersionId && this.businessTypeLookup) {
777
+ const view = await this.businessTypeLookup.findPublishedVersion(input.selection.businessTypeVersionId);
778
+ if (!view) {
779
+ throw new import_common.BadRequestException({
780
+ code: "BUSINESS_TYPE_NOT_AVAILABLE"
781
+ });
782
+ }
783
+ }
784
+ let promoEval;
785
+ if (input.selection.appliedPromoCode && this.promoPreview) {
786
+ const dryRun = computeBreakdown(catalog, {
787
+ ...input.selection,
788
+ appliedPromoCode: null
789
+ });
790
+ const previewResult = await this.promoPreview.preview({
791
+ code: input.selection.appliedPromoCode,
792
+ planId: model.planId,
793
+ billingCycle: input.selection.billingCycle,
794
+ subtotalGross: dryRun.totalGross,
795
+ email: pending.email
796
+ });
797
+ if (previewResult.valid && previewResult.discountAmount && previewResult.percent !== void 0 && previewResult.label) {
798
+ promoEval = {
799
+ discountAmount: previewResult.discountAmount,
800
+ percent: previewResult.percent,
801
+ label: previewResult.label
802
+ };
803
+ }
804
+ }
805
+ const breakdown = computeBreakdown(catalog, input.selection, promoEval);
806
+ if (promoEval) {
807
+ breakdown.appliedPromo = {
808
+ code: input.selection.appliedPromoCode,
809
+ label: promoEval.label,
810
+ percent: promoEval.percent
811
+ };
812
+ }
813
+ const now = /* @__PURE__ */ new Date();
814
+ const expiresAt = new Date(now.getTime() + import_types2.PENDING_ONBOARDING_TTL_DAYS * 24 * 60 * 60 * 1e3);
815
+ const updated = await this.repo.update(pending.id, {
816
+ status: "PLAN_SELECTED",
817
+ currentStep: 4,
818
+ selectedPlanId: model.planId,
819
+ configJson: input.selection,
820
+ billingCycle: input.selection.billingCycle,
821
+ appliedPromoCode: input.selection.appliedPromoCode ?? null,
822
+ expiresAt
823
+ });
824
+ await this.record("PLAN_SELECTED", updated.id, context, {
825
+ modelId: model.id,
826
+ planId: model.planId,
827
+ cycle: input.selection.billingCycle,
828
+ hasPromo: !!promoEval
829
+ });
830
+ return {
831
+ pendingRegistrationId: updated.id,
832
+ status: updated.status,
833
+ nextStep: import_types2.REGISTRATION_STEP_BY_STATUS[updated.status],
834
+ selection: input.selection,
835
+ breakdown
836
+ };
837
+ }
838
+ /**
839
+ * Validates a promo code against the currently stored configuration and
840
+ * returns a new breakdown. Does NOT modify the pending — for live preview
841
+ * in the frontend. If `null` is passed as `code`, the promo effect is
842
+ * removed.
843
+ */
844
+ async previewConfigPromo(pendingRegistrationId, code) {
845
+ if (!this.configuratorLookup) {
846
+ throw new import_common.BadRequestException({
847
+ code: "CONFIGURATOR_NOT_CONFIGURED"
848
+ });
849
+ }
850
+ const pending = await this.repo.findById(pendingRegistrationId);
851
+ if (!pending) {
852
+ throw new import_common.NotFoundException({
853
+ code: "PENDING_REGISTRATION_NOT_FOUND"
854
+ });
855
+ }
856
+ const config = pending.configJson;
857
+ if (!config) {
858
+ throw new import_common.BadRequestException({
859
+ code: "CONFIG_NOT_SAVED"
860
+ });
861
+ }
862
+ const catalog = await this.configuratorLookup.getCatalog();
863
+ const model = catalog.models.find((m) => m.id === config.modelId);
864
+ if (!model) {
865
+ throw new import_common.BadRequestException({
866
+ code: "MODEL_NOT_AVAILABLE"
867
+ });
868
+ }
869
+ let promoEval;
870
+ if (code && this.promoPreview) {
871
+ const dryRun = computeBreakdown(catalog, {
872
+ ...config,
873
+ appliedPromoCode: null
874
+ });
875
+ const previewResult = await this.promoPreview.preview({
876
+ code,
877
+ planId: model.planId,
878
+ billingCycle: config.billingCycle,
879
+ subtotalGross: dryRun.totalGross,
880
+ email: pending.email
881
+ });
882
+ if (previewResult.valid && previewResult.discountAmount && previewResult.percent !== void 0 && previewResult.label) {
883
+ promoEval = {
884
+ discountAmount: previewResult.discountAmount,
885
+ percent: previewResult.percent,
886
+ label: previewResult.label
887
+ };
888
+ }
889
+ }
890
+ const breakdown = computeBreakdown(catalog, config, promoEval);
891
+ if (promoEval && code) {
892
+ breakdown.appliedPromo = {
893
+ code,
894
+ label: promoEval.label,
895
+ percent: promoEval.percent
896
+ };
897
+ }
898
+ return breakdown;
899
+ }
900
+ /**
901
+ * Helper for step 3 resume: the frontend needs the current breakdown when
902
+ * the pending already has a configuration. Pure recomputation against the
903
+ * catalog (without promo validation, since promos should be freshly checked
904
+ * anyway).
905
+ */
906
+ async getCurrentBreakdown(pendingRegistrationId) {
907
+ if (!this.configuratorLookup) return null;
908
+ const pending = await this.repo.findById(pendingRegistrationId);
909
+ if (!pending || !pending.configJson) return null;
910
+ const catalog = await this.configuratorLookup.getCatalog();
911
+ return computeBreakdown(catalog, pending.configJson);
912
+ }
913
+ async resolveSlug(tenantName, requested) {
914
+ const requestedSlug = requested?.trim().toLowerCase() ?? "";
915
+ const base = requestedSlug.length > 0 ? requestedSlug : slugify(tenantName);
916
+ if (await this.slugCheck.isSlugAvailable(base)) {
917
+ return base;
918
+ }
919
+ for (let i = 2; i < 1e3; i++) {
920
+ const candidate = `${base}-${i}`;
921
+ if (await this.slugCheck.isSlugAvailable(candidate)) {
922
+ return candidate;
923
+ }
924
+ }
925
+ throw new Error(`Konnte keinen freien Slug fuer "${tenantName}" finden.`);
926
+ }
927
+ };
928
+ PendingRegistrationService = _ts_decorate([
929
+ (0, import_common.Injectable)(),
930
+ _ts_param(0, (0, import_common.Inject)(PENDING_REGISTRATION_REPOSITORY_TOKEN)),
931
+ _ts_param(1, (0, import_common.Inject)(REGISTRATION_OTP_DELIVERY_TOKEN)),
932
+ _ts_param(2, (0, import_common.Inject)(USER_ACCOUNT_LOOKUP_TOKEN)),
933
+ _ts_param(3, (0, import_common.Inject)(SLUG_AVAILABILITY_CHECK_TOKEN)),
934
+ _ts_param(4, (0, import_common.Inject)(PASSWORD_HASHER_TOKEN)),
935
+ _ts_param(5, (0, import_common.Inject)(PLAN_CATALOG_LOOKUP_TOKEN)),
936
+ _ts_param(6, (0, import_common.Inject)(PAYMENT_PROVIDER_TOKEN)),
937
+ _ts_param(7, (0, import_common.Inject)(PAYMENT_EVENT_LOG_TOKEN)),
938
+ _ts_param(8, (0, import_common.Inject)(ACTIVATION_ORCHESTRATOR_TOKEN)),
939
+ _ts_param(9, (0, import_common.Inject)(REGISTRATION_AUDIT_LOGGER_TOKEN)),
940
+ _ts_param(10, (0, import_common.Optional)()),
941
+ _ts_param(10, (0, import_common.Inject)(REGISTRATION_RESUME_TOKEN_SIGNER_TOKEN)),
942
+ _ts_param(11, (0, import_common.Optional)()),
943
+ _ts_param(11, (0, import_common.Inject)(REGISTRATION_RESUME_DELIVERY_TOKEN)),
944
+ _ts_param(12, (0, import_common.Optional)()),
945
+ _ts_param(12, (0, import_common.Inject)(REGISTRATION_RESUME_BASE_URL_TOKEN)),
946
+ _ts_param(13, (0, import_common.Optional)()),
947
+ _ts_param(13, (0, import_common.Inject)(REGISTRATION_CONFIGURATOR_LOOKUP_TOKEN)),
948
+ _ts_param(14, (0, import_common.Optional)()),
949
+ _ts_param(14, (0, import_common.Inject)(REGISTRATION_PROMO_PREVIEW_TOKEN)),
950
+ _ts_param(15, (0, import_common.Optional)()),
951
+ _ts_param(15, (0, import_common.Inject)(REGISTRATION_BUSINESS_TYPE_LOOKUP_TOKEN)),
952
+ _ts_metadata("design:type", Function),
953
+ _ts_metadata("design:paramtypes", [
954
+ typeof PendingRegistrationRepository === "undefined" ? Object : PendingRegistrationRepository,
955
+ typeof RegistrationOtpDelivery === "undefined" ? Object : RegistrationOtpDelivery,
956
+ typeof UserAccountLookup === "undefined" ? Object : UserAccountLookup,
957
+ typeof SlugAvailabilityCheck === "undefined" ? Object : SlugAvailabilityCheck,
958
+ typeof PasswordHasher === "undefined" ? Object : PasswordHasher,
959
+ typeof PlanCatalogLookup === "undefined" ? Object : PlanCatalogLookup,
960
+ typeof PaymentProvider === "undefined" ? Object : PaymentProvider,
961
+ typeof PaymentEventLog === "undefined" ? Object : PaymentEventLog,
962
+ typeof ActivationOrchestrator === "undefined" ? Object : ActivationOrchestrator,
963
+ typeof RegistrationAuditLogger === "undefined" ? Object : RegistrationAuditLogger,
964
+ typeof RegistrationResumeTokenSigner === "undefined" ? Object : RegistrationResumeTokenSigner,
965
+ typeof RegistrationResumeDelivery === "undefined" ? Object : RegistrationResumeDelivery,
966
+ String,
967
+ typeof RegistrationConfiguratorLookup === "undefined" ? Object : RegistrationConfiguratorLookup,
968
+ typeof RegistrationPromoPreview === "undefined" ? Object : RegistrationPromoPreview,
969
+ typeof RegistrationBusinessTypeLookup === "undefined" ? Object : RegistrationBusinessTypeLookup
970
+ ])
971
+ ], PendingRegistrationService);
972
+ function normalizeEmail(value) {
973
+ return value.trim().toLowerCase();
974
+ }
975
+ __name(normalizeEmail, "normalizeEmail");
976
+ function resolveOtpVerifyMaxAttempts() {
977
+ const raw = process.env.SAAS_PLATFORM_OTP_VERIFY_MAX_ATTEMPTS;
978
+ if (!raw) return import_types2.OTP_VERIFY_MAX_ATTEMPTS;
979
+ const parsed = Number.parseInt(raw, 10);
980
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : import_types2.OTP_VERIFY_MAX_ATTEMPTS;
981
+ }
982
+ __name(resolveOtpVerifyMaxAttempts, "resolveOtpVerifyMaxAttempts");
983
+ function toSnapshot(pending) {
984
+ return {
985
+ tenantName: pending.tenantName,
986
+ tenantSlug: pending.tenantSlug,
987
+ salutation: pending.salutation,
988
+ firstName: pending.firstName,
989
+ lastName: pending.lastName,
990
+ email: pending.email,
991
+ locale: pending.locale,
992
+ status: pending.status,
993
+ currentStep: pending.currentStep,
994
+ emailVerifiedAt: pending.emailVerifiedAt?.toISOString() ?? null,
995
+ selectedPlanId: pending.selectedPlanId,
996
+ config: pending.configJson,
997
+ billingCycle: pending.billingCycle,
998
+ appliedPromoCode: pending.appliedPromoCode,
999
+ checkoutSessionId: pending.checkoutSessionId
1000
+ };
1001
+ }
1002
+ __name(toSnapshot, "toSnapshot");
1003
+ function nullable(value) {
1004
+ const trimmed = value?.trim();
1005
+ return trimmed && trimmed.length > 0 ? trimmed : null;
1006
+ }
1007
+ __name(nullable, "nullable");
1008
+
1009
+ // src/registration/cleanup.cron.ts
1010
+ var import_common2 = require("@nestjs/common");
1011
+ var import_schedule = require("@nestjs/schedule");
1012
+ function _ts_decorate2(decorators, target, key, desc) {
1013
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1014
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1015
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1016
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1017
+ }
1018
+ __name(_ts_decorate2, "_ts_decorate");
1019
+ function _ts_metadata2(k, v) {
1020
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1021
+ }
1022
+ __name(_ts_metadata2, "_ts_metadata");
1023
+ var RegistrationCleanupCron = class _RegistrationCleanupCron {
1024
+ static {
1025
+ __name(this, "RegistrationCleanupCron");
1026
+ }
1027
+ service;
1028
+ logger = new import_common2.Logger(_RegistrationCleanupCron.name);
1029
+ constructor(service) {
1030
+ this.service = service;
1031
+ }
1032
+ async runDaily() {
1033
+ await this.runCleanup();
1034
+ }
1035
+ /**
1036
+ * Public API: can be triggered manually from an admin endpoint /
1037
+ * `/admin/registration/run-cleanup` (same pattern as
1038
+ * `PromoCodeExpirer.run` in the consuming apps).
1039
+ */
1040
+ async runCleanup(now = /* @__PURE__ */ new Date(), batchSize = 500) {
1041
+ let totalDeleted = 0;
1042
+ let iterations = 0;
1043
+ const MAX_ITERATIONS = 50;
1044
+ while (iterations < MAX_ITERATIONS) {
1045
+ const result = await this.service.runCleanup(now, batchSize);
1046
+ totalDeleted += result.deleted;
1047
+ iterations++;
1048
+ if (!result.moreAvailable) {
1049
+ break;
1050
+ }
1051
+ }
1052
+ if (totalDeleted > 0) {
1053
+ this.logger.log(`PendingRegistration-Cleanup abgeschlossen: ${totalDeleted} geloescht in ${iterations} Batches.`);
1054
+ }
1055
+ }
1056
+ };
1057
+ _ts_decorate2([
1058
+ (0, import_schedule.Cron)("15 4 * * *", {
1059
+ timeZone: "Europe/Berlin"
1060
+ }),
1061
+ _ts_metadata2("design:type", Function),
1062
+ _ts_metadata2("design:paramtypes", []),
1063
+ _ts_metadata2("design:returntype", Promise)
1064
+ ], RegistrationCleanupCron.prototype, "runDaily", null);
1065
+ RegistrationCleanupCron = _ts_decorate2([
1066
+ (0, import_common2.Injectable)(),
1067
+ _ts_metadata2("design:type", Function),
1068
+ _ts_metadata2("design:paramtypes", [
1069
+ typeof PendingRegistrationService === "undefined" ? Object : PendingRegistrationService
1070
+ ])
1071
+ ], RegistrationCleanupCron);
1072
+
1073
+ // src/registration/ip-rate-limit.guard.ts
1074
+ var import_common3 = require("@nestjs/common");
1075
+ function _ts_decorate3(decorators, target, key, desc) {
1076
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1077
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1078
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1079
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1080
+ }
1081
+ __name(_ts_decorate3, "_ts_decorate");
1082
+ function _ts_metadata3(k, v) {
1083
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1084
+ }
1085
+ __name(_ts_metadata3, "_ts_metadata");
1086
+ var BaseIpRateLimitGuard = class {
1087
+ static {
1088
+ __name(this, "BaseIpRateLimitGuard");
1089
+ }
1090
+ options;
1091
+ buckets = /* @__PURE__ */ new Map();
1092
+ constructor(options) {
1093
+ this.options = options;
1094
+ }
1095
+ canActivate(context) {
1096
+ if (process.env.SAAS_PLATFORM_SKIP_RATE_LIMITS === "1" && process.env.NODE_ENV !== "production") {
1097
+ return true;
1098
+ }
1099
+ const req = context.switchToHttp().getRequest();
1100
+ const key = ipFingerprint(req);
1101
+ if (this.exceeded(key)) {
1102
+ throw new import_common3.HttpException({
1103
+ code: "RATE_LIMITED",
1104
+ reason: this.options.name,
1105
+ retryAfterSeconds: Math.ceil(this.options.windowMs / 1e3)
1106
+ }, import_common3.HttpStatus.TOO_MANY_REQUESTS);
1107
+ }
1108
+ return true;
1109
+ }
1110
+ exceeded(key) {
1111
+ const now = Date.now();
1112
+ const bucket = this.buckets.get(key);
1113
+ if (!bucket || now - bucket.windowStart > this.options.windowMs) {
1114
+ this.buckets.set(key, {
1115
+ count: 1,
1116
+ windowStart: now
1117
+ });
1118
+ return false;
1119
+ }
1120
+ bucket.count += 1;
1121
+ return bucket.count > this.options.limit;
1122
+ }
1123
+ };
1124
+ BaseIpRateLimitGuard = _ts_decorate3([
1125
+ (0, import_common3.Injectable)(),
1126
+ _ts_metadata3("design:type", Function),
1127
+ _ts_metadata3("design:paramtypes", [
1128
+ Object
1129
+ ])
1130
+ ], BaseIpRateLimitGuard);
1131
+ function ipFingerprint(req) {
1132
+ const fwd = req.headers["x-forwarded-for"];
1133
+ const first = Array.isArray(fwd) ? fwd[0] : fwd?.split(",")[0]?.trim();
1134
+ return first || req.ip || "unknown";
1135
+ }
1136
+ __name(ipFingerprint, "ipFingerprint");
1137
+
1138
+ // src/registration/module.ts
1139
+ var import_common4 = require("@nestjs/common");
1140
+
1141
+ // src/core/di.ts
1142
+ function asProvider(token, impl) {
1143
+ if (typeof impl === "object" && impl !== null && "useFactory" in impl && typeof impl.useFactory === "function") {
1144
+ const factory = impl;
1145
+ return {
1146
+ provide: token,
1147
+ useFactory: factory.useFactory,
1148
+ inject: factory.inject
1149
+ };
1150
+ }
1151
+ return {
1152
+ provide: token,
1153
+ useValue: impl
1154
+ };
1155
+ }
1156
+ __name(asProvider, "asProvider");
1157
+
1158
+ // src/registration/module.ts
1159
+ function _ts_decorate4(decorators, target, key, desc) {
1160
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1161
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1162
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1163
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1164
+ }
1165
+ __name(_ts_decorate4, "_ts_decorate");
1166
+ var RegistrationModule = class _RegistrationModule {
1167
+ static {
1168
+ __name(this, "RegistrationModule");
1169
+ }
1170
+ static forRoot(options) {
1171
+ const providers = [
1172
+ ...options.extraProviders ?? [],
1173
+ asProvider(PENDING_REGISTRATION_REPOSITORY_TOKEN, options.pendingRegistrationRepository),
1174
+ asProvider(REGISTRATION_OTP_DELIVERY_TOKEN, options.otpDelivery),
1175
+ asProvider(USER_ACCOUNT_LOOKUP_TOKEN, options.userAccountLookup),
1176
+ asProvider(SLUG_AVAILABILITY_CHECK_TOKEN, options.slugAvailabilityCheck),
1177
+ asProvider(PASSWORD_HASHER_TOKEN, options.passwordHasher),
1178
+ asProvider(PLAN_CATALOG_LOOKUP_TOKEN, options.planCatalogLookup),
1179
+ asProvider(PAYMENT_PROVIDER_TOKEN, options.paymentProvider),
1180
+ asProvider(PAYMENT_EVENT_LOG_TOKEN, options.paymentEventLog),
1181
+ asProvider(ACTIVATION_ORCHESTRATOR_TOKEN, options.activationOrchestrator),
1182
+ asProvider(REGISTRATION_AUDIT_LOGGER_TOKEN, options.auditLogger),
1183
+ PendingRegistrationService
1184
+ ];
1185
+ if (options.resumeTokenSigner) {
1186
+ providers.push(asProvider(REGISTRATION_RESUME_TOKEN_SIGNER_TOKEN, options.resumeTokenSigner));
1187
+ }
1188
+ if (options.resumeDelivery) {
1189
+ providers.push(asProvider(REGISTRATION_RESUME_DELIVERY_TOKEN, options.resumeDelivery));
1190
+ }
1191
+ if (options.resumeBaseUrl !== void 0) {
1192
+ providers.push({
1193
+ provide: REGISTRATION_RESUME_BASE_URL_TOKEN,
1194
+ useValue: options.resumeBaseUrl
1195
+ });
1196
+ }
1197
+ if (options.configuratorLookup) {
1198
+ providers.push(asProvider(REGISTRATION_CONFIGURATOR_LOOKUP_TOKEN, options.configuratorLookup));
1199
+ }
1200
+ if (options.promoPreview) {
1201
+ providers.push(asProvider(REGISTRATION_PROMO_PREVIEW_TOKEN, options.promoPreview));
1202
+ }
1203
+ if (options.businessTypeLookup) {
1204
+ providers.push(asProvider(REGISTRATION_BUSINESS_TYPE_LOOKUP_TOKEN, options.businessTypeLookup));
1205
+ }
1206
+ const includeCleanupCron = options.includeCleanupCron ?? true;
1207
+ if (includeCleanupCron) {
1208
+ providers.push(RegistrationCleanupCron);
1209
+ }
1210
+ return {
1211
+ module: _RegistrationModule,
1212
+ global: options.global ?? false,
1213
+ imports: options.imports ?? [],
1214
+ providers,
1215
+ exports: includeCleanupCron ? [
1216
+ PendingRegistrationService,
1217
+ RegistrationCleanupCron
1218
+ ] : [
1219
+ PendingRegistrationService
1220
+ ]
1221
+ };
1222
+ }
1223
+ };
1224
+ RegistrationModule = _ts_decorate4([
1225
+ (0, import_common4.Module)({})
1226
+ ], RegistrationModule);
1227
+
1228
+ // src/registration/dto/register-start.dto.ts
1229
+ var import_class_validator = require("class-validator");
1230
+ function _ts_decorate5(decorators, target, key, desc) {
1231
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1232
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1233
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1234
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1235
+ }
1236
+ __name(_ts_decorate5, "_ts_decorate");
1237
+ function _ts_metadata4(k, v) {
1238
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1239
+ }
1240
+ __name(_ts_metadata4, "_ts_metadata");
1241
+ var RegisterStartDto = class {
1242
+ static {
1243
+ __name(this, "RegisterStartDto");
1244
+ }
1245
+ /** Association/tenant name (required). */
1246
+ tenantName;
1247
+ /** Optional slug. If left empty, it is generated from `tenantName`. */
1248
+ tenantSlug;
1249
+ /** Salutation of the administrator (optional). */
1250
+ salutation;
1251
+ firstName;
1252
+ lastName;
1253
+ email;
1254
+ password;
1255
+ /** Terms / privacy / data processing — mandatory acceptance (must be `true`). */
1256
+ termsAccepted;
1257
+ locale;
1258
+ };
1259
+ _ts_decorate5([
1260
+ (0, import_class_validator.IsString)(),
1261
+ (0, import_class_validator.MinLength)(2),
1262
+ (0, import_class_validator.MaxLength)(120),
1263
+ _ts_metadata4("design:type", String)
1264
+ ], RegisterStartDto.prototype, "tenantName", void 0);
1265
+ _ts_decorate5([
1266
+ (0, import_class_validator.IsOptional)(),
1267
+ (0, import_class_validator.IsString)(),
1268
+ (0, import_class_validator.Matches)(/^[a-z0-9-]+$/, {
1269
+ message: "tenantSlug darf nur Kleinbuchstaben, Ziffern und Bindestriche enthalten."
1270
+ }),
1271
+ (0, import_class_validator.MaxLength)(60),
1272
+ _ts_metadata4("design:type", String)
1273
+ ], RegisterStartDto.prototype, "tenantSlug", void 0);
1274
+ _ts_decorate5([
1275
+ (0, import_class_validator.IsOptional)(),
1276
+ (0, import_class_validator.IsString)(),
1277
+ (0, import_class_validator.MaxLength)(40),
1278
+ _ts_metadata4("design:type", String)
1279
+ ], RegisterStartDto.prototype, "salutation", void 0);
1280
+ _ts_decorate5([
1281
+ (0, import_class_validator.IsString)(),
1282
+ (0, import_class_validator.MinLength)(1),
1283
+ (0, import_class_validator.MaxLength)(60),
1284
+ _ts_metadata4("design:type", String)
1285
+ ], RegisterStartDto.prototype, "firstName", void 0);
1286
+ _ts_decorate5([
1287
+ (0, import_class_validator.IsString)(),
1288
+ (0, import_class_validator.MinLength)(1),
1289
+ (0, import_class_validator.MaxLength)(60),
1290
+ _ts_metadata4("design:type", String)
1291
+ ], RegisterStartDto.prototype, "lastName", void 0);
1292
+ _ts_decorate5([
1293
+ (0, import_class_validator.IsEmail)(),
1294
+ (0, import_class_validator.MaxLength)(160),
1295
+ _ts_metadata4("design:type", String)
1296
+ ], RegisterStartDto.prototype, "email", void 0);
1297
+ _ts_decorate5([
1298
+ (0, import_class_validator.IsString)(),
1299
+ (0, import_class_validator.MinLength)(8),
1300
+ (0, import_class_validator.MaxLength)(160),
1301
+ _ts_metadata4("design:type", String)
1302
+ ], RegisterStartDto.prototype, "password", void 0);
1303
+ _ts_decorate5([
1304
+ (0, import_class_validator.Equals)(true, {
1305
+ message: "AGB, Datenschutzerklaerung und Auftragsverarbeitung muessen zugestimmt werden."
1306
+ }),
1307
+ _ts_metadata4("design:type", Boolean)
1308
+ ], RegisterStartDto.prototype, "termsAccepted", void 0);
1309
+ _ts_decorate5([
1310
+ (0, import_class_validator.IsOptional)(),
1311
+ (0, import_class_validator.IsIn)([
1312
+ "de",
1313
+ "tr"
1314
+ ]),
1315
+ _ts_metadata4("design:type", String)
1316
+ ], RegisterStartDto.prototype, "locale", void 0);
1317
+
1318
+ // src/registration/dto/verify-registration-otp.dto.ts
1319
+ var import_class_validator2 = require("class-validator");
1320
+ function _ts_decorate6(decorators, target, key, desc) {
1321
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1322
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1323
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1324
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1325
+ }
1326
+ __name(_ts_decorate6, "_ts_decorate");
1327
+ function _ts_metadata5(k, v) {
1328
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1329
+ }
1330
+ __name(_ts_metadata5, "_ts_metadata");
1331
+ var VerifyRegistrationOtpDto = class {
1332
+ static {
1333
+ __name(this, "VerifyRegistrationOtpDto");
1334
+ }
1335
+ email;
1336
+ otp;
1337
+ };
1338
+ _ts_decorate6([
1339
+ (0, import_class_validator2.IsEmail)(),
1340
+ _ts_metadata5("design:type", String)
1341
+ ], VerifyRegistrationOtpDto.prototype, "email", void 0);
1342
+ _ts_decorate6([
1343
+ (0, import_class_validator2.IsString)(),
1344
+ (0, import_class_validator2.Matches)(/^\d{6}$/, {
1345
+ message: "OTP muss 6 Ziffern sein."
1346
+ }),
1347
+ _ts_metadata5("design:type", String)
1348
+ ], VerifyRegistrationOtpDto.prototype, "otp", void 0);
1349
+
1350
+ // src/registration/dto/resend-registration-otp.dto.ts
1351
+ var import_class_validator3 = require("class-validator");
1352
+ function _ts_decorate7(decorators, target, key, desc) {
1353
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1354
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1355
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1356
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1357
+ }
1358
+ __name(_ts_decorate7, "_ts_decorate");
1359
+ function _ts_metadata6(k, v) {
1360
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1361
+ }
1362
+ __name(_ts_metadata6, "_ts_metadata");
1363
+ var ResendRegistrationOtpDto = class {
1364
+ static {
1365
+ __name(this, "ResendRegistrationOtpDto");
1366
+ }
1367
+ email;
1368
+ };
1369
+ _ts_decorate7([
1370
+ (0, import_class_validator3.IsEmail)(),
1371
+ _ts_metadata6("design:type", String)
1372
+ ], ResendRegistrationOtpDto.prototype, "email", void 0);
1373
+
1374
+ // src/registration/dto/select-plan.dto.ts
1375
+ var import_class_validator4 = require("class-validator");
1376
+ function _ts_decorate8(decorators, target, key, desc) {
1377
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1378
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1379
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1380
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1381
+ }
1382
+ __name(_ts_decorate8, "_ts_decorate");
1383
+ function _ts_metadata7(k, v) {
1384
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1385
+ }
1386
+ __name(_ts_metadata7, "_ts_metadata");
1387
+ var SelectRegistrationPlanDto = class {
1388
+ static {
1389
+ __name(this, "SelectRegistrationPlanDto");
1390
+ }
1391
+ pendingRegistrationId;
1392
+ planId;
1393
+ };
1394
+ _ts_decorate8([
1395
+ (0, import_class_validator4.IsString)(),
1396
+ (0, import_class_validator4.MinLength)(1),
1397
+ (0, import_class_validator4.MaxLength)(80),
1398
+ _ts_metadata7("design:type", String)
1399
+ ], SelectRegistrationPlanDto.prototype, "pendingRegistrationId", void 0);
1400
+ _ts_decorate8([
1401
+ (0, import_class_validator4.IsString)(),
1402
+ (0, import_class_validator4.MinLength)(1),
1403
+ (0, import_class_validator4.MaxLength)(40),
1404
+ _ts_metadata7("design:type", String)
1405
+ ], SelectRegistrationPlanDto.prototype, "planId", void 0);
1406
+
1407
+ // src/registration/dto/start-checkout.dto.ts
1408
+ var import_class_validator5 = require("class-validator");
1409
+ function _ts_decorate9(decorators, target, key, desc) {
1410
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1411
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1412
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1413
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1414
+ }
1415
+ __name(_ts_decorate9, "_ts_decorate");
1416
+ function _ts_metadata8(k, v) {
1417
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1418
+ }
1419
+ __name(_ts_metadata8, "_ts_metadata");
1420
+ var StartRegistrationCheckoutDto = class {
1421
+ static {
1422
+ __name(this, "StartRegistrationCheckoutDto");
1423
+ }
1424
+ pendingRegistrationId;
1425
+ successUrl;
1426
+ cancelUrl;
1427
+ };
1428
+ _ts_decorate9([
1429
+ (0, import_class_validator5.IsString)(),
1430
+ (0, import_class_validator5.MinLength)(1),
1431
+ (0, import_class_validator5.MaxLength)(80),
1432
+ _ts_metadata8("design:type", String)
1433
+ ], StartRegistrationCheckoutDto.prototype, "pendingRegistrationId", void 0);
1434
+ _ts_decorate9([
1435
+ (0, import_class_validator5.IsUrl)({
1436
+ require_tld: false
1437
+ }),
1438
+ (0, import_class_validator5.MaxLength)(500),
1439
+ _ts_metadata8("design:type", String)
1440
+ ], StartRegistrationCheckoutDto.prototype, "successUrl", void 0);
1441
+ _ts_decorate9([
1442
+ (0, import_class_validator5.IsUrl)({
1443
+ require_tld: false
1444
+ }),
1445
+ (0, import_class_validator5.MaxLength)(500),
1446
+ _ts_metadata8("design:type", String)
1447
+ ], StartRegistrationCheckoutDto.prototype, "cancelUrl", void 0);
1448
+
1449
+ // src/registration/dto/payment-webhook.dto.ts
1450
+ var import_class_validator6 = require("class-validator");
1451
+ function _ts_decorate10(decorators, target, key, desc) {
1452
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1453
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1454
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1455
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1456
+ }
1457
+ __name(_ts_decorate10, "_ts_decorate");
1458
+ function _ts_metadata9(k, v) {
1459
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1460
+ }
1461
+ __name(_ts_metadata9, "_ts_metadata");
1462
+ var PaymentWebhookDto = class {
1463
+ static {
1464
+ __name(this, "PaymentWebhookDto");
1465
+ }
1466
+ eventId;
1467
+ sessionId;
1468
+ provider;
1469
+ status;
1470
+ payload;
1471
+ };
1472
+ _ts_decorate10([
1473
+ (0, import_class_validator6.IsString)(),
1474
+ (0, import_class_validator6.MinLength)(1),
1475
+ (0, import_class_validator6.MaxLength)(120),
1476
+ _ts_metadata9("design:type", String)
1477
+ ], PaymentWebhookDto.prototype, "eventId", void 0);
1478
+ _ts_decorate10([
1479
+ (0, import_class_validator6.IsOptional)(),
1480
+ (0, import_class_validator6.IsString)(),
1481
+ (0, import_class_validator6.MaxLength)(120),
1482
+ _ts_metadata9("design:type", String)
1483
+ ], PaymentWebhookDto.prototype, "sessionId", void 0);
1484
+ _ts_decorate10([
1485
+ (0, import_class_validator6.IsString)(),
1486
+ (0, import_class_validator6.MaxLength)(40),
1487
+ _ts_metadata9("design:type", String)
1488
+ ], PaymentWebhookDto.prototype, "provider", void 0);
1489
+ _ts_decorate10([
1490
+ (0, import_class_validator6.IsIn)([
1491
+ "SUCCEEDED",
1492
+ "FAILED"
1493
+ ]),
1494
+ _ts_metadata9("design:type", String)
1495
+ ], PaymentWebhookDto.prototype, "status", void 0);
1496
+ _ts_decorate10([
1497
+ (0, import_class_validator6.IsOptional)(),
1498
+ (0, import_class_validator6.IsObject)(),
1499
+ _ts_metadata9("design:type", typeof Record === "undefined" ? Object : Record)
1500
+ ], PaymentWebhookDto.prototype, "payload", void 0);
1501
+
1502
+ // src/registration/dto/continue-registration.dto.ts
1503
+ var import_class_validator7 = require("class-validator");
1504
+ function _ts_decorate11(decorators, target, key, desc) {
1505
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1506
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1507
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1508
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1509
+ }
1510
+ __name(_ts_decorate11, "_ts_decorate");
1511
+ function _ts_metadata10(k, v) {
1512
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1513
+ }
1514
+ __name(_ts_metadata10, "_ts_metadata");
1515
+ var ContinueRegistrationDto = class {
1516
+ static {
1517
+ __name(this, "ContinueRegistrationDto");
1518
+ }
1519
+ token;
1520
+ };
1521
+ _ts_decorate11([
1522
+ (0, import_class_validator7.IsString)(),
1523
+ (0, import_class_validator7.MinLength)(10),
1524
+ (0, import_class_validator7.MaxLength)(2e3),
1525
+ _ts_metadata10("design:type", String)
1526
+ ], ContinueRegistrationDto.prototype, "token", void 0);
1527
+
1528
+ // src/registration/dto/save-configuration.dto.ts
1529
+ var import_class_validator8 = require("class-validator");
1530
+ var import_class_transformer = require("class-transformer");
1531
+ function _ts_decorate12(decorators, target, key, desc) {
1532
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1533
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1534
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1535
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1536
+ }
1537
+ __name(_ts_decorate12, "_ts_decorate");
1538
+ function _ts_metadata11(k, v) {
1539
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1540
+ }
1541
+ __name(_ts_metadata11, "_ts_metadata");
1542
+ var SaveRegistrationConfigSelectionDto = class {
1543
+ static {
1544
+ __name(this, "SaveRegistrationConfigSelectionDto");
1545
+ }
1546
+ modelId;
1547
+ billingCycle;
1548
+ appliedPromoCode;
1549
+ /**
1550
+ * SPEC_V2 §11.1 M5.3 — optional business-type choice. UUID of a
1551
+ * **published** BusinessTypeVersion. The service validates existence +
1552
+ * live status against `RegistrationBusinessTypeLookup`. `null` = no
1553
+ * choice (legacy Plan-only path).
1554
+ */
1555
+ businessTypeVersionId;
1556
+ /**
1557
+ * METAMODELL §17a — Bundle consistency. ID of the `CheckoutOffer` that was
1558
+ * created on the pricing page (`<app>/register?offer=<id>`). Passed
1559
+ * through unchanged in `configJson`; the ActivationOrchestrator freezes it
1560
+ * as `packageSnapshot` when the subscription is created and sets the offer
1561
+ * to `consumed`. `null` = direct registration without a website offer.
1562
+ */
1563
+ offerId;
1564
+ };
1565
+ _ts_decorate12([
1566
+ (0, import_class_validator8.IsString)(),
1567
+ (0, import_class_validator8.MinLength)(1),
1568
+ (0, import_class_validator8.MaxLength)(40),
1569
+ _ts_metadata11("design:type", String)
1570
+ ], SaveRegistrationConfigSelectionDto.prototype, "modelId", void 0);
1571
+ _ts_decorate12([
1572
+ (0, import_class_validator8.IsIn)([
1573
+ "MONTHLY",
1574
+ "YEARLY"
1575
+ ]),
1576
+ _ts_metadata11("design:type", String)
1577
+ ], SaveRegistrationConfigSelectionDto.prototype, "billingCycle", void 0);
1578
+ _ts_decorate12([
1579
+ (0, import_class_validator8.IsOptional)(),
1580
+ (0, import_class_validator8.IsString)(),
1581
+ (0, import_class_validator8.MaxLength)(60),
1582
+ _ts_metadata11("design:type", Object)
1583
+ ], SaveRegistrationConfigSelectionDto.prototype, "appliedPromoCode", void 0);
1584
+ _ts_decorate12([
1585
+ (0, import_class_validator8.IsOptional)(),
1586
+ (0, import_class_validator8.ValidateIf)((_o, value) => value !== null),
1587
+ (0, import_class_validator8.IsUUID)(),
1588
+ _ts_metadata11("design:type", Object)
1589
+ ], SaveRegistrationConfigSelectionDto.prototype, "businessTypeVersionId", void 0);
1590
+ _ts_decorate12([
1591
+ (0, import_class_validator8.IsOptional)(),
1592
+ (0, import_class_validator8.ValidateIf)((_o, value) => value !== null),
1593
+ (0, import_class_validator8.IsUUID)(),
1594
+ _ts_metadata11("design:type", Object)
1595
+ ], SaveRegistrationConfigSelectionDto.prototype, "offerId", void 0);
1596
+ var SaveRegistrationConfigDto = class {
1597
+ static {
1598
+ __name(this, "SaveRegistrationConfigDto");
1599
+ }
1600
+ pendingRegistrationId;
1601
+ selection;
1602
+ };
1603
+ _ts_decorate12([
1604
+ (0, import_class_validator8.IsString)(),
1605
+ (0, import_class_validator8.MinLength)(1),
1606
+ (0, import_class_validator8.MaxLength)(80),
1607
+ _ts_metadata11("design:type", String)
1608
+ ], SaveRegistrationConfigDto.prototype, "pendingRegistrationId", void 0);
1609
+ _ts_decorate12([
1610
+ (0, import_class_validator8.ValidateNested)(),
1611
+ (0, import_class_transformer.Type)(() => SaveRegistrationConfigSelectionDto),
1612
+ _ts_metadata11("design:type", typeof SaveRegistrationConfigSelectionDto === "undefined" ? Object : SaveRegistrationConfigSelectionDto)
1613
+ ], SaveRegistrationConfigDto.prototype, "selection", void 0);
1614
+
1615
+ // src/registration/dto/preview-promo.dto.ts
1616
+ var import_class_validator9 = require("class-validator");
1617
+ function _ts_decorate13(decorators, target, key, desc) {
1618
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1619
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1620
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1621
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1622
+ }
1623
+ __name(_ts_decorate13, "_ts_decorate");
1624
+ function _ts_metadata12(k, v) {
1625
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1626
+ }
1627
+ __name(_ts_metadata12, "_ts_metadata");
1628
+ var PreviewRegistrationPromoDto = class {
1629
+ static {
1630
+ __name(this, "PreviewRegistrationPromoDto");
1631
+ }
1632
+ pendingRegistrationId;
1633
+ code;
1634
+ };
1635
+ _ts_decorate13([
1636
+ (0, import_class_validator9.IsString)(),
1637
+ (0, import_class_validator9.MinLength)(1),
1638
+ (0, import_class_validator9.MaxLength)(80),
1639
+ _ts_metadata12("design:type", String)
1640
+ ], PreviewRegistrationPromoDto.prototype, "pendingRegistrationId", void 0);
1641
+ _ts_decorate13([
1642
+ (0, import_class_validator9.IsOptional)(),
1643
+ (0, import_class_validator9.IsString)(),
1644
+ (0, import_class_validator9.MaxLength)(60),
1645
+ _ts_metadata12("design:type", String)
1646
+ ], PreviewRegistrationPromoDto.prototype, "code", void 0);
1647
+ // Annotate the CommonJS export names for ESM import in node:
1648
+ 0 && (module.exports = {
1649
+ ACTIVATION_ORCHESTRATOR_TOKEN,
1650
+ BaseIpRateLimitGuard,
1651
+ ContinueRegistrationDto,
1652
+ CronExpression,
1653
+ PASSWORD_HASHER_TOKEN,
1654
+ PAYMENT_EVENT_LOG_TOKEN,
1655
+ PAYMENT_PROVIDER_TOKEN,
1656
+ PENDING_REGISTRATION_REPOSITORY_TOKEN,
1657
+ PLAN_CATALOG_LOOKUP_TOKEN,
1658
+ PaymentWebhookDto,
1659
+ PendingRegistrationService,
1660
+ PreviewRegistrationPromoDto,
1661
+ REGISTRATION_AUDIT_LOGGER_TOKEN,
1662
+ REGISTRATION_BUSINESS_TYPE_LOOKUP_TOKEN,
1663
+ REGISTRATION_CONFIGURATOR_LOOKUP_TOKEN,
1664
+ REGISTRATION_CONFIG_TOKEN,
1665
+ REGISTRATION_OTP_DELIVERY_TOKEN,
1666
+ REGISTRATION_PROMO_PREVIEW_TOKEN,
1667
+ REGISTRATION_RESUME_BASE_URL_TOKEN,
1668
+ REGISTRATION_RESUME_DELIVERY_TOKEN,
1669
+ REGISTRATION_RESUME_TOKEN_SIGNER_TOKEN,
1670
+ RegisterStartDto,
1671
+ RegistrationCleanupCron,
1672
+ RegistrationModule,
1673
+ ResendRegistrationOtpDto,
1674
+ SLUG_AVAILABILITY_CHECK_TOKEN,
1675
+ SaveRegistrationConfigDto,
1676
+ SaveRegistrationConfigSelectionDto,
1677
+ SelectRegistrationPlanDto,
1678
+ StartRegistrationCheckoutDto,
1679
+ USER_ACCOUNT_LOOKUP_TOKEN,
1680
+ VerifyRegistrationOtpDto,
1681
+ computeBreakdown,
1682
+ generateOtpCode,
1683
+ hashOtpCode,
1684
+ resolveModel,
1685
+ slugify,
1686
+ verifyOtpCode
1687
+ });