@forklaunch/implementation-billing-stripe 0.5.11 → 0.5.13

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.
@@ -233,7 +233,7 @@ declare const StripePlanServiceSchemas: <SchemaValidator extends _forklaunch_val
233
233
  cadence: _sinclair_typebox.TUnion<[_sinclair_typebox.TLiteral<"week"> | _sinclair_typebox.TLiteral<"month"> | _sinclair_typebox.TLiteral<"year">]>;
234
234
  currency: _sinclair_typebox.TUnion<[_sinclair_typebox.TLiteral<"USD"> | _sinclair_typebox.TLiteral<"EUR"> | _sinclair_typebox.TLiteral<"JPY"> | _sinclair_typebox.TLiteral<"GBP"> | _sinclair_typebox.TLiteral<"AUD"> | _sinclair_typebox.TLiteral<"CAD"> | _sinclair_typebox.TLiteral<"CHF"> | _sinclair_typebox.TLiteral<"CNY"> | _sinclair_typebox.TLiteral<"AED"> | _sinclair_typebox.TLiteral<"ARS"> | _sinclair_typebox.TLiteral<"BAM"> | _sinclair_typebox.TLiteral<"BDT"> | _sinclair_typebox.TLiteral<"BGN"> | _sinclair_typebox.TLiteral<"BOB"> | _sinclair_typebox.TLiteral<"BRL"> | _sinclair_typebox.TLiteral<"CLP"> | _sinclair_typebox.TLiteral<"COP"> | _sinclair_typebox.TLiteral<"CRC"> | _sinclair_typebox.TLiteral<"CZK"> | _sinclair_typebox.TLiteral<"DKK"> | _sinclair_typebox.TLiteral<"DZD"> | _sinclair_typebox.TLiteral<"EGP"> | _sinclair_typebox.TLiteral<"ETB"> | _sinclair_typebox.TLiteral<"GEL"> | _sinclair_typebox.TLiteral<"GTQ"> | _sinclair_typebox.TLiteral<"HKD"> | _sinclair_typebox.TLiteral<"HUF"> | _sinclair_typebox.TLiteral<"IDR"> | _sinclair_typebox.TLiteral<"ILS"> | _sinclair_typebox.TLiteral<"INR"> | _sinclair_typebox.TLiteral<"ISK"> | _sinclair_typebox.TLiteral<"JMD"> | _sinclair_typebox.TLiteral<"KES"> | _sinclair_typebox.TLiteral<"KHR"> | _sinclair_typebox.TLiteral<"KRW"> | _sinclair_typebox.TLiteral<"KZT"> | _sinclair_typebox.TLiteral<"LAK"> | _sinclair_typebox.TLiteral<"LKR"> | _sinclair_typebox.TLiteral<"MAD"> | _sinclair_typebox.TLiteral<"MMK"> | _sinclair_typebox.TLiteral<"MXN"> | _sinclair_typebox.TLiteral<"MYR"> | _sinclair_typebox.TLiteral<"NGN"> | _sinclair_typebox.TLiteral<"NOK"> | _sinclair_typebox.TLiteral<"NPR"> | _sinclair_typebox.TLiteral<"NZD"> | _sinclair_typebox.TLiteral<"PEN"> | _sinclair_typebox.TLiteral<"PHP"> | _sinclair_typebox.TLiteral<"PKR"> | _sinclair_typebox.TLiteral<"PLN"> | _sinclair_typebox.TLiteral<"PYG"> | _sinclair_typebox.TLiteral<"QAR"> | _sinclair_typebox.TLiteral<"RON"> | _sinclair_typebox.TLiteral<"RSD"> | _sinclair_typebox.TLiteral<"RUB"> | _sinclair_typebox.TLiteral<"SAR"> | _sinclair_typebox.TLiteral<"SEK"> | _sinclair_typebox.TLiteral<"SGD"> | _sinclair_typebox.TLiteral<"THB"> | _sinclair_typebox.TLiteral<"TRY"> | _sinclair_typebox.TLiteral<"TWD"> | _sinclair_typebox.TLiteral<"TZS"> | _sinclair_typebox.TLiteral<"UAH"> | _sinclair_typebox.TLiteral<"UGX"> | _sinclair_typebox.TLiteral<"UYU"> | _sinclair_typebox.TLiteral<"VND"> | _sinclair_typebox.TLiteral<"ZAR"> | _sinclair_typebox.TLiteral<"ZMW">]>;
235
235
  features: _sinclair_typebox.TOptional<_sinclair_typebox.TArray<_sinclair_typebox.TString>>;
236
- stripeFields: _sinclair_typebox.TTransform<_sinclair_typebox.TAny, stripe.Stripe.Plan>;
236
+ stripeFields: _sinclair_typebox.TTransform<_sinclair_typebox.TAny, stripe.Stripe.Product>;
237
237
  externalId: _sinclair_typebox.TString;
238
238
  billingProvider: _sinclair_typebox.TOptional<_sinclair_typebox.TUnion<[_sinclair_typebox.TLiteral<"stripe">]>>;
239
239
  active: _sinclair_typebox.TTransform<_sinclair_typebox.TUnion<[_sinclair_typebox.TBoolean, _sinclair_typebox.TString]>, boolean>;
@@ -275,7 +275,7 @@ declare const StripePlanServiceSchemas: <SchemaValidator extends _forklaunch_val
275
275
  cadence: zod_v3.ZodUnion<[zod_v3.ZodLiteral<"week"> | zod_v3.ZodLiteral<"month"> | zod_v3.ZodLiteral<"year">]>;
276
276
  currency: zod_v3.ZodUnion<[zod_v3.ZodLiteral<"USD"> | zod_v3.ZodLiteral<"EUR"> | zod_v3.ZodLiteral<"JPY"> | zod_v3.ZodLiteral<"GBP"> | zod_v3.ZodLiteral<"AUD"> | zod_v3.ZodLiteral<"CAD"> | zod_v3.ZodLiteral<"CHF"> | zod_v3.ZodLiteral<"CNY"> | zod_v3.ZodLiteral<"AED"> | zod_v3.ZodLiteral<"ARS"> | zod_v3.ZodLiteral<"BAM"> | zod_v3.ZodLiteral<"BDT"> | zod_v3.ZodLiteral<"BGN"> | zod_v3.ZodLiteral<"BOB"> | zod_v3.ZodLiteral<"BRL"> | zod_v3.ZodLiteral<"CLP"> | zod_v3.ZodLiteral<"COP"> | zod_v3.ZodLiteral<"CRC"> | zod_v3.ZodLiteral<"CZK"> | zod_v3.ZodLiteral<"DKK"> | zod_v3.ZodLiteral<"DZD"> | zod_v3.ZodLiteral<"EGP"> | zod_v3.ZodLiteral<"ETB"> | zod_v3.ZodLiteral<"GEL"> | zod_v3.ZodLiteral<"GTQ"> | zod_v3.ZodLiteral<"HKD"> | zod_v3.ZodLiteral<"HUF"> | zod_v3.ZodLiteral<"IDR"> | zod_v3.ZodLiteral<"ILS"> | zod_v3.ZodLiteral<"INR"> | zod_v3.ZodLiteral<"ISK"> | zod_v3.ZodLiteral<"JMD"> | zod_v3.ZodLiteral<"KES"> | zod_v3.ZodLiteral<"KHR"> | zod_v3.ZodLiteral<"KRW"> | zod_v3.ZodLiteral<"KZT"> | zod_v3.ZodLiteral<"LAK"> | zod_v3.ZodLiteral<"LKR"> | zod_v3.ZodLiteral<"MAD"> | zod_v3.ZodLiteral<"MMK"> | zod_v3.ZodLiteral<"MXN"> | zod_v3.ZodLiteral<"MYR"> | zod_v3.ZodLiteral<"NGN"> | zod_v3.ZodLiteral<"NOK"> | zod_v3.ZodLiteral<"NPR"> | zod_v3.ZodLiteral<"NZD"> | zod_v3.ZodLiteral<"PEN"> | zod_v3.ZodLiteral<"PHP"> | zod_v3.ZodLiteral<"PKR"> | zod_v3.ZodLiteral<"PLN"> | zod_v3.ZodLiteral<"PYG"> | zod_v3.ZodLiteral<"QAR"> | zod_v3.ZodLiteral<"RON"> | zod_v3.ZodLiteral<"RSD"> | zod_v3.ZodLiteral<"RUB"> | zod_v3.ZodLiteral<"SAR"> | zod_v3.ZodLiteral<"SEK"> | zod_v3.ZodLiteral<"SGD"> | zod_v3.ZodLiteral<"THB"> | zod_v3.ZodLiteral<"TRY"> | zod_v3.ZodLiteral<"TWD"> | zod_v3.ZodLiteral<"TZS"> | zod_v3.ZodLiteral<"UAH"> | zod_v3.ZodLiteral<"UGX"> | zod_v3.ZodLiteral<"UYU"> | zod_v3.ZodLiteral<"VND"> | zod_v3.ZodLiteral<"ZAR"> | zod_v3.ZodLiteral<"ZMW">]>;
277
277
  features: zod_v3.ZodOptional<zod_v3.ZodArray<zod_v3.ZodString, "many">>;
278
- stripeFields: zod_v3.ZodType<stripe.Stripe.Plan, zod_v3.ZodTypeDef, stripe.Stripe.Plan>;
278
+ stripeFields: zod_v3.ZodType<stripe.Stripe.Product, zod_v3.ZodTypeDef, stripe.Stripe.Product>;
279
279
  externalId: zod_v3.ZodString;
280
280
  billingProvider: zod_v3.ZodOptional<zod_v3.ZodUnion<[zod_v3.ZodLiteral<"stripe">]>>;
281
281
  active: zod_v3.ZodEffects<zod_v3.ZodBoolean, boolean, unknown>;
@@ -233,7 +233,7 @@ declare const StripePlanServiceSchemas: <SchemaValidator extends _forklaunch_val
233
233
  cadence: _sinclair_typebox.TUnion<[_sinclair_typebox.TLiteral<"week"> | _sinclair_typebox.TLiteral<"month"> | _sinclair_typebox.TLiteral<"year">]>;
234
234
  currency: _sinclair_typebox.TUnion<[_sinclair_typebox.TLiteral<"USD"> | _sinclair_typebox.TLiteral<"EUR"> | _sinclair_typebox.TLiteral<"JPY"> | _sinclair_typebox.TLiteral<"GBP"> | _sinclair_typebox.TLiteral<"AUD"> | _sinclair_typebox.TLiteral<"CAD"> | _sinclair_typebox.TLiteral<"CHF"> | _sinclair_typebox.TLiteral<"CNY"> | _sinclair_typebox.TLiteral<"AED"> | _sinclair_typebox.TLiteral<"ARS"> | _sinclair_typebox.TLiteral<"BAM"> | _sinclair_typebox.TLiteral<"BDT"> | _sinclair_typebox.TLiteral<"BGN"> | _sinclair_typebox.TLiteral<"BOB"> | _sinclair_typebox.TLiteral<"BRL"> | _sinclair_typebox.TLiteral<"CLP"> | _sinclair_typebox.TLiteral<"COP"> | _sinclair_typebox.TLiteral<"CRC"> | _sinclair_typebox.TLiteral<"CZK"> | _sinclair_typebox.TLiteral<"DKK"> | _sinclair_typebox.TLiteral<"DZD"> | _sinclair_typebox.TLiteral<"EGP"> | _sinclair_typebox.TLiteral<"ETB"> | _sinclair_typebox.TLiteral<"GEL"> | _sinclair_typebox.TLiteral<"GTQ"> | _sinclair_typebox.TLiteral<"HKD"> | _sinclair_typebox.TLiteral<"HUF"> | _sinclair_typebox.TLiteral<"IDR"> | _sinclair_typebox.TLiteral<"ILS"> | _sinclair_typebox.TLiteral<"INR"> | _sinclair_typebox.TLiteral<"ISK"> | _sinclair_typebox.TLiteral<"JMD"> | _sinclair_typebox.TLiteral<"KES"> | _sinclair_typebox.TLiteral<"KHR"> | _sinclair_typebox.TLiteral<"KRW"> | _sinclair_typebox.TLiteral<"KZT"> | _sinclair_typebox.TLiteral<"LAK"> | _sinclair_typebox.TLiteral<"LKR"> | _sinclair_typebox.TLiteral<"MAD"> | _sinclair_typebox.TLiteral<"MMK"> | _sinclair_typebox.TLiteral<"MXN"> | _sinclair_typebox.TLiteral<"MYR"> | _sinclair_typebox.TLiteral<"NGN"> | _sinclair_typebox.TLiteral<"NOK"> | _sinclair_typebox.TLiteral<"NPR"> | _sinclair_typebox.TLiteral<"NZD"> | _sinclair_typebox.TLiteral<"PEN"> | _sinclair_typebox.TLiteral<"PHP"> | _sinclair_typebox.TLiteral<"PKR"> | _sinclair_typebox.TLiteral<"PLN"> | _sinclair_typebox.TLiteral<"PYG"> | _sinclair_typebox.TLiteral<"QAR"> | _sinclair_typebox.TLiteral<"RON"> | _sinclair_typebox.TLiteral<"RSD"> | _sinclair_typebox.TLiteral<"RUB"> | _sinclair_typebox.TLiteral<"SAR"> | _sinclair_typebox.TLiteral<"SEK"> | _sinclair_typebox.TLiteral<"SGD"> | _sinclair_typebox.TLiteral<"THB"> | _sinclair_typebox.TLiteral<"TRY"> | _sinclair_typebox.TLiteral<"TWD"> | _sinclair_typebox.TLiteral<"TZS"> | _sinclair_typebox.TLiteral<"UAH"> | _sinclair_typebox.TLiteral<"UGX"> | _sinclair_typebox.TLiteral<"UYU"> | _sinclair_typebox.TLiteral<"VND"> | _sinclair_typebox.TLiteral<"ZAR"> | _sinclair_typebox.TLiteral<"ZMW">]>;
235
235
  features: _sinclair_typebox.TOptional<_sinclair_typebox.TArray<_sinclair_typebox.TString>>;
236
- stripeFields: _sinclair_typebox.TTransform<_sinclair_typebox.TAny, stripe.Stripe.Plan>;
236
+ stripeFields: _sinclair_typebox.TTransform<_sinclair_typebox.TAny, stripe.Stripe.Product>;
237
237
  externalId: _sinclair_typebox.TString;
238
238
  billingProvider: _sinclair_typebox.TOptional<_sinclair_typebox.TUnion<[_sinclair_typebox.TLiteral<"stripe">]>>;
239
239
  active: _sinclair_typebox.TTransform<_sinclair_typebox.TUnion<[_sinclair_typebox.TBoolean, _sinclair_typebox.TString]>, boolean>;
@@ -275,7 +275,7 @@ declare const StripePlanServiceSchemas: <SchemaValidator extends _forklaunch_val
275
275
  cadence: zod_v3.ZodUnion<[zod_v3.ZodLiteral<"week"> | zod_v3.ZodLiteral<"month"> | zod_v3.ZodLiteral<"year">]>;
276
276
  currency: zod_v3.ZodUnion<[zod_v3.ZodLiteral<"USD"> | zod_v3.ZodLiteral<"EUR"> | zod_v3.ZodLiteral<"JPY"> | zod_v3.ZodLiteral<"GBP"> | zod_v3.ZodLiteral<"AUD"> | zod_v3.ZodLiteral<"CAD"> | zod_v3.ZodLiteral<"CHF"> | zod_v3.ZodLiteral<"CNY"> | zod_v3.ZodLiteral<"AED"> | zod_v3.ZodLiteral<"ARS"> | zod_v3.ZodLiteral<"BAM"> | zod_v3.ZodLiteral<"BDT"> | zod_v3.ZodLiteral<"BGN"> | zod_v3.ZodLiteral<"BOB"> | zod_v3.ZodLiteral<"BRL"> | zod_v3.ZodLiteral<"CLP"> | zod_v3.ZodLiteral<"COP"> | zod_v3.ZodLiteral<"CRC"> | zod_v3.ZodLiteral<"CZK"> | zod_v3.ZodLiteral<"DKK"> | zod_v3.ZodLiteral<"DZD"> | zod_v3.ZodLiteral<"EGP"> | zod_v3.ZodLiteral<"ETB"> | zod_v3.ZodLiteral<"GEL"> | zod_v3.ZodLiteral<"GTQ"> | zod_v3.ZodLiteral<"HKD"> | zod_v3.ZodLiteral<"HUF"> | zod_v3.ZodLiteral<"IDR"> | zod_v3.ZodLiteral<"ILS"> | zod_v3.ZodLiteral<"INR"> | zod_v3.ZodLiteral<"ISK"> | zod_v3.ZodLiteral<"JMD"> | zod_v3.ZodLiteral<"KES"> | zod_v3.ZodLiteral<"KHR"> | zod_v3.ZodLiteral<"KRW"> | zod_v3.ZodLiteral<"KZT"> | zod_v3.ZodLiteral<"LAK"> | zod_v3.ZodLiteral<"LKR"> | zod_v3.ZodLiteral<"MAD"> | zod_v3.ZodLiteral<"MMK"> | zod_v3.ZodLiteral<"MXN"> | zod_v3.ZodLiteral<"MYR"> | zod_v3.ZodLiteral<"NGN"> | zod_v3.ZodLiteral<"NOK"> | zod_v3.ZodLiteral<"NPR"> | zod_v3.ZodLiteral<"NZD"> | zod_v3.ZodLiteral<"PEN"> | zod_v3.ZodLiteral<"PHP"> | zod_v3.ZodLiteral<"PKR"> | zod_v3.ZodLiteral<"PLN"> | zod_v3.ZodLiteral<"PYG"> | zod_v3.ZodLiteral<"QAR"> | zod_v3.ZodLiteral<"RON"> | zod_v3.ZodLiteral<"RSD"> | zod_v3.ZodLiteral<"RUB"> | zod_v3.ZodLiteral<"SAR"> | zod_v3.ZodLiteral<"SEK"> | zod_v3.ZodLiteral<"SGD"> | zod_v3.ZodLiteral<"THB"> | zod_v3.ZodLiteral<"TRY"> | zod_v3.ZodLiteral<"TWD"> | zod_v3.ZodLiteral<"TZS"> | zod_v3.ZodLiteral<"UAH"> | zod_v3.ZodLiteral<"UGX"> | zod_v3.ZodLiteral<"UYU"> | zod_v3.ZodLiteral<"VND"> | zod_v3.ZodLiteral<"ZAR"> | zod_v3.ZodLiteral<"ZMW">]>;
277
277
  features: zod_v3.ZodOptional<zod_v3.ZodArray<zod_v3.ZodString, "many">>;
278
- stripeFields: zod_v3.ZodType<stripe.Stripe.Plan, zod_v3.ZodTypeDef, stripe.Stripe.Plan>;
278
+ stripeFields: zod_v3.ZodType<stripe.Stripe.Product, zod_v3.ZodTypeDef, stripe.Stripe.Product>;
279
279
  externalId: zod_v3.ZodString;
280
280
  billingProvider: zod_v3.ZodOptional<zod_v3.ZodUnion<[zod_v3.ZodLiteral<"stripe">]>>;
281
281
  active: zod_v3.ZodEffects<zod_v3.ZodBoolean, boolean, unknown>;
@@ -57,7 +57,7 @@ type StripeUpdatePlanDto = Omit<UpdatePlanDto<typeof PlanCadenceEnum, typeof Cur
57
57
  stripeFields?: Omit<stripe__default.PlanUpdateParams, PlanOmissions>;
58
58
  };
59
59
  type StripePlanDto = Omit<PlanDto<typeof PlanCadenceEnum, typeof CurrencyEnum, typeof BillingProviderEnum>, 'providerFields'> & {
60
- stripeFields: stripe__default.Plan;
60
+ stripeFields: stripe__default.Product;
61
61
  };
62
62
  type StripePlanDtos = {
63
63
  PlanMapper: StripePlanDto;
@@ -105,7 +105,7 @@ type StripePaymentLinkEntities<StatusEnum> = {
105
105
  UpdatePaymentLinkMapper: StripePaymentLinkEntity<StatusEnum>;
106
106
  };
107
107
  type StripePlanEntity = PlanDto<typeof PlanCadenceEnum, typeof CurrencyEnum, typeof BillingProviderEnum> & {
108
- providerFields: stripe__default.Plan;
108
+ providerFields: stripe__default.Product;
109
109
  };
110
110
  type StripePlanEntities = {
111
111
  PlanMapper: StripePlanEntity;
@@ -162,10 +162,10 @@ type StripePlanMappers<Entities extends StripePlanEntities, Dto extends StripePl
162
162
  toDto: (entity: Entities['PlanMapper']) => Promise<Dto['PlanMapper']>;
163
163
  };
164
164
  CreatePlanMapper: {
165
- toEntity: (dto: Dto['CreatePlanMapper'], em: EntityManager, stripePlan: stripe__default.Plan, ...args: unknown[]) => Promise<Entities['CreatePlanMapper']>;
165
+ toEntity: (dto: Dto['CreatePlanMapper'], em: EntityManager, stripePlan: stripe__default.Product, ...args: unknown[]) => Promise<Entities['CreatePlanMapper']>;
166
166
  };
167
167
  UpdatePlanMapper: {
168
- toEntity: (dto: Dto['UpdatePlanMapper'], em: EntityManager, stripePlan: stripe__default.Plan, ...args: unknown[]) => Promise<Entities['UpdatePlanMapper']>;
168
+ toEntity: (dto: Dto['UpdatePlanMapper'], em: EntityManager, stripePlan: stripe__default.Product, ...args: unknown[]) => Promise<Entities['UpdatePlanMapper']>;
169
169
  };
170
170
  };
171
171
 
@@ -57,7 +57,7 @@ type StripeUpdatePlanDto = Omit<UpdatePlanDto<typeof PlanCadenceEnum, typeof Cur
57
57
  stripeFields?: Omit<stripe__default.PlanUpdateParams, PlanOmissions>;
58
58
  };
59
59
  type StripePlanDto = Omit<PlanDto<typeof PlanCadenceEnum, typeof CurrencyEnum, typeof BillingProviderEnum>, 'providerFields'> & {
60
- stripeFields: stripe__default.Plan;
60
+ stripeFields: stripe__default.Product;
61
61
  };
62
62
  type StripePlanDtos = {
63
63
  PlanMapper: StripePlanDto;
@@ -105,7 +105,7 @@ type StripePaymentLinkEntities<StatusEnum> = {
105
105
  UpdatePaymentLinkMapper: StripePaymentLinkEntity<StatusEnum>;
106
106
  };
107
107
  type StripePlanEntity = PlanDto<typeof PlanCadenceEnum, typeof CurrencyEnum, typeof BillingProviderEnum> & {
108
- providerFields: stripe__default.Plan;
108
+ providerFields: stripe__default.Product;
109
109
  };
110
110
  type StripePlanEntities = {
111
111
  PlanMapper: StripePlanEntity;
@@ -162,10 +162,10 @@ type StripePlanMappers<Entities extends StripePlanEntities, Dto extends StripePl
162
162
  toDto: (entity: Entities['PlanMapper']) => Promise<Dto['PlanMapper']>;
163
163
  };
164
164
  CreatePlanMapper: {
165
- toEntity: (dto: Dto['CreatePlanMapper'], em: EntityManager, stripePlan: stripe__default.Plan, ...args: unknown[]) => Promise<Entities['CreatePlanMapper']>;
165
+ toEntity: (dto: Dto['CreatePlanMapper'], em: EntityManager, stripePlan: stripe__default.Product, ...args: unknown[]) => Promise<Entities['CreatePlanMapper']>;
166
166
  };
167
167
  UpdatePlanMapper: {
168
- toEntity: (dto: Dto['UpdatePlanMapper'], em: EntityManager, stripePlan: stripe__default.Plan, ...args: unknown[]) => Promise<Entities['UpdatePlanMapper']>;
168
+ toEntity: (dto: Dto['UpdatePlanMapper'], em: EntityManager, stripePlan: stripe__default.Product, ...args: unknown[]) => Promise<Entities['UpdatePlanMapper']>;
169
169
  };
170
170
  };
171
171
 
@@ -14,7 +14,7 @@ export type StripePlanMappers<
14
14
  toEntity: (
15
15
  dto: Dto['CreatePlanMapper'],
16
16
  em: EntityManager,
17
- stripePlan: Stripe.Plan,
17
+ stripePlan: Stripe.Product,
18
18
  ...args: unknown[]
19
19
  ) => Promise<Entities['CreatePlanMapper']>;
20
20
  };
@@ -22,7 +22,7 @@ export type StripePlanMappers<
22
22
  toEntity: (
23
23
  dto: Dto['UpdatePlanMapper'],
24
24
  em: EntityManager,
25
- stripePlan: Stripe.Plan,
25
+ stripePlan: Stripe.Product,
26
26
  ...args: unknown[]
27
27
  ) => Promise<Entities['UpdatePlanMapper']>;
28
28
  };
@@ -176,7 +176,7 @@ export type StripePlanDto = Omit<
176
176
  >,
177
177
  'providerFields'
178
178
  > & {
179
- stripeFields: Stripe.Plan;
179
+ stripeFields: Stripe.Product;
180
180
  };
181
181
 
182
182
  export type StripePlanDtos = {
@@ -59,7 +59,7 @@ export type StripePlanEntity = PlanDto<
59
59
  typeof CurrencyEnum,
60
60
  typeof BillingProviderEnum
61
61
  > & {
62
- providerFields: Stripe.Plan;
62
+ providerFields: Stripe.Product;
63
63
  };
64
64
 
65
65
  export type StripePlanEntities = {
@@ -89,7 +89,7 @@ export class StripePlanService<
89
89
  planDto: StripeCreatePlanDto,
90
90
  em?: EntityManager
91
91
  ): Promise<Dto['PlanMapper']> {
92
- const stripePlan = await this.stripeClient.plans.create({
92
+ let stripePlan = await this.stripeClient.plans.create({
93
93
  ...planDto.stripeFields,
94
94
  amount: planDto.price,
95
95
  interval: planDto.cadence,
@@ -97,6 +97,11 @@ export class StripePlanService<
97
97
  currency: planDto.currency as string
98
98
  });
99
99
 
100
+ // retrieve the plan with the product expanded
101
+ stripePlan = await this.stripeClient.plans.retrieve(stripePlan.id, {
102
+ expand: ['product']
103
+ });
104
+
100
105
  const plan = await this.basePlanService.createPlan(
101
106
  {
102
107
  ...planDto,
@@ -104,7 +109,7 @@ export class StripePlanService<
104
109
  billingProvider: 'stripe'
105
110
  },
106
111
  em ?? this.em,
107
- stripePlan
112
+ stripePlan.product as Stripe.Product
108
113
  );
109
114
 
110
115
  return plan;
@@ -115,8 +120,10 @@ export class StripePlanService<
115
120
  if (!planEntity.externalId) {
116
121
  throw new Error('Plan not found');
117
122
  }
118
- const plan = await this.stripeClient.plans.retrieve(planEntity.externalId);
119
- planEntity.stripeFields = plan;
123
+ const plan = await this.stripeClient.plans.retrieve(planEntity.externalId, {
124
+ expand: ['product']
125
+ });
126
+ planEntity.stripeFields = plan.product as Stripe.Product;
120
127
  return planEntity;
121
128
  }
122
129
 
@@ -146,7 +153,7 @@ export class StripePlanService<
146
153
  : existingProduct.id;
147
154
 
148
155
  await this.stripeClient.plans.del(planEntity.externalId);
149
- const updatedPlan = await this.stripeClient.plans.create({
156
+ let updatedPlan = await this.stripeClient.plans.create({
150
157
  ...planDto.stripeFields,
151
158
  interval: planDto.cadence ?? existingPlan.interval,
152
159
  currency: planDto.currency ?? existingPlan.currency,
@@ -154,6 +161,10 @@ export class StripePlanService<
154
161
  product: productId
155
162
  });
156
163
 
164
+ updatedPlan = await this.stripeClient.plans.retrieve(updatedPlan.id, {
165
+ expand: ['product']
166
+ });
167
+
157
168
  const updatedPlanEntity = await this.basePlanService.updatePlan(
158
169
  {
159
170
  ...planDto,
@@ -162,10 +173,10 @@ export class StripePlanService<
162
173
  billingProvider: 'stripe'
163
174
  },
164
175
  em,
165
- updatedPlan
176
+ updatedPlan.product as Stripe.Product
166
177
  );
167
178
 
168
- updatedPlanEntity.stripeFields = updatedPlan;
179
+ updatedPlanEntity.stripeFields = updatedPlan.product as Stripe.Product;
169
180
 
170
181
  return updatedPlanEntity;
171
182
  }
@@ -192,7 +203,13 @@ export class StripePlanService<
192
203
  const stripePlans = await Promise.all(
193
204
  plans.map(async (plan) => {
194
205
  try {
195
- return await this.stripeClient.plans.retrieve(plan.externalId);
206
+ const fetchedPlan = await this.stripeClient.plans.retrieve(
207
+ plan.externalId,
208
+ {
209
+ expand: ['product']
210
+ }
211
+ );
212
+ return fetchedPlan.product as Stripe.Product;
196
213
  } catch {
197
214
  return null;
198
215
  }
@@ -102,6 +102,34 @@ export class StripeWebhookService<
102
102
  this.subscriptionService = subscriptionService;
103
103
  }
104
104
 
105
+ /**
106
+ * Extract features from Stripe product metadata.
107
+ * Features can be stored as:
108
+ * - metadata.features: comma-separated string (e.g., "feature1,feature2,feature3")
109
+ * - metadata.features: JSON array string (e.g., '["feature1","feature2"]')
110
+ */
111
+ private extractFeaturesFromProduct(product: Stripe.Product): string[] {
112
+ const featuresStr = product.metadata?.features;
113
+ if (!featuresStr) {
114
+ return [];
115
+ }
116
+
117
+ // Try parsing as JSON array first
118
+ try {
119
+ const parsed = JSON.parse(featuresStr);
120
+ if (Array.isArray(parsed)) {
121
+ return parsed.filter((f): f is string => typeof f === 'string');
122
+ }
123
+ } catch {
124
+ // Not JSON, treat as comma-separated
125
+ }
126
+
127
+ return featuresStr
128
+ .split(',')
129
+ .map((f) => f.trim())
130
+ .filter((f) => f.length > 0);
131
+ }
132
+
105
133
  async handleWebhookEvent(event: Stripe.Event): Promise<void> {
106
134
  if (this.openTelemetryCollector) {
107
135
  this.openTelemetryCollector.info('Handling webhook event', event);
@@ -185,22 +213,26 @@ export class StripeWebhookService<
185
213
 
186
214
  case 'plan.created': {
187
215
  if (
188
- typeof event.data.object.product === 'object' &&
189
216
  event.data.object.product != null &&
190
217
  event.data.object.amount != null
191
218
  ) {
219
+ const productId =
220
+ typeof event.data.object.product === 'string'
221
+ ? event.data.object.product
222
+ : event.data.object.product.id;
223
+ const product = await this.stripeClient.products.retrieve(productId);
224
+ const features = this.extractFeaturesFromProduct(product);
225
+
192
226
  await this.planService.basePlanService.createPlan({
193
227
  id: event.data.object.id,
194
228
  billingProvider: BillingProviderEnum.STRIPE,
195
229
  cadence: event.data.object.interval as PlanCadenceEnum,
196
230
  currency: event.data.object.currency as CurrencyEnum,
197
- active: true,
198
- name:
199
- typeof event.data.object.product === 'string'
200
- ? event.data.object.product
201
- : event.data.object.product?.id,
231
+ active: product.active,
232
+ name: product.name,
202
233
  price: event.data.object.amount,
203
- externalId: event.data.object.id
234
+ externalId: event.data.object.id,
235
+ features
204
236
  });
205
237
  } else {
206
238
  throw new Error('Invalid plan');
@@ -210,22 +242,26 @@ export class StripeWebhookService<
210
242
 
211
243
  case 'plan.updated': {
212
244
  if (
213
- typeof event.data.object.product === 'object' &&
214
245
  event.data.object.product != null &&
215
246
  event.data.object.amount != null
216
247
  ) {
248
+ const productId =
249
+ typeof event.data.object.product === 'string'
250
+ ? event.data.object.product
251
+ : event.data.object.product.id;
252
+ const product = await this.stripeClient.products.retrieve(productId);
253
+ const features = this.extractFeaturesFromProduct(product);
254
+
217
255
  await this.planService.basePlanService.updatePlan({
218
256
  id: event.data.object.id,
219
257
  billingProvider: BillingProviderEnum.STRIPE,
220
258
  cadence: event.data.object.interval as PlanCadenceEnum,
221
259
  currency: event.data.object.currency as CurrencyEnum,
222
- active: true,
223
- name:
224
- typeof event.data.object.product === 'string'
225
- ? event.data.object.product
226
- : event.data.object.product?.id,
260
+ active: product.active,
261
+ name: product.name,
227
262
  price: event.data.object.amount,
228
- externalId: event.data.object.id
263
+ externalId: event.data.object.id,
264
+ features
229
265
  });
230
266
  } else {
231
267
  throw new Error('Invalid plan');
@@ -240,6 +276,89 @@ export class StripeWebhookService<
240
276
  break;
241
277
  }
242
278
 
279
+ case 'product.created':
280
+ case 'product.updated': {
281
+ // When a product is created/updated, sync features to all associated plans
282
+ const product = event.data.object;
283
+ const features = this.extractFeaturesFromProduct(product);
284
+
285
+ // Update all legacy plans (iterates through all pages)
286
+ await this.stripeClient.plans
287
+ .list({ product: product.id })
288
+ .autoPagingEach(async (plan) => {
289
+ try {
290
+ await this.planService.basePlanService.updatePlan({
291
+ id: plan.id,
292
+ features,
293
+ active: product.active,
294
+ name: product.name
295
+ });
296
+ } catch (error) {
297
+ this.openTelemetryCollector.warn(
298
+ `Failed to update plan ${plan.id} with product features`,
299
+ error
300
+ );
301
+ }
302
+ });
303
+
304
+ // Update all price-based plans (iterates through all pages)
305
+ await this.stripeClient.prices
306
+ .list({ product: product.id })
307
+ .autoPagingEach(async (price) => {
308
+ try {
309
+ await this.planService.basePlanService.updatePlan({
310
+ id: price.id,
311
+ features,
312
+ active: price.active && product.active,
313
+ name: product.name
314
+ });
315
+ } catch (error) {
316
+ this.openTelemetryCollector.warn(
317
+ `Failed to update price-based plan ${price.id} with product features`,
318
+ error
319
+ );
320
+ }
321
+ });
322
+ break;
323
+ }
324
+
325
+ // Handle Stripe Prices API (newer alternative to Plans)
326
+ case 'price.created':
327
+ case 'price.updated': {
328
+ const price = event.data.object;
329
+ if (
330
+ price.product != null &&
331
+ price.unit_amount != null &&
332
+ price.recurring
333
+ ) {
334
+ const productId =
335
+ typeof price.product === 'string'
336
+ ? price.product
337
+ : price.product.id;
338
+ const product = await this.stripeClient.products.retrieve(productId);
339
+ const features = this.extractFeaturesFromProduct(product);
340
+
341
+ const planData = {
342
+ id: price.id,
343
+ billingProvider: BillingProviderEnum.STRIPE,
344
+ cadence: price.recurring.interval as PlanCadenceEnum,
345
+ currency: price.currency as CurrencyEnum,
346
+ active: price.active && product.active,
347
+ name: product.name,
348
+ price: price.unit_amount,
349
+ externalId: price.id,
350
+ features
351
+ };
352
+
353
+ if (event.type === 'price.created') {
354
+ await this.planService.basePlanService.createPlan(planData);
355
+ } else {
356
+ await this.planService.basePlanService.updatePlan(planData);
357
+ }
358
+ }
359
+ break;
360
+ }
361
+
243
362
  case 'customer.subscription.created': {
244
363
  if (
245
364
  !event.data.object.items?.data ||
@@ -168,6 +168,13 @@ declare class StripeWebhookService<SchemaValidator extends AnySchemaValidator, S
168
168
  protected readonly planService: StripePlanService<SchemaValidator, PlanEntities>;
169
169
  protected readonly subscriptionService: StripeSubscriptionService<SchemaValidator, PartyEnum, SubscriptionEntities>;
170
170
  constructor(stripeClient: stripe__default, em: EntityManager, schemaValidator: SchemaValidator, openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>, billingPortalService: StripeBillingPortalService<SchemaValidator, BillingPortalEntities>, checkoutSessionService: StripeCheckoutSessionService<SchemaValidator, StatusEnum, CheckoutSessionEntities>, paymentLinkService: StripePaymentLinkService<SchemaValidator, StatusEnum, PaymentLinkEntities>, planService: StripePlanService<SchemaValidator, PlanEntities>, subscriptionService: StripeSubscriptionService<SchemaValidator, PartyEnum, SubscriptionEntities>);
171
+ /**
172
+ * Extract features from Stripe product metadata.
173
+ * Features can be stored as:
174
+ * - metadata.features: comma-separated string (e.g., "feature1,feature2,feature3")
175
+ * - metadata.features: JSON array string (e.g., '["feature1","feature2"]')
176
+ */
177
+ private extractFeaturesFromProduct;
171
178
  handleWebhookEvent(event: stripe__default.Event): Promise<void>;
172
179
  }
173
180
 
@@ -168,6 +168,13 @@ declare class StripeWebhookService<SchemaValidator extends AnySchemaValidator, S
168
168
  protected readonly planService: StripePlanService<SchemaValidator, PlanEntities>;
169
169
  protected readonly subscriptionService: StripeSubscriptionService<SchemaValidator, PartyEnum, SubscriptionEntities>;
170
170
  constructor(stripeClient: stripe__default, em: EntityManager, schemaValidator: SchemaValidator, openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>, billingPortalService: StripeBillingPortalService<SchemaValidator, BillingPortalEntities>, checkoutSessionService: StripeCheckoutSessionService<SchemaValidator, StatusEnum, CheckoutSessionEntities>, paymentLinkService: StripePaymentLinkService<SchemaValidator, StatusEnum, PaymentLinkEntities>, planService: StripePlanService<SchemaValidator, PlanEntities>, subscriptionService: StripeSubscriptionService<SchemaValidator, PartyEnum, SubscriptionEntities>);
171
+ /**
172
+ * Extract features from Stripe product metadata.
173
+ * Features can be stored as:
174
+ * - metadata.features: comma-separated string (e.g., "feature1,feature2,feature3")
175
+ * - metadata.features: JSON array string (e.g., '["feature1","feature2"]')
176
+ */
177
+ private extractFeaturesFromProduct;
171
178
  handleWebhookEvent(event: stripe__default.Event): Promise<void>;
172
179
  }
173
180
 
@@ -336,13 +336,16 @@ var StripePlanService = class {
336
336
  schemaValidator;
337
337
  mappers;
338
338
  async createPlan(planDto, em) {
339
- const stripePlan = await this.stripeClient.plans.create({
339
+ let stripePlan = await this.stripeClient.plans.create({
340
340
  ...planDto.stripeFields,
341
341
  amount: planDto.price,
342
342
  interval: planDto.cadence,
343
343
  product: planDto.name,
344
344
  currency: planDto.currency
345
345
  });
346
+ stripePlan = await this.stripeClient.plans.retrieve(stripePlan.id, {
347
+ expand: ["product"]
348
+ });
346
349
  const plan = await this.basePlanService.createPlan(
347
350
  {
348
351
  ...planDto,
@@ -350,7 +353,7 @@ var StripePlanService = class {
350
353
  billingProvider: "stripe"
351
354
  },
352
355
  em ?? this.em,
353
- stripePlan
356
+ stripePlan.product
354
357
  );
355
358
  return plan;
356
359
  }
@@ -359,8 +362,10 @@ var StripePlanService = class {
359
362
  if (!planEntity.externalId) {
360
363
  throw new Error("Plan not found");
361
364
  }
362
- const plan = await this.stripeClient.plans.retrieve(planEntity.externalId);
363
- planEntity.stripeFields = plan;
365
+ const plan = await this.stripeClient.plans.retrieve(planEntity.externalId, {
366
+ expand: ["product"]
367
+ });
368
+ planEntity.stripeFields = plan.product;
364
369
  return planEntity;
365
370
  }
366
371
  async updatePlan(planDto, em) {
@@ -379,13 +384,16 @@ var StripePlanService = class {
379
384
  }
380
385
  const productId = typeof existingProduct === "string" ? existingProduct : existingProduct.id;
381
386
  await this.stripeClient.plans.del(planEntity.externalId);
382
- const updatedPlan = await this.stripeClient.plans.create({
387
+ let updatedPlan = await this.stripeClient.plans.create({
383
388
  ...planDto.stripeFields,
384
389
  interval: planDto.cadence ?? existingPlan.interval,
385
390
  currency: planDto.currency ?? existingPlan.currency,
386
391
  amount: planDto.price ?? existingPlan.amount ?? void 0,
387
392
  product: productId
388
393
  });
394
+ updatedPlan = await this.stripeClient.plans.retrieve(updatedPlan.id, {
395
+ expand: ["product"]
396
+ });
389
397
  const updatedPlanEntity = await this.basePlanService.updatePlan(
390
398
  {
391
399
  ...planDto,
@@ -394,9 +402,9 @@ var StripePlanService = class {
394
402
  billingProvider: "stripe"
395
403
  },
396
404
  em,
397
- updatedPlan
405
+ updatedPlan.product
398
406
  );
399
- updatedPlanEntity.stripeFields = updatedPlan;
407
+ updatedPlanEntity.stripeFields = updatedPlan.product;
400
408
  return updatedPlanEntity;
401
409
  }
402
410
  async deletePlan(idDto, em) {
@@ -415,7 +423,13 @@ var StripePlanService = class {
415
423
  const stripePlans = await Promise.all(
416
424
  plans.map(async (plan) => {
417
425
  try {
418
- return await this.stripeClient.plans.retrieve(plan.externalId);
426
+ const fetchedPlan = await this.stripeClient.plans.retrieve(
427
+ plan.externalId,
428
+ {
429
+ expand: ["product"]
430
+ }
431
+ );
432
+ return fetchedPlan.product;
419
433
  } catch {
420
434
  return null;
421
435
  }
@@ -632,6 +646,26 @@ var StripeWebhookService = class {
632
646
  this.planService = planService;
633
647
  this.subscriptionService = subscriptionService;
634
648
  }
649
+ /**
650
+ * Extract features from Stripe product metadata.
651
+ * Features can be stored as:
652
+ * - metadata.features: comma-separated string (e.g., "feature1,feature2,feature3")
653
+ * - metadata.features: JSON array string (e.g., '["feature1","feature2"]')
654
+ */
655
+ extractFeaturesFromProduct(product) {
656
+ const featuresStr = product.metadata?.features;
657
+ if (!featuresStr) {
658
+ return [];
659
+ }
660
+ try {
661
+ const parsed = JSON.parse(featuresStr);
662
+ if (Array.isArray(parsed)) {
663
+ return parsed.filter((f) => typeof f === "string");
664
+ }
665
+ } catch {
666
+ }
667
+ return featuresStr.split(",").map((f) => f.trim()).filter((f) => f.length > 0);
668
+ }
635
669
  async handleWebhookEvent(event) {
636
670
  if (this.openTelemetryCollector) {
637
671
  this.openTelemetryCollector.info("Handling webhook event", event);
@@ -700,16 +734,20 @@ var StripeWebhookService = class {
700
734
  break;
701
735
  }
702
736
  case "plan.created": {
703
- if (typeof event.data.object.product === "object" && event.data.object.product != null && event.data.object.amount != null) {
737
+ if (event.data.object.product != null && event.data.object.amount != null) {
738
+ const productId = typeof event.data.object.product === "string" ? event.data.object.product : event.data.object.product.id;
739
+ const product = await this.stripeClient.products.retrieve(productId);
740
+ const features = this.extractFeaturesFromProduct(product);
704
741
  await this.planService.basePlanService.createPlan({
705
742
  id: event.data.object.id,
706
743
  billingProvider: BillingProviderEnum.STRIPE,
707
744
  cadence: event.data.object.interval,
708
745
  currency: event.data.object.currency,
709
- active: true,
710
- name: typeof event.data.object.product === "string" ? event.data.object.product : event.data.object.product?.id,
746
+ active: product.active,
747
+ name: product.name,
711
748
  price: event.data.object.amount,
712
- externalId: event.data.object.id
749
+ externalId: event.data.object.id,
750
+ features
713
751
  });
714
752
  } else {
715
753
  throw new Error("Invalid plan");
@@ -717,16 +755,20 @@ var StripeWebhookService = class {
717
755
  break;
718
756
  }
719
757
  case "plan.updated": {
720
- if (typeof event.data.object.product === "object" && event.data.object.product != null && event.data.object.amount != null) {
758
+ if (event.data.object.product != null && event.data.object.amount != null) {
759
+ const productId = typeof event.data.object.product === "string" ? event.data.object.product : event.data.object.product.id;
760
+ const product = await this.stripeClient.products.retrieve(productId);
761
+ const features = this.extractFeaturesFromProduct(product);
721
762
  await this.planService.basePlanService.updatePlan({
722
763
  id: event.data.object.id,
723
764
  billingProvider: BillingProviderEnum.STRIPE,
724
765
  cadence: event.data.object.interval,
725
766
  currency: event.data.object.currency,
726
- active: true,
727
- name: typeof event.data.object.product === "string" ? event.data.object.product : event.data.object.product?.id,
767
+ active: product.active,
768
+ name: product.name,
728
769
  price: event.data.object.amount,
729
- externalId: event.data.object.id
770
+ externalId: event.data.object.id,
771
+ features
730
772
  });
731
773
  } else {
732
774
  throw new Error("Invalid plan");
@@ -739,6 +781,69 @@ var StripeWebhookService = class {
739
781
  });
740
782
  break;
741
783
  }
784
+ case "product.created":
785
+ case "product.updated": {
786
+ const product = event.data.object;
787
+ const features = this.extractFeaturesFromProduct(product);
788
+ await this.stripeClient.plans.list({ product: product.id }).autoPagingEach(async (plan) => {
789
+ try {
790
+ await this.planService.basePlanService.updatePlan({
791
+ id: plan.id,
792
+ features,
793
+ active: product.active,
794
+ name: product.name
795
+ });
796
+ } catch (error) {
797
+ this.openTelemetryCollector.warn(
798
+ `Failed to update plan ${plan.id} with product features`,
799
+ error
800
+ );
801
+ }
802
+ });
803
+ await this.stripeClient.prices.list({ product: product.id }).autoPagingEach(async (price) => {
804
+ try {
805
+ await this.planService.basePlanService.updatePlan({
806
+ id: price.id,
807
+ features,
808
+ active: price.active && product.active,
809
+ name: product.name
810
+ });
811
+ } catch (error) {
812
+ this.openTelemetryCollector.warn(
813
+ `Failed to update price-based plan ${price.id} with product features`,
814
+ error
815
+ );
816
+ }
817
+ });
818
+ break;
819
+ }
820
+ // Handle Stripe Prices API (newer alternative to Plans)
821
+ case "price.created":
822
+ case "price.updated": {
823
+ const price = event.data.object;
824
+ if (price.product != null && price.unit_amount != null && price.recurring) {
825
+ const productId = typeof price.product === "string" ? price.product : price.product.id;
826
+ const product = await this.stripeClient.products.retrieve(productId);
827
+ const features = this.extractFeaturesFromProduct(product);
828
+ const planData = {
829
+ id: price.id,
830
+ billingProvider: BillingProviderEnum.STRIPE,
831
+ cadence: price.recurring.interval,
832
+ currency: price.currency,
833
+ active: price.active && product.active,
834
+ name: product.name,
835
+ price: price.unit_amount,
836
+ externalId: price.id,
837
+ features
838
+ };
839
+ if (event.type === "price.created") {
840
+ await this.planService.basePlanService.createPlan(planData);
841
+ } else {
842
+ await this.planService.basePlanService.updatePlan(planData);
843
+ }
844
+ }
845
+ break;
846
+ }
742
847
  case "customer.subscription.created": {
743
848
  if (!event.data.object.items?.data || event.data.object.items.data.length === 0 || !event.data.object.items.data[0]?.plan?.id) {
744
849
  throw new Error(
@@ -304,13 +304,16 @@ var StripePlanService = class {
304
304
  schemaValidator;
305
305
  mappers;
306
306
  async createPlan(planDto, em) {
307
- const stripePlan = await this.stripeClient.plans.create({
307
+ let stripePlan = await this.stripeClient.plans.create({
308
308
  ...planDto.stripeFields,
309
309
  amount: planDto.price,
310
310
  interval: planDto.cadence,
311
311
  product: planDto.name,
312
312
  currency: planDto.currency
313
313
  });
314
+ stripePlan = await this.stripeClient.plans.retrieve(stripePlan.id, {
315
+ expand: ["product"]
316
+ });
314
317
  const plan = await this.basePlanService.createPlan(
315
318
  {
316
319
  ...planDto,
@@ -318,7 +321,7 @@ var StripePlanService = class {
318
321
  billingProvider: "stripe"
319
322
  },
320
323
  em ?? this.em,
321
- stripePlan
324
+ stripePlan.product
322
325
  );
323
326
  return plan;
324
327
  }
@@ -327,8 +330,10 @@ var StripePlanService = class {
327
330
  if (!planEntity.externalId) {
328
331
  throw new Error("Plan not found");
329
332
  }
330
- const plan = await this.stripeClient.plans.retrieve(planEntity.externalId);
331
- planEntity.stripeFields = plan;
333
+ const plan = await this.stripeClient.plans.retrieve(planEntity.externalId, {
334
+ expand: ["product"]
335
+ });
336
+ planEntity.stripeFields = plan.product;
332
337
  return planEntity;
333
338
  }
334
339
  async updatePlan(planDto, em) {
@@ -347,13 +352,16 @@ var StripePlanService = class {
347
352
  }
348
353
  const productId = typeof existingProduct === "string" ? existingProduct : existingProduct.id;
349
354
  await this.stripeClient.plans.del(planEntity.externalId);
350
- const updatedPlan = await this.stripeClient.plans.create({
355
+ let updatedPlan = await this.stripeClient.plans.create({
351
356
  ...planDto.stripeFields,
352
357
  interval: planDto.cadence ?? existingPlan.interval,
353
358
  currency: planDto.currency ?? existingPlan.currency,
354
359
  amount: planDto.price ?? existingPlan.amount ?? void 0,
355
360
  product: productId
356
361
  });
362
+ updatedPlan = await this.stripeClient.plans.retrieve(updatedPlan.id, {
363
+ expand: ["product"]
364
+ });
357
365
  const updatedPlanEntity = await this.basePlanService.updatePlan(
358
366
  {
359
367
  ...planDto,
@@ -362,9 +370,9 @@ var StripePlanService = class {
362
370
  billingProvider: "stripe"
363
371
  },
364
372
  em,
365
- updatedPlan
373
+ updatedPlan.product
366
374
  );
367
- updatedPlanEntity.stripeFields = updatedPlan;
375
+ updatedPlanEntity.stripeFields = updatedPlan.product;
368
376
  return updatedPlanEntity;
369
377
  }
370
378
  async deletePlan(idDto, em) {
@@ -383,7 +391,13 @@ var StripePlanService = class {
383
391
  const stripePlans = await Promise.all(
384
392
  plans.map(async (plan) => {
385
393
  try {
386
- return await this.stripeClient.plans.retrieve(plan.externalId);
394
+ const fetchedPlan = await this.stripeClient.plans.retrieve(
395
+ plan.externalId,
396
+ {
397
+ expand: ["product"]
398
+ }
399
+ );
400
+ return fetchedPlan.product;
387
401
  } catch {
388
402
  return null;
389
403
  }
@@ -600,6 +614,26 @@ var StripeWebhookService = class {
600
614
  this.planService = planService;
601
615
  this.subscriptionService = subscriptionService;
602
616
  }
617
+ /**
618
+ * Extract features from Stripe product metadata.
619
+ * Features can be stored as:
620
+ * - metadata.features: comma-separated string (e.g., "feature1,feature2,feature3")
621
+ * - metadata.features: JSON array string (e.g., '["feature1","feature2"]')
622
+ */
623
+ extractFeaturesFromProduct(product) {
624
+ const featuresStr = product.metadata?.features;
625
+ if (!featuresStr) {
626
+ return [];
627
+ }
628
+ try {
629
+ const parsed = JSON.parse(featuresStr);
630
+ if (Array.isArray(parsed)) {
631
+ return parsed.filter((f) => typeof f === "string");
632
+ }
633
+ } catch {
634
+ }
635
+ return featuresStr.split(",").map((f) => f.trim()).filter((f) => f.length > 0);
636
+ }
603
637
  async handleWebhookEvent(event) {
604
638
  if (this.openTelemetryCollector) {
605
639
  this.openTelemetryCollector.info("Handling webhook event", event);
@@ -668,16 +702,20 @@ var StripeWebhookService = class {
668
702
  break;
669
703
  }
670
704
  case "plan.created": {
671
- if (typeof event.data.object.product === "object" && event.data.object.product != null && event.data.object.amount != null) {
705
+ if (event.data.object.product != null && event.data.object.amount != null) {
706
+ const productId = typeof event.data.object.product === "string" ? event.data.object.product : event.data.object.product.id;
707
+ const product = await this.stripeClient.products.retrieve(productId);
708
+ const features = this.extractFeaturesFromProduct(product);
672
709
  await this.planService.basePlanService.createPlan({
673
710
  id: event.data.object.id,
674
711
  billingProvider: BillingProviderEnum.STRIPE,
675
712
  cadence: event.data.object.interval,
676
713
  currency: event.data.object.currency,
677
- active: true,
678
- name: typeof event.data.object.product === "string" ? event.data.object.product : event.data.object.product?.id,
714
+ active: product.active,
715
+ name: product.name,
679
716
  price: event.data.object.amount,
680
- externalId: event.data.object.id
717
+ externalId: event.data.object.id,
718
+ features
681
719
  });
682
720
  } else {
683
721
  throw new Error("Invalid plan");
@@ -685,16 +723,20 @@ var StripeWebhookService = class {
685
723
  break;
686
724
  }
687
725
  case "plan.updated": {
688
- if (typeof event.data.object.product === "object" && event.data.object.product != null && event.data.object.amount != null) {
726
+ if (event.data.object.product != null && event.data.object.amount != null) {
727
+ const productId = typeof event.data.object.product === "string" ? event.data.object.product : event.data.object.product.id;
728
+ const product = await this.stripeClient.products.retrieve(productId);
729
+ const features = this.extractFeaturesFromProduct(product);
689
730
  await this.planService.basePlanService.updatePlan({
690
731
  id: event.data.object.id,
691
732
  billingProvider: BillingProviderEnum.STRIPE,
692
733
  cadence: event.data.object.interval,
693
734
  currency: event.data.object.currency,
694
- active: true,
695
- name: typeof event.data.object.product === "string" ? event.data.object.product : event.data.object.product?.id,
735
+ active: product.active,
736
+ name: product.name,
696
737
  price: event.data.object.amount,
697
- externalId: event.data.object.id
738
+ externalId: event.data.object.id,
739
+ features
698
740
  });
699
741
  } else {
700
742
  throw new Error("Invalid plan");
@@ -707,6 +749,69 @@ var StripeWebhookService = class {
707
749
  });
708
750
  break;
709
751
  }
752
+ case "product.created":
753
+ case "product.updated": {
754
+ const product = event.data.object;
755
+ const features = this.extractFeaturesFromProduct(product);
756
+ await this.stripeClient.plans.list({ product: product.id }).autoPagingEach(async (plan) => {
757
+ try {
758
+ await this.planService.basePlanService.updatePlan({
759
+ id: plan.id,
760
+ features,
761
+ active: product.active,
762
+ name: product.name
763
+ });
764
+ } catch (error) {
765
+ this.openTelemetryCollector.warn(
766
+ `Failed to update plan ${plan.id} with product features`,
767
+ error
768
+ );
769
+ }
770
+ });
771
+ await this.stripeClient.prices.list({ product: product.id }).autoPagingEach(async (price) => {
772
+ try {
773
+ await this.planService.basePlanService.updatePlan({
774
+ id: price.id,
775
+ features,
776
+ active: price.active && product.active,
777
+ name: product.name
778
+ });
779
+ } catch (error) {
780
+ this.openTelemetryCollector.warn(
781
+ `Failed to update price-based plan ${price.id} with product features`,
782
+ error
783
+ );
784
+ }
785
+ });
786
+ break;
787
+ }
788
+ // Handle Stripe Prices API (newer alternative to Plans)
789
+ case "price.created":
790
+ case "price.updated": {
791
+ const price = event.data.object;
792
+ if (price.product != null && price.unit_amount != null && price.recurring) {
793
+ const productId = typeof price.product === "string" ? price.product : price.product.id;
794
+ const product = await this.stripeClient.products.retrieve(productId);
795
+ const features = this.extractFeaturesFromProduct(product);
796
+ const planData = {
797
+ id: price.id,
798
+ billingProvider: BillingProviderEnum.STRIPE,
799
+ cadence: price.recurring.interval,
800
+ currency: price.currency,
801
+ active: price.active && product.active,
802
+ name: product.name,
803
+ price: price.unit_amount,
804
+ externalId: price.id,
805
+ features
806
+ };
807
+ if (event.type === "price.created") {
808
+ await this.planService.basePlanService.createPlan(planData);
809
+ } else {
810
+ await this.planService.basePlanService.updatePlan(planData);
811
+ }
812
+ }
813
+ break;
814
+ }
710
815
  case "customer.subscription.created": {
711
816
  if (!event.data.object.items?.data || event.data.object.items.data.length === 0 || !event.data.object.items.data[0]?.plan?.id) {
712
817
  throw new Error(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forklaunch/implementation-billing-stripe",
3
- "version": "0.5.11",
3
+ "version": "0.5.13",
4
4
  "description": "Stripe implementation for forklaunch billing",
5
5
  "homepage": "https://github.com/forklaunch/forklaunch-js#readme",
6
6
  "bugs": {
@@ -42,23 +42,23 @@
42
42
  "lib/**"
43
43
  ],
44
44
  "dependencies": {
45
- "@forklaunch/common": "^0.6.28",
46
- "@forklaunch/core": "^0.18.1",
47
- "@forklaunch/internal": "^0.3.28",
48
- "@forklaunch/validator": "^0.10.28",
49
- "@mikro-orm/core": "^6.6.6",
45
+ "@forklaunch/common": "^0.6.29",
46
+ "@forklaunch/core": "^0.18.2",
47
+ "@forklaunch/internal": "^0.3.29",
48
+ "@forklaunch/validator": "^0.10.29",
49
+ "@mikro-orm/core": "^6.6.7",
50
50
  "@sinclair/typebox": "^0.34.48",
51
- "ajv": "^8.17.1",
52
- "stripe": "^20.3.0",
51
+ "ajv": "^8.18.0",
52
+ "stripe": "^20.3.1",
53
53
  "zod": "^4.3.6",
54
- "@forklaunch/implementation-billing-base": "0.8.11",
55
- "@forklaunch/interfaces-billing": "0.8.11"
54
+ "@forklaunch/implementation-billing-base": "0.8.13",
55
+ "@forklaunch/interfaces-billing": "0.8.13"
56
56
  },
57
57
  "devDependencies": {
58
- "@typescript/native-preview": "7.0.0-dev.20260204.1",
58
+ "@typescript/native-preview": "7.0.0-dev.20260223.1",
59
59
  "depcheck": "^1.4.7",
60
60
  "prettier": "^3.8.1",
61
- "typedoc": "^0.28.16"
61
+ "typedoc": "^0.28.17"
62
62
  },
63
63
  "scripts": {
64
64
  "build": "tsgo --noEmit && tsup domain/schemas/index.ts services/index.ts domain/enum/index.ts domain/types/index.ts --format cjs,esm --no-splitting --dts --tsconfig tsconfig.json --out-dir lib --clean && if [ -f eject-package.bash ]; then pnpm package:eject; fi",