diff --git a/services/web/app/src/Features/Subscription/CustomerIoPlanHelpers.mjs b/services/web/app/src/Features/Subscription/CustomerIoPlanHelpers.mjs index 4792c02d76..ce39382405 100644 --- a/services/web/app/src/Features/Subscription/CustomerIoPlanHelpers.mjs +++ b/services/web/app/src/Features/Subscription/CustomerIoPlanHelpers.mjs @@ -33,13 +33,13 @@ const INACTIVE_NEXT_RENEWAL_DATE_STATES = new Set([ const PENDING_CANCELLATION_STATES = new Set(['canceled', 'cancelled']) /** - * @param {MongoSubscription} subscription + * @param {Nullable} [subscription] * @returns {string} */ function getSubscriptionState(subscription) { return ( - subscription.recurlyStatus?.state || - subscription.paymentProvider?.state || + subscription?.recurlyStatus?.state || + subscription?.paymentProvider?.state || '' ) } @@ -585,6 +585,7 @@ function getPlanProperties({ individual_subscription: Boolean( individualSubscription && !individualSubscription.groupPlan ), + past_due: getSubscriptionState(individualSubscription) === 'past_due', } if (trialEndDate != null) properties.trial_end_date = trialEndDate diff --git a/services/web/test/unit/src/Subscription/CustomerIoPlanHelpers.test.mjs b/services/web/test/unit/src/Subscription/CustomerIoPlanHelpers.test.mjs new file mode 100644 index 0000000000..941a2e706a --- /dev/null +++ b/services/web/test/unit/src/Subscription/CustomerIoPlanHelpers.test.mjs @@ -0,0 +1,83 @@ +import { expect } from 'vitest' +import CustomerIoPlanHelpers from '../../../../app/src/Features/Subscription/CustomerIoPlanHelpers.mjs' + +describe('CustomerIoPlanHelpers', function () { + describe('getPlanProperties past_due', function () { + function buildArgs(individualSubscription) { + return { + bestSubscription: { type: 'individual' }, + individualSubscription, + individualPaymentRecord: null, + memberGroupSubscriptions: [], + managedGroupSubscriptions: [], + userIsMemberOfGroupSubscription: false, + hasCommons: false, + writefullData: null, + } + } + + it('is true when the Stripe subscription state is past_due', function () { + const properties = CustomerIoPlanHelpers.getPlanProperties( + buildArgs({ + planCode: 'collaborator', + groupPlan: false, + paymentProvider: { service: 'stripe-us', state: 'past_due' }, + }) + ) + expect(properties.past_due).to.equal(true) + }) + + it('is true when the Recurly subscription state is past_due', function () { + const properties = CustomerIoPlanHelpers.getPlanProperties( + buildArgs({ + planCode: 'collaborator', + groupPlan: false, + recurlyStatus: { state: 'past_due' }, + }) + ) + expect(properties.past_due).to.equal(true) + }) + + it("is true when a group admin's group subscription is past_due", function () { + const properties = CustomerIoPlanHelpers.getPlanProperties( + buildArgs({ + planCode: 'group_collaborator', + groupPlan: true, + paymentProvider: { service: 'stripe-us', state: 'past_due' }, + }) + ) + expect(properties.past_due).to.equal(true) + }) + + it('is false for an active Stripe subscription', function () { + const properties = CustomerIoPlanHelpers.getPlanProperties( + buildArgs({ + planCode: 'collaborator', + groupPlan: false, + paymentProvider: { service: 'stripe-us', state: 'active' }, + }) + ) + expect(properties.past_due).to.equal(false) + }) + + it('is false for cancelled, expired, paused, and trial states', function () { + for (const state of ['cancelled', 'expired', 'paused', 'trial']) { + const properties = CustomerIoPlanHelpers.getPlanProperties( + buildArgs({ + planCode: 'collaborator', + groupPlan: false, + paymentProvider: { service: 'stripe-us', state }, + }) + ) + expect(properties.past_due, `state=${state}`).to.equal(false) + } + }) + + it('is false when there is no individual subscription (free user / member-only)', function () { + const properties = CustomerIoPlanHelpers.getPlanProperties( + buildArgs(undefined) + ) + expect(properties.past_due).to.equal(false) + }) + }) +})