@forklaunch/implementation-billing-base 0.1.12 → 0.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/lib/__test__/schemaEquality.test.d.ts +2 -0
  2. package/lib/__test__/schemaEquality.test.d.ts.map +1 -0
  3. package/lib/__test__/schemaEquality.test.js +409 -0
  4. package/lib/eject/domain/schemas/billingPortal.schema.ts +31 -0
  5. package/lib/eject/domain/schemas/checkoutSession.schema.ts +55 -0
  6. package/lib/eject/domain/schemas/index.ts +5 -0
  7. package/lib/eject/domain/schemas/paymentLink.schema.ts +60 -0
  8. package/lib/eject/domain/schemas/plan.schema.ts +80 -0
  9. package/lib/eject/domain/schemas/subscription.schema.ts +86 -0
  10. package/lib/eject/services/billingPortal.service.ts +145 -0
  11. package/lib/eject/services/checkoutSession.service.ts +132 -0
  12. package/lib/eject/services/index.ts +5 -0
  13. package/lib/eject/services/paymentLink.service.ts +170 -0
  14. package/lib/eject/services/plan.service.ts +132 -0
  15. package/lib/eject/services/subscription.service.ts +239 -0
  16. package/lib/jest.config.d.ts +4 -0
  17. package/lib/jest.config.d.ts.map +1 -0
  18. package/lib/jest.config.js +19 -0
  19. package/lib/schemas/billingPortal.schema.d.ts +123 -0
  20. package/lib/schemas/billingPortal.schema.d.ts.map +1 -0
  21. package/lib/schemas/billingPortal.schema.js +7 -0
  22. package/lib/schemas/checkoutSession.schema.d.ts +179 -0
  23. package/lib/schemas/checkoutSession.schema.d.ts.map +1 -0
  24. package/lib/schemas/checkoutSession.schema.js +7 -0
  25. package/lib/schemas/index.d.ts +6 -0
  26. package/lib/schemas/index.d.ts.map +1 -0
  27. package/lib/schemas/index.js +5 -0
  28. package/lib/schemas/paymentLink.schema.d.ts +220 -0
  29. package/lib/schemas/paymentLink.schema.d.ts.map +1 -0
  30. package/lib/schemas/paymentLink.schema.js +7 -0
  31. package/lib/schemas/plan.schema.d.ts +336 -0
  32. package/lib/schemas/plan.schema.d.ts.map +1 -0
  33. package/lib/schemas/plan.schema.js +7 -0
  34. package/lib/schemas/subscription.schema.d.ts +374 -0
  35. package/lib/schemas/subscription.schema.d.ts.map +1 -0
  36. package/lib/schemas/subscription.schema.js +7 -0
  37. package/lib/schemas/typebox/billingPortal.schema.d.ts +181 -0
  38. package/lib/schemas/typebox/billingPortal.schema.d.ts.map +1 -0
  39. package/lib/schemas/typebox/billingPortal.schema.js +31 -0
  40. package/lib/schemas/typebox/checkoutSession.schema.d.ts +204 -0
  41. package/lib/schemas/typebox/checkoutSession.schema.d.ts.map +1 -0
  42. package/lib/schemas/typebox/checkoutSession.schema.js +44 -0
  43. package/lib/schemas/typebox/paymentLink.schema.d.ts +282 -0
  44. package/lib/schemas/typebox/paymentLink.schema.d.ts.map +1 -0
  45. package/lib/schemas/typebox/paymentLink.schema.js +49 -0
  46. package/lib/schemas/typebox/plan.schema.d.ts +416 -0
  47. package/lib/schemas/typebox/plan.schema.d.ts.map +1 -0
  48. package/lib/schemas/typebox/plan.schema.js +57 -0
  49. package/lib/schemas/typebox/subscription.schema.d.ts +488 -0
  50. package/lib/schemas/typebox/subscription.schema.d.ts.map +1 -0
  51. package/lib/schemas/typebox/subscription.schema.js +61 -0
  52. package/lib/schemas/zod/billingPortal.schema.d.ts +51 -0
  53. package/lib/schemas/zod/billingPortal.schema.d.ts.map +1 -0
  54. package/lib/schemas/zod/billingPortal.schema.js +31 -0
  55. package/lib/schemas/zod/checkoutSession.schema.d.ts +114 -0
  56. package/lib/schemas/zod/checkoutSession.schema.d.ts.map +1 -0
  57. package/lib/schemas/zod/checkoutSession.schema.js +44 -0
  58. package/lib/schemas/zod/paymentLink.schema.d.ts +106 -0
  59. package/lib/schemas/zod/paymentLink.schema.d.ts.map +1 -0
  60. package/lib/schemas/zod/paymentLink.schema.js +49 -0
  61. package/lib/schemas/zod/plan.schema.d.ts +184 -0
  62. package/lib/schemas/zod/plan.schema.d.ts.map +1 -0
  63. package/lib/schemas/zod/plan.schema.js +57 -0
  64. package/lib/schemas/zod/subscription.schema.d.ts +188 -0
  65. package/lib/schemas/zod/subscription.schema.d.ts.map +1 -0
  66. package/lib/schemas/zod/subscription.schema.js +61 -0
  67. package/lib/services/billingPortal.service.d.ts +94 -0
  68. package/lib/services/billingPortal.service.d.ts.map +1 -0
  69. package/lib/services/billingPortal.service.js +70 -0
  70. package/lib/services/checkoutSession.service.d.ts +94 -0
  71. package/lib/services/checkoutSession.service.d.ts.map +1 -0
  72. package/lib/services/checkoutSession.service.js +60 -0
  73. package/lib/services/index.d.ts +6 -0
  74. package/lib/services/index.d.ts.map +1 -0
  75. package/lib/services/index.js +5 -0
  76. package/lib/services/paymentLink.service.d.ts +99 -0
  77. package/lib/services/paymentLink.service.d.ts.map +1 -0
  78. package/lib/services/paymentLink.service.js +90 -0
  79. package/lib/services/plan.service.d.ts +90 -0
  80. package/lib/services/plan.service.d.ts.map +1 -0
  81. package/lib/services/plan.service.js +48 -0
  82. package/lib/services/subscription.service.d.ts +145 -0
  83. package/lib/services/subscription.service.d.ts.map +1 -0
  84. package/lib/services/subscription.service.js +117 -0
  85. package/lib/tsconfig.tsbuildinfo +1 -0
  86. package/lib/vitest.config.d.ts +3 -0
  87. package/lib/vitest.config.d.ts.map +1 -0
  88. package/lib/vitest.config.js +7 -0
  89. package/package.json +8 -8
@@ -0,0 +1,86 @@
1
+ import { LiteralSchema } from '@forklaunch/validator';
2
+ import {
3
+ boolean,
4
+ date,
5
+ enum_,
6
+ optional,
7
+ string,
8
+ unknown,
9
+ uuid
10
+ } from '@{{app_name}}/core';
11
+
12
+ export const CreateSubscriptionSchema = <
13
+ T extends Record<string, LiteralSchema>,
14
+ U extends Record<string, LiteralSchema>
15
+ >(
16
+ PartyEnum: T,
17
+ BillingProviderEnum: U
18
+ ) => ({
19
+ partyId: string,
20
+ partyType: enum_(PartyEnum),
21
+ productId: string,
22
+ description: optional(string),
23
+ active: boolean,
24
+ externalId: string,
25
+ startDate: date,
26
+ endDate: date,
27
+ status: string,
28
+ billingProvider: optional(enum_(BillingProviderEnum)),
29
+ extraFields: optional(unknown)
30
+ });
31
+
32
+ export const UpdateSubscriptionSchema =
33
+ ({ uuidId }: { uuidId: boolean }) =>
34
+ <
35
+ T extends Record<string, LiteralSchema>,
36
+ U extends Record<string, LiteralSchema>
37
+ >(
38
+ PartyEnum: T,
39
+ BillingProviderEnum: U
40
+ ) => ({
41
+ id: uuidId ? uuid : string,
42
+ partyId: optional(string),
43
+ partyType: optional(enum_(PartyEnum)),
44
+ productId: optional(string),
45
+ description: optional(string),
46
+ active: optional(boolean),
47
+ externalId: optional(string),
48
+ startDate: optional(date),
49
+ endDate: optional(date),
50
+ status: optional(string),
51
+ billingProvider: optional(enum_(BillingProviderEnum)),
52
+ extraFields: optional(unknown)
53
+ });
54
+
55
+ export const SubscriptionSchema =
56
+ ({ uuidId }: { uuidId: boolean }) =>
57
+ <
58
+ T extends Record<string, LiteralSchema>,
59
+ U extends Record<string, LiteralSchema>
60
+ >(
61
+ PartyEnum: T,
62
+ BillingProviderEnum: U
63
+ ) => ({
64
+ id: uuidId ? uuid : string,
65
+ partyId: string,
66
+ partyType: enum_(PartyEnum),
67
+ productId: string,
68
+ description: optional(string),
69
+ active: boolean,
70
+ externalId: string,
71
+ startDate: date,
72
+ endDate: date,
73
+ status: string,
74
+ billingProvider: optional(enum_(BillingProviderEnum)),
75
+ extraFields: optional(unknown),
76
+ createdAt: optional(date),
77
+ updatedAt: optional(date)
78
+ });
79
+
80
+ export const BaseSubscriptionServiceSchemas = (options: {
81
+ uuidId: boolean;
82
+ }) => ({
83
+ CreateSubscriptionSchema,
84
+ UpdateSubscriptionSchema: UpdateSubscriptionSchema(options),
85
+ SubscriptionSchema: SubscriptionSchema(options)
86
+ });
@@ -0,0 +1,145 @@
1
+ import { IdDto, InstanceTypeRecord } from '@forklaunch/common';
2
+ import { createCacheKey, TtlCache } from '@forklaunch/core/cache';
3
+ import {
4
+ InternalDtoMapper,
5
+ RequestDtoMapperConstructor,
6
+ ResponseDtoMapperConstructor,
7
+ transformIntoInternalDtoMapper
8
+ } from '@forklaunch/core/mappers';
9
+ import {
10
+ MetricsDefinition,
11
+ OpenTelemetryCollector
12
+ } from '@forklaunch/core/http';
13
+ import { BillingPortalService } from '@forklaunch/interfaces-billing/interfaces';
14
+ import {
15
+ BillingPortalDto,
16
+ CreateBillingPortalDto,
17
+ UpdateBillingPortalDto
18
+ } from '@forklaunch/interfaces-billing/types';
19
+ import { AnySchemaValidator } from '@forklaunch/validator';
20
+
21
+ export class BaseBillingPortalService<
22
+ SchemaValidator extends AnySchemaValidator,
23
+ Metrics extends MetricsDefinition = MetricsDefinition,
24
+ Dto extends {
25
+ BillingPortalDtoMapper: BillingPortalDto;
26
+ CreateBillingPortalDtoMapper: CreateBillingPortalDto;
27
+ UpdateBillingPortalDtoMapper: UpdateBillingPortalDto;
28
+ } = {
29
+ BillingPortalDtoMapper: BillingPortalDto;
30
+ CreateBillingPortalDtoMapper: CreateBillingPortalDto;
31
+ UpdateBillingPortalDtoMapper: UpdateBillingPortalDto;
32
+ },
33
+ Entities extends {
34
+ BillingPortalDtoMapper: BillingPortalDto;
35
+ CreateBillingPortalDtoMapper: BillingPortalDto;
36
+ UpdateBillingPortalDtoMapper: BillingPortalDto;
37
+ } = {
38
+ BillingPortalDtoMapper: BillingPortalDto;
39
+ CreateBillingPortalDtoMapper: BillingPortalDto;
40
+ UpdateBillingPortalDtoMapper: BillingPortalDto;
41
+ }
42
+ > implements BillingPortalService
43
+ {
44
+ #mapperss: InternalDtoMapper<
45
+ InstanceTypeRecord<typeof this.mapperss>,
46
+ Entities,
47
+ Dto
48
+ >;
49
+
50
+ constructor(
51
+ protected cache: TtlCache,
52
+ protected openTelemetryCollector: OpenTelemetryCollector<Metrics>,
53
+ protected schemaValidator: SchemaValidator,
54
+ protected mapperss: {
55
+ BillingPortalDtoMapper: ResponseDtoMapperConstructor<
56
+ SchemaValidator,
57
+ Dto['BillingPortalDtoMapper'],
58
+ Entities['BillingPortalDtoMapper']
59
+ >;
60
+ CreateBillingPortalDtoMapper: RequestDtoMapperConstructor<
61
+ SchemaValidator,
62
+ Dto['CreateBillingPortalDtoMapper'],
63
+ Entities['CreateBillingPortalDtoMapper']
64
+ >;
65
+ UpdateBillingPortalDtoMapper: RequestDtoMapperConstructor<
66
+ SchemaValidator,
67
+ Dto['UpdateBillingPortalDtoMapper'],
68
+ Entities['UpdateBillingPortalDtoMapper']
69
+ >;
70
+ }
71
+ ) {
72
+ this.#mapperss = transformIntoInternalDtoMapper(mapperss, schemaValidator);
73
+ }
74
+
75
+ protected createCacheKey = createCacheKey('billing_portal_session');
76
+
77
+ async createBillingPortalSession(
78
+ billingPortalDto: Dto['CreateBillingPortalDtoMapper']
79
+ ): Promise<Dto['BillingPortalDtoMapper']> {
80
+ const billingPortalSession =
81
+ this.#mapperss.CreateBillingPortalDtoMapper.deserializeDtoToEntity(
82
+ billingPortalDto
83
+ );
84
+
85
+ this.openTelemetryCollector.debug(
86
+ 'Create billing portal session',
87
+ billingPortalSession
88
+ );
89
+
90
+ await this.cache.putRecord({
91
+ key: this.createCacheKey(billingPortalSession.id),
92
+ value: billingPortalSession,
93
+ ttlMilliseconds: this.cache.getTtlMilliseconds()
94
+ });
95
+
96
+ return this.#mapperss.BillingPortalDtoMapper.serializeEntityToDto(
97
+ billingPortalSession
98
+ );
99
+ }
100
+
101
+ async getBillingPortalSession(
102
+ idDto: IdDto
103
+ ): Promise<Dto['BillingPortalDtoMapper']> {
104
+ const billingPortalSessionDetails = await this.cache.readRecord<
105
+ Entities['BillingPortalDtoMapper']
106
+ >(this.createCacheKey(idDto.id));
107
+ if (!billingPortalSessionDetails) {
108
+ throw new Error('Session not found');
109
+ }
110
+
111
+ return this.#mapperss.BillingPortalDtoMapper.serializeEntityToDto(
112
+ billingPortalSessionDetails.value
113
+ );
114
+ }
115
+
116
+ async updateBillingPortalSession(
117
+ billingPortalDto: UpdateBillingPortalDto
118
+ ): Promise<Dto['BillingPortalDtoMapper']> {
119
+ const billingPortalSession =
120
+ this.#mapperss.UpdateBillingPortalDtoMapper.deserializeDtoToEntity(
121
+ billingPortalDto
122
+ );
123
+ // Save the updated session to your database or external service
124
+ await this.cache.putRecord({
125
+ key: this.createCacheKey(billingPortalSession.id),
126
+ value: billingPortalSession,
127
+ ttlMilliseconds: this.cache.getTtlMilliseconds()
128
+ });
129
+
130
+ return this.#mapperss.BillingPortalDtoMapper.serializeEntityToDto(
131
+ billingPortalSession
132
+ );
133
+ }
134
+
135
+ async expireBillingPortalSession(idDto: IdDto): Promise<void> {
136
+ const sessionExists = await this.cache.readRecord(
137
+ this.createCacheKey(idDto.id)
138
+ );
139
+ if (!sessionExists) {
140
+ throw new Error('Session not found');
141
+ }
142
+
143
+ await this.cache.deleteRecord(this.createCacheKey(idDto.id));
144
+ }
145
+ }
@@ -0,0 +1,132 @@
1
+ import { IdDto, InstanceTypeRecord } from '@forklaunch/common';
2
+ import { createCacheKey, TtlCache } from '@forklaunch/core/cache';
3
+ import {
4
+ InternalDtoMapper,
5
+ RequestDtoMapperConstructor,
6
+ ResponseDtoMapperConstructor,
7
+ transformIntoInternalDtoMapper
8
+ } from '@forklaunch/core/mappers';
9
+ import {
10
+ MetricsDefinition,
11
+ OpenTelemetryCollector
12
+ } from '@forklaunch/core/http';
13
+ import { CheckoutSessionService } from '@forklaunch/interfaces-billing/interfaces';
14
+ import {
15
+ CheckoutSessionDto,
16
+ CreateCheckoutSessionDto,
17
+ UpdateCheckoutSessionDto
18
+ } from '@forklaunch/interfaces-billing/types';
19
+ import { AnySchemaValidator } from '@forklaunch/validator';
20
+
21
+ export class BaseCheckoutSessionService<
22
+ SchemaValidator extends AnySchemaValidator,
23
+ PaymentMethodEnum,
24
+ Metrics extends MetricsDefinition = MetricsDefinition,
25
+ Dto extends {
26
+ CheckoutSessionDtoMapper: CheckoutSessionDto<PaymentMethodEnum>;
27
+ CreateCheckoutSessionDtoMapper: CreateCheckoutSessionDto<PaymentMethodEnum>;
28
+ UpdateCheckoutSessionDtoMapper: UpdateCheckoutSessionDto<PaymentMethodEnum>;
29
+ } = {
30
+ CheckoutSessionDtoMapper: CheckoutSessionDto<PaymentMethodEnum>;
31
+ CreateCheckoutSessionDtoMapper: CreateCheckoutSessionDto<PaymentMethodEnum>;
32
+ UpdateCheckoutSessionDtoMapper: UpdateCheckoutSessionDto<PaymentMethodEnum>;
33
+ },
34
+ Entities extends {
35
+ CheckoutSessionDtoMapper: CheckoutSessionDto<PaymentMethodEnum>;
36
+ CreateCheckoutSessionDtoMapper: CheckoutSessionDto<PaymentMethodEnum>;
37
+ UpdateCheckoutSessionDtoMapper: CheckoutSessionDto<PaymentMethodEnum>;
38
+ } = {
39
+ CheckoutSessionDtoMapper: CheckoutSessionDto<PaymentMethodEnum>;
40
+ CreateCheckoutSessionDtoMapper: CheckoutSessionDto<PaymentMethodEnum>;
41
+ UpdateCheckoutSessionDtoMapper: CheckoutSessionDto<PaymentMethodEnum>;
42
+ }
43
+ > implements CheckoutSessionService<PaymentMethodEnum>
44
+ {
45
+ #mapperss: InternalDtoMapper<
46
+ InstanceTypeRecord<typeof this.mapperss>,
47
+ Entities,
48
+ Dto
49
+ >;
50
+
51
+ constructor(
52
+ protected readonly cache: TtlCache,
53
+ protected readonly openTelemetryCollector: OpenTelemetryCollector<Metrics>,
54
+ protected readonly schemaValidator: SchemaValidator,
55
+ protected readonly mapperss: {
56
+ CheckoutSessionDtoMapper: ResponseDtoMapperConstructor<
57
+ SchemaValidator,
58
+ Dto['CheckoutSessionDtoMapper'],
59
+ Entities['CheckoutSessionDtoMapper']
60
+ >;
61
+ CreateCheckoutSessionDtoMapper: RequestDtoMapperConstructor<
62
+ SchemaValidator,
63
+ Dto['CreateCheckoutSessionDtoMapper'],
64
+ Entities['CreateCheckoutSessionDtoMapper']
65
+ >;
66
+ UpdateCheckoutSessionDtoMapper: RequestDtoMapperConstructor<
67
+ SchemaValidator,
68
+ Dto['UpdateCheckoutSessionDtoMapper'],
69
+ Entities['UpdateCheckoutSessionDtoMapper']
70
+ >;
71
+ }
72
+ ) {
73
+ this.#mapperss = transformIntoInternalDtoMapper(mapperss, schemaValidator);
74
+ }
75
+
76
+ protected createCacheKey = createCacheKey('checkout_session');
77
+
78
+ async createCheckoutSession(
79
+ checkoutSessionDto: Dto['CreateCheckoutSessionDtoMapper']
80
+ ): Promise<Dto['CheckoutSessionDtoMapper']> {
81
+ const checkoutSession =
82
+ this.#mapperss.CreateCheckoutSessionDtoMapper.deserializeDtoToEntity(
83
+ checkoutSessionDto
84
+ );
85
+
86
+ // Store the checkoutSession details in the cache
87
+ await this.cache.putRecord({
88
+ key: this.createCacheKey(checkoutSession.id),
89
+ value: checkoutSession,
90
+ ttlMilliseconds: this.cache.getTtlMilliseconds()
91
+ });
92
+
93
+ return this.#mapperss.CheckoutSessionDtoMapper.serializeEntityToDto(
94
+ checkoutSession
95
+ );
96
+ }
97
+
98
+ async getCheckoutSession({
99
+ id
100
+ }: IdDto): Promise<Dto['CheckoutSessionDtoMapper']> {
101
+ const checkoutSessionDetails = await this.cache.readRecord<
102
+ Entities['CheckoutSessionDtoMapper']
103
+ >(this.createCacheKey(id));
104
+ if (!checkoutSessionDetails) {
105
+ throw new Error('Session not found');
106
+ }
107
+
108
+ return this.#mapperss.CheckoutSessionDtoMapper.serializeEntityToDto(
109
+ checkoutSessionDetails.value
110
+ );
111
+ }
112
+
113
+ async expireCheckoutSession({ id }: IdDto): Promise<void> {
114
+ const checkoutSessionDetails = await this.cache.readRecord(
115
+ this.createCacheKey(id)
116
+ );
117
+ if (!checkoutSessionDetails) {
118
+ throw new Error('Session not found');
119
+ }
120
+ await this.cache.deleteRecord(this.createCacheKey(id));
121
+ }
122
+
123
+ async handleCheckoutSuccess({ id }: IdDto): Promise<void> {
124
+ this.openTelemetryCollector.info('Checkout success', { id });
125
+ await this.cache.deleteRecord(this.createCacheKey(id));
126
+ }
127
+
128
+ async handleCheckoutFailure({ id }: IdDto): Promise<void> {
129
+ this.openTelemetryCollector.info('Checkout failure', { id });
130
+ await this.cache.deleteRecord(this.createCacheKey(id));
131
+ }
132
+ }
@@ -0,0 +1,5 @@
1
+ export * from './billingPortal.service';
2
+ export * from './checkoutSession.service';
3
+ export * from './paymentLink.service';
4
+ export * from './plan.service';
5
+ export * from './subscription.service';
@@ -0,0 +1,170 @@
1
+ import { IdDto, IdsDto, InstanceTypeRecord } from '@forklaunch/common';
2
+ import { createCacheKey, TtlCache } from '@forklaunch/core/cache';
3
+ import {
4
+ InternalDtoMapper,
5
+ RequestDtoMapperConstructor,
6
+ ResponseDtoMapperConstructor,
7
+ transformIntoInternalDtoMapper
8
+ } from '@forklaunch/core/mappers';
9
+ import {
10
+ MetricsDefinition,
11
+ OpenTelemetryCollector
12
+ } from '@forklaunch/core/http';
13
+ import { PaymentLinkService } from '@forklaunch/interfaces-billing/interfaces';
14
+ import {
15
+ CreatePaymentLinkDto,
16
+ PaymentLinkDto,
17
+ UpdatePaymentLinkDto
18
+ } from '@forklaunch/interfaces-billing/types';
19
+ import { AnySchemaValidator } from '@forklaunch/validator';
20
+
21
+ export class BasePaymentLinkService<
22
+ SchemaValidator extends AnySchemaValidator,
23
+ CurrencyEnum,
24
+ Metrics extends MetricsDefinition = MetricsDefinition,
25
+ Dto extends {
26
+ PaymentLinkDtoMapper: PaymentLinkDto<CurrencyEnum>;
27
+ CreatePaymentLinkDtoMapper: CreatePaymentLinkDto<CurrencyEnum>;
28
+ UpdatePaymentLinkDtoMapper: UpdatePaymentLinkDto<CurrencyEnum>;
29
+ } = {
30
+ PaymentLinkDtoMapper: PaymentLinkDto<CurrencyEnum>;
31
+ CreatePaymentLinkDtoMapper: CreatePaymentLinkDto<CurrencyEnum>;
32
+ UpdatePaymentLinkDtoMapper: UpdatePaymentLinkDto<CurrencyEnum>;
33
+ },
34
+ Entities extends {
35
+ PaymentLinkDtoMapper: PaymentLinkDto<CurrencyEnum>;
36
+ CreatePaymentLinkDtoMapper: PaymentLinkDto<CurrencyEnum>;
37
+ UpdatePaymentLinkDtoMapper: PaymentLinkDto<CurrencyEnum>;
38
+ } = {
39
+ PaymentLinkDtoMapper: PaymentLinkDto<CurrencyEnum>;
40
+ CreatePaymentLinkDtoMapper: PaymentLinkDto<CurrencyEnum>;
41
+ UpdatePaymentLinkDtoMapper: PaymentLinkDto<CurrencyEnum>;
42
+ }
43
+ > implements PaymentLinkService<CurrencyEnum>
44
+ {
45
+ #mapperss: InternalDtoMapper<
46
+ InstanceTypeRecord<typeof this.mapperss>,
47
+ Entities,
48
+ Dto
49
+ >;
50
+
51
+ constructor(
52
+ protected readonly cache: TtlCache,
53
+ protected readonly openTelemetryCollector: OpenTelemetryCollector<Metrics>,
54
+ protected readonly schemaValidator: SchemaValidator,
55
+ protected readonly mapperss: {
56
+ PaymentLinkDtoMapper: ResponseDtoMapperConstructor<
57
+ SchemaValidator,
58
+ Dto['PaymentLinkDtoMapper'],
59
+ Entities['PaymentLinkDtoMapper']
60
+ >;
61
+ CreatePaymentLinkDtoMapper: RequestDtoMapperConstructor<
62
+ SchemaValidator,
63
+ Dto['CreatePaymentLinkDtoMapper'],
64
+ Entities['CreatePaymentLinkDtoMapper']
65
+ >;
66
+ UpdatePaymentLinkDtoMapper: RequestDtoMapperConstructor<
67
+ SchemaValidator,
68
+ Dto['UpdatePaymentLinkDtoMapper'],
69
+ Entities['UpdatePaymentLinkDtoMapper']
70
+ >;
71
+ }
72
+ ) {
73
+ this.#mapperss = transformIntoInternalDtoMapper(mapperss, schemaValidator);
74
+ }
75
+
76
+ protected cacheKeyPrefix = 'payment_link';
77
+ protected createCacheKey = createCacheKey(this.cacheKeyPrefix);
78
+
79
+ async createPaymentLink(
80
+ paymentLinkDto: Dto['CreatePaymentLinkDtoMapper']
81
+ ): Promise<Dto['PaymentLinkDtoMapper']> {
82
+ // TODO: Perform permission checks here
83
+ const paymentLink =
84
+ this.#mapperss.CreatePaymentLinkDtoMapper.deserializeDtoToEntity(
85
+ paymentLinkDto
86
+ );
87
+ await this.cache.putRecord({
88
+ key: this.createCacheKey(paymentLink.id),
89
+ value: paymentLink,
90
+ ttlMilliseconds: this.cache.getTtlMilliseconds()
91
+ });
92
+
93
+ return this.#mapperss.PaymentLinkDtoMapper.serializeEntityToDto(
94
+ paymentLink
95
+ );
96
+ }
97
+
98
+ async updatePaymentLink(
99
+ paymentLinkDto: Dto['UpdatePaymentLinkDtoMapper']
100
+ ): Promise<Dto['PaymentLinkDtoMapper']> {
101
+ const cacheKey = this.createCacheKey(paymentLinkDto.id);
102
+ const existingLink =
103
+ await this.cache.readRecord<Entities['PaymentLinkDtoMapper']>(cacheKey);
104
+ if (!existingLink) {
105
+ throw new Error('Payment link not found');
106
+ }
107
+ const paymentLink =
108
+ this.#mapperss.UpdatePaymentLinkDtoMapper.deserializeDtoToEntity(
109
+ paymentLinkDto
110
+ );
111
+ const updatedLink = { ...existingLink, ...paymentLink };
112
+ await this.cache.putRecord({
113
+ key: cacheKey,
114
+ value: updatedLink,
115
+ ttlMilliseconds: this.cache.getTtlMilliseconds()
116
+ });
117
+
118
+ return this.#mapperss.PaymentLinkDtoMapper.serializeEntityToDto(
119
+ updatedLink
120
+ );
121
+ }
122
+
123
+ async getPaymentLink({ id }: IdDto): Promise<Dto['PaymentLinkDtoMapper']> {
124
+ const cacheKey = this.createCacheKey(id);
125
+ const paymentLink =
126
+ await this.cache.readRecord<Entities['PaymentLinkDtoMapper']>(cacheKey);
127
+ if (!paymentLink) {
128
+ throw new Error('Payment link not found');
129
+ }
130
+
131
+ return this.#mapperss.PaymentLinkDtoMapper.serializeEntityToDto(
132
+ paymentLink.value
133
+ );
134
+ }
135
+
136
+ async expirePaymentLink({ id }: IdDto): Promise<void> {
137
+ this.openTelemetryCollector.info('Payment link expired', { id });
138
+ await this.cache.deleteRecord(this.createCacheKey(id));
139
+ }
140
+
141
+ async handlePaymentSuccess({ id }: IdDto): Promise<void> {
142
+ this.openTelemetryCollector.info('Payment link success', { id });
143
+ await this.cache.deleteRecord(this.createCacheKey(id));
144
+ }
145
+
146
+ async handlePaymentFailure({ id }: IdDto): Promise<void> {
147
+ this.openTelemetryCollector.info('Payment link failure', { id });
148
+ await this.cache.deleteRecord(this.createCacheKey(id));
149
+ }
150
+
151
+ async listPaymentLinks(
152
+ idsDto?: IdsDto
153
+ ): Promise<Dto['PaymentLinkDtoMapper'][]> {
154
+ const keys =
155
+ idsDto?.ids.map((id) => this.createCacheKey(id)) ??
156
+ (await this.cache.listKeys(this.cacheKeyPrefix));
157
+
158
+ return await Promise.all(
159
+ keys.map(async (key) => {
160
+ const paymentLink =
161
+ await this.cache.readRecord<Entities['PaymentLinkDtoMapper']>(key);
162
+ const paymentLinkDto =
163
+ this.#mapperss.PaymentLinkDtoMapper.serializeEntityToDto(
164
+ paymentLink.value
165
+ );
166
+ return paymentLinkDto;
167
+ })
168
+ );
169
+ }
170
+ }
@@ -0,0 +1,132 @@
1
+ import { IdDto, IdsDto, InstanceTypeRecord } from '@forklaunch/common';
2
+ import {
3
+ InternalDtoMapper,
4
+ RequestDtoMapperConstructor,
5
+ ResponseDtoMapperConstructor,
6
+ transformIntoInternalDtoMapper
7
+ } from '@forklaunch/core/mappers';
8
+ import {
9
+ MetricsDefinition,
10
+ OpenTelemetryCollector
11
+ } from '@forklaunch/core/http';
12
+ import { PlanService } from '@forklaunch/interfaces-billing/interfaces';
13
+ import {
14
+ CreatePlanDto,
15
+ PlanDto,
16
+ UpdatePlanDto
17
+ } from '@forklaunch/interfaces-billing/types';
18
+ import { AnySchemaValidator } from '@forklaunch/validator';
19
+ import { EntityManager } from '@mikro-orm/core';
20
+
21
+ export class BasePlanService<
22
+ SchemaValidator extends AnySchemaValidator,
23
+ PlanCadenceEnum,
24
+ BillingProviderEnum,
25
+ Metrics extends MetricsDefinition = MetricsDefinition,
26
+ Dto extends {
27
+ PlanDtoMapper: PlanDto<PlanCadenceEnum, BillingProviderEnum>;
28
+ CreatePlanDtoMapper: CreatePlanDto<PlanCadenceEnum, BillingProviderEnum>;
29
+ UpdatePlanDtoMapper: UpdatePlanDto<PlanCadenceEnum, BillingProviderEnum>;
30
+ } = {
31
+ PlanDtoMapper: PlanDto<PlanCadenceEnum, BillingProviderEnum>;
32
+ CreatePlanDtoMapper: CreatePlanDto<PlanCadenceEnum, BillingProviderEnum>;
33
+ UpdatePlanDtoMapper: UpdatePlanDto<PlanCadenceEnum, BillingProviderEnum>;
34
+ },
35
+ Entities extends {
36
+ PlanDtoMapper: PlanDto<PlanCadenceEnum, BillingProviderEnum>;
37
+ CreatePlanDtoMapper: PlanDto<PlanCadenceEnum, BillingProviderEnum>;
38
+ UpdatePlanDtoMapper: PlanDto<PlanCadenceEnum, BillingProviderEnum>;
39
+ } = {
40
+ PlanDtoMapper: PlanDto<PlanCadenceEnum, BillingProviderEnum>;
41
+ CreatePlanDtoMapper: PlanDto<PlanCadenceEnum, BillingProviderEnum>;
42
+ UpdatePlanDtoMapper: PlanDto<PlanCadenceEnum, BillingProviderEnum>;
43
+ }
44
+ > implements PlanService<PlanCadenceEnum, BillingProviderEnum>
45
+ {
46
+ #mapperss: InternalDtoMapper<
47
+ InstanceTypeRecord<typeof this.mapperss>,
48
+ Entities,
49
+ Dto
50
+ >;
51
+
52
+ constructor(
53
+ private em: EntityManager,
54
+ private readonly openTelemetryCollector: OpenTelemetryCollector<Metrics>,
55
+ private readonly schemaValidator: SchemaValidator,
56
+ private readonly mapperss: {
57
+ PlanDtoMapper: ResponseDtoMapperConstructor<
58
+ SchemaValidator,
59
+ Dto['PlanDtoMapper'],
60
+ Entities['PlanDtoMapper']
61
+ >;
62
+ CreatePlanDtoMapper: RequestDtoMapperConstructor<
63
+ SchemaValidator,
64
+ Dto['CreatePlanDtoMapper'],
65
+ Entities['CreatePlanDtoMapper']
66
+ >;
67
+ UpdatePlanDtoMapper: RequestDtoMapperConstructor<
68
+ SchemaValidator,
69
+ Dto['UpdatePlanDtoMapper'],
70
+ Entities['UpdatePlanDtoMapper']
71
+ >;
72
+ }
73
+ ) {
74
+ this.#mapperss = transformIntoInternalDtoMapper(mapperss, schemaValidator);
75
+ }
76
+
77
+ async listPlans(
78
+ idsDto?: IdsDto,
79
+ em?: EntityManager
80
+ ): Promise<Dto['PlanDtoMapper'][]> {
81
+ return (
82
+ await (em ?? this.em).findAll('Plan', {
83
+ filters: idsDto?.ids ? { id: { $in: idsDto.ids } } : undefined
84
+ })
85
+ ).map((plan) =>
86
+ this.#mapperss.PlanDtoMapper.serializeEntityToDto(
87
+ plan as Entities['PlanDtoMapper']
88
+ )
89
+ );
90
+ }
91
+
92
+ async createPlan(
93
+ planDto: Dto['CreatePlanDtoMapper'],
94
+ em?: EntityManager
95
+ ): Promise<Dto['PlanDtoMapper']> {
96
+ const plan =
97
+ this.#mapperss.CreatePlanDtoMapper.deserializeDtoToEntity(planDto);
98
+ await (em ?? this.em).transactional(async (innerEm) => {
99
+ await innerEm.persist(plan);
100
+ });
101
+ return this.#mapperss.PlanDtoMapper.serializeEntityToDto(plan);
102
+ }
103
+
104
+ async getPlan(
105
+ idDto: IdDto,
106
+ em?: EntityManager
107
+ ): Promise<Dto['PlanDtoMapper']> {
108
+ const plan = await (em ?? this.em).findOneOrFail('Plan', idDto);
109
+ return this.#mapperss.PlanDtoMapper.serializeEntityToDto(
110
+ plan as Entities['PlanDtoMapper']
111
+ );
112
+ }
113
+
114
+ async updatePlan(
115
+ planDto: Dto['UpdatePlanDtoMapper'],
116
+ em?: EntityManager
117
+ ): Promise<Dto['PlanDtoMapper']> {
118
+ const plan =
119
+ this.#mapperss.UpdatePlanDtoMapper.deserializeDtoToEntity(planDto);
120
+ const updatedPlan = await (em ?? this.em).upsert(plan);
121
+ await (em ?? this.em).transactional(async (innerEm) => {
122
+ await innerEm.persist(plan);
123
+ });
124
+ const updatedPlanDto =
125
+ this.#mapperss.PlanDtoMapper.serializeEntityToDto(updatedPlan);
126
+ return updatedPlanDto;
127
+ }
128
+
129
+ async deletePlan(idDto: { id: string }, em?: EntityManager): Promise<void> {
130
+ await (em ?? this.em).nativeDelete('Plan', idDto);
131
+ }
132
+ }