diff --git a/packages/subscription-controller/CHANGELOG.md b/packages/subscription-controller/CHANGELOG.md index 5bd5f4153a4..be5e286ae56 100644 --- a/packages/subscription-controller/CHANGELOG.md +++ b/packages/subscription-controller/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Added `lastSubscription` in state returned from `getSubscriptions` method ([#7110](https://github.com/MetaMask/core/pull/7110)) + ## [3.3.0] ### Changed diff --git a/packages/subscription-controller/src/SubscriptionController.test.ts b/packages/subscription-controller/src/SubscriptionController.test.ts index 6b91f7218c8..d5e7bceb333 100644 --- a/packages/subscription-controller/src/SubscriptionController.test.ts +++ b/packages/subscription-controller/src/SubscriptionController.test.ts @@ -80,6 +80,7 @@ const MOCK_SUBSCRIPTION: Subscription = { last4: '1234', }, }, + isEligibleForSupport: true, }; const MOCK_PRODUCT_PRICE: ProductPricing = { @@ -545,6 +546,30 @@ describe('SubscriptionController', () => { }, ); }); + + it('should update state when lastSubscription changes from undefined to defined', async () => { + await withController( + { + state: { + lastSubscription: undefined, + }, + }, + async ({ controller, mockService }) => { + mockService.getSubscriptions.mockResolvedValue({ + customerId: 'cus_1', + subscriptions: [], + trialedProducts: [], + lastSubscription: MOCK_SUBSCRIPTION, + }); + + await controller.getSubscriptions(); + + expect(controller.state.lastSubscription).toStrictEqual( + MOCK_SUBSCRIPTION, + ); + }, + ); + }); }); describe('getSubscriptionByProduct', () => { @@ -1251,7 +1276,6 @@ describe('SubscriptionController', () => { ), ).toMatchInlineSnapshot(` Object { - "subscriptions": Array [], "trialedProducts": Array [], } `); diff --git a/packages/subscription-controller/src/SubscriptionController.ts b/packages/subscription-controller/src/SubscriptionController.ts index 12d56ad3e9d..00e6c76665e 100644 --- a/packages/subscription-controller/src/SubscriptionController.ts +++ b/packages/subscription-controller/src/SubscriptionController.ts @@ -47,7 +47,8 @@ export type SubscriptionControllerState = { trialedProducts: ProductType[]; subscriptions: Subscription[]; pricing?: PricingResponse; - + /** The last subscription that user has subscribed to if any. */ + lastSubscription?: Subscription; /** * The last selected payment method for the user. * This is used to display the last selected payment method in the UI. @@ -194,7 +195,13 @@ export function getDefaultSubscriptionControllerState(): SubscriptionControllerS const subscriptionControllerMetadata: StateMetadata = { subscriptions: { - includeInStateLogs: true, + includeInStateLogs: false, + persist: true, + includeInDebugSnapshot: false, + usedInUi: true, + }, + lastSubscription: { + includeInStateLogs: false, persist: true, includeInDebugSnapshot: false, usedInUi: true, @@ -342,10 +349,12 @@ export class SubscriptionController extends StaticIntervalPollingController()< const currentSubscriptions = this.state.subscriptions; const currentTrialedProducts = this.state.trialedProducts; const currentCustomerId = this.state.customerId; + const currentLastSubscription = this.state.lastSubscription; const { customerId: newCustomerId, subscriptions: newSubscriptions, trialedProducts: newTrialedProducts, + lastSubscription: newLastSubscription, } = await this.#subscriptionService.getSubscriptions(); // check if the new subscriptions are different from the current subscriptions @@ -358,6 +367,11 @@ export class SubscriptionController extends StaticIntervalPollingController()< currentTrialedProducts, newTrialedProducts, ); + // check if the new last subscription is different from the current last subscription + const isLastSubscriptionEqual = this.#isSubscriptionEqual( + currentLastSubscription, + newLastSubscription, + ); const areCustomerIdsEqual = currentCustomerId === newCustomerId; @@ -365,6 +379,7 @@ export class SubscriptionController extends StaticIntervalPollingController()< // this prevents unnecessary state updates events, easier for the clients to handle if ( !areSubscriptionsEqual || + !isLastSubscriptionEqual || !areTrialedProductsEqual || !areCustomerIdsEqual ) { @@ -372,6 +387,7 @@ export class SubscriptionController extends StaticIntervalPollingController()< state.subscriptions = newSubscriptions; state.customerId = newCustomerId; state.trialedProducts = newTrialedProducts; + state.lastSubscription = newLastSubscription; }); this.#shouldCallRefreshAuthToken = true; } @@ -891,13 +907,25 @@ export class SubscriptionController extends StaticIntervalPollingController()< // Check if all subscriptions are equal return sortedOldSubs.every((oldSub, index) => { const newSub = sortedNewSubs[index]; - return ( - this.#stringifySubscription(oldSub) === - this.#stringifySubscription(newSub) - ); + return this.#isSubscriptionEqual(oldSub, newSub); }); } + #isSubscriptionEqual(oldSub?: Subscription, newSub?: Subscription): boolean { + // not equal if one is undefined and the other is defined + if (!oldSub || !newSub) { + if (!oldSub && !newSub) { + return true; + } + return false; + } + + return ( + this.#stringifySubscription(oldSub) === + this.#stringifySubscription(newSub) + ); + } + #stringifySubscription(subscription: Subscription): string { const subsWithSortedProducts = { ...subscription, diff --git a/packages/subscription-controller/src/SubscriptionService.test.ts b/packages/subscription-controller/src/SubscriptionService.test.ts index 1a0b26a09cc..7af56904b30 100644 --- a/packages/subscription-controller/src/SubscriptionService.test.ts +++ b/packages/subscription-controller/src/SubscriptionService.test.ts @@ -53,6 +53,7 @@ const MOCK_SUBSCRIPTION: Subscription = { last4: '1234', }, }, + isEligibleForSupport: true, }; const MOCK_ACCESS_TOKEN = 'mock-access-token-12345'; diff --git a/packages/subscription-controller/src/types.ts b/packages/subscription-controller/src/types.ts index 518058c6675..f007d2de68a 100644 --- a/packages/subscription-controller/src/types.ts +++ b/packages/subscription-controller/src/types.ts @@ -79,6 +79,12 @@ export type Subscription = { trialEnd?: string; // ISO 8601 /** Crypto payment only: next billing cycle date (e.g after 12 months) */ endDate?: string; // ISO 8601 + /** The date the subscription was canceled. */ + canceledAt?: string; // ISO 8601 + /** The date the subscription was marked as inactive (paused/past_due/canceled). */ + inactiveAt?: string; // ISO 8601 + /** Whether the user is eligible for support features (priority support and filing claims). True for active subscriptions and inactive subscriptions within grace period. */ + isEligibleForSupport: boolean; billingCycles?: number; }; @@ -110,6 +116,8 @@ export type GetSubscriptionsResponse = { customerId?: string; subscriptions: Subscription[]; trialedProducts: ProductType[]; + /** The last subscription that user has subscribed to if any. */ + lastSubscription?: Subscription; }; export type StartSubscriptionRequest = {