@metamask/subscription-controller 8.1.0 → 9.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/CHANGELOG.md +26 -1
  2. package/dist/{SubscriptionController-method-action-types.d.mts → SubscriptionController-method-action-types.d.ts} +2 -2
  3. package/dist/SubscriptionController-method-action-types.d.ts.map +1 -0
  4. package/dist/{SubscriptionController-method-action-types.mjs → SubscriptionController-method-action-types.js} +1 -1
  5. package/dist/SubscriptionController-method-action-types.js.map +1 -0
  6. package/dist/{SubscriptionController.d.mts → SubscriptionController.d.ts} +18 -18
  7. package/dist/SubscriptionController.d.ts.map +1 -0
  8. package/dist/{SubscriptionController.mjs → SubscriptionController.js} +371 -274
  9. package/dist/SubscriptionController.js.map +1 -0
  10. package/dist/{SubscriptionService-method-action-types.d.cts → SubscriptionService-method-action-types.d.ts} +2 -2
  11. package/dist/SubscriptionService-method-action-types.d.ts.map +1 -0
  12. package/dist/{SubscriptionService-method-action-types.mjs → SubscriptionService-method-action-types.js} +1 -1
  13. package/dist/SubscriptionService-method-action-types.js.map +1 -0
  14. package/dist/{SubscriptionService-structs.d.cts → SubscriptionService-structs.d.ts} +164 -164
  15. package/dist/SubscriptionService-structs.d.ts.map +1 -0
  16. package/dist/{SubscriptionService-structs.mjs → SubscriptionService-structs.js} +4 -4
  17. package/dist/SubscriptionService-structs.js.map +1 -0
  18. package/dist/{SubscriptionService.d.cts → SubscriptionService.d.ts} +10 -10
  19. package/dist/SubscriptionService.d.ts.map +1 -0
  20. package/dist/{SubscriptionService.mjs → SubscriptionService.js} +141 -143
  21. package/dist/SubscriptionService.js.map +1 -0
  22. package/dist/{constants.d.cts → constants.d.ts} +1 -1
  23. package/dist/constants.d.ts.map +1 -0
  24. package/dist/{constants.mjs → constants.js} +3 -3
  25. package/dist/constants.js.map +1 -0
  26. package/dist/{errors.d.cts → errors.d.ts} +2 -2
  27. package/dist/errors.d.ts.map +1 -0
  28. package/dist/{errors.mjs → errors.js} +5 -1
  29. package/dist/errors.js.map +1 -0
  30. package/dist/{index.d.cts → index.d.ts} +12 -12
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/{index.mjs → index.js} +7 -7
  33. package/dist/index.js.map +1 -0
  34. package/dist/logger.d.ts +4 -0
  35. package/dist/logger.d.ts.map +1 -0
  36. package/dist/logger.js +5 -0
  37. package/dist/logger.js.map +1 -0
  38. package/dist/{selectors.d.mts → selectors.d.ts} +3 -3
  39. package/dist/selectors.d.ts.map +1 -0
  40. package/dist/{selectors.mjs → selectors.js} +2 -2
  41. package/dist/selectors.js.map +1 -0
  42. package/dist/{types.d.mts → types.d.ts} +47 -47
  43. package/dist/types.d.ts.map +1 -0
  44. package/dist/{types.mjs → types.js} +1 -1
  45. package/dist/types.js.map +1 -0
  46. package/package.json +19 -23
  47. package/dist/SubscriptionController-method-action-types.cjs +0 -7
  48. package/dist/SubscriptionController-method-action-types.cjs.map +0 -1
  49. package/dist/SubscriptionController-method-action-types.d.cts +0 -261
  50. package/dist/SubscriptionController-method-action-types.d.cts.map +0 -1
  51. package/dist/SubscriptionController-method-action-types.d.mts.map +0 -1
  52. package/dist/SubscriptionController-method-action-types.mjs.map +0 -1
  53. package/dist/SubscriptionController.cjs +0 -844
  54. package/dist/SubscriptionController.cjs.map +0 -1
  55. package/dist/SubscriptionController.d.cts +0 -268
  56. package/dist/SubscriptionController.d.cts.map +0 -1
  57. package/dist/SubscriptionController.d.mts.map +0 -1
  58. package/dist/SubscriptionController.mjs.map +0 -1
  59. package/dist/SubscriptionService-method-action-types.cjs +0 -7
  60. package/dist/SubscriptionService-method-action-types.cjs.map +0 -1
  61. package/dist/SubscriptionService-method-action-types.d.cts.map +0 -1
  62. package/dist/SubscriptionService-method-action-types.d.mts +0 -164
  63. package/dist/SubscriptionService-method-action-types.d.mts.map +0 -1
  64. package/dist/SubscriptionService-method-action-types.mjs.map +0 -1
  65. package/dist/SubscriptionService-structs.cjs +0 -209
  66. package/dist/SubscriptionService-structs.cjs.map +0 -1
  67. package/dist/SubscriptionService-structs.d.cts.map +0 -1
  68. package/dist/SubscriptionService-structs.d.mts +0 -889
  69. package/dist/SubscriptionService-structs.d.mts.map +0 -1
  70. package/dist/SubscriptionService-structs.mjs.map +0 -1
  71. package/dist/SubscriptionService.cjs +0 -497
  72. package/dist/SubscriptionService.cjs.map +0 -1
  73. package/dist/SubscriptionService.d.cts.map +0 -1
  74. package/dist/SubscriptionService.d.mts +0 -155
  75. package/dist/SubscriptionService.d.mts.map +0 -1
  76. package/dist/SubscriptionService.mjs.map +0 -1
  77. package/dist/constants.cjs +0 -74
  78. package/dist/constants.cjs.map +0 -1
  79. package/dist/constants.d.cts.map +0 -1
  80. package/dist/constants.d.mts +0 -50
  81. package/dist/constants.d.mts.map +0 -1
  82. package/dist/constants.mjs.map +0 -1
  83. package/dist/errors.cjs +0 -72
  84. package/dist/errors.cjs.map +0 -1
  85. package/dist/errors.d.cts.map +0 -1
  86. package/dist/errors.d.mts +0 -34
  87. package/dist/errors.d.mts.map +0 -1
  88. package/dist/errors.mjs.map +0 -1
  89. package/dist/index.cjs +0 -35
  90. package/dist/index.cjs.map +0 -1
  91. package/dist/index.d.cts.map +0 -1
  92. package/dist/index.d.mts +0 -12
  93. package/dist/index.d.mts.map +0 -1
  94. package/dist/index.mjs.map +0 -1
  95. package/dist/logger.cjs +0 -8
  96. package/dist/logger.cjs.map +0 -1
  97. package/dist/logger.d.cts +0 -5
  98. package/dist/logger.d.cts.map +0 -1
  99. package/dist/logger.d.mts +0 -5
  100. package/dist/logger.d.mts.map +0 -1
  101. package/dist/logger.mjs +0 -5
  102. package/dist/logger.mjs.map +0 -1
  103. package/dist/selectors.cjs +0 -52
  104. package/dist/selectors.cjs.map +0 -1
  105. package/dist/selectors.d.cts +0 -38
  106. package/dist/selectors.d.cts.map +0 -1
  107. package/dist/selectors.d.mts.map +0 -1
  108. package/dist/selectors.mjs.map +0 -1
  109. package/dist/types.cjs +0 -100
  110. package/dist/types.cjs.map +0 -1
  111. package/dist/types.d.cts +0 -645
  112. package/dist/types.d.cts.map +0 -1
  113. package/dist/types.d.mts.map +0 -1
  114. package/dist/types.mjs.map +0 -1
@@ -1,23 +1,10 @@
1
- var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
2
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
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");
4
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
- };
6
- var _SubscriptionController_instances, _SubscriptionController_getSubscriptions, _SubscriptionController_updateSubscriptionStateIfChanged, _SubscriptionController_refreshBenefitsIfActive, _SubscriptionController_getSubscriptionPriceAmount, _SubscriptionController_getSubscriptionBalanceAmount, _SubscriptionController_getProductPriceByProductAndPlan, _SubscriptionController_assertValidSubscriptionStateForCryptoApproval, _SubscriptionController_assertIsUserNotSubscribed, _SubscriptionController_assertIsActiveMoneyAccountPlusSubscriber, _SubscriptionController_assertIsUserSubscribed, _SubscriptionController_assertIsPaymentMethodCrypto, _SubscriptionController_hasActiveMoneyAccountPlusSubscription, _SubscriptionController_getIsEligibleForTrialedSponsorship, _SubscriptionController_getIsTrialRequested, _SubscriptionController_findCryptoPaymentMethod, _SubscriptionController_resolveCryptoPaymentMethodDefaults, _SubscriptionController_getChainSupportsSponsorship, _SubscriptionController_areTrialedProductsEqual, _SubscriptionController_areProductEntitlementsEqual, _SubscriptionController_areSubscriptionsEqual, _SubscriptionController_isSubscriptionEqual, _SubscriptionController_stringifySubscription;
7
- function $importDefault(module) {
8
- if (module?.__esModule) {
9
- return module.default;
10
- }
11
- return module;
12
- }
13
- import { StaticIntervalPollingController } from "@metamask/polling-controller";
14
- import { TransactionType } from "@metamask/transaction-controller";
15
- import { BigNumber } from "bignumber.js";
16
- import $deepEqual from "fast-deep-equal";
17
- const deepEqual = $importDefault($deepEqual);
18
- import { ACTIVE_SUBSCRIPTION_STATUSES, controllerName, DEFAULT_POLLING_INTERVAL, SubscriptionControllerErrorMessage } from "./constants.mjs";
19
- import { createModuleLogger, projectLogger } from "./logger.mjs";
20
- import { CRYPTO_AUTH_METHODS, PAYMENT_TYPES, PRODUCT_TYPES, SUBSCRIPTION_STATUSES } from "./types.mjs";
1
+ import { StaticIntervalPollingController } from '@metamask/polling-controller';
2
+ import { TransactionType } from '@metamask/transaction-controller';
3
+ import { BigNumber } from 'bignumber.js';
4
+ import deepEqual from 'fast-deep-equal';
5
+ import { ACTIVE_SUBSCRIPTION_STATUSES, controllerName, DEFAULT_POLLING_INTERVAL, SubscriptionControllerErrorMessage, } from './constants.js';
6
+ import { createModuleLogger, projectLogger } from './logger.js';
7
+ import { CRYPTO_AUTH_METHODS, PAYMENT_TYPES, PRODUCT_TYPES, SUBSCRIPTION_STATUSES, } from './types.js';
21
8
  const log = createModuleLogger(projectLogger, controllerName);
22
9
  /**
23
10
  * Get the default state for the Subscription Controller.
@@ -139,7 +126,6 @@ export class SubscriptionController extends StaticIntervalPollingController() {
139
126
  },
140
127
  messenger,
141
128
  });
142
- _SubscriptionController_instances.add(this);
143
129
  this.setIntervalLength(pollingInterval);
144
130
  this.messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
145
131
  }
@@ -156,7 +142,68 @@ export class SubscriptionController extends StaticIntervalPollingController() {
156
142
  return pricing;
157
143
  }
158
144
  async getSubscriptions() {
159
- return await __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_getSubscriptions).call(this);
145
+ return await this.#getSubscriptions();
146
+ }
147
+ async #getSubscriptions({ refreshBenefits = true, } = {}) {
148
+ const currentSubscriptions = this.state.subscriptions;
149
+ const currentTrialedProducts = this.state.trialedProducts;
150
+ const currentCustomerId = this.state.customerId;
151
+ const currentLastSubscription = this.state.lastSubscription;
152
+ const currentRewardAccountId = this.state.rewardAccountId;
153
+ const currentProductEntitlements = this.state.productEntitlements;
154
+ const currentSubscriptionState = {
155
+ customerId: currentCustomerId,
156
+ subscriptions: currentSubscriptions,
157
+ trialedProducts: currentTrialedProducts,
158
+ lastSubscription: currentLastSubscription,
159
+ rewardAccountId: currentRewardAccountId,
160
+ productEntitlements: currentProductEntitlements,
161
+ };
162
+ const newSubscriptionState = await this.messenger.call('SubscriptionService:getSubscriptions');
163
+ this.#updateSubscriptionStateIfChanged(currentSubscriptionState, newSubscriptionState);
164
+ await this.#refreshBenefitsIfActive(refreshBenefits);
165
+ return newSubscriptionState.subscriptions;
166
+ }
167
+ /**
168
+ * Updates the subscription state and refreshes the access token when the
169
+ * fetched subscription state differs from the current state.
170
+ *
171
+ * @param currentState - The subscription state captured before the request.
172
+ * @param newState - The subscription state returned by the service.
173
+ */
174
+ #updateSubscriptionStateIfChanged(currentState, newState) {
175
+ const { subscriptions: currentSubscriptions, trialedProducts: currentTrialedProducts, customerId: currentCustomerId, lastSubscription: currentLastSubscription, rewardAccountId: currentRewardAccountId, productEntitlements: currentProductEntitlements, } = currentState;
176
+ const { customerId: newCustomerId, subscriptions: newSubscriptions, trialedProducts: newTrialedProducts, lastSubscription: newLastSubscription, rewardAccountId: newRewardAccountId, productEntitlements: newProductEntitlements, } = newState;
177
+ const areSubscriptionsEqual = this.#areSubscriptionsEqual(currentSubscriptions, newSubscriptions);
178
+ const areTrialedProductsEqual = this.#areTrialedProductsEqual(currentTrialedProducts, newTrialedProducts);
179
+ const isLastSubscriptionEqual = this.#isSubscriptionEqual(currentLastSubscription, newLastSubscription);
180
+ const areCustomerIdsEqual = currentCustomerId === newCustomerId;
181
+ const areRewardAccountIdsEqual = currentRewardAccountId === newRewardAccountId;
182
+ const areProductEntitlementsEqual = this.#areProductEntitlementsEqual(currentProductEntitlements, newProductEntitlements);
183
+ // only update the state if the subscriptions or trialed products are different
184
+ // this prevents unnecessary state updates events, easier for the clients to handle
185
+ // Only update the state if any subscription-related field changed. This
186
+ // prevents unnecessary state change events for clients to handle.
187
+ if (!areSubscriptionsEqual ||
188
+ !isLastSubscriptionEqual ||
189
+ !areTrialedProductsEqual ||
190
+ !areCustomerIdsEqual ||
191
+ !areRewardAccountIdsEqual ||
192
+ !areProductEntitlementsEqual) {
193
+ this.update((state) => {
194
+ state.subscriptions = newSubscriptions;
195
+ state.customerId = newCustomerId;
196
+ state.trialedProducts = newTrialedProducts;
197
+ state.lastSubscription = newLastSubscription;
198
+ state.rewardAccountId = newRewardAccountId;
199
+ // Omitted entitlements are an empty map so selectors fail closed
200
+ // instead of keeping last-known paid-feature flags.
201
+ state.productEntitlements = newProductEntitlements ?? {};
202
+ });
203
+ // Trigger an access token refresh to ensure the user has the latest
204
+ // access token after a subscription state change.
205
+ this.triggerAccessTokenRefresh();
206
+ }
160
207
  }
161
208
  /**
162
209
  * Gets and stores the user's subscription benefits.
@@ -166,12 +213,12 @@ export class SubscriptionController extends StaticIntervalPollingController() {
166
213
  * not eligible for benefits.
167
214
  */
168
215
  async getBenefits() {
169
- __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_assertIsActiveMoneyAccountPlusSubscriber).call(this);
216
+ this.#assertIsActiveMoneyAccountPlusSubscriber();
170
217
  const benefits = await this.messenger.call('SubscriptionService:getBenefits');
171
218
  // The subscription may have become inactive while the benefits request
172
219
  // was in flight. Re-check before persisting the response so stale
173
220
  // benefits cannot be restored after entitlement ends.
174
- __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_assertIsActiveMoneyAccountPlusSubscriber).call(this);
221
+ this.#assertIsActiveMoneyAccountPlusSubscriber();
175
222
  this.update((state) => {
176
223
  state.benefits = benefits.eligible
177
224
  ? {
@@ -204,18 +251,18 @@ export class SubscriptionController extends StaticIntervalPollingController() {
204
251
  return await this.messenger.call('SubscriptionService:getSubscriptionsEligibilities', request);
205
252
  }
206
253
  async cancelSubscription(request) {
207
- __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_assertIsUserSubscribed).call(this, { subscriptionId: request.subscriptionId });
254
+ this.#assertIsUserSubscribed({ subscriptionId: request.subscriptionId });
208
255
  const cancelledSubscription = await this.messenger.call('SubscriptionService:cancelSubscription', request);
209
256
  this.update((state) => {
210
257
  state.subscriptions = state.subscriptions.map((subscription) => subscription.id === request.subscriptionId
211
258
  ? { ...subscription, ...cancelledSubscription }
212
259
  : subscription);
213
260
  });
214
- await __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_refreshBenefitsIfActive).call(this);
261
+ await this.#refreshBenefitsIfActive();
215
262
  this.triggerAccessTokenRefresh();
216
263
  }
217
264
  async unCancelSubscription(request) {
218
- __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_assertIsUserSubscribed).call(this, { subscriptionId: request.subscriptionId });
265
+ this.#assertIsUserSubscribed({ subscriptionId: request.subscriptionId });
219
266
  const uncancelledSubscription = await this.messenger.call('SubscriptionService:unCancelSubscription', {
220
267
  subscriptionId: request.subscriptionId,
221
268
  });
@@ -224,7 +271,7 @@ export class SubscriptionController extends StaticIntervalPollingController() {
224
271
  ? { ...subscription, ...uncancelledSubscription }
225
272
  : subscription);
226
273
  });
227
- await __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_refreshBenefitsIfActive).call(this);
274
+ await this.#refreshBenefitsIfActive();
228
275
  this.triggerAccessTokenRefresh();
229
276
  }
230
277
  /**
@@ -240,10 +287,10 @@ export class SubscriptionController extends StaticIntervalPollingController() {
240
287
  async startSubscriptionWithCard(request) {
241
288
  // get the latest subscriptions state before computing trial eligibility
242
289
  await this.getSubscriptions();
243
- __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_assertIsUserNotSubscribed).call(this, { products: request.products });
290
+ this.#assertIsUserNotSubscribed({ products: request.products });
244
291
  const response = await this.messenger.call('SubscriptionService:startSubscriptionWithCard', {
245
292
  ...request,
246
- isTrialRequested: __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_getIsTrialRequested).call(this, request.products, request.recurringInterval),
293
+ isTrialRequested: this.#getIsTrialRequested(request.products, request.recurringInterval),
247
294
  });
248
295
  // note: no need to trigger access token refresh after startSubscriptionWithCard request because this only return stripe checkout session url, subscription not created yet
249
296
  return response;
@@ -266,11 +313,11 @@ export class SubscriptionController extends StaticIntervalPollingController() {
266
313
  throw new Error(SubscriptionControllerErrorMessage.SubscriptionProductsEmpty);
267
314
  }
268
315
  // get the latest subscriptions state before computing trial eligibility
269
- await __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_getSubscriptions).call(this, { refreshBenefits: false });
270
- __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_assertIsUserNotSubscribed).call(this, { products: request.products });
316
+ await this.#getSubscriptions({ refreshBenefits: false });
317
+ this.#assertIsUserNotSubscribed({ products: request.products });
271
318
  const response = await this.messenger.call('SubscriptionService:startSubscriptionWithCrypto', {
272
319
  ...request,
273
- isTrialRequested: __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_getIsTrialRequested).call(this, request.products, request.recurringInterval),
320
+ isTrialRequested: this.#getIsTrialRequested(request.products, request.recurringInterval),
274
321
  });
275
322
  // Crypto start creates the subscription immediately (unlike card checkout).
276
323
  await this.getSubscriptions();
@@ -318,13 +365,13 @@ export class SubscriptionController extends StaticIntervalPollingController() {
318
365
  throw new Error('Last selected payment method not found');
319
366
  }
320
367
  const lastSelectedPaymentMethodForProduct = lastSelectedPaymentMethod[productType];
321
- __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_assertIsPaymentMethodCrypto).call(this, lastSelectedPaymentMethodForProduct);
322
- const productPrice = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_getProductPriceByProductAndPlan).call(this, productType, lastSelectedPaymentMethodForProduct.plan);
368
+ this.#assertIsPaymentMethodCrypto(lastSelectedPaymentMethodForProduct);
369
+ const productPrice = this.#getProductPriceByProductAndPlan(productType, lastSelectedPaymentMethodForProduct.plan);
323
370
  // get the latest subscriptions state before computing trial eligibility
324
- await __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_getSubscriptions).call(this, { refreshBenefits: false });
325
- const isTrialRequested = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_getIsTrialRequested).call(this, [productType], lastSelectedPaymentMethodForProduct.plan);
371
+ await this.#getSubscriptions({ refreshBenefits: false });
372
+ const isTrialRequested = this.#getIsTrialRequested([productType], lastSelectedPaymentMethodForProduct.plan);
326
373
  const currentSubscription = this.getSubscriptionByProduct(productType);
327
- __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_assertValidSubscriptionStateForCryptoApproval).call(this, {
374
+ this.#assertValidSubscriptionStateForCryptoApproval({
328
375
  productType,
329
376
  });
330
377
  // if subscription exists, this transaction is for changing payment method
@@ -359,7 +406,7 @@ export class SubscriptionController extends StaticIntervalPollingController() {
359
406
  await this.startSubscriptionWithCrypto(params);
360
407
  }
361
408
  // update the subscriptions state after subscription created in server
362
- await __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_getSubscriptions).call(this, { refreshBenefits: false });
409
+ await this.#getSubscriptions({ refreshBenefits: false });
363
410
  }
364
411
  /**
365
412
  * Get transaction params to create crypto approve transaction for subscription payment
@@ -384,7 +431,7 @@ export class SubscriptionController extends StaticIntervalPollingController() {
384
431
  if (!price) {
385
432
  throw new Error('Price not found');
386
433
  }
387
- const chainsPaymentInfo = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_findCryptoPaymentMethod).call(this, request.productType, CRYPTO_AUTH_METHODS.ERC20_APPROVAL);
434
+ const chainsPaymentInfo = this.#findCryptoPaymentMethod(request.productType, CRYPTO_AUTH_METHODS.ERC20_APPROVAL);
388
435
  if (!chainsPaymentInfo) {
389
436
  throw new Error('Chains payment info not found');
390
437
  }
@@ -482,17 +529,17 @@ export class SubscriptionController extends StaticIntervalPollingController() {
482
529
  if (request.products.length === 0) {
483
530
  throw new Error(SubscriptionControllerErrorMessage.SubscriptionProductsEmpty);
484
531
  }
485
- __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_assertIsUserNotSubscribed).call(this, { products: request.products });
532
+ this.#assertIsUserNotSubscribed({ products: request.products });
486
533
  const selectedPaymentMethod = this.state.lastSelectedPaymentMethod?.[request.products[0]];
487
- __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_assertIsPaymentMethodCrypto).call(this, selectedPaymentMethod);
534
+ this.#assertIsPaymentMethodCrypto(selectedPaymentMethod);
488
535
  const cryptoAuthMethod = selectedPaymentMethod.cryptoAuthMethod ??
489
536
  CRYPTO_AUTH_METHODS.ERC20_APPROVAL;
490
- const isEligibleForTrialedSponsorship = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_getIsEligibleForTrialedSponsorship).call(this, request.chainId, request.products, cryptoAuthMethod);
537
+ const isEligibleForTrialedSponsorship = this.#getIsEligibleForTrialedSponsorship(request.chainId, request.products, cryptoAuthMethod);
491
538
  if (!isEligibleForTrialedSponsorship) {
492
539
  return false;
493
540
  }
494
541
  const { paymentTokenSymbol, plan } = selectedPaymentMethod;
495
- const productPrice = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_getProductPriceByProductAndPlan).call(this,
542
+ const productPrice = this.#getProductPriceByProductAndPlan(
496
543
  // we only support one product at a time for now
497
544
  request.products[0], plan);
498
545
  const billingCycles = productPrice.minBillingCycles;
@@ -533,7 +580,7 @@ export class SubscriptionController extends StaticIntervalPollingController() {
533
580
  */
534
581
  async linkRewards(request) {
535
582
  // assert that the user is subscribed to the subscription
536
- __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_assertIsUserSubscribed).call(this, { subscriptionId: request.subscriptionId });
583
+ this.#assertIsUserSubscribed({ subscriptionId: request.subscriptionId });
537
584
  // link rewards to the subscription
538
585
  const response = await this.messenger.call('SubscriptionService:linkRewards', {
539
586
  rewardAccountId: request.rewardAccountId,
@@ -545,6 +592,54 @@ export class SubscriptionController extends StaticIntervalPollingController() {
545
592
  async _executePoll() {
546
593
  await this.getSubscriptions();
547
594
  }
595
+ async #refreshBenefitsIfActive(refreshBenefits = true) {
596
+ if (!this.#hasActiveMoneyAccountPlusSubscription()) {
597
+ if (this.state.benefits) {
598
+ this.update((state) => {
599
+ state.benefits = undefined;
600
+ });
601
+ }
602
+ return;
603
+ }
604
+ if (!refreshBenefits) {
605
+ return;
606
+ }
607
+ try {
608
+ await this.getBenefits();
609
+ }
610
+ catch (error) {
611
+ log('Failed to refresh subscription benefits', error);
612
+ }
613
+ }
614
+ /**
615
+ * Calculate total subscription price amount (approval amount) from price info
616
+ * e.g: $8 per month * 12 months min billing cycles = $96
617
+ *
618
+ * @param price - The price info
619
+ * @returns The price amount
620
+ */
621
+ #getSubscriptionPriceAmount(price) {
622
+ // no need to use BigInt since max unitDecimals are always 2 for price
623
+ const amount = new BigNumber(price.unitAmount)
624
+ .div(10 ** price.unitDecimals)
625
+ .multipliedBy(price.minBillingCycles)
626
+ .toString();
627
+ return amount;
628
+ }
629
+ /**
630
+ * Calculate minimum subscription balance amount from price info
631
+ *
632
+ * @param price - The price info
633
+ * @returns The balance amount
634
+ */
635
+ #getSubscriptionBalanceAmount(price) {
636
+ // no need to use BigInt since max unitDecimals are always 2 for price
637
+ const amount = new BigNumber(price.unitAmount)
638
+ .div(10 ** price.unitDecimals)
639
+ .multipliedBy(price.minBillingCyclesForBalance)
640
+ .toString();
641
+ return amount;
642
+ }
548
643
  /**
549
644
  * Calculate token approve amount from price info
550
645
  *
@@ -558,7 +653,7 @@ export class SubscriptionController extends StaticIntervalPollingController() {
558
653
  throw new Error('Conversion rate not found');
559
654
  }
560
655
  // price of the product
561
- const priceAmount = new BigNumber(__classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_getSubscriptionPriceAmount).call(this, price));
656
+ const priceAmount = new BigNumber(this.#getSubscriptionPriceAmount(price));
562
657
  const tokenDecimal = new BigNumber(10).pow(tokenPaymentInfo.decimals);
563
658
  const tokenAmount = priceAmount
564
659
  .multipliedBy(tokenDecimal)
@@ -577,7 +672,7 @@ export class SubscriptionController extends StaticIntervalPollingController() {
577
672
  if (!conversionRate) {
578
673
  throw new Error('Conversion rate not found');
579
674
  }
580
- const balanceAmount = new BigNumber(__classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_getSubscriptionBalanceAmount).call(this, price));
675
+ const balanceAmount = new BigNumber(this.#getSubscriptionBalanceAmount(price));
581
676
  const tokenDecimal = new BigNumber(10).pow(tokenPaymentInfo.decimals);
582
677
  const tokenAmount = balanceAmount
583
678
  .multipliedBy(tokenDecimal)
@@ -602,242 +697,244 @@ export class SubscriptionController extends StaticIntervalPollingController() {
602
697
  // will be fetched.
603
698
  this.messenger.call('AuthenticationController:performSignOut');
604
699
  }
605
- }
606
- _SubscriptionController_instances = new WeakSet(), _SubscriptionController_getSubscriptions = async function _SubscriptionController_getSubscriptions({ refreshBenefits = true, } = {}) {
607
- const currentSubscriptions = this.state.subscriptions;
608
- const currentTrialedProducts = this.state.trialedProducts;
609
- const currentCustomerId = this.state.customerId;
610
- const currentLastSubscription = this.state.lastSubscription;
611
- const currentRewardAccountId = this.state.rewardAccountId;
612
- const currentProductEntitlements = this.state.productEntitlements;
613
- const currentSubscriptionState = {
614
- customerId: currentCustomerId,
615
- subscriptions: currentSubscriptions,
616
- trialedProducts: currentTrialedProducts,
617
- lastSubscription: currentLastSubscription,
618
- rewardAccountId: currentRewardAccountId,
619
- productEntitlements: currentProductEntitlements,
620
- };
621
- const newSubscriptionState = await this.messenger.call('SubscriptionService:getSubscriptions');
622
- __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_updateSubscriptionStateIfChanged).call(this, currentSubscriptionState, newSubscriptionState);
623
- await __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_refreshBenefitsIfActive).call(this, refreshBenefits);
624
- return newSubscriptionState.subscriptions;
625
- }, _SubscriptionController_updateSubscriptionStateIfChanged = function _SubscriptionController_updateSubscriptionStateIfChanged(currentState, newState) {
626
- const { subscriptions: currentSubscriptions, trialedProducts: currentTrialedProducts, customerId: currentCustomerId, lastSubscription: currentLastSubscription, rewardAccountId: currentRewardAccountId, productEntitlements: currentProductEntitlements, } = currentState;
627
- const { customerId: newCustomerId, subscriptions: newSubscriptions, trialedProducts: newTrialedProducts, lastSubscription: newLastSubscription, rewardAccountId: newRewardAccountId, productEntitlements: newProductEntitlements, } = newState;
628
- const areSubscriptionsEqual = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_areSubscriptionsEqual).call(this, currentSubscriptions, newSubscriptions);
629
- const areTrialedProductsEqual = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_areTrialedProductsEqual).call(this, currentTrialedProducts, newTrialedProducts);
630
- const isLastSubscriptionEqual = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_isSubscriptionEqual).call(this, currentLastSubscription, newLastSubscription);
631
- const areCustomerIdsEqual = currentCustomerId === newCustomerId;
632
- const areRewardAccountIdsEqual = currentRewardAccountId === newRewardAccountId;
633
- const areProductEntitlementsEqual = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_areProductEntitlementsEqual).call(this, currentProductEntitlements, newProductEntitlements);
634
- // only update the state if the subscriptions or trialed products are different
635
- // this prevents unnecessary state updates events, easier for the clients to handle
636
- // Only update the state if any subscription-related field changed. This
637
- // prevents unnecessary state change events for clients to handle.
638
- if (!areSubscriptionsEqual ||
639
- !isLastSubscriptionEqual ||
640
- !areTrialedProductsEqual ||
641
- !areCustomerIdsEqual ||
642
- !areRewardAccountIdsEqual ||
643
- !areProductEntitlementsEqual) {
644
- this.update((state) => {
645
- state.subscriptions = newSubscriptions;
646
- state.customerId = newCustomerId;
647
- state.trialedProducts = newTrialedProducts;
648
- state.lastSubscription = newLastSubscription;
649
- state.rewardAccountId = newRewardAccountId;
650
- // Omitted entitlements are an empty map so selectors fail closed
651
- // instead of keeping last-known paid-feature flags.
652
- state.productEntitlements = newProductEntitlements ?? {};
653
- });
654
- // Trigger an access token refresh to ensure the user has the latest
655
- // access token after a subscription state change.
656
- this.triggerAccessTokenRefresh();
700
+ #getProductPriceByProductAndPlan(productType, plan) {
701
+ const { pricing } = this.state;
702
+ const productPricing = pricing?.products.find((product) => product.name === productType);
703
+ const productPrice = productPricing?.prices.find((price) => price.interval === plan);
704
+ if (!productPrice) {
705
+ throw new Error(SubscriptionControllerErrorMessage.ProductPriceNotFound);
706
+ }
707
+ return productPrice;
708
+ }
709
+ #assertValidSubscriptionStateForCryptoApproval({ productType, }) {
710
+ const subscription = this.state.subscriptions.find((sub) => sub.products.some((product) => product.name === productType));
711
+ const isValid = !subscription ||
712
+ [
713
+ SUBSCRIPTION_STATUSES.pastDue,
714
+ SUBSCRIPTION_STATUSES.unpaid,
715
+ SUBSCRIPTION_STATUSES.paused,
716
+ SUBSCRIPTION_STATUSES.provisional,
717
+ SUBSCRIPTION_STATUSES.active,
718
+ SUBSCRIPTION_STATUSES.trialing,
719
+ ].includes(subscription.status);
720
+ if (!isValid) {
721
+ throw new Error(SubscriptionControllerErrorMessage.SubscriptionNotValidForCryptoApproval);
722
+ }
657
723
  }
658
- }, _SubscriptionController_refreshBenefitsIfActive = async function _SubscriptionController_refreshBenefitsIfActive(refreshBenefits = true) {
659
- if (!__classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_hasActiveMoneyAccountPlusSubscription).call(this)) {
660
- if (this.state.benefits) {
661
- this.update((state) => {
662
- state.benefits = undefined;
663
- });
724
+ #assertIsUserNotSubscribed({ products }) {
725
+ const subscription = this.state.subscriptions.find((sub) => sub.products.some((product) => products.includes(product.name)));
726
+ if (subscription &&
727
+ ACTIVE_SUBSCRIPTION_STATUSES.includes(subscription.status)) {
728
+ throw new Error(SubscriptionControllerErrorMessage.UserAlreadySubscribed);
664
729
  }
665
- return;
666
- }
667
- if (!refreshBenefits) {
668
- return;
669
- }
670
- try {
671
- await this.getBenefits();
672
- }
673
- catch (error) {
674
- log('Failed to refresh subscription benefits', error);
675
- }
676
- }, _SubscriptionController_getSubscriptionPriceAmount = function _SubscriptionController_getSubscriptionPriceAmount(price) {
677
- // no need to use BigInt since max unitDecimals are always 2 for price
678
- const amount = new BigNumber(price.unitAmount)
679
- .div(10 ** price.unitDecimals)
680
- .multipliedBy(price.minBillingCycles)
681
- .toString();
682
- return amount;
683
- }, _SubscriptionController_getSubscriptionBalanceAmount = function _SubscriptionController_getSubscriptionBalanceAmount(price) {
684
- // no need to use BigInt since max unitDecimals are always 2 for price
685
- const amount = new BigNumber(price.unitAmount)
686
- .div(10 ** price.unitDecimals)
687
- .multipliedBy(price.minBillingCyclesForBalance)
688
- .toString();
689
- return amount;
690
- }, _SubscriptionController_getProductPriceByProductAndPlan = function _SubscriptionController_getProductPriceByProductAndPlan(productType, plan) {
691
- const { pricing } = this.state;
692
- const productPricing = pricing?.products.find((product) => product.name === productType);
693
- const productPrice = productPricing?.prices.find((price) => price.interval === plan);
694
- if (!productPrice) {
695
- throw new Error(SubscriptionControllerErrorMessage.ProductPriceNotFound);
696
- }
697
- return productPrice;
698
- }, _SubscriptionController_assertValidSubscriptionStateForCryptoApproval = function _SubscriptionController_assertValidSubscriptionStateForCryptoApproval({ productType, }) {
699
- const subscription = this.state.subscriptions.find((sub) => sub.products.some((product) => product.name === productType));
700
- const isValid = !subscription ||
701
- [
702
- SUBSCRIPTION_STATUSES.pastDue,
703
- SUBSCRIPTION_STATUSES.unpaid,
704
- SUBSCRIPTION_STATUSES.paused,
705
- SUBSCRIPTION_STATUSES.provisional,
706
- SUBSCRIPTION_STATUSES.active,
707
- SUBSCRIPTION_STATUSES.trialing,
708
- ].includes(subscription.status);
709
- if (!isValid) {
710
- throw new Error(SubscriptionControllerErrorMessage.SubscriptionNotValidForCryptoApproval);
711
- }
712
- }, _SubscriptionController_assertIsUserNotSubscribed = function _SubscriptionController_assertIsUserNotSubscribed({ products }) {
713
- const subscription = this.state.subscriptions.find((sub) => sub.products.some((product) => products.includes(product.name)));
714
- if (subscription &&
715
- ACTIVE_SUBSCRIPTION_STATUSES.includes(subscription.status)) {
716
- throw new Error(SubscriptionControllerErrorMessage.UserAlreadySubscribed);
717
- }
718
- }, _SubscriptionController_assertIsActiveMoneyAccountPlusSubscriber = function _SubscriptionController_assertIsActiveMoneyAccountPlusSubscriber() {
719
- if (!__classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_hasActiveMoneyAccountPlusSubscription).call(this)) {
720
- if (this.state.benefits) {
721
- this.update((state) => {
722
- state.benefits = undefined;
723
- });
730
+ }
731
+ #assertIsActiveMoneyAccountPlusSubscriber() {
732
+ if (!this.#hasActiveMoneyAccountPlusSubscription()) {
733
+ if (this.state.benefits) {
734
+ this.update((state) => {
735
+ state.benefits = undefined;
736
+ });
737
+ }
738
+ throw new Error(SubscriptionControllerErrorMessage.UserNotSubscribed);
724
739
  }
725
- throw new Error(SubscriptionControllerErrorMessage.UserNotSubscribed);
726
- }
727
- }, _SubscriptionController_assertIsUserSubscribed = function _SubscriptionController_assertIsUserSubscribed(request) {
728
- if (!this.state.subscriptions.find((subscription) => subscription.id === request.subscriptionId)) {
729
- throw new Error(SubscriptionControllerErrorMessage.UserNotSubscribed);
730
- }
731
- }, _SubscriptionController_assertIsPaymentMethodCrypto = function _SubscriptionController_assertIsPaymentMethodCrypto(value) {
732
- if (value?.type !== PAYMENT_TYPES.byCrypto ||
733
- !value.paymentTokenAddress ||
734
- !value.paymentTokenSymbol) {
735
- throw new Error(SubscriptionControllerErrorMessage.PaymentMethodNotCrypto);
736
- }
737
- }, _SubscriptionController_hasActiveMoneyAccountPlusSubscription = function _SubscriptionController_hasActiveMoneyAccountPlusSubscription() {
738
- return this.state.subscriptions.some((subscription) => subscription.products.some((product) => product.name === PRODUCT_TYPES.MONEY_ACCOUNT_PLUS) && ACTIVE_SUBSCRIPTION_STATUSES.includes(subscription.status));
739
- }, _SubscriptionController_getIsEligibleForTrialedSponsorship = function _SubscriptionController_getIsEligibleForTrialedSponsorship(chainId, products, cryptoAuthMethod) {
740
- const isSponsorshipSupported = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_getChainSupportsSponsorship).call(this, chainId, products[0], cryptoAuthMethod);
741
- // verify if the user has trialed the provided products before
742
- const hasTrialedBefore = this.state.trialedProducts.some((product) => products.includes(product));
743
- return isSponsorshipSupported && !hasTrialedBefore;
744
- }, _SubscriptionController_getIsTrialRequested = function _SubscriptionController_getIsTrialRequested(products, plan) {
745
- return products.every((productType) => {
746
- if (this.state.trialedProducts.includes(productType)) {
747
- return false;
740
+ }
741
+ #assertIsUserSubscribed(request) {
742
+ if (!this.state.subscriptions.find((subscription) => subscription.id === request.subscriptionId)) {
743
+ throw new Error(SubscriptionControllerErrorMessage.UserNotSubscribed);
748
744
  }
749
- const productPrice = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_getProductPriceByProductAndPlan).call(this, productType, plan);
750
- return productPrice.trialPeriodDays > 0;
751
- });
752
- }, _SubscriptionController_findCryptoPaymentMethod = function _SubscriptionController_findCryptoPaymentMethod(productType, cryptoAuthMethod) {
753
- const matches = [];
754
- for (const paymentMethod of this.state.pricing?.paymentMethods ?? []) {
755
- if (paymentMethod.type !== PAYMENT_TYPES.byCrypto) {
756
- continue;
757
- }
758
- const resolved = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_resolveCryptoPaymentMethodDefaults).call(this, paymentMethod);
759
- if (!resolved) {
760
- continue;
761
- }
762
- if (resolved.cryptoAuthMethod === cryptoAuthMethod &&
763
- resolved.products.includes(productType)) {
764
- matches.push({
765
- method: paymentMethod,
766
- explicit: resolved.explicit,
767
- });
745
+ }
746
+ /**
747
+ * Asserts that the value is a valid crypto payment method.
748
+ *
749
+ * After this assert, `cryptoAuthMethod` and `useTestClock` remain optional
750
+ * because persisted cache entries may omit them.
751
+ *
752
+ * @param value - The value to assert.
753
+ * @throws an error if the value is not a valid crypto payment method.
754
+ */
755
+ #assertIsPaymentMethodCrypto(value) {
756
+ if (value?.type !== PAYMENT_TYPES.byCrypto ||
757
+ !value.paymentTokenAddress ||
758
+ !value.paymentTokenSymbol) {
759
+ throw new Error(SubscriptionControllerErrorMessage.PaymentMethodNotCrypto);
768
760
  }
769
761
  }
770
- if (matches.length === 0) {
771
- return undefined;
762
+ #hasActiveMoneyAccountPlusSubscription() {
763
+ return this.state.subscriptions.some((subscription) => subscription.products.some((product) => product.name === PRODUCT_TYPES.MONEY_ACCOUNT_PLUS) && ACTIVE_SUBSCRIPTION_STATUSES.includes(subscription.status));
772
764
  }
773
- const explicitMatches = matches.filter((match) => match.explicit);
774
- const candidates = explicitMatches.length > 0 ? explicitMatches : matches;
775
- if (candidates.length > 1) {
776
- throw new Error('Multiple matching crypto payment methods found');
765
+ /**
766
+ * Determines if the user is eligible for trialed sponsorship for the given chain and products.
767
+ * The user is eligible if the chain supports sponsorship and the user has not trialed the provided products before.
768
+ *
769
+ * @param chainId - The chain ID
770
+ * @param products - The products to check eligibility for
771
+ * @param cryptoAuthMethod - The crypto authorization method of the selected payment method
772
+ * @returns True if the user is eligible for trialed sponsorship, false otherwise
773
+ */
774
+ #getIsEligibleForTrialedSponsorship(chainId, products, cryptoAuthMethod) {
775
+ const isSponsorshipSupported = this.#getChainSupportsSponsorship(chainId, products[0], cryptoAuthMethod);
776
+ // verify if the user has trialed the provided products before
777
+ const hasTrialedBefore = this.state.trialedProducts.some((product) => products.includes(product));
778
+ return isSponsorshipSupported && !hasTrialedBefore;
777
779
  }
778
- return candidates[0].method;
779
- }, _SubscriptionController_resolveCryptoPaymentMethodDefaults = function _SubscriptionController_resolveCryptoPaymentMethodDefaults(paymentMethod) {
780
- const productsPresent = paymentMethod.products !== undefined;
781
- const authPresent = paymentMethod.cryptoAuthMethod !== undefined;
782
- if (!productsPresent && !authPresent) {
780
+ /**
781
+ * Whether a trial should be requested for the given products and plan.
782
+ * True only when every product has `trialPeriodDays > 0` and has not
783
+ * already been trialed.
784
+ *
785
+ * @param products - The products to check.
786
+ * @param plan - The recurring interval to look up pricing for.
787
+ * @returns Whether a trial should be requested.
788
+ */
789
+ #getIsTrialRequested(products, plan) {
790
+ return products.every((productType) => {
791
+ if (this.state.trialedProducts.includes(productType)) {
792
+ return false;
793
+ }
794
+ const productPrice = this.#getProductPriceByProductAndPlan(productType, plan);
795
+ return productPrice.trialPeriodDays > 0;
796
+ });
797
+ }
798
+ #findCryptoPaymentMethod(productType, cryptoAuthMethod) {
799
+ const matches = [];
800
+ for (const paymentMethod of this.state.pricing?.paymentMethods ?? []) {
801
+ if (paymentMethod.type !== PAYMENT_TYPES.byCrypto) {
802
+ continue;
803
+ }
804
+ const resolved = this.#resolveCryptoPaymentMethodDefaults(paymentMethod);
805
+ if (!resolved) {
806
+ continue;
807
+ }
808
+ if (resolved.cryptoAuthMethod === cryptoAuthMethod &&
809
+ resolved.products.includes(productType)) {
810
+ matches.push({
811
+ method: paymentMethod,
812
+ explicit: resolved.explicit,
813
+ });
814
+ }
815
+ }
816
+ if (matches.length === 0) {
817
+ return undefined;
818
+ }
819
+ const explicitMatches = matches.filter((match) => match.explicit);
820
+ const candidates = explicitMatches.length > 0 ? explicitMatches : matches;
821
+ if (candidates.length > 1) {
822
+ throw new Error('Multiple matching crypto payment methods found');
823
+ }
824
+ return candidates[0].method;
825
+ }
826
+ /**
827
+ * Resolves omitted `products` / `cryptoAuthMethod` on a crypto pricing row.
828
+ * Legacy Shield + `erc20_approval` defaults apply only when both fields are
829
+ * absent. If `products` is present, the list must be non-empty and
830
+ * `cryptoAuthMethod` must be explicit; otherwise the row is ignored.
831
+ *
832
+ * @param paymentMethod - The crypto pricing row.
833
+ * @returns Resolved products and auth method, or `undefined` if the row is
834
+ * incomplete.
835
+ */
836
+ #resolveCryptoPaymentMethodDefaults(paymentMethod) {
837
+ const productsPresent = paymentMethod.products !== undefined;
838
+ const authPresent = paymentMethod.cryptoAuthMethod !== undefined;
839
+ if (!productsPresent && !authPresent) {
840
+ return {
841
+ products: [PRODUCT_TYPES.SHIELD],
842
+ cryptoAuthMethod: CRYPTO_AUTH_METHODS.ERC20_APPROVAL,
843
+ explicit: false,
844
+ };
845
+ }
846
+ if (!paymentMethod.products?.length || !paymentMethod.cryptoAuthMethod) {
847
+ return undefined;
848
+ }
783
849
  return {
784
- products: [PRODUCT_TYPES.SHIELD],
785
- cryptoAuthMethod: CRYPTO_AUTH_METHODS.ERC20_APPROVAL,
786
- explicit: false,
850
+ products: paymentMethod.products,
851
+ cryptoAuthMethod: paymentMethod.cryptoAuthMethod,
852
+ explicit: true,
787
853
  };
788
854
  }
789
- if (!paymentMethod.products?.length || !paymentMethod.cryptoAuthMethod) {
790
- return undefined;
855
+ /**
856
+ * Whether the given chain supports sponsorship for the product and auth method.
857
+ *
858
+ * @param chainId - The chain ID
859
+ * @param productType - The product type
860
+ * @param cryptoAuthMethod - The crypto authorization method to look up
861
+ * @returns True if the chain row has sponsorship enabled, false if it is explicitly not sponsored
862
+ * @throws If the crypto payment method or chain row is missing from pricing
863
+ */
864
+ #getChainSupportsSponsorship(chainId, productType, cryptoAuthMethod) {
865
+ const cryptoPaymentInfo = this.#findCryptoPaymentMethod(productType, cryptoAuthMethod);
866
+ if (!cryptoPaymentInfo) {
867
+ throw new Error('Chains payment info not found');
868
+ }
869
+ const chainPaymentInfo = cryptoPaymentInfo.chains?.find((chain) => chain.chainId === chainId);
870
+ if (!chainPaymentInfo) {
871
+ throw new Error('Invalid chain id');
872
+ }
873
+ return Boolean(chainPaymentInfo.isSponsorshipSupported);
791
874
  }
792
- return {
793
- products: paymentMethod.products,
794
- cryptoAuthMethod: paymentMethod.cryptoAuthMethod,
795
- explicit: true,
796
- };
797
- }, _SubscriptionController_getChainSupportsSponsorship = function _SubscriptionController_getChainSupportsSponsorship(chainId, productType, cryptoAuthMethod) {
798
- const cryptoPaymentInfo = __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_findCryptoPaymentMethod).call(this, productType, cryptoAuthMethod);
799
- if (!cryptoPaymentInfo) {
800
- throw new Error('Chains payment info not found');
801
- }
802
- const chainPaymentInfo = cryptoPaymentInfo.chains?.find((chain) => chain.chainId === chainId);
803
- if (!chainPaymentInfo) {
804
- throw new Error('Invalid chain id');
805
- }
806
- return Boolean(chainPaymentInfo.isSponsorshipSupported);
807
- }, _SubscriptionController_areTrialedProductsEqual = function _SubscriptionController_areTrialedProductsEqual(oldTrialedProducts, newTrialedProducts) {
808
- return (oldTrialedProducts.length === newTrialedProducts?.length &&
809
- oldTrialedProducts.every((product) => newTrialedProducts?.includes(product)));
810
- }, _SubscriptionController_areProductEntitlementsEqual = function _SubscriptionController_areProductEntitlementsEqual(oldProductEntitlements, newProductEntitlements) {
811
- return deepEqual(oldProductEntitlements ?? {}, newProductEntitlements ?? {});
812
- }, _SubscriptionController_areSubscriptionsEqual = function _SubscriptionController_areSubscriptionsEqual(oldSubs, newSubs) {
813
- // Check if arrays have different lengths
814
- if (oldSubs.length !== newSubs.length) {
815
- return false;
816
- }
817
- // Sort both arrays by id to ensure consistent comparison
818
- const sortedOldSubs = [...oldSubs].sort((a, b) => a.id.localeCompare(b.id));
819
- const sortedNewSubs = [...newSubs].sort((a, b) => a.id.localeCompare(b.id));
820
- // Check if all subscriptions are equal
821
- return sortedOldSubs.every((oldSub, index) => {
822
- const newSub = sortedNewSubs[index];
823
- return __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_isSubscriptionEqual).call(this, oldSub, newSub);
824
- });
825
- }, _SubscriptionController_isSubscriptionEqual = function _SubscriptionController_isSubscriptionEqual(oldSub, newSub) {
826
- // not equal if one is undefined and the other is defined
827
- if (!oldSub || !newSub) {
828
- if (!oldSub && !newSub) {
829
- return true;
830
- }
831
- return false;
832
- }
833
- return (__classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_stringifySubscription).call(this, oldSub) ===
834
- __classPrivateFieldGet(this, _SubscriptionController_instances, "m", _SubscriptionController_stringifySubscription).call(this, newSub));
835
- }, _SubscriptionController_stringifySubscription = function _SubscriptionController_stringifySubscription(subscription) {
836
- const subsWithSortedProducts = {
837
- ...subscription,
838
- // order the products by name
839
- products: [...subscription.products].sort((a, b) => a.name.localeCompare(b.name)),
840
- };
841
- return JSON.stringify(subsWithSortedProducts);
842
- };
843
- //# sourceMappingURL=SubscriptionController.mjs.map
875
+ /**
876
+ * Determines whether two trialed products arrays are equal by comparing all products in the arrays.
877
+ *
878
+ * @param oldTrialedProducts - The first trialed products array to compare.
879
+ * @param newTrialedProducts - The second trialed products array to compare.
880
+ * @returns True if the trialed products arrays are equal, false otherwise.
881
+ */
882
+ #areTrialedProductsEqual(oldTrialedProducts, newTrialedProducts) {
883
+ return (oldTrialedProducts.length === newTrialedProducts?.length &&
884
+ oldTrialedProducts.every((product) => newTrialedProducts?.includes(product)));
885
+ }
886
+ /**
887
+ * Compares product entitlement maps without treating object key order as a
888
+ * change. Insertion-order-sensitive `JSON.stringify` can otherwise treat an
889
+ * equivalent API payload as different and trigger `performSignOut`.
890
+ *
891
+ * @param oldProductEntitlements - Stored product entitlements.
892
+ * @param newProductEntitlements - Freshly fetched product entitlements.
893
+ * @returns True if the maps are equal regardless of key order.
894
+ */
895
+ #areProductEntitlementsEqual(oldProductEntitlements, newProductEntitlements) {
896
+ return deepEqual(oldProductEntitlements ?? {}, newProductEntitlements ?? {});
897
+ }
898
+ /**
899
+ * Determines whether two subscription arrays are equal by comparing all properties
900
+ * of each subscription in the arrays.
901
+ *
902
+ * @param oldSubs - The first subscription array to compare.
903
+ * @param newSubs - The second subscription array to compare.
904
+ * @returns True if the subscription arrays are equal, false otherwise.
905
+ */
906
+ #areSubscriptionsEqual(oldSubs, newSubs) {
907
+ // Check if arrays have different lengths
908
+ if (oldSubs.length !== newSubs.length) {
909
+ return false;
910
+ }
911
+ // Sort both arrays by id to ensure consistent comparison
912
+ const sortedOldSubs = [...oldSubs].sort((a, b) => a.id.localeCompare(b.id));
913
+ const sortedNewSubs = [...newSubs].sort((a, b) => a.id.localeCompare(b.id));
914
+ // Check if all subscriptions are equal
915
+ return sortedOldSubs.every((oldSub, index) => {
916
+ const newSub = sortedNewSubs[index];
917
+ return this.#isSubscriptionEqual(oldSub, newSub);
918
+ });
919
+ }
920
+ #isSubscriptionEqual(oldSub, newSub) {
921
+ // not equal if one is undefined and the other is defined
922
+ if (!oldSub || !newSub) {
923
+ if (!oldSub && !newSub) {
924
+ return true;
925
+ }
926
+ return false;
927
+ }
928
+ return (this.#stringifySubscription(oldSub) ===
929
+ this.#stringifySubscription(newSub));
930
+ }
931
+ #stringifySubscription(subscription) {
932
+ const subsWithSortedProducts = {
933
+ ...subscription,
934
+ // order the products by name
935
+ products: [...subscription.products].sort((a, b) => a.name.localeCompare(b.name)),
936
+ };
937
+ return JSON.stringify(subsWithSortedProducts);
938
+ }
939
+ }
940
+ //# sourceMappingURL=SubscriptionController.js.map