@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.
Files changed (64) hide show
  1. package/CHANGELOG.md +54 -1
  2. package/dist/SubscriptionController-method-action-types.cjs.map +1 -1
  3. package/dist/SubscriptionController-method-action-types.d.cts +55 -18
  4. package/dist/SubscriptionController-method-action-types.d.cts.map +1 -1
  5. package/dist/SubscriptionController-method-action-types.d.mts +55 -18
  6. package/dist/SubscriptionController-method-action-types.d.mts.map +1 -1
  7. package/dist/SubscriptionController-method-action-types.mjs.map +1 -1
  8. package/dist/SubscriptionController.cjs +180 -68
  9. package/dist/SubscriptionController.cjs.map +1 -1
  10. package/dist/SubscriptionController.d.cts +58 -26
  11. package/dist/SubscriptionController.d.cts.map +1 -1
  12. package/dist/SubscriptionController.d.mts +58 -26
  13. package/dist/SubscriptionController.d.mts.map +1 -1
  14. package/dist/SubscriptionController.mjs +181 -69
  15. package/dist/SubscriptionController.mjs.map +1 -1
  16. package/dist/SubscriptionService-method-action-types.cjs +7 -0
  17. package/dist/SubscriptionService-method-action-types.cjs.map +1 -0
  18. package/dist/SubscriptionService-method-action-types.d.cts +155 -0
  19. package/dist/SubscriptionService-method-action-types.d.cts.map +1 -0
  20. package/dist/SubscriptionService-method-action-types.d.mts +155 -0
  21. package/dist/SubscriptionService-method-action-types.d.mts.map +1 -0
  22. package/dist/SubscriptionService-method-action-types.mjs +6 -0
  23. package/dist/SubscriptionService-method-action-types.mjs.map +1 -0
  24. package/dist/SubscriptionService-structs.cjs +158 -0
  25. package/dist/SubscriptionService-structs.cjs.map +1 -0
  26. package/dist/SubscriptionService-structs.d.cts +721 -0
  27. package/dist/SubscriptionService-structs.d.cts.map +1 -0
  28. package/dist/SubscriptionService-structs.d.mts +721 -0
  29. package/dist/SubscriptionService-structs.d.mts.map +1 -0
  30. package/dist/SubscriptionService-structs.mjs +155 -0
  31. package/dist/SubscriptionService-structs.mjs.map +1 -0
  32. package/dist/SubscriptionService.cjs +279 -87
  33. package/dist/SubscriptionService.cjs.map +1 -1
  34. package/dist/SubscriptionService.d.cts +94 -10
  35. package/dist/SubscriptionService.d.cts.map +1 -1
  36. package/dist/SubscriptionService.d.mts +94 -10
  37. package/dist/SubscriptionService.d.mts.map +1 -1
  38. package/dist/SubscriptionService.mjs +279 -87
  39. package/dist/SubscriptionService.mjs.map +1 -1
  40. package/dist/constants.cjs +2 -0
  41. package/dist/constants.cjs.map +1 -1
  42. package/dist/constants.d.cts +2 -0
  43. package/dist/constants.d.cts.map +1 -1
  44. package/dist/constants.d.mts +2 -0
  45. package/dist/constants.d.mts.map +1 -1
  46. package/dist/constants.mjs +2 -0
  47. package/dist/constants.mjs.map +1 -1
  48. package/dist/index.cjs +4 -1
  49. package/dist/index.cjs.map +1 -1
  50. package/dist/index.d.cts +7 -6
  51. package/dist/index.d.cts.map +1 -1
  52. package/dist/index.d.mts +7 -6
  53. package/dist/index.d.mts.map +1 -1
  54. package/dist/index.mjs +2 -2
  55. package/dist/index.mjs.map +1 -1
  56. package/dist/types.cjs +27 -1
  57. package/dist/types.cjs.map +1 -1
  58. package/dist/types.d.cts +171 -10
  59. package/dist/types.d.cts.map +1 -1
  60. package/dist/types.d.mts +171 -10
  61. package/dist/types.d.mts.map +1 -1
  62. package/dist/types.mjs +26 -0
  63. package/dist/types.mjs.map +1 -1
  64. package/package.json +5 -2
@@ -1,16 +1,10 @@
1
1
  "use strict";
2
- var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
3
- if (kind === "m") throw new TypeError("Private method is not writable");
4
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
5
- 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");
6
- return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
7
- };
8
2
  var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
9
3
  if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
10
4
  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");
11
5
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
6
  };
13
- var _SubscriptionController_instances, _SubscriptionController_subscriptionService, _SubscriptionController_getSubscriptionPriceAmount, _SubscriptionController_getSubscriptionBalanceAmount, _SubscriptionController_getProductPriceByProductAndPlan, _SubscriptionController_assertValidSubscriptionStateForCryptoApproval, _SubscriptionController_assertIsUserNotSubscribed, _SubscriptionController_assertIsUserSubscribed, _SubscriptionController_assertIsPaymentMethodCrypto, _SubscriptionController_getIsEligibleForTrialedSponsorship, _SubscriptionController_getChainSupportsSponsorship, _SubscriptionController_areTrialedProductsEqual, _SubscriptionController_areSubscriptionsEqual, _SubscriptionController_isSubscriptionEqual, _SubscriptionController_stringifySubscription;
7
+ 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;
14
8
  Object.defineProperty(exports, "__esModule", { value: true });
15
9
  exports.SubscriptionController = exports.getDefaultSubscriptionControllerState = void 0;
16
10
  const polling_controller_1 = require("@metamask/polling-controller");
@@ -88,10 +82,10 @@ const MESSENGER_EXPOSED_METHODS = [
88
82
  'getSubscriptionsEligibilities',
89
83
  'cancelSubscription',
90
84
  'unCancelSubscription',
91
- 'startShieldSubscriptionWithCard',
85
+ 'startSubscriptionWithCard',
92
86
  'startSubscriptionWithCrypto',
93
87
  'stopAllPolling',
94
- 'submitShieldSubscriptionCryptoApproval',
88
+ 'submitSubscriptionCryptoApproval',
95
89
  'getCryptoApproveTransactionParams',
96
90
  'updatePaymentMethod',
97
91
  'getBillingPortalUrl',
@@ -113,10 +107,10 @@ class SubscriptionController extends (0, polling_controller_1.StaticIntervalPoll
113
107
  * @param options - The options for the SubscriptionController.
114
108
  * @param options.messenger - A restricted messenger.
115
109
  * @param options.state - Initial state to set on this controller.
116
- * @param options.subscriptionService - The subscription service for communicating with subscription server.
117
110
  * @param options.pollingInterval - The polling interval to use for the subscription controller.
118
111
  */
119
- constructor({ messenger, state, subscriptionService, pollingInterval = constants_js_1.DEFAULT_POLLING_INTERVAL, }) {
112
+ constructor(options) {
113
+ const { messenger, state, pollingInterval = constants_js_1.DEFAULT_POLLING_INTERVAL, } = options;
120
114
  super({
121
115
  name: constants_js_1.controllerName,
122
116
  metadata: subscriptionControllerMetadata,
@@ -127,9 +121,7 @@ class SubscriptionController extends (0, polling_controller_1.StaticIntervalPoll
127
121
  messenger,
128
122
  });
129
123
  _SubscriptionController_instances.add(this);
130
- _SubscriptionController_subscriptionService.set(this, void 0);
131
124
  this.setIntervalLength(pollingInterval);
132
- __classPrivateFieldSet(this, _SubscriptionController_subscriptionService, subscriptionService, "f");
133
125
  this.messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
134
126
  }
135
127
  /**
@@ -138,7 +130,7 @@ class SubscriptionController extends (0, polling_controller_1.StaticIntervalPoll
138
130
  * @returns The pricing information.
139
131
  */
140
132
  async getPricing() {
141
- const pricing = await __classPrivateFieldGet(this, _SubscriptionController_subscriptionService, "f").getPricing();
133
+ const pricing = await this.messenger.call('SubscriptionService:getPricing');
142
134
  this.update((state) => {
143
135
  state.pricing = pricing;
144
136
  });
@@ -150,7 +142,7 @@ class SubscriptionController extends (0, polling_controller_1.StaticIntervalPoll
150
142
  const currentCustomerId = this.state.customerId;
151
143
  const currentLastSubscription = this.state.lastSubscription;
152
144
  const currentRewardAccountId = this.state.rewardAccountId;
153
- const { customerId: newCustomerId, subscriptions: newSubscriptions, trialedProducts: newTrialedProducts, lastSubscription: newLastSubscription, rewardAccountId: newRewardAccountId, } = await __classPrivateFieldGet(this, _SubscriptionController_subscriptionService, "f").getSubscriptions();
145
+ const { customerId: newCustomerId, subscriptions: newSubscriptions, trialedProducts: newTrialedProducts, lastSubscription: newLastSubscription, rewardAccountId: newRewardAccountId, } = await this.messenger.call('SubscriptionService:getSubscriptions');
154
146
  // check if the new subscriptions are different from the current subscriptions
155
147
  const areSubscriptionsEqual = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_areSubscriptionsEqual).call(this, currentSubscriptions, newSubscriptions);
156
148
  // check if the new trialed products are different from the current trialed products
@@ -194,11 +186,11 @@ class SubscriptionController extends (0, polling_controller_1.StaticIntervalPoll
194
186
  * @returns The subscriptions eligibilities.
195
187
  */
196
188
  async getSubscriptionsEligibilities(request) {
197
- return await __classPrivateFieldGet(this, _SubscriptionController_subscriptionService, "f").getSubscriptionsEligibilities(request);
189
+ return await this.messenger.call('SubscriptionService:getSubscriptionsEligibilities', request);
198
190
  }
199
191
  async cancelSubscription(request) {
200
192
  __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_assertIsUserSubscribed).call(this, { subscriptionId: request.subscriptionId });
201
- const cancelledSubscription = await __classPrivateFieldGet(this, _SubscriptionController_subscriptionService, "f").cancelSubscription(request);
193
+ const cancelledSubscription = await this.messenger.call('SubscriptionService:cancelSubscription', request);
202
194
  this.update((state) => {
203
195
  state.subscriptions = state.subscriptions.map((subscription) => subscription.id === request.subscriptionId
204
196
  ? { ...subscription, ...cancelledSubscription }
@@ -208,7 +200,7 @@ class SubscriptionController extends (0, polling_controller_1.StaticIntervalPoll
208
200
  }
209
201
  async unCancelSubscription(request) {
210
202
  __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_assertIsUserSubscribed).call(this, { subscriptionId: request.subscriptionId });
211
- const uncancelledSubscription = await __classPrivateFieldGet(this, _SubscriptionController_subscriptionService, "f").unCancelSubscription({
203
+ const uncancelledSubscription = await this.messenger.call('SubscriptionService:unCancelSubscription', {
212
204
  subscriptionId: request.subscriptionId,
213
205
  });
214
206
  this.update((state) => {
@@ -218,51 +210,107 @@ class SubscriptionController extends (0, polling_controller_1.StaticIntervalPoll
218
210
  });
219
211
  this.triggerAccessTokenRefresh();
220
212
  }
221
- async startShieldSubscriptionWithCard(request) {
213
+ /**
214
+ * Starts a card-paid subscription checkout session for the requested products
215
+ * (e.g. Shield or Money Account Plus).
216
+ *
217
+ * `isTrialRequested` on the request is ignored and overwritten from pricing
218
+ * (`trialPeriodDays > 0`) and `trialedProducts`.
219
+ *
220
+ * @param request - The start subscription request.
221
+ * @returns The checkout session response.
222
+ */
223
+ async startSubscriptionWithCard(request) {
224
+ // get the latest subscriptions state before computing trial eligibility
225
+ await this.getSubscriptions();
222
226
  __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_assertIsUserNotSubscribed).call(this, { products: request.products });
223
- const response = await __classPrivateFieldGet(this, _SubscriptionController_subscriptionService, "f").startSubscriptionWithCard(request);
227
+ const response = await this.messenger.call('SubscriptionService:startSubscriptionWithCard', {
228
+ ...request,
229
+ isTrialRequested: __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_getIsTrialRequested).call(this, request.products, request.recurringInterval),
230
+ });
224
231
  // note: no need to trigger access token refresh after startSubscriptionWithCard request because this only return stripe checkout session url, subscription not created yet
225
232
  return response;
226
233
  }
234
+ /**
235
+ * Starts a crypto-paid subscription for the requested products
236
+ * (e.g. Shield or Money Account Plus). Unlike card checkout, this
237
+ * creates the subscription immediately, so local state is refreshed
238
+ * afterwards.
239
+ *
240
+ * `isTrialRequested` on the request is ignored and overwritten from pricing
241
+ * (`trialPeriodDays > 0`) and `trialedProducts`.
242
+ *
243
+ * @param request - The start crypto subscription request.
244
+ * @returns The start crypto subscription response.
245
+ * @throws If `products` is empty.
246
+ */
227
247
  async startSubscriptionWithCrypto(request) {
248
+ if (request.products.length === 0) {
249
+ throw new Error(constants_js_1.SubscriptionControllerErrorMessage.SubscriptionProductsEmpty);
250
+ }
251
+ // get the latest subscriptions state before computing trial eligibility
252
+ await this.getSubscriptions();
228
253
  __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_assertIsUserNotSubscribed).call(this, { products: request.products });
229
- const response = await __classPrivateFieldGet(this, _SubscriptionController_subscriptionService, "f").startSubscriptionWithCrypto(request);
254
+ const response = await this.messenger.call('SubscriptionService:startSubscriptionWithCrypto', {
255
+ ...request,
256
+ isTrialRequested: __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_getIsTrialRequested).call(this, request.products, request.recurringInterval),
257
+ });
258
+ // Crypto start creates the subscription immediately (unlike card checkout).
259
+ await this.getSubscriptions();
230
260
  return response;
231
261
  }
232
262
  /**
233
- * Handles shield subscription crypto approval transactions.
263
+ * Submits a Shield ERC-20 crypto approval transaction to start or update a
264
+ * crypto subscription.
234
265
  *
235
- * @param txMeta - The transaction metadata.
236
- * @param isSponsored - Whether the transaction is sponsored.
237
- * @param rewardAccountId - The account ID of the reward subscription to link to the shield subscription.
266
+ * This handler is Shield / `TransactionType.shieldSubscriptionApprove` only.
267
+ * Delegation-based products (e.g. Money Account) must call
268
+ * `startSubscriptionWithCrypto` instead.
269
+ *
270
+ * @param request - The crypto approval request.
271
+ * @param request.productType - The subscription product. Typed as
272
+ * `typeof PRODUCT_TYPES.SHIELD` only at the moment (future might support more
273
+ * product).
274
+ * @param request.txMeta - The transaction metadata. Must have type
275
+ * `TransactionType.shieldSubscriptionApprove`.
276
+ * @param request.isSponsored - Whether the transaction is sponsored.
277
+ * @param request.rewardAccountId - The account ID of the reward subscription
278
+ * to link.
279
+ * @throws If `productType` is not Shield or `txMeta.type` is not
280
+ * `shieldSubscriptionApprove`.
238
281
  * @returns void
239
282
  */
240
- async submitShieldSubscriptionCryptoApproval(txMeta, isSponsored, rewardAccountId) {
241
- if (txMeta.type !== transaction_controller_1.TransactionType.shieldSubscriptionApprove) {
242
- return;
283
+ async submitSubscriptionCryptoApproval(request) {
284
+ const { productType, txMeta, isSponsored, rewardAccountId } = request;
285
+ if (
286
+ // Widen for the runtime guard: JS / unsound callers may still pass a
287
+ // non-Shield product.
288
+ productType !== types_js_1.PRODUCT_TYPES.SHIELD ||
289
+ txMeta.type !== transaction_controller_1.TransactionType.shieldSubscriptionApprove) {
290
+ throw new Error(constants_js_1.SubscriptionControllerErrorMessage.CryptoApprovalRequiresShieldApprove);
243
291
  }
244
292
  const { chainId, rawTx } = txMeta;
245
293
  if (!chainId || !rawTx) {
246
294
  throw new Error('Chain ID or raw transaction not found');
247
295
  }
248
- const { pricing, trialedProducts, lastSelectedPaymentMethod } = this.state;
296
+ const { pricing, lastSelectedPaymentMethod } = this.state;
249
297
  if (!pricing) {
250
298
  throw new Error('Subscription pricing not found');
251
299
  }
252
300
  if (!lastSelectedPaymentMethod) {
253
301
  throw new Error('Last selected payment method not found');
254
302
  }
255
- const lastSelectedPaymentMethodShield = lastSelectedPaymentMethod[types_js_1.PRODUCT_TYPES.SHIELD];
256
- __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_assertIsPaymentMethodCrypto).call(this, lastSelectedPaymentMethodShield);
257
- const productPrice = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_getProductPriceByProductAndPlan).call(this, types_js_1.PRODUCT_TYPES.SHIELD, lastSelectedPaymentMethodShield.plan);
258
- const isTrialed = trialedProducts?.includes(types_js_1.PRODUCT_TYPES.SHIELD);
259
- // get the latest subscriptions state to check if the user has an active shield subscription
303
+ const lastSelectedPaymentMethodForProduct = lastSelectedPaymentMethod[productType];
304
+ __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_assertIsPaymentMethodCrypto).call(this, lastSelectedPaymentMethodForProduct);
305
+ const productPrice = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_getProductPriceByProductAndPlan).call(this, productType, lastSelectedPaymentMethodForProduct.plan);
306
+ // get the latest subscriptions state before computing trial eligibility
260
307
  await this.getSubscriptions();
261
- const currentSubscription = this.state.subscriptions.find((subscription) => subscription.products.some((product) => product.name === types_js_1.PRODUCT_TYPES.SHIELD));
308
+ const isTrialRequested = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_getIsTrialRequested).call(this, [productType], lastSelectedPaymentMethodForProduct.plan);
309
+ const currentSubscription = this.getSubscriptionByProduct(productType);
262
310
  __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_assertValidSubscriptionStateForCryptoApproval).call(this, {
263
- productType: types_js_1.PRODUCT_TYPES.SHIELD,
311
+ productType,
264
312
  });
265
- // if shield subscription exists, this transaction is for changing payment method
313
+ // if subscription exists, this transaction is for changing payment method
266
314
  const isChangePaymentMethod = Boolean(currentSubscription);
267
315
  if (isChangePaymentMethod) {
268
316
  await this.updatePaymentMethod({
@@ -270,7 +318,7 @@ class SubscriptionController extends (0, polling_controller_1.StaticIntervalPoll
270
318
  subscriptionId: currentSubscription.id,
271
319
  chainId,
272
320
  payerAddress: txMeta.txParams.from,
273
- tokenSymbol: lastSelectedPaymentMethodShield.paymentTokenSymbol,
321
+ tokenSymbol: lastSelectedPaymentMethodForProduct.paymentTokenSymbol,
274
322
  rawTransaction: rawTx,
275
323
  recurringInterval: productPrice.interval,
276
324
  billingCycles: productPrice.minBillingCycles,
@@ -278,16 +326,17 @@ class SubscriptionController extends (0, polling_controller_1.StaticIntervalPoll
278
326
  }
279
327
  else {
280
328
  const params = {
281
- products: [types_js_1.PRODUCT_TYPES.SHIELD],
282
- isTrialRequested: !isTrialed,
329
+ products: [productType],
330
+ isTrialRequested,
283
331
  recurringInterval: productPrice.interval,
284
332
  billingCycles: productPrice.minBillingCycles,
285
333
  chainId,
286
334
  payerAddress: txMeta.txParams.from,
287
- tokenSymbol: lastSelectedPaymentMethodShield.paymentTokenSymbol,
335
+ tokenSymbol: lastSelectedPaymentMethodForProduct.paymentTokenSymbol,
288
336
  rawTransaction: rawTx,
337
+ cryptoAuthMethod: types_js_1.CRYPTO_AUTH_METHODS.ERC20_APPROVAL,
289
338
  isSponsored,
290
- useTestClock: lastSelectedPaymentMethodShield.useTestClock,
339
+ useTestClock: lastSelectedPaymentMethodForProduct.useTestClock,
291
340
  rewardAccountId,
292
341
  };
293
342
  await this.startSubscriptionWithCrypto(params);
@@ -318,7 +367,7 @@ class SubscriptionController extends (0, polling_controller_1.StaticIntervalPoll
318
367
  if (!price) {
319
368
  throw new Error('Price not found');
320
369
  }
321
- const chainsPaymentInfo = pricing.paymentMethods.find((paymentMethod) => paymentMethod.type === types_js_1.PAYMENT_TYPES.byCrypto);
370
+ const chainsPaymentInfo = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_findCryptoPaymentMethod).call(this, request.productType, types_js_1.CRYPTO_AUTH_METHODS.ERC20_APPROVAL);
322
371
  if (!chainsPaymentInfo) {
323
372
  throw new Error('Chains payment info not found');
324
373
  }
@@ -342,11 +391,11 @@ class SubscriptionController extends (0, polling_controller_1.StaticIntervalPoll
342
391
  async updatePaymentMethod(opts) {
343
392
  if (opts.paymentType === types_js_1.PAYMENT_TYPES.byCard) {
344
393
  const { paymentType, ...cardRequest } = opts;
345
- return await __classPrivateFieldGet(this, _SubscriptionController_subscriptionService, "f").updatePaymentMethodCard(cardRequest);
394
+ return await this.messenger.call('SubscriptionService:updatePaymentMethodCard', cardRequest);
346
395
  }
347
396
  else if (opts.paymentType === types_js_1.PAYMENT_TYPES.byCrypto) {
348
397
  const { paymentType, ...cryptoRequest } = opts;
349
- await __classPrivateFieldGet(this, _SubscriptionController_subscriptionService, "f").updatePaymentMethodCrypto(cryptoRequest);
398
+ await this.messenger.call('SubscriptionService:updatePaymentMethodCrypto', cryptoRequest);
350
399
  return await this.getSubscriptions();
351
400
  }
352
401
  throw new Error('Invalid payment type');
@@ -357,19 +406,20 @@ class SubscriptionController extends (0, polling_controller_1.StaticIntervalPoll
357
406
  * @returns The billing portal URL
358
407
  */
359
408
  async getBillingPortalUrl() {
360
- return await __classPrivateFieldGet(this, _SubscriptionController_subscriptionService, "f").getBillingPortalUrl();
409
+ return await this.messenger.call('SubscriptionService:getBillingPortalUrl');
361
410
  }
362
411
  /**
363
412
  * Cache the last selected payment method for a specific product.
364
413
  *
365
- * @param product - The product to cache the payment method for.
366
- * @param paymentMethod - The payment method to cache.
367
- * @param paymentMethod.type - The type of the payment method.
368
- * @param paymentMethod.paymentTokenAddress - The payment token address.
369
- * @param paymentMethod.plan - The plan of the payment method.
370
- * @param paymentMethod.product - The product of the payment method.
414
+ * @param request - The request object.
415
+ * @param request.product - The product to cache the payment method for.
416
+ * @param request.paymentMethod - The payment method to cache.
417
+ * @param request.paymentMethod.type - The type of the payment method.
418
+ * @param request.paymentMethod.paymentTokenAddress - The payment token address.
419
+ * @param request.paymentMethod.plan - The plan of the payment method.
371
420
  */
372
- cacheLastSelectedPaymentMethod(product, paymentMethod) {
421
+ cacheLastSelectedPaymentMethod(request) {
422
+ const { product, paymentMethod } = request;
373
423
  if (paymentMethod.type === types_js_1.PAYMENT_TYPES.byCrypto &&
374
424
  (!paymentMethod.paymentTokenAddress || !paymentMethod.paymentTokenSymbol)) {
375
425
  throw new Error(constants_js_1.SubscriptionControllerErrorMessage.PaymentTokenAddressAndSymbolRequiredForCrypto);
@@ -408,7 +458,8 @@ class SubscriptionController extends (0, polling_controller_1.StaticIntervalPoll
408
458
  * recurringInterval: RecurringInterval.Month,
409
459
  * billingCycles: 1,
410
460
  * }
411
- * @returns resolves to true if the sponsorship is supported and intents were submitted successfully, false otherwise
461
+ * @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
462
+ * @throws If the crypto payment method or chain is missing from pricing
412
463
  */
413
464
  async submitSponsorshipIntents(request) {
414
465
  if (request.products.length === 0) {
@@ -417,7 +468,9 @@ class SubscriptionController extends (0, polling_controller_1.StaticIntervalPoll
417
468
  __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_assertIsUserNotSubscribed).call(this, { products: request.products });
418
469
  const selectedPaymentMethod = this.state.lastSelectedPaymentMethod?.[request.products[0]];
419
470
  __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_assertIsPaymentMethodCrypto).call(this, selectedPaymentMethod);
420
- const isEligibleForTrialedSponsorship = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_getIsEligibleForTrialedSponsorship).call(this, request.chainId, request.products);
471
+ const cryptoAuthMethod = selectedPaymentMethod.cryptoAuthMethod ??
472
+ types_js_1.CRYPTO_AUTH_METHODS.ERC20_APPROVAL;
473
+ const isEligibleForTrialedSponsorship = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_getIsEligibleForTrialedSponsorship).call(this, request.chainId, request.products, cryptoAuthMethod);
421
474
  if (!isEligibleForTrialedSponsorship) {
422
475
  return false;
423
476
  }
@@ -426,7 +479,7 @@ class SubscriptionController extends (0, polling_controller_1.StaticIntervalPoll
426
479
  // we only support one product at a time for now
427
480
  request.products[0], plan);
428
481
  const billingCycles = productPrice.minBillingCycles;
429
- await __classPrivateFieldGet(this, _SubscriptionController_subscriptionService, "f").submitSponsorshipIntents({
482
+ await this.messenger.call('SubscriptionService:submitSponsorshipIntents', {
430
483
  ...request,
431
484
  paymentTokenSymbol,
432
485
  billingCycles,
@@ -441,7 +494,7 @@ class SubscriptionController extends (0, polling_controller_1.StaticIntervalPoll
441
494
  * @example { event: SubscriptionUserEvent.ShieldEntryModalViewed, cohort: 'post_tx' }
442
495
  */
443
496
  async submitUserEvent(request) {
444
- await __classPrivateFieldGet(this, _SubscriptionController_subscriptionService, "f").submitUserEvent(request);
497
+ await this.messenger.call('SubscriptionService:submitUserEvent', request);
445
498
  }
446
499
  /**
447
500
  * Assign user to a cohort.
@@ -450,7 +503,7 @@ class SubscriptionController extends (0, polling_controller_1.StaticIntervalPoll
450
503
  * @example { cohort: 'post_tx' }
451
504
  */
452
505
  async assignUserToCohort(request) {
453
- await __classPrivateFieldGet(this, _SubscriptionController_subscriptionService, "f").assignUserToCohort(request);
506
+ await this.messenger.call('SubscriptionService:assignUserToCohort', request);
454
507
  }
455
508
  /**
456
509
  * Link rewards to a subscription.
@@ -465,7 +518,7 @@ class SubscriptionController extends (0, polling_controller_1.StaticIntervalPoll
465
518
  // assert that the user is subscribed to the subscription
466
519
  __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_assertIsUserSubscribed).call(this, { subscriptionId: request.subscriptionId });
467
520
  // link rewards to the subscription
468
- const response = await __classPrivateFieldGet(this, _SubscriptionController_subscriptionService, "f").linkRewards({
521
+ const response = await this.messenger.call('SubscriptionService:linkRewards', {
469
522
  rewardAccountId: request.rewardAccountId,
470
523
  });
471
524
  if (!response.success) {
@@ -483,7 +536,7 @@ class SubscriptionController extends (0, polling_controller_1.StaticIntervalPoll
483
536
  * @returns The token approve amount
484
537
  */
485
538
  getTokenApproveAmount(price, tokenPaymentInfo) {
486
- const conversionRate = tokenPaymentInfo.conversionRate[price.currency];
539
+ const conversionRate = tokenPaymentInfo.conversionRate?.[price.currency];
487
540
  if (!conversionRate) {
488
541
  throw new Error('Conversion rate not found');
489
542
  }
@@ -503,7 +556,7 @@ class SubscriptionController extends (0, polling_controller_1.StaticIntervalPoll
503
556
  * @returns The token balance amount
504
557
  */
505
558
  getTokenMinimumBalanceAmount(price, tokenPaymentInfo) {
506
- const conversionRate = tokenPaymentInfo.conversionRate[price.currency];
559
+ const conversionRate = tokenPaymentInfo.conversionRate?.[price.currency];
507
560
  if (!conversionRate) {
508
561
  throw new Error('Conversion rate not found');
509
562
  }
@@ -534,7 +587,7 @@ class SubscriptionController extends (0, polling_controller_1.StaticIntervalPoll
534
587
  }
535
588
  }
536
589
  exports.SubscriptionController = SubscriptionController;
537
- _SubscriptionController_subscriptionService = new WeakMap(), _SubscriptionController_instances = new WeakSet(), _SubscriptionController_getSubscriptionPriceAmount = function _SubscriptionController_getSubscriptionPriceAmount(price) {
590
+ _SubscriptionController_instances = new WeakSet(), _SubscriptionController_getSubscriptionPriceAmount = function _SubscriptionController_getSubscriptionPriceAmount(price) {
538
591
  // no need to use BigInt since max unitDecimals are always 2 for price
539
592
  const amount = new bignumber_js_1.BigNumber(price.unitAmount)
540
593
  .div(10 ** price.unitDecimals)
@@ -586,15 +639,74 @@ _SubscriptionController_subscriptionService = new WeakMap(), _SubscriptionContro
586
639
  !value.paymentTokenSymbol) {
587
640
  throw new Error(constants_js_1.SubscriptionControllerErrorMessage.PaymentMethodNotCrypto);
588
641
  }
589
- }, _SubscriptionController_getIsEligibleForTrialedSponsorship = function _SubscriptionController_getIsEligibleForTrialedSponsorship(chainId, products) {
590
- const isSponsorshipSupported = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_getChainSupportsSponsorship).call(this, chainId);
642
+ }, _SubscriptionController_getIsEligibleForTrialedSponsorship = function _SubscriptionController_getIsEligibleForTrialedSponsorship(chainId, products, cryptoAuthMethod) {
643
+ const isSponsorshipSupported = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_getChainSupportsSponsorship).call(this, chainId, products[0], cryptoAuthMethod);
591
644
  // verify if the user has trialed the provided products before
592
645
  const hasTrialedBefore = this.state.trialedProducts.some((product) => products.includes(product));
593
646
  return isSponsorshipSupported && !hasTrialedBefore;
594
- }, _SubscriptionController_getChainSupportsSponsorship = function _SubscriptionController_getChainSupportsSponsorship(chainId) {
595
- const cryptoPaymentInfo = this.state.pricing?.paymentMethods.find((paymentMethod) => paymentMethod.type === types_js_1.PAYMENT_TYPES.byCrypto);
596
- const isSponsorshipSupported = cryptoPaymentInfo?.chains?.find((chain) => chain.chainId === chainId)?.isSponsorshipSupported;
597
- return Boolean(isSponsorshipSupported);
647
+ }, _SubscriptionController_getIsTrialRequested = function _SubscriptionController_getIsTrialRequested(products, plan) {
648
+ return products.every((productType) => {
649
+ if (this.state.trialedProducts.includes(productType)) {
650
+ return false;
651
+ }
652
+ const productPrice = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_getProductPriceByProductAndPlan).call(this, productType, plan);
653
+ return productPrice.trialPeriodDays > 0;
654
+ });
655
+ }, _SubscriptionController_findCryptoPaymentMethod = function _SubscriptionController_findCryptoPaymentMethod(productType, cryptoAuthMethod) {
656
+ const matches = [];
657
+ for (const paymentMethod of this.state.pricing?.paymentMethods ?? []) {
658
+ if (paymentMethod.type !== types_js_1.PAYMENT_TYPES.byCrypto) {
659
+ continue;
660
+ }
661
+ const resolved = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_resolveCryptoPaymentMethodDefaults).call(this, paymentMethod);
662
+ if (!resolved) {
663
+ continue;
664
+ }
665
+ if (resolved.cryptoAuthMethod === cryptoAuthMethod &&
666
+ resolved.products.includes(productType)) {
667
+ matches.push({
668
+ method: paymentMethod,
669
+ explicit: resolved.explicit,
670
+ });
671
+ }
672
+ }
673
+ if (matches.length === 0) {
674
+ return undefined;
675
+ }
676
+ const explicitMatches = matches.filter((match) => match.explicit);
677
+ const candidates = explicitMatches.length > 0 ? explicitMatches : matches;
678
+ if (candidates.length > 1) {
679
+ throw new Error('Multiple matching crypto payment methods found');
680
+ }
681
+ return candidates[0].method;
682
+ }, _SubscriptionController_resolveCryptoPaymentMethodDefaults = function _SubscriptionController_resolveCryptoPaymentMethodDefaults(paymentMethod) {
683
+ const productsPresent = paymentMethod.products !== undefined;
684
+ const authPresent = paymentMethod.cryptoAuthMethod !== undefined;
685
+ if (!productsPresent && !authPresent) {
686
+ return {
687
+ products: [types_js_1.PRODUCT_TYPES.SHIELD],
688
+ cryptoAuthMethod: types_js_1.CRYPTO_AUTH_METHODS.ERC20_APPROVAL,
689
+ explicit: false,
690
+ };
691
+ }
692
+ if (!paymentMethod.products?.length || !paymentMethod.cryptoAuthMethod) {
693
+ return undefined;
694
+ }
695
+ return {
696
+ products: paymentMethod.products,
697
+ cryptoAuthMethod: paymentMethod.cryptoAuthMethod,
698
+ explicit: true,
699
+ };
700
+ }, _SubscriptionController_getChainSupportsSponsorship = function _SubscriptionController_getChainSupportsSponsorship(chainId, productType, cryptoAuthMethod) {
701
+ const cryptoPaymentInfo = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_findCryptoPaymentMethod).call(this, productType, cryptoAuthMethod);
702
+ if (!cryptoPaymentInfo) {
703
+ throw new Error('Chains payment info not found');
704
+ }
705
+ const chainPaymentInfo = cryptoPaymentInfo.chains?.find((chain) => chain.chainId === chainId);
706
+ if (!chainPaymentInfo) {
707
+ throw new Error('Invalid chain id');
708
+ }
709
+ return Boolean(chainPaymentInfo.isSponsorshipSupported);
598
710
  }, _SubscriptionController_areTrialedProductsEqual = function _SubscriptionController_areTrialedProductsEqual(oldTrialedProducts, newTrialedProducts) {
599
711
  return (oldTrialedProducts.length === newTrialedProducts?.length &&
600
712
  oldTrialedProducts.every((product) => newTrialedProducts?.includes(product)));