@metamask/subscription-controller 6.2.2 → 8.0.0
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/CHANGELOG.md +54 -1
- package/dist/SubscriptionController-method-action-types.cjs.map +1 -1
- package/dist/SubscriptionController-method-action-types.d.cts +55 -18
- package/dist/SubscriptionController-method-action-types.d.cts.map +1 -1
- package/dist/SubscriptionController-method-action-types.d.mts +55 -18
- package/dist/SubscriptionController-method-action-types.d.mts.map +1 -1
- package/dist/SubscriptionController-method-action-types.mjs.map +1 -1
- package/dist/SubscriptionController.cjs +180 -68
- package/dist/SubscriptionController.cjs.map +1 -1
- package/dist/SubscriptionController.d.cts +58 -26
- package/dist/SubscriptionController.d.cts.map +1 -1
- package/dist/SubscriptionController.d.mts +58 -26
- package/dist/SubscriptionController.d.mts.map +1 -1
- package/dist/SubscriptionController.mjs +181 -69
- package/dist/SubscriptionController.mjs.map +1 -1
- package/dist/SubscriptionService-method-action-types.cjs +7 -0
- package/dist/SubscriptionService-method-action-types.cjs.map +1 -0
- package/dist/SubscriptionService-method-action-types.d.cts +155 -0
- package/dist/SubscriptionService-method-action-types.d.cts.map +1 -0
- package/dist/SubscriptionService-method-action-types.d.mts +155 -0
- package/dist/SubscriptionService-method-action-types.d.mts.map +1 -0
- package/dist/SubscriptionService-method-action-types.mjs +6 -0
- package/dist/SubscriptionService-method-action-types.mjs.map +1 -0
- package/dist/SubscriptionService-structs.cjs +158 -0
- package/dist/SubscriptionService-structs.cjs.map +1 -0
- package/dist/SubscriptionService-structs.d.cts +721 -0
- package/dist/SubscriptionService-structs.d.cts.map +1 -0
- package/dist/SubscriptionService-structs.d.mts +721 -0
- package/dist/SubscriptionService-structs.d.mts.map +1 -0
- package/dist/SubscriptionService-structs.mjs +155 -0
- package/dist/SubscriptionService-structs.mjs.map +1 -0
- package/dist/SubscriptionService.cjs +279 -87
- package/dist/SubscriptionService.cjs.map +1 -1
- package/dist/SubscriptionService.d.cts +94 -10
- package/dist/SubscriptionService.d.cts.map +1 -1
- package/dist/SubscriptionService.d.mts +94 -10
- package/dist/SubscriptionService.d.mts.map +1 -1
- package/dist/SubscriptionService.mjs +279 -87
- package/dist/SubscriptionService.mjs.map +1 -1
- package/dist/constants.cjs +2 -0
- package/dist/constants.cjs.map +1 -1
- package/dist/constants.d.cts +2 -0
- package/dist/constants.d.cts.map +1 -1
- package/dist/constants.d.mts +2 -0
- package/dist/constants.d.mts.map +1 -1
- package/dist/constants.mjs +2 -0
- package/dist/constants.mjs.map +1 -1
- package/dist/index.cjs +4 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -6
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +7 -6
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/dist/types.cjs +27 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +171 -10
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +171 -10
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs +26 -0
- package/dist/types.mjs.map +1 -1
- package/package.json +5 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SubscriptionController.d.mts","sourceRoot":"","sources":["../src/SubscriptionController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,0BAA0B,EAC1B,wBAAwB,EACzB,kCAAkC;AACnC,OAAO,KAAK,EAAE,SAAS,EAAE,4BAA4B;AAErD,OAAO,KAAK,EAAE,wBAAwB,EAAE,0CAA0C;
|
|
1
|
+
{"version":3,"file":"SubscriptionController.d.mts","sourceRoot":"","sources":["../src/SubscriptionController.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,20 +1,14 @@
|
|
|
1
|
-
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
2
|
-
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
3
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
4
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
5
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6
|
-
};
|
|
7
1
|
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
2
|
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
3
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
4
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
5
|
};
|
|
12
|
-
var _SubscriptionController_instances,
|
|
6
|
+
var _SubscriptionController_instances, _SubscriptionController_getSubscriptionPriceAmount, _SubscriptionController_getSubscriptionBalanceAmount, _SubscriptionController_getProductPriceByProductAndPlan, _SubscriptionController_assertValidSubscriptionStateForCryptoApproval, _SubscriptionController_assertIsUserNotSubscribed, _SubscriptionController_assertIsUserSubscribed, _SubscriptionController_assertIsPaymentMethodCrypto, _SubscriptionController_getIsEligibleForTrialedSponsorship, _SubscriptionController_getIsTrialRequested, _SubscriptionController_findCryptoPaymentMethod, _SubscriptionController_resolveCryptoPaymentMethodDefaults, _SubscriptionController_getChainSupportsSponsorship, _SubscriptionController_areTrialedProductsEqual, _SubscriptionController_areSubscriptionsEqual, _SubscriptionController_isSubscriptionEqual, _SubscriptionController_stringifySubscription;
|
|
13
7
|
import { StaticIntervalPollingController } from "@metamask/polling-controller";
|
|
14
8
|
import { TransactionType } from "@metamask/transaction-controller";
|
|
15
9
|
import { BigNumber } from "bignumber.js";
|
|
16
10
|
import { ACTIVE_SUBSCRIPTION_STATUSES, controllerName, DEFAULT_POLLING_INTERVAL, SubscriptionControllerErrorMessage } from "./constants.mjs";
|
|
17
|
-
import { PAYMENT_TYPES, PRODUCT_TYPES, SUBSCRIPTION_STATUSES } from "./types.mjs";
|
|
11
|
+
import { CRYPTO_AUTH_METHODS, PAYMENT_TYPES, PRODUCT_TYPES, SUBSCRIPTION_STATUSES } from "./types.mjs";
|
|
18
12
|
/**
|
|
19
13
|
* Get the default state for the Subscription Controller.
|
|
20
14
|
*
|
|
@@ -84,10 +78,10 @@ const MESSENGER_EXPOSED_METHODS = [
|
|
|
84
78
|
'getSubscriptionsEligibilities',
|
|
85
79
|
'cancelSubscription',
|
|
86
80
|
'unCancelSubscription',
|
|
87
|
-
'
|
|
81
|
+
'startSubscriptionWithCard',
|
|
88
82
|
'startSubscriptionWithCrypto',
|
|
89
83
|
'stopAllPolling',
|
|
90
|
-
'
|
|
84
|
+
'submitSubscriptionCryptoApproval',
|
|
91
85
|
'getCryptoApproveTransactionParams',
|
|
92
86
|
'updatePaymentMethod',
|
|
93
87
|
'getBillingPortalUrl',
|
|
@@ -109,10 +103,10 @@ export class SubscriptionController extends StaticIntervalPollingController() {
|
|
|
109
103
|
* @param options - The options for the SubscriptionController.
|
|
110
104
|
* @param options.messenger - A restricted messenger.
|
|
111
105
|
* @param options.state - Initial state to set on this controller.
|
|
112
|
-
* @param options.subscriptionService - The subscription service for communicating with subscription server.
|
|
113
106
|
* @param options.pollingInterval - The polling interval to use for the subscription controller.
|
|
114
107
|
*/
|
|
115
|
-
constructor(
|
|
108
|
+
constructor(options) {
|
|
109
|
+
const { messenger, state, pollingInterval = DEFAULT_POLLING_INTERVAL, } = options;
|
|
116
110
|
super({
|
|
117
111
|
name: controllerName,
|
|
118
112
|
metadata: subscriptionControllerMetadata,
|
|
@@ -123,9 +117,7 @@ export class SubscriptionController extends StaticIntervalPollingController() {
|
|
|
123
117
|
messenger,
|
|
124
118
|
});
|
|
125
119
|
_SubscriptionController_instances.add(this);
|
|
126
|
-
_SubscriptionController_subscriptionService.set(this, void 0);
|
|
127
120
|
this.setIntervalLength(pollingInterval);
|
|
128
|
-
__classPrivateFieldSet(this, _SubscriptionController_subscriptionService, subscriptionService, "f");
|
|
129
121
|
this.messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
|
|
130
122
|
}
|
|
131
123
|
/**
|
|
@@ -134,7 +126,7 @@ export class SubscriptionController extends StaticIntervalPollingController() {
|
|
|
134
126
|
* @returns The pricing information.
|
|
135
127
|
*/
|
|
136
128
|
async getPricing() {
|
|
137
|
-
const pricing = await
|
|
129
|
+
const pricing = await this.messenger.call('SubscriptionService:getPricing');
|
|
138
130
|
this.update((state) => {
|
|
139
131
|
state.pricing = pricing;
|
|
140
132
|
});
|
|
@@ -146,7 +138,7 @@ export class SubscriptionController extends StaticIntervalPollingController() {
|
|
|
146
138
|
const currentCustomerId = this.state.customerId;
|
|
147
139
|
const currentLastSubscription = this.state.lastSubscription;
|
|
148
140
|
const currentRewardAccountId = this.state.rewardAccountId;
|
|
149
|
-
const { customerId: newCustomerId, subscriptions: newSubscriptions, trialedProducts: newTrialedProducts, lastSubscription: newLastSubscription, rewardAccountId: newRewardAccountId, } = await
|
|
141
|
+
const { customerId: newCustomerId, subscriptions: newSubscriptions, trialedProducts: newTrialedProducts, lastSubscription: newLastSubscription, rewardAccountId: newRewardAccountId, } = await this.messenger.call('SubscriptionService:getSubscriptions');
|
|
150
142
|
// check if the new subscriptions are different from the current subscriptions
|
|
151
143
|
const areSubscriptionsEqual = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_areSubscriptionsEqual).call(this, currentSubscriptions, newSubscriptions);
|
|
152
144
|
// check if the new trialed products are different from the current trialed products
|
|
@@ -190,11 +182,11 @@ export class SubscriptionController extends StaticIntervalPollingController() {
|
|
|
190
182
|
* @returns The subscriptions eligibilities.
|
|
191
183
|
*/
|
|
192
184
|
async getSubscriptionsEligibilities(request) {
|
|
193
|
-
return await
|
|
185
|
+
return await this.messenger.call('SubscriptionService:getSubscriptionsEligibilities', request);
|
|
194
186
|
}
|
|
195
187
|
async cancelSubscription(request) {
|
|
196
188
|
__classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_assertIsUserSubscribed).call(this, { subscriptionId: request.subscriptionId });
|
|
197
|
-
const cancelledSubscription = await
|
|
189
|
+
const cancelledSubscription = await this.messenger.call('SubscriptionService:cancelSubscription', request);
|
|
198
190
|
this.update((state) => {
|
|
199
191
|
state.subscriptions = state.subscriptions.map((subscription) => subscription.id === request.subscriptionId
|
|
200
192
|
? { ...subscription, ...cancelledSubscription }
|
|
@@ -204,7 +196,7 @@ export class SubscriptionController extends StaticIntervalPollingController() {
|
|
|
204
196
|
}
|
|
205
197
|
async unCancelSubscription(request) {
|
|
206
198
|
__classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_assertIsUserSubscribed).call(this, { subscriptionId: request.subscriptionId });
|
|
207
|
-
const uncancelledSubscription = await
|
|
199
|
+
const uncancelledSubscription = await this.messenger.call('SubscriptionService:unCancelSubscription', {
|
|
208
200
|
subscriptionId: request.subscriptionId,
|
|
209
201
|
});
|
|
210
202
|
this.update((state) => {
|
|
@@ -214,51 +206,107 @@ export class SubscriptionController extends StaticIntervalPollingController() {
|
|
|
214
206
|
});
|
|
215
207
|
this.triggerAccessTokenRefresh();
|
|
216
208
|
}
|
|
217
|
-
|
|
209
|
+
/**
|
|
210
|
+
* Starts a card-paid subscription checkout session for the requested products
|
|
211
|
+
* (e.g. Shield or Money Account Plus).
|
|
212
|
+
*
|
|
213
|
+
* `isTrialRequested` on the request is ignored and overwritten from pricing
|
|
214
|
+
* (`trialPeriodDays > 0`) and `trialedProducts`.
|
|
215
|
+
*
|
|
216
|
+
* @param request - The start subscription request.
|
|
217
|
+
* @returns The checkout session response.
|
|
218
|
+
*/
|
|
219
|
+
async startSubscriptionWithCard(request) {
|
|
220
|
+
// get the latest subscriptions state before computing trial eligibility
|
|
221
|
+
await this.getSubscriptions();
|
|
218
222
|
__classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_assertIsUserNotSubscribed).call(this, { products: request.products });
|
|
219
|
-
const response = await
|
|
223
|
+
const response = await this.messenger.call('SubscriptionService:startSubscriptionWithCard', {
|
|
224
|
+
...request,
|
|
225
|
+
isTrialRequested: __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_getIsTrialRequested).call(this, request.products, request.recurringInterval),
|
|
226
|
+
});
|
|
220
227
|
// note: no need to trigger access token refresh after startSubscriptionWithCard request because this only return stripe checkout session url, subscription not created yet
|
|
221
228
|
return response;
|
|
222
229
|
}
|
|
230
|
+
/**
|
|
231
|
+
* Starts a crypto-paid subscription for the requested products
|
|
232
|
+
* (e.g. Shield or Money Account Plus). Unlike card checkout, this
|
|
233
|
+
* creates the subscription immediately, so local state is refreshed
|
|
234
|
+
* afterwards.
|
|
235
|
+
*
|
|
236
|
+
* `isTrialRequested` on the request is ignored and overwritten from pricing
|
|
237
|
+
* (`trialPeriodDays > 0`) and `trialedProducts`.
|
|
238
|
+
*
|
|
239
|
+
* @param request - The start crypto subscription request.
|
|
240
|
+
* @returns The start crypto subscription response.
|
|
241
|
+
* @throws If `products` is empty.
|
|
242
|
+
*/
|
|
223
243
|
async startSubscriptionWithCrypto(request) {
|
|
244
|
+
if (request.products.length === 0) {
|
|
245
|
+
throw new Error(SubscriptionControllerErrorMessage.SubscriptionProductsEmpty);
|
|
246
|
+
}
|
|
247
|
+
// get the latest subscriptions state before computing trial eligibility
|
|
248
|
+
await this.getSubscriptions();
|
|
224
249
|
__classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_assertIsUserNotSubscribed).call(this, { products: request.products });
|
|
225
|
-
const response = await
|
|
250
|
+
const response = await this.messenger.call('SubscriptionService:startSubscriptionWithCrypto', {
|
|
251
|
+
...request,
|
|
252
|
+
isTrialRequested: __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_getIsTrialRequested).call(this, request.products, request.recurringInterval),
|
|
253
|
+
});
|
|
254
|
+
// Crypto start creates the subscription immediately (unlike card checkout).
|
|
255
|
+
await this.getSubscriptions();
|
|
226
256
|
return response;
|
|
227
257
|
}
|
|
228
258
|
/**
|
|
229
|
-
*
|
|
259
|
+
* Submits a Shield ERC-20 crypto approval transaction to start or update a
|
|
260
|
+
* crypto subscription.
|
|
230
261
|
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
262
|
+
* This handler is Shield / `TransactionType.shieldSubscriptionApprove` only.
|
|
263
|
+
* Delegation-based products (e.g. Money Account) must call
|
|
264
|
+
* `startSubscriptionWithCrypto` instead.
|
|
265
|
+
*
|
|
266
|
+
* @param request - The crypto approval request.
|
|
267
|
+
* @param request.productType - The subscription product. Typed as
|
|
268
|
+
* `typeof PRODUCT_TYPES.SHIELD` only at the moment (future might support more
|
|
269
|
+
* product).
|
|
270
|
+
* @param request.txMeta - The transaction metadata. Must have type
|
|
271
|
+
* `TransactionType.shieldSubscriptionApprove`.
|
|
272
|
+
* @param request.isSponsored - Whether the transaction is sponsored.
|
|
273
|
+
* @param request.rewardAccountId - The account ID of the reward subscription
|
|
274
|
+
* to link.
|
|
275
|
+
* @throws If `productType` is not Shield or `txMeta.type` is not
|
|
276
|
+
* `shieldSubscriptionApprove`.
|
|
234
277
|
* @returns void
|
|
235
278
|
*/
|
|
236
|
-
async
|
|
237
|
-
|
|
238
|
-
|
|
279
|
+
async submitSubscriptionCryptoApproval(request) {
|
|
280
|
+
const { productType, txMeta, isSponsored, rewardAccountId } = request;
|
|
281
|
+
if (
|
|
282
|
+
// Widen for the runtime guard: JS / unsound callers may still pass a
|
|
283
|
+
// non-Shield product.
|
|
284
|
+
productType !== PRODUCT_TYPES.SHIELD ||
|
|
285
|
+
txMeta.type !== TransactionType.shieldSubscriptionApprove) {
|
|
286
|
+
throw new Error(SubscriptionControllerErrorMessage.CryptoApprovalRequiresShieldApprove);
|
|
239
287
|
}
|
|
240
288
|
const { chainId, rawTx } = txMeta;
|
|
241
289
|
if (!chainId || !rawTx) {
|
|
242
290
|
throw new Error('Chain ID or raw transaction not found');
|
|
243
291
|
}
|
|
244
|
-
const { pricing,
|
|
292
|
+
const { pricing, lastSelectedPaymentMethod } = this.state;
|
|
245
293
|
if (!pricing) {
|
|
246
294
|
throw new Error('Subscription pricing not found');
|
|
247
295
|
}
|
|
248
296
|
if (!lastSelectedPaymentMethod) {
|
|
249
297
|
throw new Error('Last selected payment method not found');
|
|
250
298
|
}
|
|
251
|
-
const
|
|
252
|
-
__classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_assertIsPaymentMethodCrypto).call(this,
|
|
253
|
-
const productPrice = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_getProductPriceByProductAndPlan).call(this,
|
|
254
|
-
|
|
255
|
-
// get the latest subscriptions state to check if the user has an active shield subscription
|
|
299
|
+
const lastSelectedPaymentMethodForProduct = lastSelectedPaymentMethod[productType];
|
|
300
|
+
__classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_assertIsPaymentMethodCrypto).call(this, lastSelectedPaymentMethodForProduct);
|
|
301
|
+
const productPrice = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_getProductPriceByProductAndPlan).call(this, productType, lastSelectedPaymentMethodForProduct.plan);
|
|
302
|
+
// get the latest subscriptions state before computing trial eligibility
|
|
256
303
|
await this.getSubscriptions();
|
|
257
|
-
const
|
|
304
|
+
const isTrialRequested = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_getIsTrialRequested).call(this, [productType], lastSelectedPaymentMethodForProduct.plan);
|
|
305
|
+
const currentSubscription = this.getSubscriptionByProduct(productType);
|
|
258
306
|
__classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_assertValidSubscriptionStateForCryptoApproval).call(this, {
|
|
259
|
-
productType
|
|
307
|
+
productType,
|
|
260
308
|
});
|
|
261
|
-
// if
|
|
309
|
+
// if subscription exists, this transaction is for changing payment method
|
|
262
310
|
const isChangePaymentMethod = Boolean(currentSubscription);
|
|
263
311
|
if (isChangePaymentMethod) {
|
|
264
312
|
await this.updatePaymentMethod({
|
|
@@ -266,7 +314,7 @@ export class SubscriptionController extends StaticIntervalPollingController() {
|
|
|
266
314
|
subscriptionId: currentSubscription.id,
|
|
267
315
|
chainId,
|
|
268
316
|
payerAddress: txMeta.txParams.from,
|
|
269
|
-
tokenSymbol:
|
|
317
|
+
tokenSymbol: lastSelectedPaymentMethodForProduct.paymentTokenSymbol,
|
|
270
318
|
rawTransaction: rawTx,
|
|
271
319
|
recurringInterval: productPrice.interval,
|
|
272
320
|
billingCycles: productPrice.minBillingCycles,
|
|
@@ -274,16 +322,17 @@ export class SubscriptionController extends StaticIntervalPollingController() {
|
|
|
274
322
|
}
|
|
275
323
|
else {
|
|
276
324
|
const params = {
|
|
277
|
-
products: [
|
|
278
|
-
isTrialRequested
|
|
325
|
+
products: [productType],
|
|
326
|
+
isTrialRequested,
|
|
279
327
|
recurringInterval: productPrice.interval,
|
|
280
328
|
billingCycles: productPrice.minBillingCycles,
|
|
281
329
|
chainId,
|
|
282
330
|
payerAddress: txMeta.txParams.from,
|
|
283
|
-
tokenSymbol:
|
|
331
|
+
tokenSymbol: lastSelectedPaymentMethodForProduct.paymentTokenSymbol,
|
|
284
332
|
rawTransaction: rawTx,
|
|
333
|
+
cryptoAuthMethod: CRYPTO_AUTH_METHODS.ERC20_APPROVAL,
|
|
285
334
|
isSponsored,
|
|
286
|
-
useTestClock:
|
|
335
|
+
useTestClock: lastSelectedPaymentMethodForProduct.useTestClock,
|
|
287
336
|
rewardAccountId,
|
|
288
337
|
};
|
|
289
338
|
await this.startSubscriptionWithCrypto(params);
|
|
@@ -314,7 +363,7 @@ export class SubscriptionController extends StaticIntervalPollingController() {
|
|
|
314
363
|
if (!price) {
|
|
315
364
|
throw new Error('Price not found');
|
|
316
365
|
}
|
|
317
|
-
const chainsPaymentInfo =
|
|
366
|
+
const chainsPaymentInfo = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_findCryptoPaymentMethod).call(this, request.productType, CRYPTO_AUTH_METHODS.ERC20_APPROVAL);
|
|
318
367
|
if (!chainsPaymentInfo) {
|
|
319
368
|
throw new Error('Chains payment info not found');
|
|
320
369
|
}
|
|
@@ -338,11 +387,11 @@ export class SubscriptionController extends StaticIntervalPollingController() {
|
|
|
338
387
|
async updatePaymentMethod(opts) {
|
|
339
388
|
if (opts.paymentType === PAYMENT_TYPES.byCard) {
|
|
340
389
|
const { paymentType, ...cardRequest } = opts;
|
|
341
|
-
return await
|
|
390
|
+
return await this.messenger.call('SubscriptionService:updatePaymentMethodCard', cardRequest);
|
|
342
391
|
}
|
|
343
392
|
else if (opts.paymentType === PAYMENT_TYPES.byCrypto) {
|
|
344
393
|
const { paymentType, ...cryptoRequest } = opts;
|
|
345
|
-
await
|
|
394
|
+
await this.messenger.call('SubscriptionService:updatePaymentMethodCrypto', cryptoRequest);
|
|
346
395
|
return await this.getSubscriptions();
|
|
347
396
|
}
|
|
348
397
|
throw new Error('Invalid payment type');
|
|
@@ -353,19 +402,20 @@ export class SubscriptionController extends StaticIntervalPollingController() {
|
|
|
353
402
|
* @returns The billing portal URL
|
|
354
403
|
*/
|
|
355
404
|
async getBillingPortalUrl() {
|
|
356
|
-
return await
|
|
405
|
+
return await this.messenger.call('SubscriptionService:getBillingPortalUrl');
|
|
357
406
|
}
|
|
358
407
|
/**
|
|
359
408
|
* Cache the last selected payment method for a specific product.
|
|
360
409
|
*
|
|
361
|
-
* @param
|
|
362
|
-
* @param
|
|
363
|
-
* @param paymentMethod
|
|
364
|
-
* @param paymentMethod.
|
|
365
|
-
* @param paymentMethod.
|
|
366
|
-
* @param paymentMethod.
|
|
410
|
+
* @param request - The request object.
|
|
411
|
+
* @param request.product - The product to cache the payment method for.
|
|
412
|
+
* @param request.paymentMethod - The payment method to cache.
|
|
413
|
+
* @param request.paymentMethod.type - The type of the payment method.
|
|
414
|
+
* @param request.paymentMethod.paymentTokenAddress - The payment token address.
|
|
415
|
+
* @param request.paymentMethod.plan - The plan of the payment method.
|
|
367
416
|
*/
|
|
368
|
-
cacheLastSelectedPaymentMethod(
|
|
417
|
+
cacheLastSelectedPaymentMethod(request) {
|
|
418
|
+
const { product, paymentMethod } = request;
|
|
369
419
|
if (paymentMethod.type === PAYMENT_TYPES.byCrypto &&
|
|
370
420
|
(!paymentMethod.paymentTokenAddress || !paymentMethod.paymentTokenSymbol)) {
|
|
371
421
|
throw new Error(SubscriptionControllerErrorMessage.PaymentTokenAddressAndSymbolRequiredForCrypto);
|
|
@@ -404,7 +454,8 @@ export class SubscriptionController extends StaticIntervalPollingController() {
|
|
|
404
454
|
* recurringInterval: RecurringInterval.Month,
|
|
405
455
|
* billingCycles: 1,
|
|
406
456
|
* }
|
|
407
|
-
* @returns resolves to true if the sponsorship is supported and intents were submitted successfully, false
|
|
457
|
+
* @returns resolves to true if the sponsorship is supported and intents were submitted successfully, false if the chain does not support sponsorship or the user has already trialed
|
|
458
|
+
* @throws If the crypto payment method or chain is missing from pricing
|
|
408
459
|
*/
|
|
409
460
|
async submitSponsorshipIntents(request) {
|
|
410
461
|
if (request.products.length === 0) {
|
|
@@ -413,7 +464,9 @@ export class SubscriptionController extends StaticIntervalPollingController() {
|
|
|
413
464
|
__classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_assertIsUserNotSubscribed).call(this, { products: request.products });
|
|
414
465
|
const selectedPaymentMethod = this.state.lastSelectedPaymentMethod?.[request.products[0]];
|
|
415
466
|
__classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_assertIsPaymentMethodCrypto).call(this, selectedPaymentMethod);
|
|
416
|
-
const
|
|
467
|
+
const cryptoAuthMethod = selectedPaymentMethod.cryptoAuthMethod ??
|
|
468
|
+
CRYPTO_AUTH_METHODS.ERC20_APPROVAL;
|
|
469
|
+
const isEligibleForTrialedSponsorship = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_getIsEligibleForTrialedSponsorship).call(this, request.chainId, request.products, cryptoAuthMethod);
|
|
417
470
|
if (!isEligibleForTrialedSponsorship) {
|
|
418
471
|
return false;
|
|
419
472
|
}
|
|
@@ -422,7 +475,7 @@ export class SubscriptionController extends StaticIntervalPollingController() {
|
|
|
422
475
|
// we only support one product at a time for now
|
|
423
476
|
request.products[0], plan);
|
|
424
477
|
const billingCycles = productPrice.minBillingCycles;
|
|
425
|
-
await
|
|
478
|
+
await this.messenger.call('SubscriptionService:submitSponsorshipIntents', {
|
|
426
479
|
...request,
|
|
427
480
|
paymentTokenSymbol,
|
|
428
481
|
billingCycles,
|
|
@@ -437,7 +490,7 @@ export class SubscriptionController extends StaticIntervalPollingController() {
|
|
|
437
490
|
* @example { event: SubscriptionUserEvent.ShieldEntryModalViewed, cohort: 'post_tx' }
|
|
438
491
|
*/
|
|
439
492
|
async submitUserEvent(request) {
|
|
440
|
-
await
|
|
493
|
+
await this.messenger.call('SubscriptionService:submitUserEvent', request);
|
|
441
494
|
}
|
|
442
495
|
/**
|
|
443
496
|
* Assign user to a cohort.
|
|
@@ -446,7 +499,7 @@ export class SubscriptionController extends StaticIntervalPollingController() {
|
|
|
446
499
|
* @example { cohort: 'post_tx' }
|
|
447
500
|
*/
|
|
448
501
|
async assignUserToCohort(request) {
|
|
449
|
-
await
|
|
502
|
+
await this.messenger.call('SubscriptionService:assignUserToCohort', request);
|
|
450
503
|
}
|
|
451
504
|
/**
|
|
452
505
|
* Link rewards to a subscription.
|
|
@@ -461,7 +514,7 @@ export class SubscriptionController extends StaticIntervalPollingController() {
|
|
|
461
514
|
// assert that the user is subscribed to the subscription
|
|
462
515
|
__classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_assertIsUserSubscribed).call(this, { subscriptionId: request.subscriptionId });
|
|
463
516
|
// link rewards to the subscription
|
|
464
|
-
const response = await
|
|
517
|
+
const response = await this.messenger.call('SubscriptionService:linkRewards', {
|
|
465
518
|
rewardAccountId: request.rewardAccountId,
|
|
466
519
|
});
|
|
467
520
|
if (!response.success) {
|
|
@@ -479,7 +532,7 @@ export class SubscriptionController extends StaticIntervalPollingController() {
|
|
|
479
532
|
* @returns The token approve amount
|
|
480
533
|
*/
|
|
481
534
|
getTokenApproveAmount(price, tokenPaymentInfo) {
|
|
482
|
-
const conversionRate = tokenPaymentInfo.conversionRate[price.currency];
|
|
535
|
+
const conversionRate = tokenPaymentInfo.conversionRate?.[price.currency];
|
|
483
536
|
if (!conversionRate) {
|
|
484
537
|
throw new Error('Conversion rate not found');
|
|
485
538
|
}
|
|
@@ -499,7 +552,7 @@ export class SubscriptionController extends StaticIntervalPollingController() {
|
|
|
499
552
|
* @returns The token balance amount
|
|
500
553
|
*/
|
|
501
554
|
getTokenMinimumBalanceAmount(price, tokenPaymentInfo) {
|
|
502
|
-
const conversionRate = tokenPaymentInfo.conversionRate[price.currency];
|
|
555
|
+
const conversionRate = tokenPaymentInfo.conversionRate?.[price.currency];
|
|
503
556
|
if (!conversionRate) {
|
|
504
557
|
throw new Error('Conversion rate not found');
|
|
505
558
|
}
|
|
@@ -529,7 +582,7 @@ export class SubscriptionController extends StaticIntervalPollingController() {
|
|
|
529
582
|
this.messenger.call('AuthenticationController:performSignOut');
|
|
530
583
|
}
|
|
531
584
|
}
|
|
532
|
-
|
|
585
|
+
_SubscriptionController_instances = new WeakSet(), _SubscriptionController_getSubscriptionPriceAmount = function _SubscriptionController_getSubscriptionPriceAmount(price) {
|
|
533
586
|
// no need to use BigInt since max unitDecimals are always 2 for price
|
|
534
587
|
const amount = new BigNumber(price.unitAmount)
|
|
535
588
|
.div(10 ** price.unitDecimals)
|
|
@@ -581,15 +634,74 @@ _SubscriptionController_subscriptionService = new WeakMap(), _SubscriptionContro
|
|
|
581
634
|
!value.paymentTokenSymbol) {
|
|
582
635
|
throw new Error(SubscriptionControllerErrorMessage.PaymentMethodNotCrypto);
|
|
583
636
|
}
|
|
584
|
-
}, _SubscriptionController_getIsEligibleForTrialedSponsorship = function _SubscriptionController_getIsEligibleForTrialedSponsorship(chainId, products) {
|
|
585
|
-
const isSponsorshipSupported = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_getChainSupportsSponsorship).call(this, chainId);
|
|
637
|
+
}, _SubscriptionController_getIsEligibleForTrialedSponsorship = function _SubscriptionController_getIsEligibleForTrialedSponsorship(chainId, products, cryptoAuthMethod) {
|
|
638
|
+
const isSponsorshipSupported = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_getChainSupportsSponsorship).call(this, chainId, products[0], cryptoAuthMethod);
|
|
586
639
|
// verify if the user has trialed the provided products before
|
|
587
640
|
const hasTrialedBefore = this.state.trialedProducts.some((product) => products.includes(product));
|
|
588
641
|
return isSponsorshipSupported && !hasTrialedBefore;
|
|
589
|
-
},
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
642
|
+
}, _SubscriptionController_getIsTrialRequested = function _SubscriptionController_getIsTrialRequested(products, plan) {
|
|
643
|
+
return products.every((productType) => {
|
|
644
|
+
if (this.state.trialedProducts.includes(productType)) {
|
|
645
|
+
return false;
|
|
646
|
+
}
|
|
647
|
+
const productPrice = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_getProductPriceByProductAndPlan).call(this, productType, plan);
|
|
648
|
+
return productPrice.trialPeriodDays > 0;
|
|
649
|
+
});
|
|
650
|
+
}, _SubscriptionController_findCryptoPaymentMethod = function _SubscriptionController_findCryptoPaymentMethod(productType, cryptoAuthMethod) {
|
|
651
|
+
const matches = [];
|
|
652
|
+
for (const paymentMethod of this.state.pricing?.paymentMethods ?? []) {
|
|
653
|
+
if (paymentMethod.type !== PAYMENT_TYPES.byCrypto) {
|
|
654
|
+
continue;
|
|
655
|
+
}
|
|
656
|
+
const resolved = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_resolveCryptoPaymentMethodDefaults).call(this, paymentMethod);
|
|
657
|
+
if (!resolved) {
|
|
658
|
+
continue;
|
|
659
|
+
}
|
|
660
|
+
if (resolved.cryptoAuthMethod === cryptoAuthMethod &&
|
|
661
|
+
resolved.products.includes(productType)) {
|
|
662
|
+
matches.push({
|
|
663
|
+
method: paymentMethod,
|
|
664
|
+
explicit: resolved.explicit,
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
if (matches.length === 0) {
|
|
669
|
+
return undefined;
|
|
670
|
+
}
|
|
671
|
+
const explicitMatches = matches.filter((match) => match.explicit);
|
|
672
|
+
const candidates = explicitMatches.length > 0 ? explicitMatches : matches;
|
|
673
|
+
if (candidates.length > 1) {
|
|
674
|
+
throw new Error('Multiple matching crypto payment methods found');
|
|
675
|
+
}
|
|
676
|
+
return candidates[0].method;
|
|
677
|
+
}, _SubscriptionController_resolveCryptoPaymentMethodDefaults = function _SubscriptionController_resolveCryptoPaymentMethodDefaults(paymentMethod) {
|
|
678
|
+
const productsPresent = paymentMethod.products !== undefined;
|
|
679
|
+
const authPresent = paymentMethod.cryptoAuthMethod !== undefined;
|
|
680
|
+
if (!productsPresent && !authPresent) {
|
|
681
|
+
return {
|
|
682
|
+
products: [PRODUCT_TYPES.SHIELD],
|
|
683
|
+
cryptoAuthMethod: CRYPTO_AUTH_METHODS.ERC20_APPROVAL,
|
|
684
|
+
explicit: false,
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
if (!paymentMethod.products?.length || !paymentMethod.cryptoAuthMethod) {
|
|
688
|
+
return undefined;
|
|
689
|
+
}
|
|
690
|
+
return {
|
|
691
|
+
products: paymentMethod.products,
|
|
692
|
+
cryptoAuthMethod: paymentMethod.cryptoAuthMethod,
|
|
693
|
+
explicit: true,
|
|
694
|
+
};
|
|
695
|
+
}, _SubscriptionController_getChainSupportsSponsorship = function _SubscriptionController_getChainSupportsSponsorship(chainId, productType, cryptoAuthMethod) {
|
|
696
|
+
const cryptoPaymentInfo = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_findCryptoPaymentMethod).call(this, productType, cryptoAuthMethod);
|
|
697
|
+
if (!cryptoPaymentInfo) {
|
|
698
|
+
throw new Error('Chains payment info not found');
|
|
699
|
+
}
|
|
700
|
+
const chainPaymentInfo = cryptoPaymentInfo.chains?.find((chain) => chain.chainId === chainId);
|
|
701
|
+
if (!chainPaymentInfo) {
|
|
702
|
+
throw new Error('Invalid chain id');
|
|
703
|
+
}
|
|
704
|
+
return Boolean(chainPaymentInfo.isSponsorshipSupported);
|
|
593
705
|
}, _SubscriptionController_areTrialedProductsEqual = function _SubscriptionController_areTrialedProductsEqual(oldTrialedProducts, newTrialedProducts) {
|
|
594
706
|
return (oldTrialedProducts.length === newTrialedProducts?.length &&
|
|
595
707
|
oldTrialedProducts.every((product) => newTrialedProducts?.includes(product)));
|