Skip to content

Commit

Permalink
feat: calculate subscription status per subscription plan id
Browse files Browse the repository at this point in the history
  • Loading branch information
stfsy committed Sep 11, 2022
1 parent f5c7508 commit 9eb1e30
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 80 deletions.
63 changes: 33 additions & 30 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,19 +142,19 @@ class Subscriptions {
unit_price: subscription.unit_price,
quantity: subscription.quantity,
start_at: subscription.event_time,
}

const subscriptionModel = {
cancel_url: subscription.cancel_url,
checkout_id: subscription.checkout_id,
status: this._storage._arrayUnion(statusModel),
source: subscription.source,
update_url: subscription.update_url,
subscription_id: subscription.subscription_id,
subscription_plan_id: subscription.subscription_plan_id,
cancel_url: subscription.cancel_url,
checkout_id: subscription.checkout_id,
vendor_user_id: subscription.user_id,
}

const subscriptionModel = {
status: this._storage._arrayUnion(statusModel)
}

const flatModel = flattenObject(subscriptionModel, 'subscription')
await this._storage.update(ids, flatModel)
}
Expand All @@ -173,24 +173,24 @@ class Subscriptions {
const statusModel = {
alert_id: subscription.alert_id,
alert_name: subscription.alert_name,
cancel_url: subscription.cancel_url,
checkout_id: subscription.checkout_id,
currency: subscription.currency,
description: subscription.status,
next_bill_date: subscription.next_bill_date,
unit_price: subscription.new_unit_price,
quantity: subscription.new_quantity,
start_at: subscription.event_time,
}

const subscriptionModel = {
cancel_url: subscription.cancel_url,
checkout_id: subscription.checkout_id,
status: this._storage._arrayUnion(statusModel),
update_url: subscription.update_url,
subscription_id: subscription.subscription_id,
subscription_plan_id: subscription.subscription_plan_id,
vendor_user_id: subscription.user_id,
}

const subscriptionModel = {
status: this._storage._arrayUnion(statusModel),
}

const flatModel = flattenObject(subscriptionModel, 'subscription')
await this._storage.update(ids, flatModel)
}
Expand All @@ -214,11 +214,12 @@ class Subscriptions {
unit_price: subscription.unit_price,
quantity: subscription.quantity ? subscription.quantity : '',
start_at: subscription.cancellation_effective_date,
subscription_plan_id: subscription.subscription_plan_id,
vendor_user_id: subscription.user_id,
}

const subscriptionModel = {
status: this._storage._arrayUnion(statusModel),
subscription_plan_id: subscription.subscription_plan_id,
}

const flatModel = flattenObject(subscriptionModel, 'subscription')
Expand All @@ -232,7 +233,7 @@ class Subscriptions {
* @returns {boolean} true subscription is active
*/
async isSubscriptionActive(subscription, atDate = new Date()) {
return this._isOneOfSubscriptionsActive([subscription], atDate)
return this._isOneOfSubscriptionsActive(subscription, atDate)
}

/**
Expand Down Expand Up @@ -362,30 +363,32 @@ class Subscriptions {

/**
*
* @param {Array} subscriptions
* @param {Object} subscription
* @param {Date} [atDate=new Date()] date for the calculation
* @returns {boolean} true if an active subscription was given
*/
async _isOneOfSubscriptionsActive(subscriptions, atDate) {
async getAllSubscriptionsStatus(subscriptions, atDate = new Date()) {
const result = {}
const now = atDate.getTime()

const allStatus = subscriptions.map(subscription => {
// filter all status object that have a start date in the past
// and sort by start date descending
return subscription.status
.filter(status => new Date(status.start_at).getTime() < now)
.sort((a, b) => new Date(b.start_at).getTime() - new Date(a.start_at).getTime())
})
const allStatusBySubscriptionPlan = subscriptions.status.reduce((context, next) => {
if (new Date(next.start_at).getTime() < now) {

// now compare the first status element of each subscription and take the latest one
let latestStatus = null
for (const status of allStatus) {
if (!latestStatus || new Date(latestStatus.start_at).getTime() < new Date(status[0].start_at).getTime()) {
latestStatus = status[0]
if (!Array.isArray(context[next.subscription_plan_id])) {
context[next.subscription_plan_id] = []
}
context[next.subscription_plan_id].push(next)
}
}
return context
}, {})

Object.entries(allStatusBySubscriptionPlan).forEach(([subscriptionPlanId, list]) => {
list.sort((a, b) => new Date(a.start_at).getTime() - new Date(b.start_at).getTime())

result[subscriptionPlanId] = this._isSubscriptionStatusCurrentlyActive(list.at(-1))
})

return this._isSubscriptionStatusCurrentlyActive(latestStatus)
return result
}

/**
Expand Down
22 changes: 11 additions & 11 deletions test-e2e/spec/subscription.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ async function createNewSubscription(page) {
}

async function updatePaymentMethod(page, subscription) {
await page.goto(subscription.update_url)
await page.goto(subscription.status[1].update_url)
await page.locator('[data-testid="CARD_PaymentSelectionButton"]').click()
await page.locator('[data-testid="cardNumberInput"]').click()
await page.locator('[data-testid="cardNumberInput"]').fill('4000 0038 0000 0446')
Expand All @@ -77,7 +77,7 @@ async function updatePaymentMethod(page, subscription) {
}

async function addSubscriptionCancelledStatus(page, subscription) {
await page.goto(subscription.cancel_url)
await page.goto(subscription.status[1].cancel_url)
await page.locator('text=Cancel Subscription').click()
}

Expand All @@ -91,7 +91,7 @@ function validateStatus(status) {
chaiExpect(status.description).to.match(/active|deleted/)
chaiExpect(status.unit_price).to.match(/[0-9]{1,2}\.[0-9]{2}/)
chaiExpect(status.quantity).to.match(/[0-9]{1}/)
chaiExpect(status.start_at).to.match(/^[0-9]{4}-[0-9]{2}-[0-9]{2}/)
chaiExpect(status.event_time).to.match(/^[0-9]{4}-[0-9]{2}-[0-9]{2}/)
}

test('test receives and stores webhooks', async ({ page }) => {
Expand All @@ -108,8 +108,8 @@ test('test receives and stores webhooks', async ({ page }) => {
validateStatus(subscription.status[1])

// .. and check it is active
let isActive = await subscriptions.isSubscriptionActive(subscription)
expect(isActive).toBeTruthy()
let sub = await subscriptions.getAllSubscriptionsStatus(subscription)
expect(sub['33590']).toBeTruthy()

// update payment method ...
await updatePaymentMethod(page, subscription)
Expand All @@ -122,8 +122,8 @@ test('test receives and stores webhooks', async ({ page }) => {
expect(subscription.payments).toHaveLength(1)

// .. and still active
isActive = await subscriptions.isSubscriptionActive(subscription)
expect(isActive).toBeTruthy()
sub = await subscriptions.getAllSubscriptionsStatus(subscription)
expect(sub['33590']).toBeTruthy()

// cancel subscription ...
await addSubscriptionCancelledStatus(page, subscription)
Expand All @@ -136,10 +136,10 @@ test('test receives and stores webhooks', async ({ page }) => {

validateStatus(subscription.status[2])

isActive = await subscriptions.isSubscriptionActive(subscription)
expect(isActive).toBeTruthy()
sub = await subscriptions.getAllSubscriptionsStatus(subscription)
expect(sub['33590']).toBeTruthy()

// and not active next month (35 days)
isActive = await subscriptions.isSubscriptionActive(subscription, new Date(new Date().getTime() + 1000 * 3600 * 24 * 35))
expect(isActive).toBeFalsy()
sub = await subscriptions.getAllSubscriptionsStatus(subscription, new Date(new Date().getTime() + 1000 * 3600 * 24 * 35))
expect(sub['33590']).toBeFalsy()
})

0 comments on commit 9eb1e30

Please sign in to comment.