@forklaunch/implementation-billing-stripe 0.4.3 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -57,12 +57,13 @@ var StripeBillingPortalService = class {
57
57
  });
58
58
  const session = await this.stripeClient.billingPortal.sessions.create({
59
59
  ...billingPortalDto.stripeFields,
60
- customer: existingSession.customerId
60
+ customer: billingPortalDto.customerId || existingSession.customerId
61
61
  });
62
62
  return await this.baseBillingPortalService.updateBillingPortalSession(
63
63
  {
64
64
  ...billingPortalDto,
65
- id: session.id,
65
+ id: existingSession.id,
66
+ // Use the original database ID, not the new Stripe session ID
66
67
  uri: session.url,
67
68
  expiresAt: new Date(
68
69
  Date.now() + this.billingPortalSessionExpiryDurationMs
@@ -105,6 +106,7 @@ var StripeCheckoutSessionService = class {
105
106
  async createCheckoutSession(checkoutSessionDto, ...args) {
106
107
  const session = await this.stripeClient.checkout.sessions.create({
107
108
  ...checkoutSessionDto.stripeFields,
109
+ mode: "subscription",
108
110
  payment_method_types: checkoutSessionDto.paymentMethods,
109
111
  currency: checkoutSessionDto.currency,
110
112
  success_url: checkoutSessionDto.successRedirectUri,
@@ -190,10 +192,10 @@ var StripePaymentLinkService = class {
190
192
  {
191
193
  ...paymentLinkDto,
192
194
  id: session.id,
193
- amount: session.line_items?.data.reduce(
195
+ amount: paymentLinkDto.amount ?? session.line_items?.data.reduce(
194
196
  (total, item) => total + item.amount_total,
195
197
  0
196
- ) ?? 0
198
+ )
197
199
  },
198
200
  this.em,
199
201
  session,
@@ -259,24 +261,21 @@ var StripePaymentLinkService = class {
259
261
  await this.basePaymentLinkService.handlePaymentFailure({ id });
260
262
  }
261
263
  async listPaymentLinks(idsDto) {
264
+ this.openTelemetryCollector.log("info", idsDto ?? "idsDto is undefined");
262
265
  const stripePaymentLinks = await this.stripeClient.paymentLinks.list({
263
266
  active: true
264
267
  });
265
268
  const databasePaymentLinks = await this.basePaymentLinkService.listPaymentLinks(idsDto);
266
- return await Promise.all(
267
- databasePaymentLinks.map(async (paymentLink) => {
268
- const stripePaymentLink = stripePaymentLinks.data.find(
269
- (sp) => sp.id === paymentLink.id
270
- );
271
- if (!stripePaymentLink) {
272
- throw new Error(
273
- `Stripe payment link not found for id: ${paymentLink.id}`
274
- );
275
- }
276
- paymentLink.stripeFields = stripePaymentLink;
277
- return paymentLink;
278
- })
279
- );
269
+ return databasePaymentLinks.map((paymentLink) => {
270
+ const stripePaymentLink = stripePaymentLinks.data.find(
271
+ (sp) => sp.id === paymentLink.id
272
+ );
273
+ if (!stripePaymentLink) {
274
+ return null;
275
+ }
276
+ paymentLink.stripeFields = stripePaymentLink;
277
+ return paymentLink;
278
+ }).filter((paymentLink) => paymentLink !== null);
280
279
  }
281
280
  };
282
281
 
@@ -307,6 +306,7 @@ var StripePlanService = class {
307
306
  async createPlan(planDto, em) {
308
307
  const stripePlan = await this.stripeClient.plans.create({
309
308
  ...planDto.stripeFields,
309
+ amount: planDto.price,
310
310
  interval: planDto.cadence,
311
311
  product: planDto.name,
312
312
  currency: planDto.currency
@@ -323,68 +323,76 @@ var StripePlanService = class {
323
323
  return plan;
324
324
  }
325
325
  async getPlan(idDto, em) {
326
- const plan = await this.stripeClient.plans.retrieve(idDto.id);
327
- const id = (await em?.findOne(
328
- this.options?.databaseTableName ?? "plan",
329
- { externalId: idDto.id }
330
- ))?.id;
331
- if (!id) {
326
+ const planEntity = await this.basePlanService.getPlan(idDto, em);
327
+ if (!planEntity.externalId) {
332
328
  throw new Error("Plan not found");
333
329
  }
334
- const planEntity = await this.basePlanService.getPlan({ id }, em);
330
+ const plan = await this.stripeClient.plans.retrieve(planEntity.externalId);
335
331
  planEntity.stripeFields = plan;
336
332
  return planEntity;
337
333
  }
338
334
  async updatePlan(planDto, em) {
339
- const existingPlan = await this.stripeClient.plans.retrieve(planDto.id);
340
- const plan = await this.stripeClient.plans.del(planDto.id).then(
341
- () => this.stripeClient.plans.create({
342
- ...planDto.stripeFields,
343
- interval: planDto.cadence ?? existingPlan.interval,
344
- product: planDto.name,
345
- currency: planDto.currency ?? existingPlan.currency
346
- })
347
- );
348
- const planEntity = await this.basePlanService.updatePlan(
349
- await this.mappers.UpdatePlanMapper.toEntity(
350
- {
351
- ...planDto,
352
- externalId: plan.id,
353
- billingProvider: "stripe"
354
- },
355
- em ?? this.em,
356
- plan
357
- ),
335
+ const planEntity = await this.basePlanService.getPlan(
336
+ {
337
+ id: planDto.id
338
+ },
358
339
  em
359
340
  );
360
- planEntity.stripeFields = plan;
361
- return planEntity;
341
+ const existingPlan = await this.stripeClient.plans.retrieve(
342
+ planEntity.externalId
343
+ );
344
+ const existingProduct = existingPlan.product;
345
+ if (!existingProduct) {
346
+ throw new Error("Plan product not found");
347
+ }
348
+ const productId = typeof existingProduct === "string" ? existingProduct : existingProduct.id;
349
+ await this.stripeClient.plans.del(planEntity.externalId);
350
+ const updatedPlan = await this.stripeClient.plans.create({
351
+ ...planDto.stripeFields,
352
+ interval: planDto.cadence ?? existingPlan.interval,
353
+ currency: planDto.currency ?? existingPlan.currency,
354
+ amount: planDto.price ?? existingPlan.amount ?? void 0,
355
+ product: productId
356
+ });
357
+ const updatedPlanEntity = await this.basePlanService.updatePlan(
358
+ {
359
+ ...planDto,
360
+ externalId: updatedPlan.id,
361
+ name: planDto.name,
362
+ billingProvider: "stripe"
363
+ },
364
+ em,
365
+ updatedPlan
366
+ );
367
+ updatedPlanEntity.stripeFields = updatedPlan;
368
+ return updatedPlanEntity;
362
369
  }
363
370
  async deletePlan(idDto, em) {
364
- await this.stripeClient.plans.del(idDto.id);
371
+ const plan = await this.basePlanService.getPlan(idDto, em);
372
+ if (!plan.externalId) {
373
+ throw new Error("Plan not found");
374
+ }
375
+ await this.stripeClient.plans.del(plan.externalId);
365
376
  await this.basePlanService.deletePlan(idDto, em);
366
377
  }
367
378
  async listPlans(idsDto, em) {
368
- const plans = await this.stripeClient.plans.list({
369
- active: true
370
- });
371
- const planIds = (await em?.findAll(
372
- this.options?.databaseTableName ?? "plan",
373
- { where: { externalId: { $in: plans.data.map((plan) => plan.id) } } }
374
- ))?.filter((s) => idsDto?.ids?.includes(s.id))?.map((s) => s.id);
375
- if (!planIds) {
376
- throw new Error("Plans not found");
379
+ const plans = await this.basePlanService.listPlans(idsDto, em);
380
+ if (!plans || plans.length === 0) {
381
+ return [];
377
382
  }
378
- return await Promise.all(
379
- (await this.basePlanService.listPlans({ ids: planIds }, em)).map(
380
- async (plan) => ({
381
- ...plan,
382
- stripeFields: plans.data.find(
383
- (stripePlan) => stripePlan.id === plan.externalId
384
- )
385
- })
386
- )
383
+ const stripePlans = await Promise.all(
384
+ plans.map(async (plan) => {
385
+ try {
386
+ return await this.stripeClient.plans.retrieve(plan.externalId);
387
+ } catch {
388
+ return null;
389
+ }
390
+ })
387
391
  );
392
+ return plans.map((plan, index) => ({
393
+ ...plan,
394
+ stripeFields: stripePlans[index]
395
+ })).filter((plan) => plan.stripeFields !== null);
388
396
  }
389
397
  };
390
398
 
@@ -434,94 +442,127 @@ var StripeSubscriptionService = class {
434
442
  }
435
443
  async getSubscription(idDto, em) {
436
444
  const subscriptionEntity = await this.baseSubscriptionService.getSubscription(idDto, em);
445
+ if (!subscriptionEntity.externalId) {
446
+ throw new Error("Subscription not found");
447
+ }
437
448
  const stripeSubscription = await this.stripeClient.subscriptions.retrieve(
438
- idDto.id
449
+ subscriptionEntity.externalId
439
450
  );
440
451
  subscriptionEntity.stripeFields = stripeSubscription;
441
452
  return subscriptionEntity;
442
453
  }
443
454
  async getUserSubscription(idDto, em) {
444
455
  const subscriptionEntity = await this.baseSubscriptionService.getUserSubscription(idDto, em);
456
+ if (!subscriptionEntity.externalId) {
457
+ throw new Error("Subscription not found");
458
+ }
445
459
  const stripeSubscription = await this.stripeClient.subscriptions.retrieve(
446
- idDto.id
460
+ subscriptionEntity.externalId
447
461
  );
448
462
  subscriptionEntity.stripeFields = stripeSubscription;
449
463
  return subscriptionEntity;
450
464
  }
451
465
  async getOrganizationSubscription(idDto, em) {
452
- const id = (await em?.findOne(
453
- this.options?.databaseTableName ?? "subscription",
454
- { externalId: idDto.id }
455
- ))?.id;
456
- if (!id) {
466
+ const subscriptionEntity = await this.baseSubscriptionService.getOrganizationSubscription(idDto, em);
467
+ if (!subscriptionEntity.externalId) {
457
468
  throw new Error("Subscription not found");
458
469
  }
459
- const subscriptionEntity = await this.baseSubscriptionService.getOrganizationSubscription(
460
- { id },
461
- em
462
- );
463
470
  const stripeSubscription = await this.stripeClient.subscriptions.retrieve(
464
- idDto.id
471
+ subscriptionEntity.externalId
465
472
  );
466
473
  subscriptionEntity.stripeFields = stripeSubscription;
467
474
  return subscriptionEntity;
468
475
  }
469
476
  async updateSubscription(subscriptionDto, em) {
470
- const subscription = await this.stripeClient.subscriptions.update(
471
- subscriptionDto.id,
477
+ const subscriptionEntity = await this.baseSubscriptionService.getSubscription(
478
+ {
479
+ id: subscriptionDto.id
480
+ },
481
+ em
482
+ );
483
+ if (!subscriptionEntity.externalId) {
484
+ throw new Error("Subscription not found");
485
+ }
486
+ const existingStripeSubscription = await this.stripeClient.subscriptions.retrieve(
487
+ subscriptionEntity.externalId
488
+ );
489
+ const updatedSubscription = await this.stripeClient.subscriptions.update(
490
+ subscriptionEntity.externalId,
472
491
  {
473
492
  ...subscriptionDto.stripeFields,
474
- items: [
475
- {
476
- plan: subscriptionDto.productId
477
- }
478
- ]
493
+ items: existingStripeSubscription.items.data.map((item) => ({
494
+ id: item.id,
495
+ plan: subscriptionDto.productId || item.plan?.id
496
+ }))
479
497
  }
480
498
  );
481
- return await this.baseSubscriptionService.updateSubscription(
499
+ const updatedSubscriptionEntity = await this.baseSubscriptionService.updateSubscription(
482
500
  {
483
501
  ...subscriptionDto,
484
- externalId: subscription.id,
485
- billingProvider: "stripe",
486
- providerFields: subscription
502
+ externalId: updatedSubscription.id,
503
+ billingProvider: "stripe"
487
504
  },
488
505
  em ?? this.em,
489
- subscription
506
+ updatedSubscription
490
507
  );
508
+ updatedSubscriptionEntity.stripeFields = updatedSubscription;
509
+ return updatedSubscriptionEntity;
491
510
  }
492
511
  async deleteSubscription(idDto, em) {
493
- await this.stripeClient.subscriptions.cancel(idDto.id);
512
+ const subscription = await this.baseSubscriptionService.getSubscription(
513
+ idDto,
514
+ em
515
+ );
516
+ if (!subscription.externalId) {
517
+ throw new Error("Subscription not found");
518
+ }
519
+ await this.stripeClient.subscriptions.cancel(subscription.externalId);
494
520
  await this.baseSubscriptionService.deleteSubscription(idDto, em);
495
521
  }
496
522
  async listSubscriptions(idsDto, em) {
497
- const subscriptions = (await this.stripeClient.subscriptions.list({
498
- status: "active"
499
- })).data.filter((s) => idsDto.ids?.includes(s.id));
500
- const ids = (await em?.findAll(
501
- this.options?.databaseTableName ?? "subscription",
502
- { where: { externalId: { $in: subscriptions.map((s) => s.id) } } }
503
- ))?.map((s) => s.id);
504
- if (!ids) {
505
- throw new Error("Subscriptions not found");
523
+ const subscriptions = await this.baseSubscriptionService.listSubscriptions(
524
+ idsDto,
525
+ em
526
+ );
527
+ if (!subscriptions || subscriptions.length === 0) {
528
+ return [];
506
529
  }
507
- return await Promise.all(
508
- (await this.baseSubscriptionService.listSubscriptions({ ids }, em)).map(
509
- async (subscription) => {
510
- const stripeSubscription = subscriptions.find(
511
- (s) => s.id === subscription.externalId
530
+ const stripeSubscriptions = await Promise.all(
531
+ subscriptions.map(async (subscription) => {
532
+ try {
533
+ return await this.stripeClient.subscriptions.retrieve(
534
+ subscription.externalId
512
535
  );
513
- subscription.stripeFields = stripeSubscription;
514
- return subscription;
536
+ } catch {
537
+ return null;
515
538
  }
516
- )
539
+ })
517
540
  );
541
+ return subscriptions.map((subscription, index) => ({
542
+ ...subscription,
543
+ stripeFields: stripeSubscriptions[index]
544
+ })).filter((subscription) => subscription.stripeFields !== null);
518
545
  }
519
546
  async cancelSubscription(idDto, em) {
520
- await this.stripeClient.subscriptions.cancel(idDto.id);
547
+ const subscription = await this.baseSubscriptionService.getSubscription(
548
+ idDto,
549
+ em
550
+ );
551
+ if (!subscription.externalId) {
552
+ throw new Error("Subscription not found");
553
+ }
554
+ await this.stripeClient.subscriptions.cancel(subscription.externalId);
521
555
  await this.baseSubscriptionService.cancelSubscription(idDto, em);
522
556
  }
523
557
  async resumeSubscription(idDto, em) {
524
- await this.stripeClient.subscriptions.resume(idDto.id);
558
+ const subscription = await this.baseSubscriptionService.getSubscription(
559
+ idDto,
560
+ em
561
+ );
562
+ if (!subscription.externalId) {
563
+ throw new Error("Subscription not found");
564
+ }
565
+ await this.stripeClient.subscriptions.resume(subscription.externalId);
525
566
  await this.baseSubscriptionService.resumeSubscription(idDto, em);
526
567
  }
527
568
  };
@@ -727,6 +768,7 @@ var StripeWebhookService = class {
727
768
 
728
769
  // services/index.ts
729
770
  export * from "@forklaunch/interfaces-billing/interfaces";
771
+ export * from "@forklaunch/interfaces-billing/types";
730
772
  export {
731
773
  StripeBillingPortalService,
732
774
  StripeCheckoutSessionService,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forklaunch/implementation-billing-stripe",
3
- "version": "0.4.3",
3
+ "version": "0.5.0",
4
4
  "description": "Stripe implementation for forklaunch billing",
5
5
  "homepage": "https://github.com/forklaunch/forklaunch-js#readme",
6
6
  "bugs": {
@@ -42,20 +42,20 @@
42
42
  "lib/**"
43
43
  ],
44
44
  "dependencies": {
45
- "@forklaunch/common": "^0.6.14",
46
- "@forklaunch/core": "^0.15.3",
47
- "@forklaunch/internal": "^0.3.14",
48
- "@forklaunch/validator": "^0.10.14",
49
- "@mikro-orm/core": "^6.5.6",
45
+ "@forklaunch/common": "^0.6.17",
46
+ "@forklaunch/core": "^0.15.6",
47
+ "@forklaunch/internal": "^0.3.17",
48
+ "@forklaunch/validator": "^0.10.17",
49
+ "@mikro-orm/core": "^6.5.7",
50
50
  "@sinclair/typebox": "^0.34.41",
51
51
  "ajv": "^8.17.1",
52
- "stripe": "^18.5.0",
53
- "zod": "^4.1.11",
54
- "@forklaunch/implementation-billing-base": "0.7.3",
55
- "@forklaunch/interfaces-billing": "0.7.3"
52
+ "stripe": "^19.1.0",
53
+ "zod": "^4.1.12",
54
+ "@forklaunch/interfaces-billing": "0.8.0",
55
+ "@forklaunch/implementation-billing-base": "0.8.0"
56
56
  },
57
57
  "devDependencies": {
58
- "@typescript/native-preview": "7.0.0-dev.20250930.1",
58
+ "@typescript/native-preview": "7.0.0-dev.20251007.1",
59
59
  "depcheck": "^1.4.7",
60
60
  "prettier": "^3.6.2",
61
61
  "typedoc": "^0.28.13"