@casual-simulation/aux-records 3.4.6-alpha.14601027727 → 3.5.0-alpha.15117651144
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/AIController.js +835 -890
- package/AIController.js.map +1 -1
- package/AIHumeInterface.js +43 -54
- package/AIHumeInterface.js.map +1 -1
- package/AIOpenAIRealtimeInterface.js +60 -71
- package/AIOpenAIRealtimeInterface.js.map +1 -1
- package/AnthropicAIChatInterface.js +96 -142
- package/AnthropicAIChatInterface.js.map +1 -1
- package/AuthController.d.ts +3 -2
- package/AuthController.js +1907 -1933
- package/AuthController.js.map +1 -1
- package/AuthStore.d.ts +1 -10
- package/BlockadeLabsGenerateSkyboxInterface.js +57 -72
- package/BlockadeLabsGenerateSkyboxInterface.js.map +1 -1
- package/CachingConfigStore.js +30 -45
- package/CachingConfigStore.js.map +1 -1
- package/CachingPolicyStore.d.ts +8 -2
- package/CachingPolicyStore.js +108 -135
- package/CachingPolicyStore.js.map +1 -1
- package/ComIdConfig.d.ts +18 -18
- package/ComIdConfig.js.map +1 -1
- package/ConsoleAuthMessenger.js +7 -20
- package/ConsoleAuthMessenger.js.map +1 -1
- package/DataRecordsController.d.ts +2 -2
- package/DataRecordsController.js +369 -377
- package/DataRecordsController.js.map +1 -1
- package/DataRecordsStore.d.ts +1 -1
- package/DataRecordsStore.js +1 -1
- package/DataRecordsStore.js.map +1 -1
- package/EventRecordsController.js +226 -240
- package/EventRecordsController.js.map +1 -1
- package/FileRecordsController.d.ts +13 -2
- package/FileRecordsController.js +458 -450
- package/FileRecordsController.js.map +1 -1
- package/GoogleAIChatInterface.js +133 -179
- package/GoogleAIChatInterface.js.map +1 -1
- package/LivekitController.js +43 -54
- package/LivekitController.js.map +1 -1
- package/LoomController.js +64 -75
- package/LoomController.js.map +1 -1
- package/MemoryAuthMessenger.js +10 -23
- package/MemoryAuthMessenger.js.map +1 -1
- package/MemoryCache.js +18 -35
- package/MemoryCache.js.map +1 -1
- package/MemoryFileRecordsLookup.js +105 -125
- package/MemoryFileRecordsLookup.js.map +1 -1
- package/MemoryModerationJobProvider.js +17 -30
- package/MemoryModerationJobProvider.js.map +1 -1
- package/MemoryRateLimiter.js +12 -27
- package/MemoryRateLimiter.js.map +1 -1
- package/MemoryStore.d.ts +18 -6
- package/MemoryStore.js +1879 -1997
- package/MemoryStore.js.map +1 -1
- package/MetricsStore.d.ts +2 -2
- package/ModerationController.js +186 -200
- package/ModerationController.js.map +1 -1
- package/OpenAIChatInterface.js +105 -135
- package/OpenAIChatInterface.js.map +1 -1
- package/OpenAIImageInterface.js +57 -51
- package/OpenAIImageInterface.js.map +1 -1
- package/PolicyController.d.ts +150 -10
- package/PolicyController.js +1546 -1299
- package/PolicyController.js.map +1 -1
- package/PolicyStore.d.ts +110 -2
- package/PolicyStore.js +36 -1
- package/PolicyStore.js.map +1 -1
- package/PrivoClient.js +398 -435
- package/PrivoClient.js.map +1 -1
- package/RateLimitController.js +25 -36
- package/RateLimitController.js.map +1 -1
- package/RecordsClient.js +51 -74
- package/RecordsClient.js.map +1 -1
- package/RecordsController.d.ts +2 -42
- package/RecordsController.js +1026 -1182
- package/RecordsController.js.map +1 -1
- package/RecordsServer.d.ts +196 -27
- package/RecordsServer.js +1701 -1343
- package/RecordsServer.js.map +1 -1
- package/RecordsStore.d.ts +1 -10
- package/RecordsStore.js.map +1 -1
- package/ServerConfig.d.ts +339 -195
- package/ServerConfig.js +13 -0
- package/ServerConfig.js.map +1 -1
- package/SloydInterface.js +62 -75
- package/SloydInterface.js.map +1 -1
- package/StabilityAIImageInterface.js +150 -167
- package/StabilityAIImageInterface.js.map +1 -1
- package/SubscriptionConfigBuilder.d.ts +6 -1
- package/SubscriptionConfigBuilder.js +22 -0
- package/SubscriptionConfigBuilder.js.map +1 -1
- package/SubscriptionConfiguration.d.ts +266 -169
- package/SubscriptionConfiguration.js +101 -79
- package/SubscriptionConfiguration.js.map +1 -1
- package/SubscriptionController.d.ts +2 -1
- package/SubscriptionController.js +643 -650
- package/SubscriptionController.js.map +1 -1
- package/SystemNotificationMessenger.d.ts +21 -4
- package/SystemNotificationMessenger.js +36 -30
- package/SystemNotificationMessenger.js.map +1 -1
- package/TestUtils.d.ts +9 -1
- package/TestUtils.js +105 -129
- package/TestUtils.js.map +1 -1
- package/Utils.d.ts +2 -16
- package/Utils.js +21 -22
- package/Utils.js.map +1 -1
- package/crud/CrudHelpers.js +17 -26
- package/crud/CrudHelpers.js.map +1 -1
- package/crud/CrudRecordsController.d.ts +1 -1
- package/crud/CrudRecordsController.js +259 -267
- package/crud/CrudRecordsController.js.map +1 -1
- package/crud/CrudRecordsControllerTests.js +174 -185
- package/crud/CrudRecordsControllerTests.js.map +1 -1
- package/crud/CrudRecordsStore.d.ts +7 -3
- package/crud/MemoryCrudRecordsStore.d.ts +4 -4
- package/crud/MemoryCrudRecordsStore.js +98 -118
- package/crud/MemoryCrudRecordsStore.js.map +1 -1
- package/crud/sub/MemorySubCrudRecordsStore.d.ts +24 -0
- package/crud/sub/MemorySubCrudRecordsStore.js +146 -0
- package/crud/sub/MemorySubCrudRecordsStore.js.map +1 -0
- package/crud/sub/SubCrudRecordsController.d.ts +182 -0
- package/crud/sub/SubCrudRecordsController.js +360 -0
- package/crud/sub/SubCrudRecordsController.js.map +1 -0
- package/crud/sub/SubCrudRecordsControllerTests.d.ts +39 -0
- package/crud/sub/SubCrudRecordsControllerTests.js +821 -0
- package/crud/sub/SubCrudRecordsControllerTests.js.map +1 -0
- package/crud/sub/SubCrudRecordsStore.d.ts +95 -0
- package/{forms/index.js → crud/sub/SubCrudRecordsStore.js} +2 -2
- package/crud/sub/SubCrudRecordsStore.js.map +1 -0
- package/crud/sub/index.d.ts +3 -0
- package/crud/sub/index.js +20 -0
- package/{forms → crud/sub}/index.js.map +1 -1
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/index.js.map +1 -1
- package/notifications/MemoryNotificationRecordsStore.js +189 -198
- package/notifications/MemoryNotificationRecordsStore.js.map +1 -1
- package/notifications/NotificationRecordsController.js +438 -460
- package/notifications/NotificationRecordsController.js.map +1 -1
- package/notifications/NotificationRecordsStore.d.ts +2 -1
- package/notifications/WebPushInterface.d.ts +0 -1
- package/notifications/WebPushInterface.js +0 -1
- package/notifications/WebPushInterface.js.map +1 -1
- package/package.json +6 -6
- package/packages/MemoryPackageRecordsStore.d.ts +10 -0
- package/packages/MemoryPackageRecordsStore.js +38 -0
- package/packages/MemoryPackageRecordsStore.js.map +1 -0
- package/packages/PackageRecordsController.d.ts +26 -0
- package/packages/PackageRecordsController.js +49 -0
- package/packages/PackageRecordsController.js.map +1 -0
- package/packages/PackageRecordsStore.d.ts +32 -0
- package/packages/PackageRecordsStore.js +19 -0
- package/packages/PackageRecordsStore.js.map +1 -0
- package/packages/index.d.ts +4 -0
- package/packages/index.js +21 -0
- package/packages/index.js.map +1 -0
- package/packages/version/MemoryPackageVersionRecordsStore.d.ts +21 -0
- package/packages/version/MemoryPackageVersionRecordsStore.js +177 -0
- package/packages/version/MemoryPackageVersionRecordsStore.js.map +1 -0
- package/packages/version/PackageVersionRecordsController.d.ts +144 -0
- package/packages/version/PackageVersionRecordsController.js +656 -0
- package/packages/version/PackageVersionRecordsController.js.map +1 -0
- package/packages/version/PackageVersionRecordsStore.d.ts +342 -0
- package/packages/version/PackageVersionRecordsStore.js +126 -0
- package/packages/version/PackageVersionRecordsStore.js.map +1 -0
- package/packages/version/index.d.ts +4 -0
- package/packages/version/index.js +21 -0
- package/packages/version/index.js.map +1 -0
- package/tracing/TracingDecorators.js +31 -40
- package/tracing/TracingDecorators.js.map +1 -1
- package/webhooks/MemoryWebhookRecordsStore.js +56 -72
- package/webhooks/MemoryWebhookRecordsStore.js.map +1 -1
- package/webhooks/WebhookEnvironment.d.ts +3 -3
- package/webhooks/WebhookRecordsController.d.ts +2 -1
- package/webhooks/WebhookRecordsController.js +389 -382
- package/webhooks/WebhookRecordsController.js.map +1 -1
- package/webhooks/WebhookRecordsStore.d.ts +2 -1
- package/websockets/InstRecordsStore.d.ts +50 -0
- package/websockets/InstRecordsStore.js +17 -0
- package/websockets/InstRecordsStore.js.map +1 -1
- package/websockets/MemoryTempInstRecordsStore.d.ts +5 -0
- package/websockets/MemoryTempInstRecordsStore.js +168 -179
- package/websockets/MemoryTempInstRecordsStore.js.map +1 -1
- package/websockets/MemoryWebsocketConnectionStore.js +98 -135
- package/websockets/MemoryWebsocketConnectionStore.js.map +1 -1
- package/websockets/MemoryWebsocketMessenger.js +29 -48
- package/websockets/MemoryWebsocketMessenger.js.map +1 -1
- package/websockets/SplitInstRecordsStore.d.ts +4 -1
- package/websockets/SplitInstRecordsStore.js +167 -185
- package/websockets/SplitInstRecordsStore.js.map +1 -1
- package/websockets/TemporaryInstRecordsStore.d.ts +19 -1
- package/websockets/TemporaryInstRecordsStore.js +17 -0
- package/websockets/TemporaryInstRecordsStore.js.map +1 -1
- package/websockets/WebsocketController.d.ts +147 -3
- package/websockets/WebsocketController.js +1735 -1391
- package/websockets/WebsocketController.js.map +1 -1
- package/websockets/index.d.ts +0 -1
- package/websockets/index.js +0 -1
- package/websockets/index.js.map +1 -1
- package/AAGUID.d.ts +0 -11
- package/AAGUID.js +0 -116
- package/AAGUID.js.map +0 -1
- package/AuthUtils.d.ts +0 -162
- package/AuthUtils.js +0 -327
- package/AuthUtils.js.map +0 -1
- package/forms/FormError.d.ts +0 -43
- package/forms/FormError.js +0 -56
- package/forms/FormError.js.map +0 -1
- package/forms/index.d.ts +0 -2
- package/websockets/Utils.d.ts +0 -33
- package/websockets/Utils.js +0 -82
- package/websockets/Utils.js.map +0 -1
|
@@ -4,21 +4,12 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
8
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
9
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
10
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
11
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
12
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
13
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
14
|
-
});
|
|
15
|
-
};
|
|
16
7
|
import { INVALID_KEY_ERROR_MESSAGE } from './AuthController';
|
|
17
|
-
import { STRIPE_EVENT_INVOICE_PAID_SCHEMA
|
|
8
|
+
import { STRIPE_EVENT_INVOICE_PAID_SCHEMA } from './StripeInterface';
|
|
18
9
|
import { isActiveSubscription } from './Utils';
|
|
19
|
-
import { isSuperUserRole } from './AuthUtils';
|
|
20
10
|
import { traced } from './tracing/TracingDecorators';
|
|
21
11
|
import { SpanStatusCode, trace } from '@opentelemetry/api';
|
|
12
|
+
import { isSuperUserRole } from '@casual-simulation/aux-common';
|
|
22
13
|
const TRACE_NAME = 'SubscriptionController';
|
|
23
14
|
/**
|
|
24
15
|
* Defines a class that is able to handle subscriptions.
|
|
@@ -31,279 +22,273 @@ export class SubscriptionController {
|
|
|
31
22
|
this._recordsStore = recordsStore;
|
|
32
23
|
this._config = config;
|
|
33
24
|
}
|
|
34
|
-
_getConfig() {
|
|
35
|
-
return
|
|
36
|
-
return yield this._config.getSubscriptionConfiguration();
|
|
37
|
-
});
|
|
25
|
+
async _getConfig() {
|
|
26
|
+
return await this._config.getSubscriptionConfiguration();
|
|
38
27
|
}
|
|
39
28
|
/**
|
|
40
29
|
* Gets the status of the given user's scription.
|
|
41
30
|
* @param request
|
|
42
31
|
*/
|
|
43
|
-
getSubscriptionStatus(request) {
|
|
32
|
+
async getSubscriptionStatus(request) {
|
|
44
33
|
var _a, _b;
|
|
45
|
-
|
|
46
|
-
|
|
34
|
+
if (!this._stripe) {
|
|
35
|
+
return {
|
|
36
|
+
success: false,
|
|
37
|
+
errorCode: 'not_supported',
|
|
38
|
+
errorMessage: 'This method is not supported.',
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
if (request.userId && request.studioId) {
|
|
47
43
|
return {
|
|
48
44
|
success: false,
|
|
49
|
-
errorCode: '
|
|
50
|
-
errorMessage: '
|
|
45
|
+
errorCode: 'unacceptable_request',
|
|
46
|
+
errorMessage: 'The given request is invalid. It must not specify both a user ID and a studio ID.',
|
|
51
47
|
};
|
|
52
48
|
}
|
|
53
|
-
|
|
54
|
-
if (request.userId
|
|
49
|
+
if (request.userId) {
|
|
50
|
+
if (typeof request.userId !== 'string' ||
|
|
51
|
+
request.userId === '') {
|
|
52
|
+
return {
|
|
53
|
+
success: false,
|
|
54
|
+
errorCode: 'unacceptable_user_id',
|
|
55
|
+
errorMessage: 'The given user ID is invalid. It must be a correctly formatted string.',
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else if (request.studioId) {
|
|
60
|
+
if (typeof request.studioId !== 'string' ||
|
|
61
|
+
request.studioId === '') {
|
|
55
62
|
return {
|
|
56
63
|
success: false,
|
|
57
|
-
errorCode: '
|
|
58
|
-
errorMessage: 'The given
|
|
64
|
+
errorCode: 'unacceptable_studio_id',
|
|
65
|
+
errorMessage: 'The given studio ID is invalid. It must be a correctly formatted string.',
|
|
59
66
|
};
|
|
60
67
|
}
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
return {
|
|
71
|
+
success: false,
|
|
72
|
+
errorCode: 'unacceptable_request',
|
|
73
|
+
errorMessage: 'The given request is invalid. It must have a valid user ID or studio ID.',
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const keyResult = await this._auth.validateSessionKey(request.sessionKey);
|
|
77
|
+
let customerId;
|
|
78
|
+
let role;
|
|
79
|
+
if (keyResult.success === false) {
|
|
80
|
+
return keyResult;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
61
83
|
if (request.userId) {
|
|
62
|
-
if (
|
|
63
|
-
|
|
84
|
+
if (!isSuperUserRole(keyResult.role) &&
|
|
85
|
+
keyResult.userId !== request.userId) {
|
|
86
|
+
console.log('[SubscriptionController] [getSubscriptionStatus] Request User ID doesnt match session key User ID!');
|
|
64
87
|
return {
|
|
65
88
|
success: false,
|
|
66
|
-
errorCode: '
|
|
67
|
-
errorMessage:
|
|
89
|
+
errorCode: 'invalid_key',
|
|
90
|
+
errorMessage: INVALID_KEY_ERROR_MESSAGE,
|
|
68
91
|
};
|
|
69
92
|
}
|
|
93
|
+
const user = await this._authStore.findUser(request.userId);
|
|
94
|
+
customerId = user.stripeCustomerId;
|
|
95
|
+
role = 'user';
|
|
70
96
|
}
|
|
71
97
|
else if (request.studioId) {
|
|
72
|
-
|
|
73
|
-
|
|
98
|
+
const assignments = await this._recordsStore.listStudioAssignments(request.studioId, {
|
|
99
|
+
userId: keyResult.userId,
|
|
100
|
+
role: 'admin',
|
|
101
|
+
});
|
|
102
|
+
if (!isSuperUserRole(keyResult.role) &&
|
|
103
|
+
assignments.length <= 0) {
|
|
104
|
+
console.log('[SubscriptionController] [getSubscriptionStatus] Request user does not have access to studio!');
|
|
74
105
|
return {
|
|
75
106
|
success: false,
|
|
76
|
-
errorCode: '
|
|
77
|
-
errorMessage:
|
|
107
|
+
errorCode: 'invalid_key',
|
|
108
|
+
errorMessage: INVALID_KEY_ERROR_MESSAGE,
|
|
78
109
|
};
|
|
79
110
|
}
|
|
111
|
+
const studio = await this._recordsStore.getStudioById(request.studioId);
|
|
112
|
+
customerId = studio.stripeCustomerId;
|
|
113
|
+
role = 'studio';
|
|
80
114
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
const keyResult = yield this._auth.validateSessionKey(request.sessionKey);
|
|
89
|
-
let customerId;
|
|
90
|
-
let role;
|
|
91
|
-
if (keyResult.success === false) {
|
|
92
|
-
return keyResult;
|
|
93
|
-
}
|
|
94
|
-
else {
|
|
95
|
-
if (request.userId) {
|
|
96
|
-
if (!isSuperUserRole(keyResult.role) &&
|
|
97
|
-
keyResult.userId !== request.userId) {
|
|
98
|
-
console.log('[SubscriptionController] [getSubscriptionStatus] Request User ID doesnt match session key User ID!');
|
|
99
|
-
return {
|
|
100
|
-
success: false,
|
|
101
|
-
errorCode: 'invalid_key',
|
|
102
|
-
errorMessage: INVALID_KEY_ERROR_MESSAGE,
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
const user = yield this._authStore.findUser(request.userId);
|
|
106
|
-
customerId = user.stripeCustomerId;
|
|
107
|
-
role = 'user';
|
|
108
|
-
}
|
|
109
|
-
else if (request.studioId) {
|
|
110
|
-
const assignments = yield this._recordsStore.listStudioAssignments(request.studioId, {
|
|
111
|
-
userId: keyResult.userId,
|
|
112
|
-
role: 'admin',
|
|
113
|
-
});
|
|
114
|
-
if (!isSuperUserRole(keyResult.role) &&
|
|
115
|
-
assignments.length <= 0) {
|
|
116
|
-
console.log('[SubscriptionController] [getSubscriptionStatus] Request user does not have access to studio!');
|
|
117
|
-
return {
|
|
118
|
-
success: false,
|
|
119
|
-
errorCode: 'invalid_key',
|
|
120
|
-
errorMessage: INVALID_KEY_ERROR_MESSAGE,
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
const studio = yield this._recordsStore.getStudioById(request.studioId);
|
|
124
|
-
customerId = studio.stripeCustomerId;
|
|
125
|
-
role = 'studio';
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
// const user = await this._authStore.findUser(keyResult.userId);
|
|
129
|
-
// let customerId = user.stripeCustomerId;
|
|
130
|
-
if (!customerId) {
|
|
131
|
-
const config = yield this._getConfig();
|
|
132
|
-
return {
|
|
133
|
-
success: true,
|
|
134
|
-
userId: (_a = request.userId) !== null && _a !== void 0 ? _a : keyResult.userId,
|
|
135
|
-
studioId: request.studioId,
|
|
136
|
-
publishableKey: this._stripe.publishableKey,
|
|
137
|
-
subscriptions: [],
|
|
138
|
-
purchasableSubscriptions: yield this._getPurchasableSubscriptions(role, config),
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
const listResult = yield this._stripe.listActiveSubscriptionsForCustomer(customerId);
|
|
142
|
-
const config = yield this._getConfig();
|
|
143
|
-
const subscriptions = listResult.subscriptions.map((s) => {
|
|
144
|
-
const item = s.items[0];
|
|
145
|
-
const subscriptionInfo = config.subscriptions.find((sub) => {
|
|
146
|
-
return (sub.eligibleProducts &&
|
|
147
|
-
sub.eligibleProducts.some((p) => p === item.price.product.id));
|
|
148
|
-
});
|
|
149
|
-
const featureList = subscriptionInfo === null || subscriptionInfo === void 0 ? void 0 : subscriptionInfo.featureList;
|
|
150
|
-
return {
|
|
151
|
-
active: s.status === 'active',
|
|
152
|
-
statusCode: s.status,
|
|
153
|
-
startDate: s.start_date,
|
|
154
|
-
cancelDate: s.cancel_at,
|
|
155
|
-
canceledDate: s.canceled_at,
|
|
156
|
-
endedDate: s.ended_at,
|
|
157
|
-
currentPeriodEnd: s.current_period_end,
|
|
158
|
-
currentPeriodStart: s.current_period_start,
|
|
159
|
-
renewalInterval: item.price.interval,
|
|
160
|
-
intervalLength: item.price.interval_count,
|
|
161
|
-
intervalCost: item.price.unit_amount,
|
|
162
|
-
currency: item.price.currency,
|
|
163
|
-
productName: item.price.product.name,
|
|
164
|
-
featureList,
|
|
165
|
-
};
|
|
166
|
-
});
|
|
167
|
-
const purchasableSubscriptions = subscriptions.length > 0
|
|
168
|
-
? []
|
|
169
|
-
: yield this._getPurchasableSubscriptions(role, config);
|
|
115
|
+
}
|
|
116
|
+
// const user = await this._authStore.findUser(keyResult.userId);
|
|
117
|
+
// let customerId = user.stripeCustomerId;
|
|
118
|
+
if (!customerId) {
|
|
119
|
+
const config = await this._getConfig();
|
|
170
120
|
return {
|
|
171
121
|
success: true,
|
|
172
|
-
userId: (
|
|
122
|
+
userId: (_a = request.userId) !== null && _a !== void 0 ? _a : keyResult.userId,
|
|
173
123
|
studioId: request.studioId,
|
|
174
124
|
publishableKey: this._stripe.publishableKey,
|
|
175
|
-
subscriptions,
|
|
176
|
-
purchasableSubscriptions,
|
|
125
|
+
subscriptions: [],
|
|
126
|
+
purchasableSubscriptions: await this._getPurchasableSubscriptions(role, config),
|
|
177
127
|
};
|
|
178
128
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
129
|
+
const listResult = await this._stripe.listActiveSubscriptionsForCustomer(customerId);
|
|
130
|
+
const config = await this._getConfig();
|
|
131
|
+
const subscriptions = listResult.subscriptions.map((s) => {
|
|
132
|
+
const item = s.items[0];
|
|
133
|
+
const subscriptionInfo = config.subscriptions.find((sub) => {
|
|
134
|
+
return (sub.eligibleProducts &&
|
|
135
|
+
sub.eligibleProducts.some((p) => p === item.price.product.id));
|
|
136
|
+
});
|
|
137
|
+
const featureList = subscriptionInfo === null || subscriptionInfo === void 0 ? void 0 : subscriptionInfo.featureList;
|
|
184
138
|
return {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
139
|
+
active: s.status === 'active',
|
|
140
|
+
statusCode: s.status,
|
|
141
|
+
startDate: s.start_date,
|
|
142
|
+
cancelDate: s.cancel_at,
|
|
143
|
+
canceledDate: s.canceled_at,
|
|
144
|
+
endedDate: s.ended_at,
|
|
145
|
+
currentPeriodEnd: s.current_period_end,
|
|
146
|
+
currentPeriodStart: s.current_period_start,
|
|
147
|
+
renewalInterval: item.price.interval,
|
|
148
|
+
intervalLength: item.price.interval_count,
|
|
149
|
+
intervalCost: item.price.unit_amount,
|
|
150
|
+
currency: item.price.currency,
|
|
151
|
+
productName: item.price.product.name,
|
|
152
|
+
featureList,
|
|
188
153
|
};
|
|
189
|
-
}
|
|
190
|
-
|
|
154
|
+
});
|
|
155
|
+
const purchasableSubscriptions = subscriptions.length > 0
|
|
156
|
+
? []
|
|
157
|
+
: await this._getPurchasableSubscriptions(role, config);
|
|
158
|
+
return {
|
|
159
|
+
success: true,
|
|
160
|
+
userId: (_b = request.userId) !== null && _b !== void 0 ? _b : keyResult.userId,
|
|
161
|
+
studioId: request.studioId,
|
|
162
|
+
publishableKey: this._stripe.publishableKey,
|
|
163
|
+
subscriptions,
|
|
164
|
+
purchasableSubscriptions,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
catch (err) {
|
|
168
|
+
const span = trace.getActiveSpan();
|
|
169
|
+
span === null || span === void 0 ? void 0 : span.recordException(err);
|
|
170
|
+
span === null || span === void 0 ? void 0 : span.setStatus({ code: SpanStatusCode.ERROR });
|
|
171
|
+
console.error('[SubscriptionController] An error occurred while getting subscription status:', err);
|
|
172
|
+
return {
|
|
173
|
+
success: false,
|
|
174
|
+
errorCode: 'server_error',
|
|
175
|
+
errorMessage: 'A server error occurred.',
|
|
176
|
+
};
|
|
177
|
+
}
|
|
191
178
|
}
|
|
192
179
|
/**
|
|
193
180
|
* Attempts to update the subscription for the given user.
|
|
194
181
|
* @param request The request to update the subscription.
|
|
195
182
|
*/
|
|
196
|
-
updateSubscription(request) {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
183
|
+
async updateSubscription(request) {
|
|
184
|
+
try {
|
|
185
|
+
if (!request.currentUserId) {
|
|
186
|
+
return {
|
|
187
|
+
success: false,
|
|
188
|
+
errorCode: 'not_logged_in',
|
|
189
|
+
errorMessage: 'You must be logged in to update a subscription.',
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
if (!isSuperUserRole(request.currentUserRole)) {
|
|
193
|
+
return {
|
|
194
|
+
success: false,
|
|
195
|
+
errorCode: 'not_authorized',
|
|
196
|
+
errorMessage: 'You are not authorized to perform this action.',
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
if (!request.userId && !request.studioId) {
|
|
200
|
+
return {
|
|
201
|
+
success: false,
|
|
202
|
+
errorCode: 'invalid_request',
|
|
203
|
+
errorMessage: 'The given request is invalid. It must have a user ID or studio ID.',
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
if (request.userId) {
|
|
207
|
+
console.log(`[SubscriptionController] [updateSubscription currentUserId: ${request.currentUserId}, userId: ${request.userId}, subscriptionId: ${request.subscriptionId}, subscriptionStatus: ${request.subscriptionStatus}] Updating subscription.`);
|
|
208
|
+
const user = await this._authStore.findUser(request.userId);
|
|
209
|
+
if (!user) {
|
|
207
210
|
return {
|
|
208
211
|
success: false,
|
|
209
|
-
errorCode: '
|
|
210
|
-
errorMessage: '
|
|
212
|
+
errorCode: 'user_not_found',
|
|
213
|
+
errorMessage: 'The user could not be found.',
|
|
211
214
|
};
|
|
212
215
|
}
|
|
213
|
-
if (
|
|
216
|
+
if (user.subscriptionInfoId &&
|
|
217
|
+
isActiveSubscription(user.subscriptionStatus, user.subscriptionPeriodStartMs, user.subscriptionPeriodEndMs)) {
|
|
214
218
|
return {
|
|
215
219
|
success: false,
|
|
216
220
|
errorCode: 'invalid_request',
|
|
217
|
-
errorMessage: 'The
|
|
221
|
+
errorMessage: 'The user already has an active stripe subscription. Currently, this operation only supports updating the subscription of a user who does not have an active stripe subscription.',
|
|
218
222
|
};
|
|
219
223
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
: null,
|
|
244
|
-
currentPeriodStartMs: request.subscriptionId
|
|
245
|
-
? request.subscriptionPeriodStartMs
|
|
246
|
-
: null,
|
|
247
|
-
currentPeriodEndMs: request.subscriptionId
|
|
248
|
-
? request.subscriptionPeriodEndMs
|
|
249
|
-
: null,
|
|
250
|
-
stripeCustomerId: null,
|
|
251
|
-
stripeSubscriptionId: null,
|
|
252
|
-
});
|
|
224
|
+
await this._authStore.updateSubscriptionInfo({
|
|
225
|
+
userId: user.id,
|
|
226
|
+
subscriptionId: request.subscriptionId,
|
|
227
|
+
subscriptionStatus: request.subscriptionId
|
|
228
|
+
? request.subscriptionStatus
|
|
229
|
+
: null,
|
|
230
|
+
currentPeriodStartMs: request.subscriptionId
|
|
231
|
+
? request.subscriptionPeriodStartMs
|
|
232
|
+
: null,
|
|
233
|
+
currentPeriodEndMs: request.subscriptionId
|
|
234
|
+
? request.subscriptionPeriodEndMs
|
|
235
|
+
: null,
|
|
236
|
+
stripeCustomerId: null,
|
|
237
|
+
stripeSubscriptionId: null,
|
|
238
|
+
});
|
|
239
|
+
return {
|
|
240
|
+
success: true,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
console.log(`[SubscriptionController] [updateSubscription currentUserId: ${request.currentUserId}, studioId: ${request.studioId}, subscriptionId: ${request.subscriptionId}, subscriptionStatus: ${request.subscriptionStatus}] Updating subscription.`);
|
|
245
|
+
const studio = await this._recordsStore.getStudioById(request.studioId);
|
|
246
|
+
if (!studio) {
|
|
253
247
|
return {
|
|
254
|
-
success:
|
|
248
|
+
success: false,
|
|
249
|
+
errorCode: 'studio_not_found',
|
|
250
|
+
errorMessage: 'The studio could not be found.',
|
|
255
251
|
};
|
|
256
252
|
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
const studio = yield this._recordsStore.getStudioById(request.studioId);
|
|
260
|
-
if (!studio) {
|
|
261
|
-
return {
|
|
262
|
-
success: false,
|
|
263
|
-
errorCode: 'studio_not_found',
|
|
264
|
-
errorMessage: 'The studio could not be found.',
|
|
265
|
-
};
|
|
266
|
-
}
|
|
267
|
-
if (studio.subscriptionInfoId &&
|
|
268
|
-
isActiveSubscription(studio.subscriptionStatus, studio.subscriptionPeriodStartMs, studio.subscriptionPeriodEndMs)) {
|
|
269
|
-
return {
|
|
270
|
-
success: false,
|
|
271
|
-
errorCode: 'invalid_request',
|
|
272
|
-
errorMessage: 'The studio already has an active stripe subscription. Currently, this operation only supports updating the subscription of a studio which does not have an active stripe subscription.',
|
|
273
|
-
};
|
|
274
|
-
}
|
|
275
|
-
yield this._authStore.updateSubscriptionInfo({
|
|
276
|
-
studioId: studio.id,
|
|
277
|
-
subscriptionId: request.subscriptionId,
|
|
278
|
-
subscriptionStatus: request.subscriptionId
|
|
279
|
-
? request.subscriptionStatus
|
|
280
|
-
: null,
|
|
281
|
-
currentPeriodStartMs: request.subscriptionId
|
|
282
|
-
? request.subscriptionPeriodStartMs
|
|
283
|
-
: null,
|
|
284
|
-
currentPeriodEndMs: request.subscriptionId
|
|
285
|
-
? request.subscriptionPeriodEndMs
|
|
286
|
-
: null,
|
|
287
|
-
stripeCustomerId: null,
|
|
288
|
-
stripeSubscriptionId: null,
|
|
289
|
-
});
|
|
253
|
+
if (studio.subscriptionInfoId &&
|
|
254
|
+
isActiveSubscription(studio.subscriptionStatus, studio.subscriptionPeriodStartMs, studio.subscriptionPeriodEndMs)) {
|
|
290
255
|
return {
|
|
291
|
-
success:
|
|
256
|
+
success: false,
|
|
257
|
+
errorCode: 'invalid_request',
|
|
258
|
+
errorMessage: 'The studio already has an active stripe subscription. Currently, this operation only supports updating the subscription of a studio which does not have an active stripe subscription.',
|
|
292
259
|
};
|
|
293
260
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
261
|
+
await this._authStore.updateSubscriptionInfo({
|
|
262
|
+
studioId: studio.id,
|
|
263
|
+
subscriptionId: request.subscriptionId,
|
|
264
|
+
subscriptionStatus: request.subscriptionId
|
|
265
|
+
? request.subscriptionStatus
|
|
266
|
+
: null,
|
|
267
|
+
currentPeriodStartMs: request.subscriptionId
|
|
268
|
+
? request.subscriptionPeriodStartMs
|
|
269
|
+
: null,
|
|
270
|
+
currentPeriodEndMs: request.subscriptionId
|
|
271
|
+
? request.subscriptionPeriodEndMs
|
|
272
|
+
: null,
|
|
273
|
+
stripeCustomerId: null,
|
|
274
|
+
stripeSubscriptionId: null,
|
|
275
|
+
});
|
|
300
276
|
return {
|
|
301
|
-
success:
|
|
302
|
-
errorCode: 'server_error',
|
|
303
|
-
errorMessage: 'A server error occurred.',
|
|
277
|
+
success: true,
|
|
304
278
|
};
|
|
305
279
|
}
|
|
306
|
-
}
|
|
280
|
+
}
|
|
281
|
+
catch (err) {
|
|
282
|
+
const span = trace.getActiveSpan();
|
|
283
|
+
span === null || span === void 0 ? void 0 : span.recordException(err);
|
|
284
|
+
span === null || span === void 0 ? void 0 : span.setStatus({ code: SpanStatusCode.ERROR });
|
|
285
|
+
console.error('[SubscriptionController] An error occurred while updating a subscription:', err);
|
|
286
|
+
return {
|
|
287
|
+
success: false,
|
|
288
|
+
errorCode: 'server_error',
|
|
289
|
+
errorMessage: 'A server error occurred.',
|
|
290
|
+
};
|
|
291
|
+
}
|
|
307
292
|
}
|
|
308
293
|
_getPurchasableSubscriptionsForRole(role, config) {
|
|
309
294
|
return config.subscriptions.filter((s) => {
|
|
@@ -317,396 +302,420 @@ export class SubscriptionController {
|
|
|
317
302
|
return isPurchasable && matchesRole;
|
|
318
303
|
});
|
|
319
304
|
}
|
|
320
|
-
_getPurchasableSubscriptions(role, config) {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
info: yield this._stripe.getProductAndPriceInfo(s.product),
|
|
327
|
-
};
|
|
328
|
-
}
|
|
329
|
-
else {
|
|
330
|
-
return {
|
|
331
|
-
sub: s,
|
|
332
|
-
info: {
|
|
333
|
-
description: s.description,
|
|
334
|
-
name: s.name,
|
|
335
|
-
default_price: null,
|
|
336
|
-
id: null,
|
|
337
|
-
},
|
|
338
|
-
};
|
|
339
|
-
}
|
|
340
|
-
}));
|
|
341
|
-
const productInfo = yield Promise.all(promises);
|
|
342
|
-
return productInfo
|
|
343
|
-
.filter((i) => !!i.info)
|
|
344
|
-
.map((i) => {
|
|
345
|
-
let prices = [];
|
|
346
|
-
if (i.info.default_price) {
|
|
347
|
-
prices.push({
|
|
348
|
-
id: 'default',
|
|
349
|
-
currency: i.info.default_price.currency,
|
|
350
|
-
cost: i.info.default_price.unit_amount,
|
|
351
|
-
interval: i.info.default_price.recurring.interval,
|
|
352
|
-
intervalLength: i.info.default_price.recurring.interval_count,
|
|
353
|
-
});
|
|
354
|
-
}
|
|
355
|
-
let result = {
|
|
356
|
-
id: i.sub.id,
|
|
357
|
-
name: i.info.name,
|
|
358
|
-
description: i.info.description,
|
|
359
|
-
featureList: i.sub.featureList,
|
|
360
|
-
prices,
|
|
305
|
+
async _getPurchasableSubscriptions(role, config) {
|
|
306
|
+
const promises = this._getPurchasableSubscriptionsForRole(role, config).map(async (s) => {
|
|
307
|
+
if (s.product) {
|
|
308
|
+
return {
|
|
309
|
+
sub: s,
|
|
310
|
+
info: await this._stripe.getProductAndPriceInfo(s.product),
|
|
361
311
|
};
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
return {
|
|
315
|
+
sub: s,
|
|
316
|
+
info: {
|
|
317
|
+
description: s.description,
|
|
318
|
+
name: s.name,
|
|
319
|
+
default_price: null,
|
|
320
|
+
id: null,
|
|
321
|
+
},
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
const productInfo = await Promise.all(promises);
|
|
326
|
+
return productInfo
|
|
327
|
+
.filter((i) => !!i.info)
|
|
328
|
+
.map((i) => {
|
|
329
|
+
let prices = [];
|
|
330
|
+
if (i.info.default_price) {
|
|
331
|
+
prices.push({
|
|
332
|
+
id: 'default',
|
|
333
|
+
currency: i.info.default_price.currency,
|
|
334
|
+
cost: i.info.default_price.unit_amount,
|
|
335
|
+
interval: i.info.default_price.recurring.interval,
|
|
336
|
+
intervalLength: i.info.default_price.recurring.interval_count,
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
let result = {
|
|
340
|
+
id: i.sub.id,
|
|
341
|
+
name: i.info.name,
|
|
342
|
+
description: i.info.description,
|
|
343
|
+
featureList: i.sub.featureList,
|
|
344
|
+
prices,
|
|
345
|
+
};
|
|
346
|
+
if ('defaultSubscription' in i.sub) {
|
|
347
|
+
result.defaultSubscription = i.sub.defaultSubscription;
|
|
348
|
+
}
|
|
349
|
+
return result;
|
|
367
350
|
});
|
|
368
351
|
}
|
|
369
352
|
/**
|
|
370
353
|
* Creates a link that the user can be redirected to in order to manage their subscription.
|
|
371
354
|
* Returns a link that the user can be redirected to to initiate a purchase of the subscription.
|
|
372
355
|
*/
|
|
373
|
-
createManageSubscriptionLink(request) {
|
|
356
|
+
async createManageSubscriptionLink(request) {
|
|
374
357
|
var _a;
|
|
375
|
-
|
|
376
|
-
|
|
358
|
+
if (!this._stripe) {
|
|
359
|
+
return {
|
|
360
|
+
success: false,
|
|
361
|
+
errorCode: 'not_supported',
|
|
362
|
+
errorMessage: 'This method is not supported.',
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
try {
|
|
366
|
+
if (request.userId && request.studioId) {
|
|
377
367
|
return {
|
|
378
368
|
success: false,
|
|
379
|
-
errorCode: '
|
|
380
|
-
errorMessage: '
|
|
369
|
+
errorCode: 'unacceptable_request',
|
|
370
|
+
errorMessage: 'The given request is invalid. It must not specify both a user ID and a studio ID.',
|
|
381
371
|
};
|
|
382
372
|
}
|
|
383
|
-
|
|
384
|
-
if (request.userId
|
|
373
|
+
if (request.userId) {
|
|
374
|
+
if (typeof request.userId !== 'string' ||
|
|
375
|
+
request.userId === '') {
|
|
385
376
|
return {
|
|
386
377
|
success: false,
|
|
387
|
-
errorCode: '
|
|
388
|
-
errorMessage: 'The given
|
|
378
|
+
errorCode: 'unacceptable_user_id',
|
|
379
|
+
errorMessage: 'The given user ID is invalid. It must be a correctly formatted string.',
|
|
389
380
|
};
|
|
390
381
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
success: false,
|
|
396
|
-
errorCode: 'unacceptable_user_id',
|
|
397
|
-
errorMessage: 'The given user ID is invalid. It must be a correctly formatted string.',
|
|
398
|
-
};
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
else if (request.studioId) {
|
|
402
|
-
if (typeof request.studioId !== 'string' ||
|
|
403
|
-
request.studioId === '') {
|
|
404
|
-
return {
|
|
405
|
-
success: false,
|
|
406
|
-
errorCode: 'unacceptable_studio_id',
|
|
407
|
-
errorMessage: 'The given studio ID is invalid. It must be a correctly formatted string.',
|
|
408
|
-
};
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
else {
|
|
382
|
+
}
|
|
383
|
+
else if (request.studioId) {
|
|
384
|
+
if (typeof request.studioId !== 'string' ||
|
|
385
|
+
request.studioId === '') {
|
|
412
386
|
return {
|
|
413
387
|
success: false,
|
|
414
|
-
errorCode: '
|
|
415
|
-
errorMessage: 'The given
|
|
388
|
+
errorCode: 'unacceptable_studio_id',
|
|
389
|
+
errorMessage: 'The given studio ID is invalid. It must be a correctly formatted string.',
|
|
416
390
|
};
|
|
417
391
|
}
|
|
418
|
-
|
|
419
|
-
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
return {
|
|
395
|
+
success: false,
|
|
396
|
+
errorCode: 'unacceptable_request',
|
|
397
|
+
errorMessage: 'The given request is invalid. It must have a valid user ID or studio ID.',
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
if (!!request.subscriptionId &&
|
|
401
|
+
typeof request.subscriptionId !== 'string') {
|
|
402
|
+
return {
|
|
403
|
+
success: false,
|
|
404
|
+
errorCode: 'unacceptable_request',
|
|
405
|
+
errorMessage: 'The given Subscription ID is invalid. If provided, it must be a correctly formatted string.',
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
if (!!request.expectedPrice &&
|
|
409
|
+
typeof request.expectedPrice !== 'object') {
|
|
410
|
+
return {
|
|
411
|
+
success: false,
|
|
412
|
+
errorCode: 'unacceptable_request',
|
|
413
|
+
errorMessage: 'The given Price ID is invalid. If provided, it must be an object.',
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
const keyResult = await this._auth.validateSessionKey(request.sessionKey);
|
|
417
|
+
let customerId;
|
|
418
|
+
let customerName;
|
|
419
|
+
let customerEmail;
|
|
420
|
+
let customerPhone;
|
|
421
|
+
let role;
|
|
422
|
+
let user;
|
|
423
|
+
let studio;
|
|
424
|
+
let customerMetadata = {};
|
|
425
|
+
let metadata = {};
|
|
426
|
+
if (keyResult.success === false) {
|
|
427
|
+
return keyResult;
|
|
428
|
+
}
|
|
429
|
+
else if (request.userId) {
|
|
430
|
+
if (keyResult.userId !== request.userId) {
|
|
431
|
+
console.log('[SubscriptionController] [createManageSubscriptionLink] Request User ID doesnt match session key User ID!');
|
|
420
432
|
return {
|
|
421
433
|
success: false,
|
|
422
|
-
errorCode: '
|
|
423
|
-
errorMessage:
|
|
434
|
+
errorCode: 'invalid_key',
|
|
435
|
+
errorMessage: INVALID_KEY_ERROR_MESSAGE,
|
|
424
436
|
};
|
|
425
437
|
}
|
|
426
|
-
|
|
427
|
-
|
|
438
|
+
user = await this._authStore.findUser(keyResult.userId);
|
|
439
|
+
customerId = user.stripeCustomerId;
|
|
440
|
+
customerName = user.name;
|
|
441
|
+
customerEmail = user.email;
|
|
442
|
+
customerPhone = user.phoneNumber;
|
|
443
|
+
metadata.userId = user.id;
|
|
444
|
+
customerMetadata.role = 'user';
|
|
445
|
+
customerMetadata.userId = user.id;
|
|
446
|
+
role = 'user';
|
|
447
|
+
console.log(`[SubscriptionController] [createManageSubscriptionLink] Creating a checkout/management session for User (${keyResult.userId}).`);
|
|
448
|
+
}
|
|
449
|
+
else if (request.studioId) {
|
|
450
|
+
const assignments = await this._recordsStore.listStudioAssignments(request.studioId, {
|
|
451
|
+
role: 'admin',
|
|
452
|
+
});
|
|
453
|
+
const userAssignment = assignments.find((a) => a.userId === keyResult.userId);
|
|
454
|
+
if (!userAssignment) {
|
|
455
|
+
console.log('[SubscriptionController] [getSubscriptionStatus] Request user does not have access to studio!');
|
|
428
456
|
return {
|
|
429
457
|
success: false,
|
|
430
|
-
errorCode: '
|
|
431
|
-
errorMessage:
|
|
458
|
+
errorCode: 'invalid_key',
|
|
459
|
+
errorMessage: INVALID_KEY_ERROR_MESSAGE,
|
|
432
460
|
};
|
|
433
461
|
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
let
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
let metadata = {};
|
|
444
|
-
if (keyResult.success === false) {
|
|
445
|
-
return keyResult;
|
|
446
|
-
}
|
|
447
|
-
else if (request.userId) {
|
|
448
|
-
if (keyResult.userId !== request.userId) {
|
|
449
|
-
console.log('[SubscriptionController] [createManageSubscriptionLink] Request User ID doesnt match session key User ID!');
|
|
450
|
-
return {
|
|
451
|
-
success: false,
|
|
452
|
-
errorCode: 'invalid_key',
|
|
453
|
-
errorMessage: INVALID_KEY_ERROR_MESSAGE,
|
|
454
|
-
};
|
|
455
|
-
}
|
|
456
|
-
user = yield this._authStore.findUser(keyResult.userId);
|
|
457
|
-
customerId = user.stripeCustomerId;
|
|
458
|
-
customerName = user.name;
|
|
459
|
-
customerEmail = user.email;
|
|
460
|
-
customerPhone = user.phoneNumber;
|
|
461
|
-
metadata.userId = user.id;
|
|
462
|
-
customerMetadata.role = 'user';
|
|
463
|
-
customerMetadata.userId = user.id;
|
|
464
|
-
role = 'user';
|
|
465
|
-
console.log(`[SubscriptionController] [createManageSubscriptionLink] Creating a checkout/management session for User (${keyResult.userId}).`);
|
|
466
|
-
}
|
|
467
|
-
else if (request.studioId) {
|
|
468
|
-
const assignments = yield this._recordsStore.listStudioAssignments(request.studioId, {
|
|
469
|
-
role: 'admin',
|
|
470
|
-
});
|
|
471
|
-
const userAssignment = assignments.find((a) => a.userId === keyResult.userId);
|
|
472
|
-
if (!userAssignment) {
|
|
473
|
-
console.log('[SubscriptionController] [getSubscriptionStatus] Request user does not have access to studio!');
|
|
474
|
-
return {
|
|
475
|
-
success: false,
|
|
476
|
-
errorCode: 'invalid_key',
|
|
477
|
-
errorMessage: INVALID_KEY_ERROR_MESSAGE,
|
|
478
|
-
};
|
|
479
|
-
}
|
|
480
|
-
studio = yield this._recordsStore.getStudioById(request.studioId);
|
|
481
|
-
customerId = studio.stripeCustomerId;
|
|
482
|
-
customerName = studio.displayName;
|
|
483
|
-
customerMetadata.role = 'studio';
|
|
484
|
-
customerMetadata.studioId = studio.id;
|
|
485
|
-
metadata.studioId = studio.id;
|
|
486
|
-
let primaryAssignment;
|
|
487
|
-
if (userAssignment.isPrimaryContact) {
|
|
488
|
-
primaryAssignment = userAssignment;
|
|
489
|
-
}
|
|
490
|
-
else {
|
|
491
|
-
primaryAssignment = assignments.find((a) => a.isPrimaryContact);
|
|
492
|
-
}
|
|
493
|
-
if (primaryAssignment) {
|
|
494
|
-
customerEmail = primaryAssignment.user.email;
|
|
495
|
-
customerPhone = primaryAssignment.user.phoneNumber;
|
|
496
|
-
metadata.contactUserId = keyResult.userId;
|
|
497
|
-
customerMetadata.contactUserId = keyResult.userId;
|
|
498
|
-
}
|
|
499
|
-
role = 'studio';
|
|
500
|
-
console.log(`[SubscriptionController] [createManageSubscriptionLink] Creating a checkout/management session for Studio (userId: ${keyResult.userId}, studioId: ${studio.id}).`);
|
|
462
|
+
studio = await this._recordsStore.getStudioById(request.studioId);
|
|
463
|
+
customerId = studio.stripeCustomerId;
|
|
464
|
+
customerName = studio.displayName;
|
|
465
|
+
customerMetadata.role = 'studio';
|
|
466
|
+
customerMetadata.studioId = studio.id;
|
|
467
|
+
metadata.studioId = studio.id;
|
|
468
|
+
let primaryAssignment;
|
|
469
|
+
if (userAssignment.isPrimaryContact) {
|
|
470
|
+
primaryAssignment = userAssignment;
|
|
501
471
|
}
|
|
502
472
|
else {
|
|
503
|
-
|
|
473
|
+
primaryAssignment = assignments.find((a) => a.isPrimaryContact);
|
|
504
474
|
}
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
success: false,
|
|
511
|
-
errorCode: 'not_supported',
|
|
512
|
-
errorMessage: 'New subscriptions are not supported.',
|
|
513
|
-
};
|
|
514
|
-
}
|
|
515
|
-
console.log('[SubscriptionController] [createManageSubscriptionLink] No Stripe Customer ID. Creating New Customer and Checkout Session in Stripe.');
|
|
516
|
-
const result = yield this._stripe.createCustomer({
|
|
517
|
-
name: customerName,
|
|
518
|
-
email: customerEmail,
|
|
519
|
-
phone: customerPhone,
|
|
520
|
-
metadata: customerMetadata,
|
|
521
|
-
});
|
|
522
|
-
customerId = result.id;
|
|
523
|
-
if (user) {
|
|
524
|
-
user.stripeCustomerId = customerId;
|
|
525
|
-
console.log(`[SubscriptionController] [createManageSubscriptionLink] Saving Stripe Customer ID (${customerId}) to User Record (${user.id}).`);
|
|
526
|
-
yield this._authStore.saveUser(Object.assign({}, user));
|
|
527
|
-
}
|
|
528
|
-
else if (studio) {
|
|
529
|
-
studio.stripeCustomerId = customerId;
|
|
530
|
-
console.log(`[SubscriptionController] [createManageSubscriptionLink] Saving Stripe Customer ID (${customerId}) to Studio Record (${studio.id}).`);
|
|
531
|
-
yield this._recordsStore.updateStudio(Object.assign({}, studio));
|
|
532
|
-
}
|
|
533
|
-
return yield this._createCheckoutSession(request, customerId, metadata, role, user, studio);
|
|
475
|
+
if (primaryAssignment) {
|
|
476
|
+
customerEmail = primaryAssignment.user.email;
|
|
477
|
+
customerPhone = primaryAssignment.user.phoneNumber;
|
|
478
|
+
metadata.contactUserId = keyResult.userId;
|
|
479
|
+
customerMetadata.contactUserId = keyResult.userId;
|
|
534
480
|
}
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
return false;
|
|
546
|
-
}
|
|
547
|
-
const hasManagableProduct = config.subscriptions.some((sub) => sub.eligibleProducts &&
|
|
548
|
-
sub.eligibleProducts.some((p) => s.items.some((i) => i.price.product.id === p)));
|
|
549
|
-
return hasManagableProduct;
|
|
550
|
-
});
|
|
551
|
-
if (hasSubscription) {
|
|
552
|
-
console.log(`[SubscriptionController] [createManageSubscriptionLink] Customer has a managable subscription. Creating a portal session.`);
|
|
553
|
-
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) }));
|
|
554
|
-
console.log(`[SubscriptionController] [createManageSubscriptionLink] Portal session success!`);
|
|
481
|
+
role = 'studio';
|
|
482
|
+
console.log(`[SubscriptionController] [createManageSubscriptionLink] Creating a checkout/management session for Studio (userId: ${keyResult.userId}, studioId: ${studio.id}).`);
|
|
483
|
+
}
|
|
484
|
+
else {
|
|
485
|
+
throw new Error('Should not reach this point');
|
|
486
|
+
}
|
|
487
|
+
metadata.subjectId = keyResult.userId;
|
|
488
|
+
const config = await this._getConfig();
|
|
489
|
+
if (!customerId) {
|
|
490
|
+
if (config.subscriptions.length <= 0) {
|
|
555
491
|
return {
|
|
556
|
-
success:
|
|
557
|
-
|
|
492
|
+
success: false,
|
|
493
|
+
errorCode: 'not_supported',
|
|
494
|
+
errorMessage: 'New subscriptions are not supported.',
|
|
558
495
|
};
|
|
559
496
|
}
|
|
560
|
-
console.log(
|
|
561
|
-
|
|
497
|
+
console.log('[SubscriptionController] [createManageSubscriptionLink] No Stripe Customer ID. Creating New Customer and Checkout Session in Stripe.');
|
|
498
|
+
const result = await this._stripe.createCustomer({
|
|
499
|
+
name: customerName,
|
|
500
|
+
email: customerEmail,
|
|
501
|
+
phone: customerPhone,
|
|
502
|
+
metadata: customerMetadata,
|
|
503
|
+
});
|
|
504
|
+
customerId = result.id;
|
|
505
|
+
if (user) {
|
|
506
|
+
user.stripeCustomerId = customerId;
|
|
507
|
+
console.log(`[SubscriptionController] [createManageSubscriptionLink] Saving Stripe Customer ID (${customerId}) to User Record (${user.id}).`);
|
|
508
|
+
await this._authStore.saveUser({
|
|
509
|
+
...user,
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
else if (studio) {
|
|
513
|
+
studio.stripeCustomerId = customerId;
|
|
514
|
+
console.log(`[SubscriptionController] [createManageSubscriptionLink] Saving Stripe Customer ID (${customerId}) to Studio Record (${studio.id}).`);
|
|
515
|
+
await this._recordsStore.updateStudio({
|
|
516
|
+
...studio,
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
return await this._createCheckoutSession(request, customerId, metadata, role, user, studio);
|
|
562
520
|
}
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
521
|
+
console.log(`[SubscriptionController] [createManageSubscriptionLink] Has Stripe Customer ID (${customerId}). Checking active subscriptions for customer.`);
|
|
522
|
+
const subs = await this._stripe.listActiveSubscriptionsForCustomer(customerId);
|
|
523
|
+
const hasSubscription = subs.subscriptions.some((s) => {
|
|
524
|
+
const isManagable = s.status === 'active' ||
|
|
525
|
+
s.status === 'trialing' ||
|
|
526
|
+
s.status === 'paused' ||
|
|
527
|
+
s.status === 'incomplete' ||
|
|
528
|
+
s.status === 'past_due' ||
|
|
529
|
+
s.status === 'unpaid';
|
|
530
|
+
if (!isManagable) {
|
|
531
|
+
return false;
|
|
532
|
+
}
|
|
533
|
+
const hasManagableProduct = config.subscriptions.some((sub) => sub.eligibleProducts &&
|
|
534
|
+
sub.eligibleProducts.some((p) => s.items.some((i) => i.price.product.id === p)));
|
|
535
|
+
return hasManagableProduct;
|
|
536
|
+
});
|
|
537
|
+
if (hasSubscription) {
|
|
538
|
+
console.log(`[SubscriptionController] [createManageSubscriptionLink] Customer has a managable subscription. Creating a portal session.`);
|
|
539
|
+
const session = await this._stripe.createPortalSession({
|
|
540
|
+
...((_a = config.portalConfig) !== null && _a !== void 0 ? _a : {}),
|
|
541
|
+
customer: customerId,
|
|
542
|
+
return_url: returnRoute(config.returnUrl, user, studio),
|
|
543
|
+
});
|
|
544
|
+
console.log(`[SubscriptionController] [createManageSubscriptionLink] Portal session success!`);
|
|
568
545
|
return {
|
|
569
|
-
success:
|
|
570
|
-
|
|
571
|
-
errorMessage: 'A server error occurred.',
|
|
546
|
+
success: true,
|
|
547
|
+
url: session.url,
|
|
572
548
|
};
|
|
573
549
|
}
|
|
574
|
-
|
|
550
|
+
console.log(`[SubscriptionController] [createManageSubscriptionLink] Customer does not have a managable subscription. Creating a checkout session.`);
|
|
551
|
+
return await this._createCheckoutSession(request, customerId, metadata, role, user, studio);
|
|
552
|
+
}
|
|
553
|
+
catch (err) {
|
|
554
|
+
const span = trace.getActiveSpan();
|
|
555
|
+
span === null || span === void 0 ? void 0 : span.recordException(err);
|
|
556
|
+
span === null || span === void 0 ? void 0 : span.setStatus({ code: SpanStatusCode.ERROR });
|
|
557
|
+
console.error('[SubscriptionController] An error occurred while creating a manage subscription link:', err);
|
|
558
|
+
return {
|
|
559
|
+
success: false,
|
|
560
|
+
errorCode: 'server_error',
|
|
561
|
+
errorMessage: 'A server error occurred.',
|
|
562
|
+
};
|
|
563
|
+
}
|
|
575
564
|
}
|
|
576
|
-
_createCheckoutSession(request, customerId, metadata, role, user, studio) {
|
|
565
|
+
async _createCheckoutSession(request, customerId, metadata, role, user, studio) {
|
|
577
566
|
var _a;
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
console.log(`[SubscriptionController] [createManageSubscriptionLink] Using specified subscription (${request.subscriptionId}).`);
|
|
586
|
-
}
|
|
567
|
+
const config = await this._getConfig();
|
|
568
|
+
const purchasableSubscriptions = this._getPurchasableSubscriptionsForRole(role, config);
|
|
569
|
+
let sub;
|
|
570
|
+
if (request.subscriptionId) {
|
|
571
|
+
sub = purchasableSubscriptions.find((s) => s.id === request.subscriptionId);
|
|
572
|
+
if (sub) {
|
|
573
|
+
console.log(`[SubscriptionController] [createManageSubscriptionLink] Using specified subscription (${request.subscriptionId}).`);
|
|
587
574
|
}
|
|
588
|
-
|
|
575
|
+
}
|
|
576
|
+
if (!sub || !sub.product || sub.purchasable === false) {
|
|
577
|
+
return {
|
|
578
|
+
success: false,
|
|
579
|
+
errorCode: 'unacceptable_request',
|
|
580
|
+
errorMessage: 'The given subscription is not purchasable.',
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
const productInfo = await this._stripe.getProductAndPriceInfo(sub.product);
|
|
584
|
+
if (request.expectedPrice) {
|
|
585
|
+
if (request.expectedPrice.currency !==
|
|
586
|
+
productInfo.default_price.currency ||
|
|
587
|
+
request.expectedPrice.cost !==
|
|
588
|
+
productInfo.default_price.unit_amount ||
|
|
589
|
+
request.expectedPrice.interval !==
|
|
590
|
+
productInfo.default_price.recurring.interval ||
|
|
591
|
+
request.expectedPrice.intervalLength !==
|
|
592
|
+
productInfo.default_price.recurring.interval_count) {
|
|
593
|
+
console.log(`[SubscriptionController] [createManageSubscriptionLink] Expected price does not match actual price.`);
|
|
589
594
|
return {
|
|
590
595
|
success: false,
|
|
591
|
-
errorCode: '
|
|
592
|
-
errorMessage: 'The
|
|
596
|
+
errorCode: 'price_does_not_match',
|
|
597
|
+
errorMessage: 'The expected price does not match the actual price.',
|
|
593
598
|
};
|
|
594
599
|
}
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
};
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
console.log(`[SubscriptionController] [createManageSubscriptionLink] Creating Checkout Session.`);
|
|
614
|
-
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: [
|
|
615
|
-
{
|
|
616
|
-
price: productInfo.default_price.id,
|
|
617
|
-
quantity: 1,
|
|
618
|
-
},
|
|
619
|
-
], mode: 'subscription', metadata: metadata }));
|
|
620
|
-
console.log(`[SubscriptionController] [createManageSubscriptionLink] Checkout Session Success!`);
|
|
621
|
-
return {
|
|
622
|
-
success: true,
|
|
623
|
-
url: session.url,
|
|
624
|
-
};
|
|
600
|
+
}
|
|
601
|
+
console.log(`[SubscriptionController] [createManageSubscriptionLink] Creating Checkout Session.`);
|
|
602
|
+
const session = await this._stripe.createCheckoutSession({
|
|
603
|
+
...((_a = config.checkoutConfig) !== null && _a !== void 0 ? _a : {}),
|
|
604
|
+
customer: customerId,
|
|
605
|
+
success_url: returnRoute(config.successUrl, user, studio),
|
|
606
|
+
cancel_url: returnRoute(config.cancelUrl, user, studio),
|
|
607
|
+
line_items: [
|
|
608
|
+
{
|
|
609
|
+
price: productInfo.default_price.id,
|
|
610
|
+
quantity: 1,
|
|
611
|
+
},
|
|
612
|
+
],
|
|
613
|
+
mode: 'subscription',
|
|
614
|
+
metadata: metadata,
|
|
625
615
|
});
|
|
616
|
+
console.log(`[SubscriptionController] [createManageSubscriptionLink] Checkout Session Success!`);
|
|
617
|
+
return {
|
|
618
|
+
success: true,
|
|
619
|
+
url: session.url,
|
|
620
|
+
};
|
|
626
621
|
}
|
|
627
622
|
/**
|
|
628
623
|
* Handles the webhook from Stripe for updating the internal database.
|
|
629
624
|
*/
|
|
630
|
-
handleStripeWebhook(request) {
|
|
625
|
+
async handleStripeWebhook(request) {
|
|
631
626
|
var _a;
|
|
632
|
-
|
|
633
|
-
|
|
627
|
+
if (!this._stripe) {
|
|
628
|
+
return {
|
|
629
|
+
success: false,
|
|
630
|
+
errorCode: 'not_supported',
|
|
631
|
+
errorMessage: 'This method is not supported.',
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
try {
|
|
635
|
+
if (typeof request.requestBody !== 'string' ||
|
|
636
|
+
request.requestBody === '') {
|
|
637
|
+
return {
|
|
638
|
+
success: false,
|
|
639
|
+
errorCode: 'invalid_request',
|
|
640
|
+
errorMessage: 'The request was not valid.',
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
if (typeof request.signature !== 'string' ||
|
|
644
|
+
request.signature === '') {
|
|
634
645
|
return {
|
|
635
646
|
success: false,
|
|
636
|
-
errorCode: '
|
|
637
|
-
errorMessage: '
|
|
647
|
+
errorCode: 'invalid_request',
|
|
648
|
+
errorMessage: 'The request was not valid.',
|
|
638
649
|
};
|
|
639
650
|
}
|
|
651
|
+
const config = await this._getConfig();
|
|
652
|
+
const body = request.requestBody;
|
|
653
|
+
const signature = request.signature;
|
|
654
|
+
let event;
|
|
640
655
|
try {
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
656
|
+
event = this._stripe.constructWebhookEvent(body, signature, config.webhookSecret);
|
|
657
|
+
}
|
|
658
|
+
catch (err) {
|
|
659
|
+
console.log(`[SubscriptionController] [handleStripeWebhook] Unable to construct webhook event:`, err);
|
|
660
|
+
return {
|
|
661
|
+
success: false,
|
|
662
|
+
errorCode: 'invalid_request',
|
|
663
|
+
errorMessage: 'The request was not valid.',
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
console.log(`[SubscriptionController] [handleStripeWebhook] Got event: ${event.type}`);
|
|
667
|
+
if (event.type === 'customer.subscription.created' ||
|
|
668
|
+
event.type === 'customer.subscription.deleted' ||
|
|
669
|
+
event.type === 'customer.subscription.updated') {
|
|
670
|
+
const subscription = event.data.object;
|
|
671
|
+
const items = subscription.items.data;
|
|
672
|
+
let item;
|
|
673
|
+
let sub;
|
|
674
|
+
items_loop: for (let i of items) {
|
|
675
|
+
for (let s of config.subscriptions) {
|
|
676
|
+
if (s.eligibleProducts &&
|
|
677
|
+
s.eligibleProducts.some((p) => p === i.price.product)) {
|
|
678
|
+
sub = s;
|
|
679
|
+
item = i;
|
|
680
|
+
break items_loop;
|
|
681
|
+
}
|
|
682
|
+
}
|
|
648
683
|
}
|
|
649
|
-
if (
|
|
650
|
-
|
|
684
|
+
if (!item || !sub) {
|
|
685
|
+
console.log(`[SubscriptionController] [handleStripeWebhook] No item in the subscription matches an eligible product in the config.`);
|
|
651
686
|
return {
|
|
652
|
-
success:
|
|
653
|
-
errorCode: 'invalid_request',
|
|
654
|
-
errorMessage: 'The request was not valid.',
|
|
687
|
+
success: true,
|
|
655
688
|
};
|
|
656
689
|
}
|
|
657
|
-
|
|
658
|
-
const
|
|
659
|
-
const
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
690
|
+
console.log(`[SubscriptionController] [handleStripeWebhook] Subscription (${sub.id}) found!`);
|
|
691
|
+
const status = subscription.status;
|
|
692
|
+
const active = isActiveSubscription(status);
|
|
693
|
+
const tier = (_a = sub.tier) !== null && _a !== void 0 ? _a : 'beta';
|
|
694
|
+
const customerId = subscription.customer;
|
|
695
|
+
const stripeSubscriptionId = subscription.id;
|
|
696
|
+
const periodStartMs = subscription.current_period_start * 1000;
|
|
697
|
+
const periodEndMs = subscription.current_period_end * 1000;
|
|
698
|
+
console.log(`[SubscriptionController] [handleStripeWebhook] Customer ID: ${customerId}. Subscription status: ${status}. Tier: ${tier}. Is Active: ${active}.`);
|
|
699
|
+
let user = await this._authStore.findUserByStripeCustomerId(customerId);
|
|
700
|
+
let studio;
|
|
701
|
+
if (user) {
|
|
702
|
+
await this._authStore.updateSubscriptionInfo({
|
|
703
|
+
userId: user.id,
|
|
704
|
+
subscriptionStatus: status,
|
|
705
|
+
subscriptionId: sub.id,
|
|
706
|
+
stripeSubscriptionId,
|
|
707
|
+
stripeCustomerId: customerId,
|
|
708
|
+
currentPeriodEndMs: periodEndMs,
|
|
709
|
+
currentPeriodStartMs: periodStartMs,
|
|
710
|
+
});
|
|
671
711
|
}
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
let sub;
|
|
680
|
-
items_loop: for (let i of items) {
|
|
681
|
-
for (let s of config.subscriptions) {
|
|
682
|
-
if (s.eligibleProducts &&
|
|
683
|
-
s.eligibleProducts.some((p) => p === i.price.product)) {
|
|
684
|
-
sub = s;
|
|
685
|
-
item = i;
|
|
686
|
-
break items_loop;
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
if (!item || !sub) {
|
|
691
|
-
console.log(`[SubscriptionController] [handleStripeWebhook] No item in the subscription matches an eligible product in the config.`);
|
|
692
|
-
return {
|
|
693
|
-
success: true,
|
|
694
|
-
};
|
|
695
|
-
}
|
|
696
|
-
console.log(`[SubscriptionController] [handleStripeWebhook] Subscription (${sub.id}) found!`);
|
|
697
|
-
const status = subscription.status;
|
|
698
|
-
const active = isActiveSubscription(status);
|
|
699
|
-
const tier = (_a = sub.tier) !== null && _a !== void 0 ? _a : 'beta';
|
|
700
|
-
const customerId = subscription.customer;
|
|
701
|
-
const stripeSubscriptionId = subscription.id;
|
|
702
|
-
const periodStartMs = subscription.current_period_start * 1000;
|
|
703
|
-
const periodEndMs = subscription.current_period_end * 1000;
|
|
704
|
-
console.log(`[SubscriptionController] [handleStripeWebhook] Customer ID: ${customerId}. Subscription status: ${status}. Tier: ${tier}. Is Active: ${active}.`);
|
|
705
|
-
let user = yield this._authStore.findUserByStripeCustomerId(customerId);
|
|
706
|
-
let studio;
|
|
707
|
-
if (user) {
|
|
708
|
-
yield this._authStore.updateSubscriptionInfo({
|
|
709
|
-
userId: user.id,
|
|
712
|
+
else {
|
|
713
|
+
console.log(`[SubscriptionController] [handleStripeWebhook] No user found for Customer ID (${customerId})`);
|
|
714
|
+
studio =
|
|
715
|
+
await this._recordsStore.getStudioByStripeCustomerId(customerId);
|
|
716
|
+
if (studio) {
|
|
717
|
+
await this._authStore.updateSubscriptionInfo({
|
|
718
|
+
studioId: studio.id,
|
|
710
719
|
subscriptionStatus: status,
|
|
711
720
|
subscriptionId: sub.id,
|
|
712
721
|
stripeSubscriptionId,
|
|
@@ -716,63 +725,63 @@ export class SubscriptionController {
|
|
|
716
725
|
});
|
|
717
726
|
}
|
|
718
727
|
else {
|
|
719
|
-
console.log(`[SubscriptionController] [handleStripeWebhook] No
|
|
720
|
-
studio =
|
|
721
|
-
yield this._recordsStore.getStudioByStripeCustomerId(customerId);
|
|
722
|
-
if (studio) {
|
|
723
|
-
yield this._authStore.updateSubscriptionInfo({
|
|
724
|
-
studioId: studio.id,
|
|
725
|
-
subscriptionStatus: status,
|
|
726
|
-
subscriptionId: sub.id,
|
|
727
|
-
stripeSubscriptionId,
|
|
728
|
-
stripeCustomerId: customerId,
|
|
729
|
-
currentPeriodEndMs: periodEndMs,
|
|
730
|
-
currentPeriodStartMs: periodStartMs,
|
|
731
|
-
});
|
|
732
|
-
}
|
|
733
|
-
else {
|
|
734
|
-
console.log(`[SubscriptionController] [handleStripeWebhook] No studio found for Customer ID (${customerId})`);
|
|
735
|
-
}
|
|
728
|
+
console.log(`[SubscriptionController] [handleStripeWebhook] No studio found for Customer ID (${customerId})`);
|
|
736
729
|
}
|
|
730
|
+
}
|
|
731
|
+
return {
|
|
732
|
+
success: true,
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
else if (event.type === 'invoice.paid') {
|
|
736
|
+
const parseResult = STRIPE_EVENT_INVOICE_PAID_SCHEMA.safeParse(event);
|
|
737
|
+
if (parseResult.success === false) {
|
|
738
|
+
console.error(`[SubscriptionController] [handleStripeWebhook] Unable to parse stripe event!`, parseResult.error);
|
|
737
739
|
return {
|
|
738
740
|
success: true,
|
|
739
741
|
};
|
|
740
742
|
}
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
743
|
+
const invoice = parseResult.data.data.object;
|
|
744
|
+
const stripeSubscriptionId = invoice.subscription;
|
|
745
|
+
const subscription = await this._stripe.getSubscriptionById(stripeSubscriptionId);
|
|
746
|
+
const status = subscription.status;
|
|
747
|
+
const customerId = invoice.customer;
|
|
748
|
+
const lineItems = invoice.lines.data;
|
|
749
|
+
const periodStartMs = subscription.current_period_start * 1000;
|
|
750
|
+
const periodEndMs = subscription.current_period_end * 1000;
|
|
751
|
+
const { sub, item } = findMatchingSubscription(lineItems);
|
|
752
|
+
const authInvoice = {
|
|
753
|
+
currency: invoice.currency,
|
|
754
|
+
description: invoice.description,
|
|
755
|
+
paid: invoice.paid,
|
|
756
|
+
status: invoice.status,
|
|
757
|
+
tax: invoice.tax,
|
|
758
|
+
total: invoice.total,
|
|
759
|
+
subtotal: invoice.subtotal,
|
|
760
|
+
stripeInvoiceId: invoice.id,
|
|
761
|
+
stripeHostedInvoiceUrl: invoice.hosted_invoice_url,
|
|
762
|
+
stripeInvoicePdfUrl: invoice.invoice_pdf,
|
|
763
|
+
};
|
|
764
|
+
console.log(`[SubscriptionController] [handleStripeWebhook] New invoice paid for customer ID (${customerId}). Subscription ID: ${subscription.id}. Period start: ${periodStartMs}. Period end: ${periodEndMs}.`);
|
|
765
|
+
const user = await this._authStore.findUserByStripeCustomerId(customerId);
|
|
766
|
+
if (user) {
|
|
767
|
+
console.log(`[SubscriptionController] [handleStripeWebhook] Found user (${user.id}) with customer ID (${customerId}).`);
|
|
768
|
+
await this._authStore.updateSubscriptionPeriod({
|
|
769
|
+
userId: user.id,
|
|
770
|
+
subscriptionStatus: status,
|
|
771
|
+
subscriptionId: sub.id,
|
|
772
|
+
stripeSubscriptionId,
|
|
773
|
+
stripeCustomerId: customerId,
|
|
774
|
+
currentPeriodEndMs: periodEndMs,
|
|
775
|
+
currentPeriodStartMs: periodStartMs,
|
|
776
|
+
invoice: authInvoice,
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
else {
|
|
780
|
+
console.log(`[SubscriptionController] [handleStripeWebhook] No user found for customer ID (${customerId}).`);
|
|
781
|
+
const studio = await this._recordsStore.getStudioByStripeCustomerId(customerId);
|
|
782
|
+
if (studio) {
|
|
783
|
+
await this._authStore.updateSubscriptionPeriod({
|
|
784
|
+
studioId: studio.id,
|
|
776
785
|
subscriptionStatus: status,
|
|
777
786
|
subscriptionId: sub.id,
|
|
778
787
|
stripeSubscriptionId,
|
|
@@ -783,55 +792,39 @@ export class SubscriptionController {
|
|
|
783
792
|
});
|
|
784
793
|
}
|
|
785
794
|
else {
|
|
786
|
-
console.log(`[SubscriptionController] [handleStripeWebhook] No
|
|
787
|
-
const studio = yield this._recordsStore.getStudioByStripeCustomerId(customerId);
|
|
788
|
-
if (studio) {
|
|
789
|
-
yield this._authStore.updateSubscriptionPeriod({
|
|
790
|
-
studioId: studio.id,
|
|
791
|
-
subscriptionStatus: status,
|
|
792
|
-
subscriptionId: sub.id,
|
|
793
|
-
stripeSubscriptionId,
|
|
794
|
-
stripeCustomerId: customerId,
|
|
795
|
-
currentPeriodEndMs: periodEndMs,
|
|
796
|
-
currentPeriodStartMs: periodStartMs,
|
|
797
|
-
invoice: authInvoice,
|
|
798
|
-
});
|
|
799
|
-
}
|
|
800
|
-
else {
|
|
801
|
-
console.log(`[SubscriptionController] [handleStripeWebhook] No studio found for customer ID (${customerId}).`);
|
|
802
|
-
}
|
|
795
|
+
console.log(`[SubscriptionController] [handleStripeWebhook] No studio found for customer ID (${customerId}).`);
|
|
803
796
|
}
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
797
|
+
}
|
|
798
|
+
function findMatchingSubscription(lineItems) {
|
|
799
|
+
let item;
|
|
800
|
+
let sub;
|
|
801
|
+
items_loop: for (let i of lineItems) {
|
|
802
|
+
for (let s of config.subscriptions) {
|
|
803
|
+
if (s.eligibleProducts &&
|
|
804
|
+
s.eligibleProducts.some((p) => p === i.price.product)) {
|
|
805
|
+
sub = s;
|
|
806
|
+
item = i;
|
|
807
|
+
break items_loop;
|
|
815
808
|
}
|
|
816
809
|
}
|
|
817
|
-
return { item, sub };
|
|
818
810
|
}
|
|
811
|
+
return { item, sub };
|
|
819
812
|
}
|
|
820
|
-
return {
|
|
821
|
-
success: true,
|
|
822
|
-
};
|
|
823
|
-
}
|
|
824
|
-
catch (err) {
|
|
825
|
-
const span = trace.getActiveSpan();
|
|
826
|
-
span.recordException(err);
|
|
827
|
-
console.error('[SubscriptionController] An error occurred while handling a stripe webhook:', err);
|
|
828
|
-
return {
|
|
829
|
-
success: false,
|
|
830
|
-
errorCode: 'server_error',
|
|
831
|
-
errorMessage: 'A server error occurred.',
|
|
832
|
-
};
|
|
833
813
|
}
|
|
834
|
-
|
|
814
|
+
return {
|
|
815
|
+
success: true,
|
|
816
|
+
};
|
|
817
|
+
}
|
|
818
|
+
catch (err) {
|
|
819
|
+
const span = trace.getActiveSpan();
|
|
820
|
+
span.recordException(err);
|
|
821
|
+
console.error('[SubscriptionController] An error occurred while handling a stripe webhook:', err);
|
|
822
|
+
return {
|
|
823
|
+
success: false,
|
|
824
|
+
errorCode: 'server_error',
|
|
825
|
+
errorMessage: 'A server error occurred.',
|
|
826
|
+
};
|
|
827
|
+
}
|
|
835
828
|
}
|
|
836
829
|
}
|
|
837
830
|
__decorate([
|