@forklaunch/implementation-billing-stripe 1.2.12 → 1.3.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 (60) hide show
  1. package/lib/domain/schemas/billingPortal.schema.d.ts +2 -2
  2. package/lib/domain/schemas/index.js +27 -14
  3. package/lib/domain/schemas/index.mjs +33 -14
  4. package/lib/domain/schemas/plan.schema.d.ts +2 -2
  5. package/lib/domain/schemas/typebox/billingPortal.schema.d.ts +2 -2
  6. package/lib/domain/schemas/typebox/checkoutSession.schema.d.ts +3 -3
  7. package/lib/domain/schemas/typebox/checkoutSession.schema.d.ts.map +1 -1
  8. package/lib/domain/schemas/typebox/paymentLink.schema.d.ts +2 -1
  9. package/lib/domain/schemas/typebox/paymentLink.schema.d.ts.map +1 -1
  10. package/lib/domain/schemas/typebox/plan.schema.d.ts +2 -2
  11. package/lib/domain/schemas/typebox/subscription.schema.d.ts +1 -1
  12. package/lib/domain/schemas/zod/billingPortal.schema.d.ts +2 -2
  13. package/lib/domain/schemas/zod/checkoutSession.schema.d.ts +3 -3
  14. package/lib/domain/schemas/zod/checkoutSession.schema.d.ts.map +1 -1
  15. package/lib/domain/schemas/zod/paymentLink.schema.d.ts.map +1 -1
  16. package/lib/domain/schemas/zod/plan.schema.d.ts +2 -2
  17. package/lib/domain/schemas/zod/subscription.schema.d.ts +1 -1
  18. package/lib/domain/schemas/zod/subscription.schema.d.ts.map +1 -1
  19. package/lib/domain/types/billingPortal.mapper.types.d.ts +8 -0
  20. package/lib/domain/types/billingPortal.mapper.types.d.ts.map +1 -1
  21. package/lib/domain/types/checkoutSession.mapper.types.d.ts +6 -0
  22. package/lib/domain/types/checkoutSession.mapper.types.d.ts.map +1 -1
  23. package/lib/domain/types/paymentLink.mapper.types.d.ts +20 -0
  24. package/lib/domain/types/paymentLink.mapper.types.d.ts.map +1 -1
  25. package/lib/domain/types/plan.mapper.types.d.ts +10 -0
  26. package/lib/domain/types/plan.mapper.types.d.ts.map +1 -1
  27. package/lib/domain/types/stripe.dto.types.d.ts +6 -8
  28. package/lib/domain/types/stripe.dto.types.d.ts.map +1 -1
  29. package/lib/domain/types/stripe.entity.types.d.ts.map +1 -1
  30. package/lib/domain/types/subscription.mapper.types.d.ts +15 -0
  31. package/lib/domain/types/subscription.mapper.types.d.ts.map +1 -1
  32. package/lib/eject/domain/schemas/billingPortal.schema.ts +1 -1
  33. package/lib/eject/domain/schemas/checkoutSession.schema.ts +5 -3
  34. package/lib/eject/domain/schemas/paymentLink.schema.ts +12 -1
  35. package/lib/eject/domain/schemas/plan.schema.ts +1 -1
  36. package/lib/eject/domain/schemas/subscription.schema.ts +2 -1
  37. package/lib/eject/domain/types/billingPortal.mapper.types.ts +17 -0
  38. package/lib/eject/domain/types/checkoutSession.mapper.types.ts +9 -0
  39. package/lib/eject/domain/types/paymentLink.mapper.types.ts +36 -0
  40. package/lib/eject/domain/types/plan.mapper.types.ts +16 -0
  41. package/lib/eject/domain/types/stripe.dto.types.ts +6 -8
  42. package/lib/eject/domain/types/stripe.entity.types.ts +0 -6
  43. package/lib/eject/domain/types/subscription.mapper.types.ts +21 -0
  44. package/lib/eject/services/billingPortal.service.ts +24 -0
  45. package/lib/eject/services/checkoutSession.service.ts +54 -9
  46. package/lib/eject/services/paymentLink.service.ts +149 -25
  47. package/lib/eject/services/plan.service.ts +34 -4
  48. package/lib/eject/services/subscription.service.ts +42 -1
  49. package/lib/eject/services/webhook.service.ts +10 -1
  50. package/lib/services/billingPortal.service.d.ts.map +1 -1
  51. package/lib/services/checkoutSession.service.d.ts +8 -0
  52. package/lib/services/checkoutSession.service.d.ts.map +1 -1
  53. package/lib/services/index.js +238 -25
  54. package/lib/services/index.mjs +238 -25
  55. package/lib/services/paymentLink.service.d.ts +12 -0
  56. package/lib/services/paymentLink.service.d.ts.map +1 -1
  57. package/lib/services/plan.service.d.ts.map +1 -1
  58. package/lib/services/subscription.service.d.ts.map +1 -1
  59. package/lib/services/webhook.service.d.ts.map +1 -1
  60. package/package.json +8 -8
@@ -60,10 +60,12 @@ var StripeBillingPortalService = class {
60
60
  billingPortalSessionExpiryDurationMs = 5 * 60 * 1e3;
61
61
  mappers;
62
62
  async createBillingPortalSession(billingPortalDto, ...args) {
63
+ await this.mappers.CreateBillingPortalMapper.validateCreate?.(billingPortalDto);
63
64
  const session = await this.stripeClient.billingPortal.sessions.create({
64
65
  ...billingPortalDto.stripeFields,
65
66
  customer: billingPortalDto.customerId
66
67
  });
68
+ await this.mappers.CreateBillingPortalMapper.validateProviderCreate?.(session);
67
69
  return await this.baseBillingPortalService.createBillingPortalSession(
68
70
  {
69
71
  ...billingPortalDto,
@@ -88,10 +90,20 @@ var StripeBillingPortalService = class {
88
90
  const existingSession = await this.baseBillingPortalService.getBillingPortalSession({
89
91
  id: billingPortalDto.id
90
92
  });
93
+ if (!this.options?.enableDatabaseBackup && this.mappers.UpdateBillingPortalMapper.toCachedDto && billingPortalDto.customerId !== void 0 && billingPortalDto.customerId !== existingSession.customerId) {
94
+ throw new Error("Cached session identity cannot change");
95
+ }
96
+ if (!this.options?.enableDatabaseBackup && this.mappers.UpdateBillingPortalMapper.toCachedDto && this.mappers.UpdateBillingPortalMapper.validateCachedUpdate) {
97
+ await this.mappers.UpdateBillingPortalMapper.validateCachedUpdate(
98
+ billingPortalDto,
99
+ existingSession
100
+ );
101
+ }
91
102
  const session = await this.stripeClient.billingPortal.sessions.create({
92
103
  ...billingPortalDto.stripeFields,
93
104
  customer: billingPortalDto.customerId || existingSession.customerId
94
105
  });
106
+ await this.mappers.CreateBillingPortalMapper.validateProviderCreate?.(session);
95
107
  return await this.baseBillingPortalService.updateBillingPortalSession(
96
108
  {
97
109
  ...billingPortalDto,
@@ -138,6 +150,7 @@ var StripeCheckoutSessionService = class {
138
150
  schemaValidator;
139
151
  mappers;
140
152
  async createCheckoutSession(checkoutSessionDto, ...args) {
153
+ await this.mappers.CreateCheckoutSessionMapper.validateCreate?.(checkoutSessionDto);
141
154
  const session = await this.stripeClient.checkout.sessions.create({
142
155
  ...checkoutSessionDto.stripeFields,
143
156
  mode: "subscription",
@@ -146,6 +159,7 @@ var StripeCheckoutSessionService = class {
146
159
  success_url: checkoutSessionDto.successRedirectUri,
147
160
  cancel_url: checkoutSessionDto.cancelRedirectUri
148
161
  });
162
+ await this.mappers.CreateCheckoutSessionMapper.validateProviderCreate?.(session);
149
163
  return await this.baseCheckoutSessionService.createCheckoutSession(
150
164
  {
151
165
  ...checkoutSessionDto,
@@ -160,6 +174,17 @@ var StripeCheckoutSessionService = class {
160
174
  );
161
175
  }
162
176
  async getCheckoutSession(idDto) {
177
+ const readCachedDto = this.mappers.CheckoutSessionMapper.readCachedDto;
178
+ if (readCachedDto) {
179
+ const existing = await this.baseCheckoutSessionService.getCheckoutSession(idDto);
180
+ if (existing.id !== idDto.id) {
181
+ throw new Error("Cached checkout identity does not match");
182
+ }
183
+ return readCachedDto(
184
+ existing,
185
+ (providerId) => this.stripeClient.checkout.sessions.retrieve(providerId)
186
+ );
187
+ }
163
188
  const session = await this.stripeClient.checkout.sessions.retrieve(
164
189
  idDto.id
165
190
  );
@@ -167,33 +192,73 @@ var StripeCheckoutSessionService = class {
167
192
  databaseCheckoutSession.stripeFields = session;
168
193
  return databaseCheckoutSession;
169
194
  }
195
+ /**
196
+ * The Stripe id (`cs_...`) stored with a checkout session when it was
197
+ * created. Public ids are local; Stripe only knows its own id. The stored
198
+ * provider object's id wins; a record without one is still usable when its
199
+ * own id is a Stripe id. Anything else fails here rather than sending Stripe
200
+ * a local id.
201
+ */
202
+ async stripeCheckoutSessionId(id) {
203
+ const checkoutSession = await this.baseCheckoutSessionService.getCheckoutSession({ id });
204
+ const stored = checkoutSession?.stripeFields?.id;
205
+ const stripeId = stored === void 0 ? checkoutSession?.id : stored;
206
+ if (typeof stripeId !== "string" || !/^cs_[A-Za-z0-9_]+$/.test(stripeId)) {
207
+ throw new Error(
208
+ `Checkout session ${id} has no stored Stripe checkout session id`
209
+ );
210
+ }
211
+ return stripeId;
212
+ }
170
213
  async expireCheckoutSession({ id }) {
171
- await this.stripeClient.checkout.sessions.expire(id);
214
+ await this.stripeClient.checkout.sessions.expire(
215
+ await this.stripeCheckoutSessionId(id)
216
+ );
172
217
  await this.baseCheckoutSessionService.expireCheckoutSession({ id });
173
218
  }
174
219
  async handleCheckoutSuccess({ id }) {
175
- await this.stripeClient.checkout.sessions.update(id, {
176
- metadata: {
177
- status: "SUCCESS"
220
+ await this.stripeClient.checkout.sessions.update(
221
+ await this.stripeCheckoutSessionId(id),
222
+ {
223
+ metadata: {
224
+ status: "SUCCESS"
225
+ }
178
226
  }
179
- });
227
+ );
180
228
  await this.baseCheckoutSessionService.handleCheckoutSuccess({ id });
181
229
  }
182
230
  async handleCheckoutFailure({ id }) {
183
- await this.stripeClient.checkout.sessions.update(id, {
184
- metadata: {
185
- status: "FAILED"
231
+ await this.stripeClient.checkout.sessions.update(
232
+ await this.stripeCheckoutSessionId(id),
233
+ {
234
+ metadata: {
235
+ status: "FAILED"
236
+ }
186
237
  }
187
- });
238
+ );
188
239
  await this.baseCheckoutSessionService.handleCheckoutFailure({ id });
189
240
  }
190
241
  };
191
242
 
192
243
  // services/paymentLink.service.ts
193
244
  var import_services3 = require("@forklaunch/implementation-billing-base/services");
245
+ function validatePaymentLinkCreateFields(value) {
246
+ if (!value || typeof value !== "object" || Array.isArray(value) || !Array.isArray(value.line_items) || !value.line_items.every(
247
+ (item) => item !== null && typeof item === "object" && !Array.isArray(item)
248
+ )) {
249
+ throw new Error(
250
+ "Payment link provider options require a line_items array of objects"
251
+ );
252
+ }
253
+ }
194
254
  var StripePaymentLinkService = class {
195
255
  constructor(stripeClient, em, cache, openTelemetryCollector, schemaValidator, mappers, options) {
196
256
  this.options = options;
257
+ const limit = options?.maxConfiguredListRecords === void 0 ? 100 : options.maxConfiguredListRecords;
258
+ if (!Number.isSafeInteger(limit) || limit < 1 || limit > 1e3) {
259
+ throw new Error("Invalid configured payment link list limit");
260
+ }
261
+ this.maxConfiguredListRecords = limit;
197
262
  this.stripeClient = stripeClient;
198
263
  this.em = em;
199
264
  this.cache = cache;
@@ -211,6 +276,7 @@ var StripePaymentLinkService = class {
211
276
  }
212
277
  options;
213
278
  basePaymentLinkService;
279
+ maxConfiguredListRecords;
214
280
  stripeClient;
215
281
  em;
216
282
  cache;
@@ -218,11 +284,16 @@ var StripePaymentLinkService = class {
218
284
  schemaValidator;
219
285
  mappers;
220
286
  async createPaymentLink(paymentLinkDto, ...args) {
287
+ await this.mappers.CreatePaymentLinkMapper.validateCreate?.(paymentLinkDto);
288
+ validatePaymentLinkCreateFields(paymentLinkDto.stripeFields);
221
289
  const session = await this.stripeClient.paymentLinks.create({
222
290
  ...paymentLinkDto.stripeFields,
223
291
  payment_method_types: paymentLinkDto.paymentMethods,
224
292
  currency: paymentLinkDto.currency
225
293
  });
294
+ await this.mappers.CreatePaymentLinkMapper.validateProviderCreate?.(
295
+ session
296
+ );
226
297
  const paymentLink = await this.basePaymentLinkService.createPaymentLink(
227
298
  {
228
299
  ...paymentLinkDto,
@@ -239,20 +310,55 @@ var StripePaymentLinkService = class {
239
310
  paymentLink.stripeFields = session;
240
311
  return paymentLink;
241
312
  }
313
+ /**
314
+ * The Stripe id (`plink_...`) stored with a payment link when it was created.
315
+ * Public ids are local; Stripe only knows its own id. The stored provider
316
+ * object's id wins; a record without one is still usable when its own id is a
317
+ * Stripe id. Anything else fails here rather than sending Stripe a local id.
318
+ */
319
+ stripePaymentLinkId(paymentLink, id) {
320
+ const stored = paymentLink?.stripeFields?.id;
321
+ const stripeId = stored === void 0 ? paymentLink?.id : stored;
322
+ if (typeof stripeId !== "string" || !/^plink_[A-Za-z0-9_]+$/.test(stripeId)) {
323
+ throw new Error(
324
+ `Payment link ${id} has no stored Stripe payment link id`
325
+ );
326
+ }
327
+ return stripeId;
328
+ }
242
329
  async updatePaymentLink(paymentLinkDto, ...args) {
243
- const session = await this.stripeClient.paymentLinks.update(
244
- paymentLinkDto.id,
245
- {
246
- ...paymentLinkDto.stripeFields,
247
- payment_method_types: paymentLinkDto.paymentMethods
248
- }
249
- );
330
+ const existing = await this.basePaymentLinkService.getPaymentLink({
331
+ id: paymentLinkDto.id
332
+ });
333
+ if (this.mappers.UpdatePaymentLinkMapper.validateCachedUpdate) {
334
+ await this.mappers.UpdatePaymentLinkMapper.validateCachedUpdate(
335
+ paymentLinkDto,
336
+ existing
337
+ );
338
+ }
339
+ const stripeId = this.stripePaymentLinkId(existing, paymentLinkDto.id);
340
+ const session = await this.stripeClient.paymentLinks.update(stripeId, {
341
+ ...paymentLinkDto.stripeFields,
342
+ payment_method_types: paymentLinkDto.paymentMethods
343
+ });
344
+ if (session.id !== stripeId) {
345
+ throw new Error("Invalid Stripe billing response");
346
+ }
347
+ const toLocalUpdate = this.mappers.UpdatePaymentLinkMapper.toLocalUpdate;
348
+ if (toLocalUpdate) {
349
+ const patch = await toLocalUpdate(paymentLinkDto, session);
350
+ return this.basePaymentLinkService.updatePaymentLink(
351
+ patch,
352
+ this.em,
353
+ session,
354
+ ...args
355
+ );
356
+ }
250
357
  const paymentLink = await this.basePaymentLinkService.updatePaymentLink(
251
358
  await this.mappers.PaymentLinkMapper.toDto(
252
359
  await this.mappers.UpdatePaymentLinkMapper.toEntity(
253
360
  {
254
361
  ...paymentLinkDto,
255
- id: session.id,
256
362
  amount: session.line_items?.data.reduce(
257
363
  (total, item) => total + item.amount_total,
258
364
  0
@@ -268,13 +374,28 @@ var StripePaymentLinkService = class {
268
374
  return paymentLink;
269
375
  }
270
376
  async getPaymentLink({ id }) {
377
+ const readCachedDto = this.mappers.PaymentLinkMapper.readCachedDto;
378
+ if (readCachedDto) {
379
+ const existing = await this.basePaymentLinkService.getPaymentLink({ id });
380
+ if (existing.id !== id) {
381
+ throw new Error("Cached payment link identity does not match");
382
+ }
383
+ return readCachedDto(
384
+ existing,
385
+ (providerId) => this.stripeClient.paymentLinks.retrieve(providerId)
386
+ );
387
+ }
271
388
  const stripePaymentLink = await this.stripeClient.paymentLinks.retrieve(id);
272
389
  const databasePaymentLink = await this.basePaymentLinkService.getPaymentLink({ id });
273
390
  databasePaymentLink.stripeFields = stripePaymentLink;
274
391
  return databasePaymentLink;
275
392
  }
276
393
  async expirePaymentLink({ id }) {
277
- await this.stripeClient.paymentLinks.update(id, {
394
+ const stripeId = this.stripePaymentLinkId(
395
+ await this.basePaymentLinkService.getPaymentLink({ id }),
396
+ id
397
+ );
398
+ await this.stripeClient.paymentLinks.update(stripeId, {
278
399
  metadata: {
279
400
  status: "EXPIRED"
280
401
  }
@@ -282,7 +403,11 @@ var StripePaymentLinkService = class {
282
403
  await this.basePaymentLinkService.expirePaymentLink({ id });
283
404
  }
284
405
  async handlePaymentSuccess({ id }) {
285
- await this.stripeClient.paymentLinks.update(id, {
406
+ const stripeId = this.stripePaymentLinkId(
407
+ await this.basePaymentLinkService.getPaymentLink({ id }),
408
+ id
409
+ );
410
+ await this.stripeClient.paymentLinks.update(stripeId, {
286
411
  metadata: {
287
412
  status: "COMPLETED"
288
413
  }
@@ -290,7 +415,11 @@ var StripePaymentLinkService = class {
290
415
  await this.basePaymentLinkService.handlePaymentSuccess({ id });
291
416
  }
292
417
  async handlePaymentFailure({ id }) {
293
- await this.stripeClient.paymentLinks.update(id, {
418
+ const stripeId = this.stripePaymentLinkId(
419
+ await this.basePaymentLinkService.getPaymentLink({ id }),
420
+ id
421
+ );
422
+ await this.stripeClient.paymentLinks.update(stripeId, {
294
423
  metadata: {
295
424
  status: "FAILED"
296
425
  }
@@ -299,6 +428,26 @@ var StripePaymentLinkService = class {
299
428
  }
300
429
  async listPaymentLinks(idsDto) {
301
430
  this.openTelemetryCollector.log("info", idsDto ?? "idsDto is undefined");
431
+ const { readCachedDto, validateCachedRead } = this.mappers.PaymentLinkMapper;
432
+ if (readCachedDto && validateCachedRead) {
433
+ const cached = await this.basePaymentLinkService.listPaymentLinks(
434
+ idsDto,
435
+ this.maxConfiguredListRecords
436
+ );
437
+ for (const value of cached) validateCachedRead(value);
438
+ const active = [];
439
+ for (const value of cached) {
440
+ const fresh = await readCachedDto(
441
+ value,
442
+ (providerId) => this.stripeClient.paymentLinks.retrieve(providerId)
443
+ );
444
+ if (typeof fresh.stripeFields.active !== "boolean") {
445
+ throw new Error("Invalid payment link active state");
446
+ }
447
+ if (fresh.stripeFields.active) active.push(fresh);
448
+ }
449
+ return active;
450
+ }
302
451
  const stripePaymentLinks = await this.stripeClient.paymentLinks.list({
303
452
  active: true
304
453
  });
@@ -342,6 +491,7 @@ var StripePlanService = class {
342
491
  schemaValidator;
343
492
  mappers;
344
493
  async createPlan(planDto, em) {
494
+ await this.mappers.CreatePlanMapper.validateCreate?.(planDto);
345
495
  let stripePlan = await this.stripeClient.plans.create({
346
496
  ...planDto.stripeFields,
347
497
  amount: planDto.price,
@@ -349,9 +499,12 @@ var StripePlanService = class {
349
499
  product: planDto.name,
350
500
  currency: planDto.currency
351
501
  });
352
- stripePlan = await this.stripeClient.plans.retrieve(stripePlan.id, {
502
+ await this.mappers.CreatePlanMapper.validateProviderCreate?.(stripePlan);
503
+ const createdId = stripePlan.id;
504
+ stripePlan = await this.stripeClient.plans.retrieve(createdId, {
353
505
  expand: ["product"]
354
506
  });
507
+ await this.mappers.PlanMapper.validateProviderRead?.(createdId, stripePlan);
355
508
  const plan = await this.basePlanService.createPlan(
356
509
  {
357
510
  ...planDto,
@@ -371,10 +524,15 @@ var StripePlanService = class {
371
524
  const plan = await this.stripeClient.plans.retrieve(planEntity.externalId, {
372
525
  expand: ["product"]
373
526
  });
527
+ await this.mappers.PlanMapper.validateProviderRead?.(
528
+ planEntity.externalId,
529
+ plan
530
+ );
374
531
  planEntity.stripeFields = plan.product;
375
532
  return planEntity;
376
533
  }
377
534
  async updatePlan(planDto, em) {
535
+ await this.mappers.UpdatePlanMapper.validateUpdate?.(planDto);
378
536
  const planEntity = await this.basePlanService.getPlan(
379
537
  {
380
538
  id: planDto.id
@@ -384,6 +542,14 @@ var StripePlanService = class {
384
542
  const existingPlan = await this.stripeClient.plans.retrieve(
385
543
  planEntity.externalId
386
544
  );
545
+ await this.mappers.PlanMapper.validateProviderRead?.(
546
+ planEntity.externalId,
547
+ existingPlan
548
+ );
549
+ await this.mappers.UpdatePlanMapper.validateProviderUpdate?.(
550
+ planDto,
551
+ existingPlan
552
+ );
387
553
  const existingProduct = existingPlan.product;
388
554
  if (!existingProduct) {
389
555
  throw new Error("Plan product not found");
@@ -398,14 +564,16 @@ var StripePlanService = class {
398
564
  amount: planDto.price ?? existingPlan.amount ?? void 0,
399
565
  product: productId
400
566
  });
401
- updatedPlan = await this.stripeClient.plans.retrieve(updatedPlan.id, {
567
+ await this.mappers.CreatePlanMapper.validateProviderCreate?.(updatedPlan);
568
+ const createdId = updatedPlan.id;
569
+ updatedPlan = await this.stripeClient.plans.retrieve(createdId, {
402
570
  expand: ["product"]
403
571
  });
572
+ await this.mappers.PlanMapper.validateProviderRead?.(createdId, updatedPlan);
404
573
  const updatedPlanEntity = await this.basePlanService.updatePlan(
405
574
  {
406
575
  ...planDto,
407
576
  externalId: updatedPlan.id,
408
- name: planDto.name,
409
577
  billingProvider: "stripe"
410
578
  },
411
579
  em,
@@ -436,6 +604,10 @@ var StripePlanService = class {
436
604
  expand: ["product"]
437
605
  }
438
606
  );
607
+ await this.mappers.PlanMapper.validateProviderRead?.(
608
+ plan.externalId,
609
+ fetchedPlan
610
+ );
439
611
  return fetchedPlan.product;
440
612
  } catch {
441
613
  return null;
@@ -475,6 +647,7 @@ var StripeSubscriptionService = class {
475
647
  schemaValidator;
476
648
  mappers;
477
649
  async createSubscription(subscriptionDto, em) {
650
+ await this.mappers.CreateSubscriptionMapper.validateCreate?.(subscriptionDto);
478
651
  const subscription = await this.stripeClient.subscriptions.create({
479
652
  ...subscriptionDto.stripeFields,
480
653
  customer: subscriptionDto.partyId,
@@ -484,6 +657,7 @@ var StripeSubscriptionService = class {
484
657
  }
485
658
  ]
486
659
  });
660
+ await this.mappers.CreateSubscriptionMapper.validateProviderCreate?.(subscription);
487
661
  return await this.baseSubscriptionService.createSubscription(
488
662
  {
489
663
  ...subscriptionDto,
@@ -502,6 +676,10 @@ var StripeSubscriptionService = class {
502
676
  const stripeSubscription = await this.stripeClient.subscriptions.retrieve(
503
677
  subscriptionEntity.externalId
504
678
  );
679
+ await this.mappers.SubscriptionMapper.validateProviderRead?.(
680
+ subscriptionEntity.externalId,
681
+ stripeSubscription
682
+ );
505
683
  subscriptionEntity.stripeFields = stripeSubscription;
506
684
  return subscriptionEntity;
507
685
  }
@@ -513,6 +691,10 @@ var StripeSubscriptionService = class {
513
691
  const stripeSubscription = await this.stripeClient.subscriptions.retrieve(
514
692
  subscriptionEntity.externalId
515
693
  );
694
+ await this.mappers.SubscriptionMapper.validateProviderRead?.(
695
+ subscriptionEntity.externalId,
696
+ stripeSubscription
697
+ );
516
698
  subscriptionEntity.stripeFields = stripeSubscription;
517
699
  return subscriptionEntity;
518
700
  }
@@ -524,6 +706,10 @@ var StripeSubscriptionService = class {
524
706
  const stripeSubscription = await this.stripeClient.subscriptions.retrieve(
525
707
  subscriptionEntity.externalId
526
708
  );
709
+ await this.mappers.SubscriptionMapper.validateProviderRead?.(
710
+ subscriptionEntity.externalId,
711
+ stripeSubscription
712
+ );
527
713
  subscriptionEntity.stripeFields = stripeSubscription;
528
714
  return subscriptionEntity;
529
715
  }
@@ -537,9 +723,21 @@ var StripeSubscriptionService = class {
537
723
  if (!subscriptionEntity.externalId) {
538
724
  throw new Error("Subscription not found");
539
725
  }
726
+ await this.mappers.UpdateSubscriptionMapper.validateUpdate?.(
727
+ subscriptionDto,
728
+ subscriptionEntity
729
+ );
540
730
  const existingStripeSubscription = await this.stripeClient.subscriptions.retrieve(
541
731
  subscriptionEntity.externalId
542
732
  );
733
+ await this.mappers.SubscriptionMapper.validateProviderRead?.(
734
+ subscriptionEntity.externalId,
735
+ existingStripeSubscription
736
+ );
737
+ await this.mappers.UpdateSubscriptionMapper.validateProviderUpdate?.(
738
+ subscriptionDto,
739
+ existingStripeSubscription
740
+ );
543
741
  const updatedSubscription = await this.stripeClient.subscriptions.update(
544
742
  subscriptionEntity.externalId,
545
743
  {
@@ -550,6 +748,10 @@ var StripeSubscriptionService = class {
550
748
  }))
551
749
  }
552
750
  );
751
+ await this.mappers.SubscriptionMapper.validateProviderRead?.(
752
+ subscriptionEntity.externalId,
753
+ updatedSubscription
754
+ );
553
755
  const updatedSubscriptionEntity = await this.baseSubscriptionService.updateSubscription(
554
756
  {
555
757
  ...subscriptionDto,
@@ -584,9 +786,14 @@ var StripeSubscriptionService = class {
584
786
  const stripeSubscriptions = await Promise.all(
585
787
  subscriptions.map(async (subscription) => {
586
788
  try {
587
- return await this.stripeClient.subscriptions.retrieve(
789
+ const provider = await this.stripeClient.subscriptions.retrieve(
588
790
  subscription.externalId
589
791
  );
792
+ await this.mappers.SubscriptionMapper.validateProviderRead?.(
793
+ subscription.externalId,
794
+ provider
795
+ );
796
+ return provider;
590
797
  } catch {
591
798
  return null;
592
799
  }
@@ -628,6 +835,12 @@ var BillingProviderEnum = {
628
835
 
629
836
  // services/webhook.service.ts
630
837
  var isActiveStatus = (status) => status === "active" || status === "trialing";
838
+ var portalExpiry = (created) => {
839
+ if (!Number.isSafeInteger(created) || created < 0 || created > 253402300499) {
840
+ throw new Error("Billing webhook payload is invalid");
841
+ }
842
+ return new Date((created + 300) * 1e3);
843
+ };
631
844
  var StripeWebhookService = class {
632
845
  partyEnum;
633
846
  webhookEventEntity;
@@ -723,7 +936,7 @@ var StripeWebhookService = class {
723
936
  {
724
937
  id: event.data.object.id,
725
938
  customerId: event.data.object.customer,
726
- expiresAt: new Date(event.data.object.created + 5 * 60 * 1e3),
939
+ expiresAt: portalExpiry(event.data.object.created),
727
940
  uri: event.data.object.url
728
941
  }
729
942
  );