@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.
- package/lib/domain/schemas/index.d.mts +2 -2
- package/lib/domain/schemas/index.d.ts +2 -2
- package/lib/domain/types/index.d.mts +4 -4
- package/lib/domain/types/index.d.ts +4 -4
- package/lib/eject/domain/types/plan.mapper.types.ts +2 -2
- package/lib/eject/domain/types/stripe.dto.types.ts +1 -1
- package/lib/eject/domain/types/stripe.entity.types.ts +1 -1
- package/lib/eject/services/plan.service.ts +25 -8
- package/lib/eject/services/webhook.service.ts +133 -14
- package/lib/services/index.d.mts +7 -0
- package/lib/services/index.d.ts +7 -0
- package/lib/services/index.js +121 -16
- package/lib/services/index.mjs +121 -16
- package/package.json +12 -12
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
25
|
+
stripePlan: Stripe.Product,
|
|
26
26
|
...args: unknown[]
|
|
27
27
|
) => Promise<Entities['UpdatePlanMapper']>;
|
|
28
28
|
};
|
|
@@ -89,7 +89,7 @@ export class StripePlanService<
|
|
|
89
89
|
planDto: StripeCreatePlanDto,
|
|
90
90
|
em?: EntityManager
|
|
91
91
|
): Promise<Dto['PlanMapper']> {
|
|
92
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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 ||
|
package/lib/services/index.d.mts
CHANGED
|
@@ -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
|
|
package/lib/services/index.d.ts
CHANGED
|
@@ -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
|
|
package/lib/services/index.js
CHANGED
|
@@ -336,13 +336,16 @@ var StripePlanService = class {
|
|
|
336
336
|
schemaValidator;
|
|
337
337
|
mappers;
|
|
338
338
|
async createPlan(planDto, em) {
|
|
339
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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:
|
|
710
|
-
name:
|
|
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 (
|
|
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:
|
|
727
|
-
name:
|
|
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(
|
package/lib/services/index.mjs
CHANGED
|
@@ -304,13 +304,16 @@ var StripePlanService = class {
|
|
|
304
304
|
schemaValidator;
|
|
305
305
|
mappers;
|
|
306
306
|
async createPlan(planDto, em) {
|
|
307
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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:
|
|
678
|
-
name:
|
|
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 (
|
|
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:
|
|
695
|
-
name:
|
|
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.
|
|
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.
|
|
46
|
-
"@forklaunch/core": "^0.18.
|
|
47
|
-
"@forklaunch/internal": "^0.3.
|
|
48
|
-
"@forklaunch/validator": "^0.10.
|
|
49
|
-
"@mikro-orm/core": "^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.
|
|
52
|
-
"stripe": "^20.3.
|
|
51
|
+
"ajv": "^8.18.0",
|
|
52
|
+
"stripe": "^20.3.1",
|
|
53
53
|
"zod": "^4.3.6",
|
|
54
|
-
"@forklaunch/implementation-billing-base": "0.8.
|
|
55
|
-
"@forklaunch/interfaces-billing": "0.8.
|
|
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.
|
|
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.
|
|
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",
|