@casual-simulation/aux-records 3.2.3 → 3.2.4-alpha.5940763320
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/AIChatInterface.d.ts +8 -0
- package/AIChatInterface.js.map +1 -1
- package/AIController.d.ts +10 -4
- package/AIController.js +112 -3
- package/AIController.js.map +1 -1
- package/AuthController.d.ts +4 -8
- package/AuthController.js +39 -45
- package/AuthController.js.map +1 -1
- package/AuthStore.d.ts +236 -4
- package/ConfigurationStore.d.ts +12 -0
- package/ConfigurationStore.js +2 -0
- package/ConfigurationStore.js.map +1 -0
- package/DataRecordsController.d.ts +18 -6
- package/DataRecordsController.js +31 -5
- package/DataRecordsController.js.map +1 -1
- package/Errors.d.ts +4 -0
- package/EventRecordsController.d.ts +12 -3
- package/EventRecordsController.js +27 -5
- package/EventRecordsController.js.map +1 -1
- package/FileRecordsController.d.ts +13 -3
- package/FileRecordsController.js +46 -9
- package/FileRecordsController.js.map +1 -1
- package/MemoryFileRecordsLookup.d.ts +11 -0
- package/MemoryFileRecordsLookup.js +128 -0
- package/MemoryFileRecordsLookup.js.map +1 -0
- package/MemoryStore.d.ts +191 -0
- package/MemoryStore.js +1381 -0
- package/MemoryStore.js.map +1 -0
- package/MetricsStore.d.ts +186 -0
- package/MetricsStore.js +2 -0
- package/MetricsStore.js.map +1 -0
- package/OpenAIChatInterface.js +1 -0
- package/OpenAIChatInterface.js.map +1 -1
- package/PolicyController.d.ts +7 -4
- package/PolicyController.js +33 -8
- package/PolicyController.js.map +1 -1
- package/RecordsController.d.ts +185 -7
- package/RecordsController.js +588 -70
- package/RecordsController.js.map +1 -1
- package/RecordsHttpServer.d.ts +8 -0
- package/RecordsHttpServer.js +450 -8
- package/RecordsHttpServer.js.map +1 -1
- package/RecordsStore.d.ts +263 -5
- package/StripeInterface.d.ts +331 -0
- package/StripeInterface.js +37 -1
- package/StripeInterface.js.map +1 -1
- package/SubscriptionConfiguration.d.ts +2523 -33
- package/SubscriptionConfiguration.js +130 -1
- package/SubscriptionConfiguration.js.map +1 -1
- package/SubscriptionController.d.ts +23 -6
- package/SubscriptionController.js +344 -69
- package/SubscriptionController.js.map +1 -1
- package/TestUtils.d.ts +7 -6
- package/TestUtils.js +28 -14
- package/TestUtils.js.map +1 -1
- package/Utils.js +9 -0
- package/Utils.js.map +1 -1
- package/index.d.ts +5 -4
- package/index.js +5 -4
- package/index.js.map +1 -1
- package/package.json +2 -2
- package/MemoryAuthStore.d.ts +0 -33
- package/MemoryAuthStore.js +0 -186
- package/MemoryAuthStore.js.map +0 -1
- package/MemoryDataRecordsStore.d.ts +0 -10
- package/MemoryDataRecordsStore.js +0 -98
- package/MemoryDataRecordsStore.js.map +0 -1
- package/MemoryEventRecordsStore.d.ts +0 -10
- package/MemoryEventRecordsStore.js +0 -89
- package/MemoryEventRecordsStore.js.map +0 -1
- package/MemoryFileRecordsStore.d.ts +0 -25
- package/MemoryFileRecordsStore.js +0 -310
- package/MemoryFileRecordsStore.js.map +0 -1
- package/MemoryPolicyStore.d.ts +0 -43
- package/MemoryPolicyStore.js +0 -255
- package/MemoryPolicyStore.js.map +0 -1
- package/MemoryRecordsStore.d.ts +0 -13
- package/MemoryRecordsStore.js +0 -67
- package/MemoryRecordsStore.js.map +0 -1
|
@@ -8,17 +8,24 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
10
|
import { INVALID_KEY_ERROR_MESSAGE, } from './AuthController';
|
|
11
|
+
import { STRIPE_EVENT_INVOICE_PAID_SCHEMA, } from './StripeInterface';
|
|
11
12
|
import { isActiveSubscription, tryParseJson } from './Utils';
|
|
12
13
|
/**
|
|
13
14
|
* Defines a class that is able to handle subscriptions.
|
|
14
15
|
*/
|
|
15
16
|
export class SubscriptionController {
|
|
16
|
-
constructor(stripe, auth, authStore, config) {
|
|
17
|
+
constructor(stripe, auth, authStore, recordsStore, config) {
|
|
17
18
|
this._stripe = stripe;
|
|
18
19
|
this._auth = auth;
|
|
19
20
|
this._authStore = authStore;
|
|
21
|
+
this._recordsStore = recordsStore;
|
|
20
22
|
this._config = config;
|
|
21
23
|
}
|
|
24
|
+
_getConfig() {
|
|
25
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
26
|
+
return yield this._config.getSubscriptionConfiguration();
|
|
27
|
+
});
|
|
28
|
+
}
|
|
22
29
|
/**
|
|
23
30
|
* Gets the status of the given user's scription.
|
|
24
31
|
* @param request
|
|
@@ -33,40 +40,96 @@ export class SubscriptionController {
|
|
|
33
40
|
};
|
|
34
41
|
}
|
|
35
42
|
try {
|
|
36
|
-
if (
|
|
43
|
+
if (request.userId && request.studioId) {
|
|
37
44
|
return {
|
|
38
45
|
success: false,
|
|
39
|
-
errorCode: '
|
|
40
|
-
errorMessage: 'The given
|
|
46
|
+
errorCode: 'unacceptable_request',
|
|
47
|
+
errorMessage: 'The given request is invalid. It must not specify both a user ID and a studio ID.',
|
|
41
48
|
};
|
|
42
49
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
50
|
+
if (request.userId) {
|
|
51
|
+
if (typeof request.userId !== 'string' ||
|
|
52
|
+
request.userId === '') {
|
|
53
|
+
return {
|
|
54
|
+
success: false,
|
|
55
|
+
errorCode: 'unacceptable_user_id',
|
|
56
|
+
errorMessage: 'The given user ID is invalid. It must be a correctly formatted string.',
|
|
57
|
+
};
|
|
58
|
+
}
|
|
46
59
|
}
|
|
47
|
-
else if (
|
|
48
|
-
|
|
60
|
+
else if (request.studioId) {
|
|
61
|
+
if (typeof request.studioId !== 'string' ||
|
|
62
|
+
request.studioId === '') {
|
|
63
|
+
return {
|
|
64
|
+
success: false,
|
|
65
|
+
errorCode: 'unacceptable_studio_id',
|
|
66
|
+
errorMessage: 'The given studio ID is invalid. It must be a correctly formatted string.',
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
49
71
|
return {
|
|
50
72
|
success: false,
|
|
51
|
-
errorCode: '
|
|
52
|
-
errorMessage:
|
|
73
|
+
errorCode: 'unacceptable_request',
|
|
74
|
+
errorMessage: 'The given request is invalid. It must have a valid user ID or studio ID.',
|
|
53
75
|
};
|
|
54
76
|
}
|
|
55
|
-
const
|
|
56
|
-
let customerId
|
|
77
|
+
const keyResult = yield this._auth.validateSessionKey(request.sessionKey);
|
|
78
|
+
let customerId;
|
|
79
|
+
let role;
|
|
80
|
+
if (keyResult.success === false) {
|
|
81
|
+
return keyResult;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
if (request.userId) {
|
|
85
|
+
if (keyResult.userId !== request.userId) {
|
|
86
|
+
console.log('[SubscriptionController] [getSubscriptionStatus] Request User ID doesnt match session key User ID!');
|
|
87
|
+
return {
|
|
88
|
+
success: false,
|
|
89
|
+
errorCode: 'invalid_key',
|
|
90
|
+
errorMessage: INVALID_KEY_ERROR_MESSAGE,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
const user = yield this._authStore.findUser(keyResult.userId);
|
|
94
|
+
customerId = user.stripeCustomerId;
|
|
95
|
+
role = 'user';
|
|
96
|
+
}
|
|
97
|
+
else if (request.studioId) {
|
|
98
|
+
const assignments = yield this._recordsStore.listStudioAssignments(request.studioId, {
|
|
99
|
+
userId: keyResult.userId,
|
|
100
|
+
role: 'admin',
|
|
101
|
+
});
|
|
102
|
+
if (assignments.length <= 0) {
|
|
103
|
+
console.log('[SubscriptionController] [getSubscriptionStatus] Request user does not have access to studio!');
|
|
104
|
+
return {
|
|
105
|
+
success: false,
|
|
106
|
+
errorCode: 'invalid_key',
|
|
107
|
+
errorMessage: INVALID_KEY_ERROR_MESSAGE,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
const studio = yield this._recordsStore.getStudioById(request.studioId);
|
|
111
|
+
customerId = studio.stripeCustomerId;
|
|
112
|
+
role = 'studio';
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// const user = await this._authStore.findUser(keyResult.userId);
|
|
116
|
+
// let customerId = user.stripeCustomerId;
|
|
57
117
|
if (!customerId) {
|
|
118
|
+
const config = yield this._getConfig();
|
|
58
119
|
return {
|
|
59
120
|
success: true,
|
|
60
121
|
userId: keyResult.userId,
|
|
122
|
+
studioId: request.studioId,
|
|
61
123
|
publishableKey: this._stripe.publishableKey,
|
|
62
124
|
subscriptions: [],
|
|
63
|
-
purchasableSubscriptions: yield this._getPurchasableSubscriptions(),
|
|
125
|
+
purchasableSubscriptions: yield this._getPurchasableSubscriptions(role, config),
|
|
64
126
|
};
|
|
65
127
|
}
|
|
66
128
|
const listResult = yield this._stripe.listActiveSubscriptionsForCustomer(customerId);
|
|
129
|
+
const config = yield this._getConfig();
|
|
67
130
|
const subscriptions = listResult.subscriptions.map((s) => {
|
|
68
131
|
const item = s.items[0];
|
|
69
|
-
const subscriptionInfo =
|
|
132
|
+
const subscriptionInfo = config.subscriptions.find((sub) => {
|
|
70
133
|
return sub.eligibleProducts.some((p) => p === item.price.product.id);
|
|
71
134
|
});
|
|
72
135
|
const featureList = subscriptionInfo === null || subscriptionInfo === void 0 ? void 0 : subscriptionInfo.featureList;
|
|
@@ -89,10 +152,11 @@ export class SubscriptionController {
|
|
|
89
152
|
});
|
|
90
153
|
const purchasableSubscriptions = subscriptions.length > 0
|
|
91
154
|
? []
|
|
92
|
-
: yield this._getPurchasableSubscriptions();
|
|
155
|
+
: yield this._getPurchasableSubscriptions(role, config);
|
|
93
156
|
return {
|
|
94
157
|
success: true,
|
|
95
158
|
userId: keyResult.userId,
|
|
159
|
+
studioId: request.studioId,
|
|
96
160
|
publishableKey: this._stripe.publishableKey,
|
|
97
161
|
subscriptions,
|
|
98
162
|
purchasableSubscriptions,
|
|
@@ -108,11 +172,21 @@ export class SubscriptionController {
|
|
|
108
172
|
}
|
|
109
173
|
});
|
|
110
174
|
}
|
|
111
|
-
|
|
175
|
+
_getPurchasableSubscriptionsForRole(role, config) {
|
|
176
|
+
return config.subscriptions.filter((s) => {
|
|
177
|
+
var _a, _b, _c;
|
|
178
|
+
const isPurchasable = (_a = s.purchasable) !== null && _a !== void 0 ? _a : true;
|
|
179
|
+
const isUserOnly = (_b = s.userOnly) !== null && _b !== void 0 ? _b : false;
|
|
180
|
+
const isStudioOnly = (_c = s.studioOnly) !== null && _c !== void 0 ? _c : false;
|
|
181
|
+
const matchesRole = (isUserOnly && role === 'user') ||
|
|
182
|
+
(isStudioOnly && role === 'studio') ||
|
|
183
|
+
(!isUserOnly && !isStudioOnly);
|
|
184
|
+
return isPurchasable && matchesRole;
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
_getPurchasableSubscriptions(role, config) {
|
|
112
188
|
return __awaiter(this, void 0, void 0, function* () {
|
|
113
|
-
const promises = this.
|
|
114
|
-
.filter((s) => { var _a; return (_a = s.purchasable) !== null && _a !== void 0 ? _a : true; })
|
|
115
|
-
.map((s) => __awaiter(this, void 0, void 0, function* () {
|
|
189
|
+
const promises = this._getPurchasableSubscriptionsForRole(role, config).map((s) => __awaiter(this, void 0, void 0, function* () {
|
|
116
190
|
return ({
|
|
117
191
|
sub: s,
|
|
118
192
|
info: yield this._stripe.getProductAndPriceInfo(s.product),
|
|
@@ -153,11 +227,38 @@ export class SubscriptionController {
|
|
|
153
227
|
};
|
|
154
228
|
}
|
|
155
229
|
try {
|
|
156
|
-
if (
|
|
230
|
+
if (request.userId && request.studioId) {
|
|
157
231
|
return {
|
|
158
232
|
success: false,
|
|
159
|
-
errorCode: '
|
|
160
|
-
errorMessage: 'The given
|
|
233
|
+
errorCode: 'unacceptable_request',
|
|
234
|
+
errorMessage: 'The given request is invalid. It must not specify both a user ID and a studio ID.',
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
if (request.userId) {
|
|
238
|
+
if (typeof request.userId !== 'string' ||
|
|
239
|
+
request.userId === '') {
|
|
240
|
+
return {
|
|
241
|
+
success: false,
|
|
242
|
+
errorCode: 'unacceptable_user_id',
|
|
243
|
+
errorMessage: 'The given user ID is invalid. It must be a correctly formatted string.',
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
else if (request.studioId) {
|
|
248
|
+
if (typeof request.studioId !== 'string' ||
|
|
249
|
+
request.studioId === '') {
|
|
250
|
+
return {
|
|
251
|
+
success: false,
|
|
252
|
+
errorCode: 'unacceptable_studio_id',
|
|
253
|
+
errorMessage: 'The given studio ID is invalid. It must be a correctly formatted string.',
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
return {
|
|
259
|
+
success: false,
|
|
260
|
+
errorCode: 'unacceptable_request',
|
|
261
|
+
errorMessage: 'The given request is invalid. It must have a valid user ID or studio ID.',
|
|
161
262
|
};
|
|
162
263
|
}
|
|
163
264
|
if (!!request.subscriptionId &&
|
|
@@ -177,29 +278,80 @@ export class SubscriptionController {
|
|
|
177
278
|
};
|
|
178
279
|
}
|
|
179
280
|
const keyResult = yield this._auth.validateSessionKey(request.sessionKey);
|
|
281
|
+
let customerId;
|
|
282
|
+
let customerName;
|
|
283
|
+
let customerEmail;
|
|
284
|
+
let customerPhone;
|
|
285
|
+
let role;
|
|
286
|
+
let user;
|
|
287
|
+
let studio;
|
|
288
|
+
let customerMetadata = {};
|
|
289
|
+
let metadata = {};
|
|
180
290
|
if (keyResult.success === false) {
|
|
181
291
|
return keyResult;
|
|
182
292
|
}
|
|
183
|
-
else if (
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
293
|
+
else if (request.userId) {
|
|
294
|
+
if (keyResult.userId !== request.userId) {
|
|
295
|
+
console.log('[SubscriptionController] [createManageSubscriptionLink] Request User ID doesnt match session key User ID!');
|
|
296
|
+
return {
|
|
297
|
+
success: false,
|
|
298
|
+
errorCode: 'invalid_key',
|
|
299
|
+
errorMessage: INVALID_KEY_ERROR_MESSAGE,
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
user = yield this._authStore.findUser(keyResult.userId);
|
|
303
|
+
customerId = user.stripeCustomerId;
|
|
304
|
+
customerName = user.name;
|
|
305
|
+
customerEmail = user.email;
|
|
306
|
+
customerPhone = user.phoneNumber;
|
|
307
|
+
metadata.userId = user.id;
|
|
308
|
+
customerMetadata.role = 'user';
|
|
309
|
+
customerMetadata.userId = user.id;
|
|
310
|
+
role = 'user';
|
|
311
|
+
console.log(`[SubscriptionController] [createManageSubscriptionLink] Creating a checkout/management session for User (${keyResult.userId}).`);
|
|
190
312
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
313
|
+
else if (request.studioId) {
|
|
314
|
+
const assignments = yield this._recordsStore.listStudioAssignments(request.studioId, {
|
|
315
|
+
role: 'admin',
|
|
316
|
+
});
|
|
317
|
+
const userAssignment = assignments.find((a) => a.userId === keyResult.userId);
|
|
318
|
+
if (!userAssignment) {
|
|
319
|
+
console.log('[SubscriptionController] [getSubscriptionStatus] Request user does not have access to studio!');
|
|
320
|
+
return {
|
|
321
|
+
success: false,
|
|
322
|
+
errorCode: 'invalid_key',
|
|
323
|
+
errorMessage: INVALID_KEY_ERROR_MESSAGE,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
studio = yield this._recordsStore.getStudioById(request.studioId);
|
|
327
|
+
customerId = studio.stripeCustomerId;
|
|
328
|
+
customerName = studio.displayName;
|
|
329
|
+
customerMetadata.role = 'studio';
|
|
330
|
+
customerMetadata.studioId = studio.id;
|
|
331
|
+
metadata.studioId = studio.id;
|
|
332
|
+
let primaryAssignment;
|
|
333
|
+
if (userAssignment.isPrimaryContact) {
|
|
334
|
+
primaryAssignment = userAssignment;
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
primaryAssignment = assignments.find((a) => a.isPrimaryContact);
|
|
338
|
+
}
|
|
339
|
+
if (primaryAssignment) {
|
|
340
|
+
customerEmail = primaryAssignment.user.email;
|
|
341
|
+
customerPhone = primaryAssignment.user.phoneNumber;
|
|
342
|
+
metadata.contactUserId = keyResult.userId;
|
|
343
|
+
customerMetadata.contactUserId = keyResult.userId;
|
|
344
|
+
}
|
|
345
|
+
role = 'studio';
|
|
346
|
+
console.log(`[SubscriptionController] [createManageSubscriptionLink] Creating a checkout/management session for Studio (userId: ${keyResult.userId}, studioId: ${studio.id}).`);
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
throw new Error('Should not reach this point');
|
|
350
|
+
}
|
|
351
|
+
metadata.subjectId = keyResult.userId;
|
|
352
|
+
const config = yield this._getConfig();
|
|
201
353
|
if (!customerId) {
|
|
202
|
-
if (
|
|
354
|
+
if (config.subscriptions.length <= 0) {
|
|
203
355
|
return {
|
|
204
356
|
success: false,
|
|
205
357
|
errorCode: 'not_supported',
|
|
@@ -208,16 +360,25 @@ export class SubscriptionController {
|
|
|
208
360
|
}
|
|
209
361
|
console.log('[SubscriptionController] [createManageSubscriptionLink] No Stripe Customer ID. Creating New Customer and Checkout Session in Stripe.');
|
|
210
362
|
const result = yield this._stripe.createCustomer({
|
|
211
|
-
name:
|
|
212
|
-
email:
|
|
213
|
-
phone:
|
|
363
|
+
name: customerName,
|
|
364
|
+
email: customerEmail,
|
|
365
|
+
phone: customerPhone,
|
|
366
|
+
metadata: customerMetadata,
|
|
214
367
|
});
|
|
215
|
-
customerId =
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
368
|
+
customerId = result.id;
|
|
369
|
+
if (user) {
|
|
370
|
+
user.stripeCustomerId = customerId;
|
|
371
|
+
console.log(`[SubscriptionController] [createManageSubscriptionLink] Saving Stripe Customer ID (${customerId}) to User Record (${user.id}).`);
|
|
372
|
+
yield this._authStore.saveUser(Object.assign({}, user));
|
|
373
|
+
}
|
|
374
|
+
else if (studio) {
|
|
375
|
+
studio.stripeCustomerId = customerId;
|
|
376
|
+
console.log(`[SubscriptionController] [createManageSubscriptionLink] Saving Stripe Customer ID (${customerId}) to Studio Record (${studio.id}).`);
|
|
377
|
+
yield this._recordsStore.updateStudio(Object.assign({}, studio));
|
|
378
|
+
}
|
|
379
|
+
return yield this._createCheckoutSession(request, customerId, metadata, role, user, studio);
|
|
219
380
|
}
|
|
220
|
-
console.log(`[SubscriptionController] [createManageSubscriptionLink]
|
|
381
|
+
console.log(`[SubscriptionController] [createManageSubscriptionLink] Has Stripe Customer ID (${customerId}). Checking active subscriptions for customer.`);
|
|
221
382
|
const subs = yield this._stripe.listActiveSubscriptionsForCustomer(customerId);
|
|
222
383
|
const hasSubscription = subs.subscriptions.some((s) => {
|
|
223
384
|
const isManagable = s.status === 'active' ||
|
|
@@ -229,12 +390,12 @@ export class SubscriptionController {
|
|
|
229
390
|
if (!isManagable) {
|
|
230
391
|
return false;
|
|
231
392
|
}
|
|
232
|
-
const hasManagableProduct =
|
|
393
|
+
const hasManagableProduct = config.subscriptions.some((sub) => sub.eligibleProducts.some((p) => s.items.some((i) => i.price.product.id === p)));
|
|
233
394
|
return hasManagableProduct;
|
|
234
395
|
});
|
|
235
396
|
if (hasSubscription) {
|
|
236
397
|
console.log(`[SubscriptionController] [createManageSubscriptionLink] Customer has a managable subscription. Creating a portal session.`);
|
|
237
|
-
const session = yield this._stripe.createPortalSession(Object.assign(Object.assign({}, ((_a =
|
|
398
|
+
const session = yield this._stripe.createPortalSession(Object.assign(Object.assign({}, ((_a = config.portalConfig) !== null && _a !== void 0 ? _a : {})), { customer: customerId, return_url: returnRoute(config.returnUrl, user, studio) }));
|
|
238
399
|
console.log(`[SubscriptionController] [createManageSubscriptionLink] Portal session success!`);
|
|
239
400
|
return {
|
|
240
401
|
success: true,
|
|
@@ -242,10 +403,10 @@ export class SubscriptionController {
|
|
|
242
403
|
};
|
|
243
404
|
}
|
|
244
405
|
console.log(`[SubscriptionController] [createManageSubscriptionLink] Customer does not have a managable subscription. Creating a checkout session.`);
|
|
245
|
-
return yield this._createCheckoutSession(request, user,
|
|
406
|
+
return yield this._createCheckoutSession(request, customerId, metadata, role, user, studio);
|
|
246
407
|
}
|
|
247
408
|
catch (err) {
|
|
248
|
-
console.
|
|
409
|
+
console.error('[SubscriptionController] An error occurred while creating a manage subscription link:', err);
|
|
249
410
|
return {
|
|
250
411
|
success: false,
|
|
251
412
|
errorCode: 'server_error',
|
|
@@ -254,24 +415,26 @@ export class SubscriptionController {
|
|
|
254
415
|
}
|
|
255
416
|
});
|
|
256
417
|
}
|
|
257
|
-
_createCheckoutSession(request, user,
|
|
418
|
+
_createCheckoutSession(request, customerId, metadata, role, user, studio) {
|
|
258
419
|
var _a;
|
|
259
420
|
return __awaiter(this, void 0, void 0, function* () {
|
|
421
|
+
const config = yield this._getConfig();
|
|
422
|
+
const purchasableSubscriptions = this._getPurchasableSubscriptionsForRole(role, config);
|
|
260
423
|
let sub;
|
|
261
424
|
if (request.subscriptionId) {
|
|
262
|
-
sub =
|
|
425
|
+
sub = purchasableSubscriptions.find((s) => s.id === request.subscriptionId);
|
|
263
426
|
if (sub) {
|
|
264
427
|
console.log(`[SubscriptionController] [createManageSubscriptionLink] Using specified subscription (${request.subscriptionId}).`);
|
|
265
428
|
}
|
|
266
429
|
}
|
|
267
430
|
if (!sub) {
|
|
268
|
-
sub =
|
|
431
|
+
sub = purchasableSubscriptions.find((s) => s.defaultSubscription);
|
|
269
432
|
if (sub) {
|
|
270
433
|
console.log(`[SubscriptionController] [createManageSubscriptionLink] Using default subscription.`);
|
|
271
434
|
}
|
|
272
435
|
}
|
|
273
436
|
if (!sub) {
|
|
274
|
-
sub =
|
|
437
|
+
sub = purchasableSubscriptions[0];
|
|
275
438
|
console.log(`[SubscriptionController] [createManageSubscriptionLink] Using first subscription.`);
|
|
276
439
|
}
|
|
277
440
|
const productInfo = yield this._stripe.getProductAndPriceInfo(sub.product);
|
|
@@ -293,14 +456,12 @@ export class SubscriptionController {
|
|
|
293
456
|
}
|
|
294
457
|
}
|
|
295
458
|
console.log(`[SubscriptionController] [createManageSubscriptionLink] Creating Checkout Session.`);
|
|
296
|
-
const session = yield this._stripe.createCheckoutSession(Object.assign(Object.assign({}, ((_a =
|
|
459
|
+
const session = yield this._stripe.createCheckoutSession(Object.assign(Object.assign({}, ((_a = config.checkoutConfig) !== null && _a !== void 0 ? _a : {})), { customer: customerId, success_url: returnRoute(config.successUrl, user, studio), cancel_url: returnRoute(config.cancelUrl, user, studio), line_items: [
|
|
297
460
|
{
|
|
298
461
|
price: productInfo.default_price.id,
|
|
299
462
|
quantity: 1,
|
|
300
463
|
},
|
|
301
|
-
], mode: 'subscription', metadata:
|
|
302
|
-
userId: user.id,
|
|
303
|
-
} }));
|
|
464
|
+
], mode: 'subscription', metadata: metadata }));
|
|
304
465
|
console.log(`[SubscriptionController] [createManageSubscriptionLink] Checkout Session Success!`);
|
|
305
466
|
return {
|
|
306
467
|
success: true,
|
|
@@ -338,11 +499,12 @@ export class SubscriptionController {
|
|
|
338
499
|
errorMessage: 'The request was not valid.',
|
|
339
500
|
};
|
|
340
501
|
}
|
|
502
|
+
const config = yield this._getConfig();
|
|
341
503
|
const body = request.requestBody;
|
|
342
504
|
const signature = request.signature;
|
|
343
505
|
let event;
|
|
344
506
|
try {
|
|
345
|
-
event = this._stripe.constructWebhookEvent(body, signature,
|
|
507
|
+
event = this._stripe.constructWebhookEvent(body, signature, config.webhookSecret);
|
|
346
508
|
}
|
|
347
509
|
catch (err) {
|
|
348
510
|
console.log(`[SubscriptionController] [handleStripeWebhook] Unable to construct webhook event:`, err);
|
|
@@ -361,7 +523,7 @@ export class SubscriptionController {
|
|
|
361
523
|
let item;
|
|
362
524
|
let sub;
|
|
363
525
|
items_loop: for (let i of items) {
|
|
364
|
-
for (let s of
|
|
526
|
+
for (let s of config.subscriptions) {
|
|
365
527
|
if (s.eligibleProducts.some((p) => p === i.price.product)) {
|
|
366
528
|
sub = s;
|
|
367
529
|
item = i;
|
|
@@ -380,18 +542,120 @@ export class SubscriptionController {
|
|
|
380
542
|
const active = isActiveSubscription(status);
|
|
381
543
|
const tier = (_a = sub.tier) !== null && _a !== void 0 ? _a : 'beta';
|
|
382
544
|
const customerId = subscription.customer;
|
|
545
|
+
const stripeSubscriptionId = subscription.id;
|
|
383
546
|
console.log(`[SubscriptionController] [handleStripeWebhook] Customer ID: ${customerId}. Subscription status: ${status}. Tier: ${tier}. Is Active: ${active}.`);
|
|
384
|
-
|
|
385
|
-
|
|
547
|
+
let user = yield this._authStore.findUserByStripeCustomerId(customerId);
|
|
548
|
+
let studio;
|
|
549
|
+
if (user) {
|
|
550
|
+
yield this._authStore.updateSubscriptionInfo({
|
|
551
|
+
userId: user.id,
|
|
552
|
+
subscriptionStatus: status,
|
|
553
|
+
subscriptionId: sub.id,
|
|
554
|
+
stripeSubscriptionId,
|
|
555
|
+
stripeCustomerId: customerId,
|
|
556
|
+
currentPeriodEndMs: null,
|
|
557
|
+
currentPeriodStartMs: null,
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
else {
|
|
386
561
|
console.log(`[SubscriptionController] [handleStripeWebhook] No user found for Customer ID (${customerId})`);
|
|
562
|
+
studio =
|
|
563
|
+
yield this._recordsStore.getStudioByStripeCustomerId(customerId);
|
|
564
|
+
if (studio) {
|
|
565
|
+
yield this._authStore.updateSubscriptionInfo({
|
|
566
|
+
studioId: studio.id,
|
|
567
|
+
subscriptionStatus: status,
|
|
568
|
+
subscriptionId: sub.id,
|
|
569
|
+
stripeSubscriptionId,
|
|
570
|
+
stripeCustomerId: customerId,
|
|
571
|
+
currentPeriodEndMs: null,
|
|
572
|
+
currentPeriodStartMs: null,
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
else {
|
|
576
|
+
console.log(`[SubscriptionController] [handleStripeWebhook] No studio found for Customer ID (${customerId})`);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
return {
|
|
580
|
+
success: true,
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
else if (event.type === 'invoice.paid') {
|
|
584
|
+
const parseResult = STRIPE_EVENT_INVOICE_PAID_SCHEMA.safeParse(event);
|
|
585
|
+
if (parseResult.success === false) {
|
|
586
|
+
console.error(`[SubscriptionController] [handleStripeWebhook] Unable to parse stripe event!`, parseResult.error);
|
|
387
587
|
return {
|
|
388
588
|
success: true,
|
|
389
589
|
};
|
|
390
590
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
591
|
+
const invoice = parseResult.data.data.object;
|
|
592
|
+
const stripeSubscriptionId = invoice.subscription;
|
|
593
|
+
const subscription = yield this._stripe.getSubscriptionById(stripeSubscriptionId);
|
|
594
|
+
const status = subscription.status;
|
|
595
|
+
const customerId = invoice.customer;
|
|
596
|
+
const lineItems = invoice.lines.data;
|
|
597
|
+
const periodStartMs = subscription.current_period_start * 1000;
|
|
598
|
+
const periodEndMs = subscription.current_period_end * 1000;
|
|
599
|
+
const { sub, item } = findMatchingSubscription(lineItems);
|
|
600
|
+
const authInvoice = {
|
|
601
|
+
currency: invoice.currency,
|
|
602
|
+
description: invoice.description,
|
|
603
|
+
paid: invoice.paid,
|
|
604
|
+
status: invoice.status,
|
|
605
|
+
tax: invoice.tax,
|
|
606
|
+
total: invoice.total,
|
|
607
|
+
subtotal: invoice.subtotal,
|
|
608
|
+
stripeInvoiceId: invoice.id,
|
|
609
|
+
stripeHostedInvoiceUrl: invoice.hosted_invoice_url,
|
|
610
|
+
stripeInvoicePdfUrl: invoice.invoice_pdf,
|
|
611
|
+
};
|
|
612
|
+
console.log(`[SubscriptionController] [handleStripeWebhook] New invoice paid for customer ID (${customerId}). Subscription ID: ${subscription.id}. Period start: ${periodStartMs}. Period end: ${periodEndMs}.`);
|
|
613
|
+
const user = yield this._authStore.findUserByStripeCustomerId(customerId);
|
|
614
|
+
if (user) {
|
|
615
|
+
console.log(`[SubscriptionController] [handleStripeWebhook] Found user (${user.id}) with customer ID (${customerId}).`);
|
|
616
|
+
yield this._authStore.updateSubscriptionPeriod({
|
|
617
|
+
userId: user.id,
|
|
618
|
+
subscriptionStatus: status,
|
|
619
|
+
subscriptionId: sub.id,
|
|
620
|
+
stripeSubscriptionId,
|
|
621
|
+
stripeCustomerId: customerId,
|
|
622
|
+
currentPeriodEndMs: periodEndMs,
|
|
623
|
+
currentPeriodStartMs: periodStartMs,
|
|
624
|
+
invoice: authInvoice,
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
else {
|
|
628
|
+
console.log(`[SubscriptionController] [handleStripeWebhook] No user found for customer ID (${customerId}).`);
|
|
629
|
+
const studio = yield this._recordsStore.getStudioByStripeCustomerId(customerId);
|
|
630
|
+
if (studio) {
|
|
631
|
+
yield this._authStore.updateSubscriptionPeriod({
|
|
632
|
+
studioId: studio.id,
|
|
633
|
+
subscriptionStatus: status,
|
|
634
|
+
subscriptionId: sub.id,
|
|
635
|
+
stripeSubscriptionId,
|
|
636
|
+
stripeCustomerId: customerId,
|
|
637
|
+
currentPeriodEndMs: periodEndMs,
|
|
638
|
+
currentPeriodStartMs: periodStartMs,
|
|
639
|
+
invoice: authInvoice,
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
else {
|
|
643
|
+
console.log(`[SubscriptionController] [handleStripeWebhook] No studio found for customer ID (${customerId}).`);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
function findMatchingSubscription(lineItems) {
|
|
647
|
+
let item;
|
|
648
|
+
let sub;
|
|
649
|
+
items_loop: for (let i of lineItems) {
|
|
650
|
+
for (let s of config.subscriptions) {
|
|
651
|
+
if (s.eligibleProducts.some((p) => p === i.price.product)) {
|
|
652
|
+
sub = s;
|
|
653
|
+
item = i;
|
|
654
|
+
break items_loop;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
return { item, sub };
|
|
395
659
|
}
|
|
396
660
|
}
|
|
397
661
|
return {
|
|
@@ -438,4 +702,15 @@ function isValidSubscription(sub) {
|
|
|
438
702
|
typeof sub.product === 'string' &&
|
|
439
703
|
typeof sub.defaultSubscription === 'boolean');
|
|
440
704
|
}
|
|
705
|
+
function returnRoute(basePath, user, studio) {
|
|
706
|
+
if (user) {
|
|
707
|
+
return basePath;
|
|
708
|
+
}
|
|
709
|
+
else {
|
|
710
|
+
return studiosRoute(basePath, studio.id, studio.displayName);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
function studiosRoute(basePath, studioId, studioName) {
|
|
714
|
+
return new URL(`/studios/${encodeURIComponent(studioId)}/${encodeURIComponent(studioName)}`, basePath).href;
|
|
715
|
+
}
|
|
441
716
|
//# sourceMappingURL=SubscriptionController.js.map
|