Skip to content

Commit

Permalink
feat(subscription-info): add cancel subscription method
Browse files Browse the repository at this point in the history
  • Loading branch information
stfsy committed Oct 11, 2022
1 parent 84ad151 commit 888c5f2
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 2 deletions.
46 changes: 45 additions & 1 deletion lib/subscription-info.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,23 @@ const { PLACEHOLDER_DESCRIPTION, UPCOMING_PAYMENTS_DESCRIPTION, ACTIVE_SUBSCRIPT

class SubscriptionInfo {

constructor(storagePath) {
/**
*
* @param {String} storagePath Path to the target collection
* @param {import('./paddle/api')} api paddle api instance
*/

constructor(storagePath, api = {}) {
this._storage = resource({ documentPath: storagePath, resourceName: 'wrapper' })
this._api = api
}

static get ERROR_MESSAGE_NOT_FOUND() {
return 'NOT_FOUND'
}

static get ERROR_SUBSCRIPTION_ALREADY_CANCELLED() {
return 'ERROR_SUBSCRIPTION_ALREADY_CANCELLED'
}

/**
Expand Down Expand Up @@ -67,6 +82,35 @@ class SubscriptionInfo {
}, {})
}

/**
*
* @param {Object} subscription
* @param {String} subscriptionPlanId the plan that should be cancelled
* @returns
*/
async cancelSubscription(subscription, subscriptionPlanId) {
const future = new Date(2099, 1).getTime()
const statusByPlanId = this._bySubscriptionId(subscription.status, future)
const startAndEndDates = await this.getStartAndEndDates(subscription, future)

// check whether plan id exists
if (!statusByPlanId[subscriptionPlanId] || statusByPlanId.length < 1) {
throw new Error(SubscriptionInfo.ERROR_MESSAGE_NOT_FOUND)
}

// check whether subscription was already cancelled
if (startAndEndDates[subscriptionPlanId].end) {
throw new Error(SubscriptionInfo.ERROR_SUBSCRIPTION_ALREADY_CANCELLED)
}

// cancel
const statusArray = statusByPlanId[subscriptionPlanId]
const subscriptionId = statusArray.at(0).subscription_id
const cancelled = await this._api.cancelSubscription({ subscription_id: subscriptionId })

return cancelled !== false && cancelled !== 'false'
}

/**
* @typedef {Object} StartAndEndDateBySubscription
* @property {StartAndEndDate} [any] start and end date of a subscription plan
Expand Down
41 changes: 40 additions & 1 deletion test-e2e/spec/subscription.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ const testPageRunner = require('../test-page-runner')

const { SubscriptionsHooks, SubscriptionInfo } = require('../../lib/index')
const subscriptions = new SubscriptionsHooks('api_client')
const subscriptionInfo = new SubscriptionInfo('api_client')

const storageResource = require('../../lib/firestore/nested-firestore-resource')
const storage = storageResource({ documentPath: 'api_client', resourceName: 'api_clients' })

let subscriptionInfo
let api

test.beforeAll(emulatorRunner.start)
Expand All @@ -26,6 +26,8 @@ test.beforeAll(async () => {
const API = await (await import('../../lib/paddle/api.js')).default
api = new API({ useSandbox: true, authCode: process.env.AUTH_CODE, vendorId: process.env.VENDOR_ID })
await api.init()

subscriptionInfo = new SubscriptionInfo('api_client', api)
})

test.beforeEach(async () => {
Expand Down Expand Up @@ -128,6 +130,43 @@ function validateStatus(status) {
chaiExpect(status.event_time).to.match(/^[0-9]{4}-[0-9]{2}-[0-9]{2}/)
}

test('test cancel via subscription info', async ({ page }) => {
// create new subscription and ...
await createNewSubscription(page)

// .. check it was stored and payment status was received ..
let { subscription } = await storage.get(['4815162342'])
expect(subscription).not.toBeFalsy()
expect(subscription.status).toHaveLength(2)
expect(subscription.payments).toHaveLength(1)

validateStatus(subscription.status[1])

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

// cancel subscription ...
({ subscription } = await storage.get(['4815162342']))
const success = await subscriptionInfo.cancelSubscription(subscription, '33590')
expect(success).toBeTruthy()
await page.waitForTimeout(10000);

// ... verify subscription still active today ...
({ subscription } = await storage.get(['4815162342']))
expect(subscription.status).toHaveLength(3)
expect(subscription.payments).toHaveLength(1)

validateStatus(subscription.status[2])

sub = await subscriptionInfo.getAllSubscriptionsStatus(subscription)
expect(sub['33590']).toBeTruthy()

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

test('test cancel via api', async ({ page }) => {
// create new subscription and ...
await createNewSubscription(page)
Expand Down
45 changes: 45 additions & 0 deletions test/spec/subscription-info.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,51 @@ describe('SubscriptionInfo', () => {
})
})

describe('.cancelSubscription', () => {
beforeEach(async () => {
const subscriptionId = uuid()
const createPayload = Object.assign({}, subscriptionCreated,
{
subscription_id: subscriptionId,
passthrough: JSON.stringify({ ids }),
event_time: new Date().toISOString()
}
)
await subscriptions.addSubscriptionCreatedStatus(createPayload)
})
it('throws if no subscription with plan was found', async () => {
const { subscription: sub } = await storage.get(ids)

try {
await subscriptionInfo.cancelSubscription(sub, '99')
throw new Error('Method must throw not found')
} catch (e) {
const message = e.message
expect(message).to.equal(SubscriptionInfo.ERROR_MESSAGE_NOT_FOUND)
}
})
it('throws if subscription was already cancelled', async () => {
const payload = Object.assign({}, subscriptionCancelled,
{
subscription_id: 'subscriptionId',
passthrough: JSON.stringify({ ids }),
cancellation_effective_date: new Date(new Date().getTime()).toISOString()
}
)

await subscriptions.addSubscriptionCancelledStatus(payload)
const { subscription: sub } = await storage.get(ids)

try {
await subscriptionInfo.cancelSubscription(sub, '8')
throw new Error('Method must throw "SubscriptionInfo.ERROR_SUBSCRIPTION_ALREADY_CANCELLED"')
} catch (e) {
const message = e.message
expect(message).to.equal(SubscriptionInfo.ERROR_SUBSCRIPTION_ALREADY_CANCELLED)
}
})
})

describe('.getAllSubscriptionsStatus', () => {
it('takes the most recent status into account', async () => {
const subscriptionId = uuid()
Expand Down

0 comments on commit 888c5f2

Please sign in to comment.