@forklaunch/implementation-billing-stripe 0.0.1
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/LICENSE +21 -0
- package/lib/domain/index.d.mts +189 -0
- package/lib/domain/index.d.ts +189 -0
- package/lib/domain/index.js +231 -0
- package/lib/domain/index.mjs +201 -0
- package/lib/eject/domain/schemas/billingPortal.schema.ts +37 -0
- package/lib/eject/domain/schemas/checkoutSession.schema.ts +74 -0
- package/lib/eject/domain/schemas/index.ts +5 -0
- package/lib/eject/domain/schemas/paymentLink.schema.ts +62 -0
- package/lib/eject/domain/schemas/plan.schema.ts +68 -0
- package/lib/eject/domain/schemas/subscription.schema.ts +78 -0
- package/lib/eject/services/billingPortal.service.ts +185 -0
- package/lib/eject/services/checkoutSession.service.ts +179 -0
- package/lib/eject/services/index.ts +6 -0
- package/lib/eject/services/paymentLink.service.ts +239 -0
- package/lib/eject/services/plan.service.ts +219 -0
- package/lib/eject/services/subscription.service.ts +285 -0
- package/lib/eject/services/webhook.service.ts +272 -0
- package/lib/eject/types/index.ts +2 -0
- package/lib/eject/types/stripe.dto.types.ts +214 -0
- package/lib/eject/types/stripe.entity.types.ts +83 -0
- package/lib/schemas/index.d.mts +376 -0
- package/lib/schemas/index.d.ts +376 -0
- package/lib/schemas/index.js +704 -0
- package/lib/schemas/index.mjs +737 -0
- package/lib/services/index.d.mts +223 -0
- package/lib/services/index.d.ts +223 -0
- package/lib/services/index.js +788 -0
- package/lib/services/index.mjs +776 -0
- package/lib/types/index.d.mts +121 -0
- package/lib/types/index.d.ts +121 -0
- package/lib/types/index.js +18 -0
- package/lib/types/index.mjs +0 -0
- package/package.json +74 -0
|
@@ -0,0 +1,788 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// services/index.ts
|
|
21
|
+
var services_exports = {};
|
|
22
|
+
__export(services_exports, {
|
|
23
|
+
StripeBillingPortalService: () => StripeBillingPortalService,
|
|
24
|
+
StripeCheckoutSessionService: () => StripeCheckoutSessionService,
|
|
25
|
+
StripePaymentLinkService: () => StripePaymentLinkService,
|
|
26
|
+
StripePlanService: () => StripePlanService,
|
|
27
|
+
StripeSubscriptionService: () => StripeSubscriptionService,
|
|
28
|
+
StripeWebhookService: () => StripeWebhookService
|
|
29
|
+
});
|
|
30
|
+
module.exports = __toCommonJS(services_exports);
|
|
31
|
+
|
|
32
|
+
// services/billingPortal.service.ts
|
|
33
|
+
var import_services = require("@forklaunch/implementation-billing-base/services");
|
|
34
|
+
var import_internal = require("@forklaunch/internal");
|
|
35
|
+
var StripeBillingPortalService = class {
|
|
36
|
+
constructor(stripeClient, em, cache, openTelemetryCollector, schemaValidator, mappers, options) {
|
|
37
|
+
this.stripeClient = stripeClient;
|
|
38
|
+
this.em = em;
|
|
39
|
+
this.cache = cache;
|
|
40
|
+
this.openTelemetryCollector = openTelemetryCollector;
|
|
41
|
+
this.schemaValidator = schemaValidator;
|
|
42
|
+
this.mappers = mappers;
|
|
43
|
+
this.options = options;
|
|
44
|
+
this._mappers = (0, import_internal.transformIntoInternalMapper)(mappers, schemaValidator);
|
|
45
|
+
this.baseBillingPortalService = new import_services.BaseBillingPortalService(
|
|
46
|
+
em,
|
|
47
|
+
cache,
|
|
48
|
+
openTelemetryCollector,
|
|
49
|
+
schemaValidator,
|
|
50
|
+
{
|
|
51
|
+
BillingPortalMapper: import_internal.IdentityResponseMapper,
|
|
52
|
+
CreateBillingPortalMapper: import_internal.IdentityRequestMapper,
|
|
53
|
+
UpdateBillingPortalMapper: import_internal.IdentityRequestMapper
|
|
54
|
+
},
|
|
55
|
+
options
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
billingPortalSessionExpiryDurationMs = 5 * 60 * 1e3;
|
|
59
|
+
baseBillingPortalService;
|
|
60
|
+
_mappers;
|
|
61
|
+
async createBillingPortalSession(billingPortalDto) {
|
|
62
|
+
const session = await this.stripeClient.billingPortal.sessions.create({
|
|
63
|
+
...billingPortalDto.stripeFields,
|
|
64
|
+
customer: billingPortalDto.customerId
|
|
65
|
+
});
|
|
66
|
+
const billingPortalEntity = await this.baseBillingPortalService.createBillingPortalSession(
|
|
67
|
+
await this._mappers.CreateBillingPortalMapper.deserializeDtoToEntity(
|
|
68
|
+
{
|
|
69
|
+
...billingPortalDto,
|
|
70
|
+
id: session.id,
|
|
71
|
+
uri: session.url,
|
|
72
|
+
expiresAt: new Date(
|
|
73
|
+
Date.now() + this.billingPortalSessionExpiryDurationMs
|
|
74
|
+
)
|
|
75
|
+
},
|
|
76
|
+
this.em,
|
|
77
|
+
session
|
|
78
|
+
)
|
|
79
|
+
);
|
|
80
|
+
return this._mappers.BillingPortalMapper.serializeEntityToDto(
|
|
81
|
+
billingPortalEntity
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
async getBillingPortalSession(idDto) {
|
|
85
|
+
const billingPortalEntity = await this.baseBillingPortalService.getBillingPortalSession(idDto);
|
|
86
|
+
return this._mappers.BillingPortalMapper.serializeEntityToDto(
|
|
87
|
+
billingPortalEntity
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
async expireBillingPortalSession(idDto) {
|
|
91
|
+
return this.baseBillingPortalService.expireBillingPortalSession(idDto);
|
|
92
|
+
}
|
|
93
|
+
async updateBillingPortalSession(billingPortalDto) {
|
|
94
|
+
const existingSession = await this.baseBillingPortalService.getBillingPortalSession({
|
|
95
|
+
id: billingPortalDto.id
|
|
96
|
+
});
|
|
97
|
+
const session = await this.stripeClient.billingPortal.sessions.create({
|
|
98
|
+
...billingPortalDto.stripeFields,
|
|
99
|
+
customer: existingSession.customerId
|
|
100
|
+
});
|
|
101
|
+
const baseBillingPortalDto = await this.baseBillingPortalService.updateBillingPortalSession(
|
|
102
|
+
await this._mappers.UpdateBillingPortalMapper.deserializeDtoToEntity(
|
|
103
|
+
{
|
|
104
|
+
...billingPortalDto,
|
|
105
|
+
id: session.id,
|
|
106
|
+
uri: session.url,
|
|
107
|
+
expiresAt: new Date(
|
|
108
|
+
Date.now() + this.billingPortalSessionExpiryDurationMs
|
|
109
|
+
)
|
|
110
|
+
},
|
|
111
|
+
this.em,
|
|
112
|
+
session
|
|
113
|
+
)
|
|
114
|
+
);
|
|
115
|
+
return this._mappers.BillingPortalMapper.serializeEntityToDto(
|
|
116
|
+
baseBillingPortalDto
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// services/checkoutSession.service.ts
|
|
122
|
+
var import_services2 = require("@forklaunch/implementation-billing-base/services");
|
|
123
|
+
var import_internal2 = require("@forklaunch/internal");
|
|
124
|
+
var StripeCheckoutSessionService = class {
|
|
125
|
+
constructor(stripeClient, em, cache, openTelemetryCollector, schemaValidator, mappers, options) {
|
|
126
|
+
this.stripeClient = stripeClient;
|
|
127
|
+
this.em = em;
|
|
128
|
+
this.cache = cache;
|
|
129
|
+
this.openTelemetryCollector = openTelemetryCollector;
|
|
130
|
+
this.schemaValidator = schemaValidator;
|
|
131
|
+
this.mappers = mappers;
|
|
132
|
+
this.options = options;
|
|
133
|
+
this._mappers = (0, import_internal2.transformIntoInternalMapper)(mappers, schemaValidator);
|
|
134
|
+
this.baseCheckoutSessionService = new import_services2.BaseCheckoutSessionService(
|
|
135
|
+
em,
|
|
136
|
+
cache,
|
|
137
|
+
openTelemetryCollector,
|
|
138
|
+
schemaValidator,
|
|
139
|
+
{
|
|
140
|
+
CheckoutSessionMapper: import_internal2.IdentityResponseMapper,
|
|
141
|
+
CreateCheckoutSessionMapper: import_internal2.IdentityRequestMapper,
|
|
142
|
+
UpdateCheckoutSessionMapper: import_internal2.IdentityRequestMapper
|
|
143
|
+
},
|
|
144
|
+
options
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
baseCheckoutSessionService;
|
|
148
|
+
_mappers;
|
|
149
|
+
async createCheckoutSession(checkoutSessionDto) {
|
|
150
|
+
const session = await this.stripeClient.checkout.sessions.create({
|
|
151
|
+
...checkoutSessionDto.stripeFields,
|
|
152
|
+
payment_method_types: checkoutSessionDto.paymentMethods,
|
|
153
|
+
currency: checkoutSessionDto.currency,
|
|
154
|
+
success_url: checkoutSessionDto.successRedirectUri,
|
|
155
|
+
cancel_url: checkoutSessionDto.cancelRedirectUri
|
|
156
|
+
});
|
|
157
|
+
const checkoutSessionEntity = await this.baseCheckoutSessionService.createCheckoutSession(
|
|
158
|
+
await this._mappers.CreateCheckoutSessionMapper.deserializeDtoToEntity(
|
|
159
|
+
{
|
|
160
|
+
...checkoutSessionDto,
|
|
161
|
+
id: session.id,
|
|
162
|
+
uri: session.url,
|
|
163
|
+
expiresAt: new Date(Date.now() + 5 * 60 * 1e3),
|
|
164
|
+
providerFields: session
|
|
165
|
+
},
|
|
166
|
+
this.em,
|
|
167
|
+
session
|
|
168
|
+
)
|
|
169
|
+
);
|
|
170
|
+
return this._mappers.CheckoutSessionMapper.serializeEntityToDto(
|
|
171
|
+
checkoutSessionEntity
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
async getCheckoutSession({
|
|
175
|
+
id
|
|
176
|
+
}) {
|
|
177
|
+
const databaseCheckoutSession = await this.baseCheckoutSessionService.getCheckoutSession({ id });
|
|
178
|
+
return {
|
|
179
|
+
...this._mappers.CheckoutSessionMapper.serializeEntityToDto(
|
|
180
|
+
databaseCheckoutSession
|
|
181
|
+
),
|
|
182
|
+
stripeFields: await this.stripeClient.checkout.sessions.retrieve(id)
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
async expireCheckoutSession({ id }) {
|
|
186
|
+
await this.stripeClient.checkout.sessions.expire(id);
|
|
187
|
+
await this.baseCheckoutSessionService.expireCheckoutSession({ id });
|
|
188
|
+
}
|
|
189
|
+
async handleCheckoutSuccess({ id }) {
|
|
190
|
+
await this.stripeClient.checkout.sessions.update(id, {
|
|
191
|
+
metadata: {
|
|
192
|
+
status: "SUCCESS"
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
await this.baseCheckoutSessionService.handleCheckoutSuccess({ id });
|
|
196
|
+
}
|
|
197
|
+
async handleCheckoutFailure({ id }) {
|
|
198
|
+
await this.stripeClient.checkout.sessions.update(id, {
|
|
199
|
+
metadata: {
|
|
200
|
+
status: "FAILED"
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
await this.baseCheckoutSessionService.handleCheckoutFailure({ id });
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
// services/paymentLink.service.ts
|
|
208
|
+
var import_services3 = require("@forklaunch/implementation-billing-base/services");
|
|
209
|
+
var import_internal3 = require("@forklaunch/internal");
|
|
210
|
+
var StripePaymentLinkService = class {
|
|
211
|
+
constructor(stripeClient, em, cache, openTelemetryCollector, schemaValidator, mappers, options) {
|
|
212
|
+
this.stripeClient = stripeClient;
|
|
213
|
+
this.em = em;
|
|
214
|
+
this.cache = cache;
|
|
215
|
+
this.openTelemetryCollector = openTelemetryCollector;
|
|
216
|
+
this.schemaValidator = schemaValidator;
|
|
217
|
+
this.mappers = mappers;
|
|
218
|
+
this.options = options;
|
|
219
|
+
this._mappers = (0, import_internal3.transformIntoInternalMapper)(mappers, schemaValidator);
|
|
220
|
+
this.basePaymentLinkService = new import_services3.BasePaymentLinkService(
|
|
221
|
+
em,
|
|
222
|
+
cache,
|
|
223
|
+
openTelemetryCollector,
|
|
224
|
+
schemaValidator,
|
|
225
|
+
{
|
|
226
|
+
PaymentLinkMapper: import_internal3.IdentityResponseMapper,
|
|
227
|
+
CreatePaymentLinkMapper: import_internal3.IdentityRequestMapper,
|
|
228
|
+
UpdatePaymentLinkMapper: import_internal3.IdentityRequestMapper
|
|
229
|
+
},
|
|
230
|
+
options
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
basePaymentLinkService;
|
|
234
|
+
_mappers;
|
|
235
|
+
async createPaymentLink(paymentLinkDto) {
|
|
236
|
+
const session = await this.stripeClient.paymentLinks.create({
|
|
237
|
+
...paymentLinkDto.stripeFields,
|
|
238
|
+
payment_method_types: paymentLinkDto.paymentMethods,
|
|
239
|
+
currency: paymentLinkDto.currency
|
|
240
|
+
});
|
|
241
|
+
const paymentLinkEntity = await this.basePaymentLinkService.createPaymentLink(
|
|
242
|
+
await this._mappers.CreatePaymentLinkMapper.deserializeDtoToEntity(
|
|
243
|
+
{
|
|
244
|
+
...paymentLinkDto,
|
|
245
|
+
id: session.id,
|
|
246
|
+
amount: session.line_items?.data.reduce(
|
|
247
|
+
(total, item) => total + item.amount_total,
|
|
248
|
+
0
|
|
249
|
+
) ?? 0
|
|
250
|
+
},
|
|
251
|
+
this.em,
|
|
252
|
+
session
|
|
253
|
+
)
|
|
254
|
+
);
|
|
255
|
+
return this._mappers.PaymentLinkMapper.serializeEntityToDto(
|
|
256
|
+
paymentLinkEntity
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
async updatePaymentLink(paymentLinkDto) {
|
|
260
|
+
const session = await this.stripeClient.paymentLinks.update(
|
|
261
|
+
paymentLinkDto.id,
|
|
262
|
+
{
|
|
263
|
+
...paymentLinkDto.stripeFields,
|
|
264
|
+
payment_method_types: paymentLinkDto.paymentMethods
|
|
265
|
+
}
|
|
266
|
+
);
|
|
267
|
+
const paymentLinkEntity = await this.basePaymentLinkService.updatePaymentLink(
|
|
268
|
+
await this._mappers.UpdatePaymentLinkMapper.deserializeDtoToEntity(
|
|
269
|
+
{
|
|
270
|
+
...paymentLinkDto,
|
|
271
|
+
id: session.id,
|
|
272
|
+
amount: session.line_items?.data.reduce(
|
|
273
|
+
(total, item) => total + item.amount_total,
|
|
274
|
+
0
|
|
275
|
+
) ?? 0
|
|
276
|
+
},
|
|
277
|
+
this.em,
|
|
278
|
+
session
|
|
279
|
+
)
|
|
280
|
+
);
|
|
281
|
+
return this._mappers.PaymentLinkMapper.serializeEntityToDto(
|
|
282
|
+
paymentLinkEntity
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
async getPaymentLink({ id }) {
|
|
286
|
+
const databasePaymentLink = await this.basePaymentLinkService.getPaymentLink({ id });
|
|
287
|
+
return {
|
|
288
|
+
...this._mappers.PaymentLinkMapper.serializeEntityToDto(
|
|
289
|
+
databasePaymentLink
|
|
290
|
+
),
|
|
291
|
+
stripeFields: await this.stripeClient.paymentLinks.retrieve(id)
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
async expirePaymentLink({ id }) {
|
|
295
|
+
await this.stripeClient.paymentLinks.update(id, {
|
|
296
|
+
metadata: {
|
|
297
|
+
status: "EXPIRED"
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
await this.basePaymentLinkService.expirePaymentLink({ id });
|
|
301
|
+
}
|
|
302
|
+
async handlePaymentSuccess({ id }) {
|
|
303
|
+
await this.stripeClient.paymentLinks.update(id, {
|
|
304
|
+
metadata: {
|
|
305
|
+
status: "COMPLETED"
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
await this.basePaymentLinkService.handlePaymentSuccess({ id });
|
|
309
|
+
}
|
|
310
|
+
async handlePaymentFailure({ id }) {
|
|
311
|
+
await this.stripeClient.paymentLinks.update(id, {
|
|
312
|
+
metadata: {
|
|
313
|
+
status: "FAILED"
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
await this.basePaymentLinkService.handlePaymentFailure({ id });
|
|
317
|
+
}
|
|
318
|
+
async listPaymentLinks(idsDto) {
|
|
319
|
+
const paymentLinks = await this.stripeClient.paymentLinks.list({
|
|
320
|
+
active: true
|
|
321
|
+
});
|
|
322
|
+
return await Promise.all(
|
|
323
|
+
(await this.basePaymentLinkService.listPaymentLinks(idsDto)).map(
|
|
324
|
+
async (paymentLink) => ({
|
|
325
|
+
...await this._mappers.PaymentLinkMapper.serializeEntityToDto(
|
|
326
|
+
paymentLink
|
|
327
|
+
),
|
|
328
|
+
stripeFields: paymentLinks.data.find(
|
|
329
|
+
(paymentLink2) => paymentLink2.id === paymentLink2.id
|
|
330
|
+
)
|
|
331
|
+
})
|
|
332
|
+
)
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
// services/plan.service.ts
|
|
338
|
+
var import_services4 = require("@forklaunch/implementation-billing-base/services");
|
|
339
|
+
var import_internal4 = require("@forklaunch/internal");
|
|
340
|
+
var StripePlanService = class {
|
|
341
|
+
constructor(stripeClient, em, openTelemetryCollector, schemaValidator, mappers, options) {
|
|
342
|
+
this.stripeClient = stripeClient;
|
|
343
|
+
this.em = em;
|
|
344
|
+
this.openTelemetryCollector = openTelemetryCollector;
|
|
345
|
+
this.schemaValidator = schemaValidator;
|
|
346
|
+
this.mappers = mappers;
|
|
347
|
+
this.options = options;
|
|
348
|
+
this._mappers = (0, import_internal4.transformIntoInternalMapper)(mappers, schemaValidator);
|
|
349
|
+
this.basePlanService = new import_services4.BasePlanService(
|
|
350
|
+
em,
|
|
351
|
+
openTelemetryCollector,
|
|
352
|
+
schemaValidator,
|
|
353
|
+
{
|
|
354
|
+
PlanMapper: import_internal4.IdentityResponseMapper,
|
|
355
|
+
CreatePlanMapper: import_internal4.IdentityRequestMapper,
|
|
356
|
+
UpdatePlanMapper: import_internal4.IdentityRequestMapper
|
|
357
|
+
},
|
|
358
|
+
options
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
basePlanService;
|
|
362
|
+
_mappers;
|
|
363
|
+
async createPlan(planDto, em) {
|
|
364
|
+
const plan = await this.stripeClient.plans.create({
|
|
365
|
+
...planDto.stripeFields,
|
|
366
|
+
interval: planDto.cadence,
|
|
367
|
+
product: planDto.name,
|
|
368
|
+
currency: planDto.currency
|
|
369
|
+
});
|
|
370
|
+
const planEntity = await this.basePlanService.createPlan(
|
|
371
|
+
await this._mappers.CreatePlanMapper.deserializeDtoToEntity(
|
|
372
|
+
{
|
|
373
|
+
...planDto,
|
|
374
|
+
externalId: plan.id,
|
|
375
|
+
billingProvider: "stripe"
|
|
376
|
+
},
|
|
377
|
+
em ?? this.em,
|
|
378
|
+
plan
|
|
379
|
+
),
|
|
380
|
+
em
|
|
381
|
+
);
|
|
382
|
+
return this._mappers.PlanMapper.serializeEntityToDto(planEntity);
|
|
383
|
+
}
|
|
384
|
+
async getPlan(idDto, em) {
|
|
385
|
+
const plan = await this.stripeClient.plans.retrieve(idDto.id);
|
|
386
|
+
const id = (await em?.findOne(
|
|
387
|
+
this.options?.databaseTableName ?? "plan",
|
|
388
|
+
{ externalId: idDto.id }
|
|
389
|
+
))?.id;
|
|
390
|
+
if (!id) {
|
|
391
|
+
throw new Error("Plan not found");
|
|
392
|
+
}
|
|
393
|
+
return {
|
|
394
|
+
...await this._mappers.PlanMapper.serializeEntityToDto(
|
|
395
|
+
await this.basePlanService.getPlan({ id }, em)
|
|
396
|
+
),
|
|
397
|
+
stripeFields: plan
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
async updatePlan(planDto, em) {
|
|
401
|
+
const existingPlan = await this.stripeClient.plans.retrieve(planDto.id);
|
|
402
|
+
const plan = await this.stripeClient.plans.del(planDto.id).then(
|
|
403
|
+
() => this.stripeClient.plans.create({
|
|
404
|
+
...planDto.stripeFields,
|
|
405
|
+
interval: planDto.cadence ?? existingPlan.interval,
|
|
406
|
+
product: planDto.name,
|
|
407
|
+
currency: planDto.currency ?? existingPlan.currency
|
|
408
|
+
})
|
|
409
|
+
);
|
|
410
|
+
const planEntity = await this.basePlanService.updatePlan(
|
|
411
|
+
await this._mappers.UpdatePlanMapper.deserializeDtoToEntity(
|
|
412
|
+
{
|
|
413
|
+
...planDto,
|
|
414
|
+
externalId: plan.id,
|
|
415
|
+
billingProvider: "stripe"
|
|
416
|
+
},
|
|
417
|
+
em ?? this.em,
|
|
418
|
+
plan
|
|
419
|
+
),
|
|
420
|
+
em
|
|
421
|
+
);
|
|
422
|
+
return this._mappers.PlanMapper.serializeEntityToDto(planEntity);
|
|
423
|
+
}
|
|
424
|
+
async deletePlan(idDto, em) {
|
|
425
|
+
await this.stripeClient.plans.del(idDto.id);
|
|
426
|
+
await this.basePlanService.deletePlan(idDto, em);
|
|
427
|
+
}
|
|
428
|
+
async listPlans(idsDto, em) {
|
|
429
|
+
const plans = await this.stripeClient.plans.list({
|
|
430
|
+
active: true
|
|
431
|
+
});
|
|
432
|
+
const ids = (await em?.findAll(
|
|
433
|
+
this.options?.databaseTableName ?? "plan",
|
|
434
|
+
{ where: { externalId: { $in: plans.data.map((plan) => plan.id) } } }
|
|
435
|
+
))?.filter((s) => idsDto?.ids?.includes(s.id))?.map((s) => s.id);
|
|
436
|
+
if (!ids) {
|
|
437
|
+
throw new Error("Plans not found");
|
|
438
|
+
}
|
|
439
|
+
return await Promise.all(
|
|
440
|
+
(await this.basePlanService.listPlans({ ids }, em)).map(async (plan) => ({
|
|
441
|
+
...await this._mappers.PlanMapper.serializeEntityToDto(plan),
|
|
442
|
+
stripeFields: plans.data.find(
|
|
443
|
+
(stripePlan) => stripePlan.id === plan.externalId
|
|
444
|
+
)
|
|
445
|
+
}))
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
// services/subscription.service.ts
|
|
451
|
+
var import_services5 = require("@forklaunch/implementation-billing-base/services");
|
|
452
|
+
var import_internal5 = require("@forklaunch/internal");
|
|
453
|
+
var StripeSubscriptionService = class {
|
|
454
|
+
constructor(stripe, em, openTelemetryCollector, schemaValidator, mappers, options) {
|
|
455
|
+
this.stripe = stripe;
|
|
456
|
+
this.em = em;
|
|
457
|
+
this.openTelemetryCollector = openTelemetryCollector;
|
|
458
|
+
this.schemaValidator = schemaValidator;
|
|
459
|
+
this.mappers = mappers;
|
|
460
|
+
this.options = options;
|
|
461
|
+
this._mappers = (0, import_internal5.transformIntoInternalMapper)(mappers, schemaValidator);
|
|
462
|
+
this.baseSubscriptionService = new import_services5.BaseSubscriptionService(
|
|
463
|
+
em,
|
|
464
|
+
openTelemetryCollector,
|
|
465
|
+
schemaValidator,
|
|
466
|
+
{
|
|
467
|
+
SubscriptionMapper: import_internal5.IdentityResponseMapper,
|
|
468
|
+
CreateSubscriptionMapper: import_internal5.IdentityRequestMapper,
|
|
469
|
+
UpdateSubscriptionMapper: import_internal5.IdentityRequestMapper
|
|
470
|
+
},
|
|
471
|
+
options
|
|
472
|
+
);
|
|
473
|
+
}
|
|
474
|
+
baseSubscriptionService;
|
|
475
|
+
_mappers;
|
|
476
|
+
async createSubscription(subscriptionDto, em) {
|
|
477
|
+
const subscription = await this.stripe.subscriptions.create({
|
|
478
|
+
...subscriptionDto.stripeFields,
|
|
479
|
+
customer: subscriptionDto.partyId,
|
|
480
|
+
items: [
|
|
481
|
+
{
|
|
482
|
+
plan: subscriptionDto.productId
|
|
483
|
+
}
|
|
484
|
+
]
|
|
485
|
+
});
|
|
486
|
+
const subscriptionEntity = await this.baseSubscriptionService.createSubscription(
|
|
487
|
+
await this._mappers.CreateSubscriptionMapper.deserializeDtoToEntity(
|
|
488
|
+
{
|
|
489
|
+
...subscriptionDto,
|
|
490
|
+
externalId: subscription.id,
|
|
491
|
+
billingProvider: "stripe"
|
|
492
|
+
},
|
|
493
|
+
em ?? this.em,
|
|
494
|
+
subscription
|
|
495
|
+
),
|
|
496
|
+
em
|
|
497
|
+
);
|
|
498
|
+
return this._mappers.SubscriptionMapper.serializeEntityToDto(
|
|
499
|
+
subscriptionEntity
|
|
500
|
+
);
|
|
501
|
+
}
|
|
502
|
+
async getSubscription(idDto, em) {
|
|
503
|
+
return {
|
|
504
|
+
...await this._mappers.SubscriptionMapper.serializeEntityToDto(
|
|
505
|
+
await this.baseSubscriptionService.getSubscription(idDto, em)
|
|
506
|
+
),
|
|
507
|
+
stripeFields: await this.stripe.subscriptions.retrieve(idDto.id)
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
async getUserSubscription(idDto, em) {
|
|
511
|
+
return {
|
|
512
|
+
...await this._mappers.SubscriptionMapper.serializeEntityToDto(
|
|
513
|
+
await this.baseSubscriptionService.getUserSubscription(idDto, em)
|
|
514
|
+
),
|
|
515
|
+
stripeFields: await this.stripe.subscriptions.retrieve(idDto.id)
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
async getOrganizationSubscription(idDto, em) {
|
|
519
|
+
const id = (await em?.findOne(
|
|
520
|
+
this.options?.databaseTableName ?? "subscription",
|
|
521
|
+
{ externalId: idDto.id }
|
|
522
|
+
))?.id;
|
|
523
|
+
if (!id) {
|
|
524
|
+
throw new Error("Subscription not found");
|
|
525
|
+
}
|
|
526
|
+
return {
|
|
527
|
+
...await this._mappers.SubscriptionMapper.serializeEntityToDto(
|
|
528
|
+
await this.baseSubscriptionService.getOrganizationSubscription(
|
|
529
|
+
{ id },
|
|
530
|
+
em
|
|
531
|
+
)
|
|
532
|
+
),
|
|
533
|
+
stripeFields: await this.stripe.subscriptions.retrieve(idDto.id)
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
async updateSubscription(subscriptionDto, em) {
|
|
537
|
+
const subscription = await this.stripe.subscriptions.update(
|
|
538
|
+
subscriptionDto.id,
|
|
539
|
+
{
|
|
540
|
+
...subscriptionDto.stripeFields,
|
|
541
|
+
items: [
|
|
542
|
+
{
|
|
543
|
+
plan: subscriptionDto.productId
|
|
544
|
+
}
|
|
545
|
+
]
|
|
546
|
+
}
|
|
547
|
+
);
|
|
548
|
+
const subscriptionEntity = await this.baseSubscriptionService.updateSubscription(
|
|
549
|
+
await this._mappers.UpdateSubscriptionMapper.deserializeDtoToEntity(
|
|
550
|
+
{
|
|
551
|
+
...subscriptionDto,
|
|
552
|
+
externalId: subscription.id,
|
|
553
|
+
billingProvider: "stripe",
|
|
554
|
+
providerFields: subscription
|
|
555
|
+
},
|
|
556
|
+
em ?? this.em,
|
|
557
|
+
subscription
|
|
558
|
+
),
|
|
559
|
+
em
|
|
560
|
+
);
|
|
561
|
+
return this._mappers.SubscriptionMapper.serializeEntityToDto(
|
|
562
|
+
subscriptionEntity
|
|
563
|
+
);
|
|
564
|
+
}
|
|
565
|
+
async deleteSubscription(idDto, em) {
|
|
566
|
+
await this.stripe.subscriptions.cancel(idDto.id);
|
|
567
|
+
await this.baseSubscriptionService.deleteSubscription(idDto, em);
|
|
568
|
+
}
|
|
569
|
+
async listSubscriptions(idsDto, em) {
|
|
570
|
+
const subscriptions = (await this.stripe.subscriptions.list({
|
|
571
|
+
status: "active"
|
|
572
|
+
})).data.filter((s) => idsDto.ids?.includes(s.id));
|
|
573
|
+
const ids = (await em?.findAll(
|
|
574
|
+
this.options?.databaseTableName ?? "subscription",
|
|
575
|
+
{ where: { externalId: { $in: subscriptions.map((s) => s.id) } } }
|
|
576
|
+
))?.map((s) => s.id);
|
|
577
|
+
if (!ids) {
|
|
578
|
+
throw new Error("Subscriptions not found");
|
|
579
|
+
}
|
|
580
|
+
return await Promise.all(
|
|
581
|
+
(await this.baseSubscriptionService.listSubscriptions({ ids }, em)).map(
|
|
582
|
+
async (subscription) => {
|
|
583
|
+
return {
|
|
584
|
+
...await this._mappers.SubscriptionMapper.serializeEntityToDto(
|
|
585
|
+
subscription
|
|
586
|
+
),
|
|
587
|
+
stripeFields: subscriptions.find(
|
|
588
|
+
(s) => s.id === subscription.externalId
|
|
589
|
+
)
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
)
|
|
593
|
+
);
|
|
594
|
+
}
|
|
595
|
+
async cancelSubscription(idDto, em) {
|
|
596
|
+
await this.stripe.subscriptions.cancel(idDto.id);
|
|
597
|
+
await this.baseSubscriptionService.cancelSubscription(idDto, em);
|
|
598
|
+
}
|
|
599
|
+
async resumeSubscription(idDto, em) {
|
|
600
|
+
await this.stripe.subscriptions.resume(idDto.id);
|
|
601
|
+
await this.baseSubscriptionService.resumeSubscription(idDto, em);
|
|
602
|
+
}
|
|
603
|
+
};
|
|
604
|
+
|
|
605
|
+
// services/webhook.service.ts
|
|
606
|
+
var StripeWebhookService = class {
|
|
607
|
+
constructor(stripeClient, em, schemaValidator, openTelemetryCollector, billingPortalService, checkoutSessionService, paymentLinkService, planService, subscriptionService) {
|
|
608
|
+
this.stripeClient = stripeClient;
|
|
609
|
+
this.em = em;
|
|
610
|
+
this.schemaValidator = schemaValidator;
|
|
611
|
+
this.openTelemetryCollector = openTelemetryCollector;
|
|
612
|
+
this.billingPortalService = billingPortalService;
|
|
613
|
+
this.checkoutSessionService = checkoutSessionService;
|
|
614
|
+
this.paymentLinkService = paymentLinkService;
|
|
615
|
+
this.planService = planService;
|
|
616
|
+
this.subscriptionService = subscriptionService;
|
|
617
|
+
}
|
|
618
|
+
async handleWebhookEvent(event) {
|
|
619
|
+
if (this.openTelemetryCollector) {
|
|
620
|
+
this.openTelemetryCollector.info("Handling webhook event", event);
|
|
621
|
+
}
|
|
622
|
+
const eventType = event.type;
|
|
623
|
+
switch (eventType) {
|
|
624
|
+
case "billing_portal.session.created": {
|
|
625
|
+
this.billingPortalService.baseBillingPortalService.createBillingPortalSession(
|
|
626
|
+
{
|
|
627
|
+
id: event.data.object.id,
|
|
628
|
+
customerId: event.data.object.customer,
|
|
629
|
+
expiresAt: new Date(event.data.object.created + 5 * 60 * 1e3),
|
|
630
|
+
uri: event.data.object.url,
|
|
631
|
+
providerFields: event.data.object
|
|
632
|
+
}
|
|
633
|
+
);
|
|
634
|
+
break;
|
|
635
|
+
}
|
|
636
|
+
case "checkout.session.expired": {
|
|
637
|
+
this.checkoutSessionService.handleCheckoutFailure({
|
|
638
|
+
id: event.data.object.id
|
|
639
|
+
});
|
|
640
|
+
break;
|
|
641
|
+
}
|
|
642
|
+
case "checkout.session.completed": {
|
|
643
|
+
this.checkoutSessionService.handleCheckoutSuccess({
|
|
644
|
+
id: event.data.object.id
|
|
645
|
+
});
|
|
646
|
+
break;
|
|
647
|
+
}
|
|
648
|
+
case "payment_link.created":
|
|
649
|
+
{
|
|
650
|
+
this.paymentLinkService.basePaymentLinkService.createPaymentLink({
|
|
651
|
+
id: event.data.object.id,
|
|
652
|
+
amount: event.data.object.line_items?.data.reduce(
|
|
653
|
+
(total, item) => total + item.amount_total,
|
|
654
|
+
0
|
|
655
|
+
) ?? 0,
|
|
656
|
+
paymentMethods: event.data.object.payment_method_types,
|
|
657
|
+
status: "CREATED",
|
|
658
|
+
currency: event.data.object.currency,
|
|
659
|
+
providerFields: event.data.object
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
break;
|
|
663
|
+
case "payment_link.updated": {
|
|
664
|
+
this.paymentLinkService.basePaymentLinkService.updatePaymentLink({
|
|
665
|
+
id: event.data.object.id,
|
|
666
|
+
amount: event.data.object.line_items?.data.reduce(
|
|
667
|
+
(total, item) => total + item.amount_total,
|
|
668
|
+
0
|
|
669
|
+
) ?? 0,
|
|
670
|
+
paymentMethods: event.data.object.payment_method_types,
|
|
671
|
+
status: "UPDATED",
|
|
672
|
+
currency: event.data.object.currency,
|
|
673
|
+
providerFields: event.data.object
|
|
674
|
+
});
|
|
675
|
+
break;
|
|
676
|
+
}
|
|
677
|
+
case "plan.created": {
|
|
678
|
+
if (typeof event.data.object.product === "object" && event.data.object.product != null && event.data.object.amount != null) {
|
|
679
|
+
this.planService.basePlanService.createPlan({
|
|
680
|
+
id: event.data.object.id,
|
|
681
|
+
billingProvider: "stripe" /* STRIPE */,
|
|
682
|
+
cadence: event.data.object.interval,
|
|
683
|
+
currency: event.data.object.currency,
|
|
684
|
+
active: true,
|
|
685
|
+
name: typeof event.data.object.product === "string" ? event.data.object.product : event.data.object.product?.id,
|
|
686
|
+
price: event.data.object.amount,
|
|
687
|
+
externalId: event.data.object.id,
|
|
688
|
+
providerFields: event.data.object
|
|
689
|
+
});
|
|
690
|
+
} else {
|
|
691
|
+
throw new Error("Invalid plan");
|
|
692
|
+
}
|
|
693
|
+
break;
|
|
694
|
+
}
|
|
695
|
+
case "plan.updated": {
|
|
696
|
+
if (typeof event.data.object.product === "object" && event.data.object.product != null && event.data.object.amount != null) {
|
|
697
|
+
this.planService.basePlanService.updatePlan({
|
|
698
|
+
id: event.data.object.id,
|
|
699
|
+
billingProvider: "stripe" /* STRIPE */,
|
|
700
|
+
cadence: event.data.object.interval,
|
|
701
|
+
currency: event.data.object.currency,
|
|
702
|
+
active: true,
|
|
703
|
+
name: typeof event.data.object.product === "string" ? event.data.object.product : event.data.object.product?.id,
|
|
704
|
+
price: event.data.object.amount,
|
|
705
|
+
externalId: event.data.object.id,
|
|
706
|
+
providerFields: event.data.object
|
|
707
|
+
});
|
|
708
|
+
} else {
|
|
709
|
+
throw new Error("Invalid plan");
|
|
710
|
+
}
|
|
711
|
+
break;
|
|
712
|
+
}
|
|
713
|
+
case "plan.deleted": {
|
|
714
|
+
this.planService.deletePlan({
|
|
715
|
+
id: event.data.object.id
|
|
716
|
+
});
|
|
717
|
+
break;
|
|
718
|
+
}
|
|
719
|
+
case "customer.subscription.created": {
|
|
720
|
+
this.subscriptionService.baseSubscriptionService.createSubscription({
|
|
721
|
+
id: event.data.object.id,
|
|
722
|
+
partyId: typeof event.data.object.customer === "string" ? event.data.object.customer : event.data.object.customer.id,
|
|
723
|
+
partyType: "USER",
|
|
724
|
+
description: event.data.object.description ?? void 0,
|
|
725
|
+
active: true,
|
|
726
|
+
productId: event.data.object.items.data[0].plan.id,
|
|
727
|
+
providerFields: event.data.object,
|
|
728
|
+
externalId: event.data.object.id,
|
|
729
|
+
billingProvider: "stripe" /* STRIPE */,
|
|
730
|
+
startDate: new Date(event.data.object.created),
|
|
731
|
+
endDate: event.data.object.cancel_at ? new Date(event.data.object.cancel_at) : /* @__PURE__ */ new Date(Infinity),
|
|
732
|
+
status: event.data.object.status
|
|
733
|
+
});
|
|
734
|
+
break;
|
|
735
|
+
}
|
|
736
|
+
case "customer.subscription.updated": {
|
|
737
|
+
this.subscriptionService.baseSubscriptionService.updateSubscription({
|
|
738
|
+
id: event.data.object.id,
|
|
739
|
+
partyId: typeof event.data.object.customer === "string" ? event.data.object.customer : event.data.object.customer.id,
|
|
740
|
+
partyType: "USER",
|
|
741
|
+
description: event.data.object.description ?? void 0,
|
|
742
|
+
active: true,
|
|
743
|
+
providerFields: event.data.object,
|
|
744
|
+
externalId: event.data.object.id,
|
|
745
|
+
billingProvider: "stripe" /* STRIPE */,
|
|
746
|
+
startDate: new Date(event.data.object.created),
|
|
747
|
+
endDate: event.data.object.cancel_at ? new Date(event.data.object.cancel_at) : /* @__PURE__ */ new Date(Infinity),
|
|
748
|
+
productId: event.data.object.items.data[0].plan.id,
|
|
749
|
+
status: event.data.object.status
|
|
750
|
+
});
|
|
751
|
+
break;
|
|
752
|
+
}
|
|
753
|
+
case "customer.subscription.deleted": {
|
|
754
|
+
this.subscriptionService.deleteSubscription({
|
|
755
|
+
id: event.data.object.id
|
|
756
|
+
});
|
|
757
|
+
break;
|
|
758
|
+
}
|
|
759
|
+
case "customer.subscription.paused": {
|
|
760
|
+
this.subscriptionService.cancelSubscription({
|
|
761
|
+
id: event.data.object.id
|
|
762
|
+
});
|
|
763
|
+
break;
|
|
764
|
+
}
|
|
765
|
+
case "customer.subscription.resumed": {
|
|
766
|
+
this.subscriptionService.resumeSubscription({
|
|
767
|
+
id: event.data.object.id
|
|
768
|
+
});
|
|
769
|
+
break;
|
|
770
|
+
}
|
|
771
|
+
default:
|
|
772
|
+
this.openTelemetryCollector.info(
|
|
773
|
+
"Unprocessed stripe event type",
|
|
774
|
+
eventType
|
|
775
|
+
);
|
|
776
|
+
break;
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
};
|
|
780
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
781
|
+
0 && (module.exports = {
|
|
782
|
+
StripeBillingPortalService,
|
|
783
|
+
StripeCheckoutSessionService,
|
|
784
|
+
StripePaymentLinkService,
|
|
785
|
+
StripePlanService,
|
|
786
|
+
StripeSubscriptionService,
|
|
787
|
+
StripeWebhookService
|
|
788
|
+
});
|