Merge pull request #3971 from overleaf/tm-migrate-recurly-cancellations

Migrate cancel/reactivatation of recurly subscription to node client

GitOrigin-RevId: 68a8a3eba7b653ec923d020a74a09e49efa8ba21
This commit is contained in:
Miguel Serrano 2021-05-05 15:38:20 +02:00 committed by Copybot
parent a5e905f730
commit 6583d6de21
6 changed files with 75 additions and 133 deletions

View file

@ -67,6 +67,29 @@ async function removeSubscriptionChangeByUuid(subscriptionUuid) {
return await removeSubscriptionChange('uuid-' + subscriptionUuid) return await removeSubscriptionChange('uuid-' + subscriptionUuid)
} }
async function reactivateSubscriptionByUuid(subscriptionUuid) {
return await client.reactivateSubscription('uuid-' + subscriptionUuid)
}
async function cancelSubscriptionByUuid(subscriptionUuid) {
try {
return await client.cancelSubscription('uuid-' + subscriptionUuid)
} catch (err) {
if (err instanceof recurly.errors.ValidationError) {
if (
err.message === 'Only active and future subscriptions can be canceled.'
) {
logger.log(
{ subscriptionUuid },
'subscription cancellation failed, subscription not active'
)
}
} else {
throw err
}
}
}
module.exports = { module.exports = {
errors: recurly.errors, errors: recurly.errors,
@ -77,6 +100,8 @@ module.exports = {
changeSubscriptionByUuid: callbackify(changeSubscriptionByUuid), changeSubscriptionByUuid: callbackify(changeSubscriptionByUuid),
removeSubscriptionChange: callbackify(removeSubscriptionChange), removeSubscriptionChange: callbackify(removeSubscriptionChange),
removeSubscriptionChangeByUuid: callbackify(removeSubscriptionChangeByUuid), removeSubscriptionChangeByUuid: callbackify(removeSubscriptionChangeByUuid),
reactivateSubscriptionByUuid: callbackify(reactivateSubscriptionByUuid),
cancelSubscriptionByUuid: callbackify(cancelSubscriptionByUuid),
promises: { promises: {
getSubscription, getSubscription,
@ -86,5 +111,7 @@ module.exports = {
changeSubscriptionByUuid, changeSubscriptionByUuid,
removeSubscriptionChange, removeSubscriptionChange,
removeSubscriptionChangeByUuid, removeSubscriptionChangeByUuid,
reactivateSubscriptionByUuid,
cancelSubscriptionByUuid,
}, },
} }

View file

@ -770,49 +770,6 @@ const RecurlyWrapper = {
) )
}, },
cancelSubscription(subscriptionId, callback) {
logger.log({ subscriptionId }, 'telling recurly to cancel subscription')
return RecurlyWrapper.apiRequest(
{
url: `subscriptions/${subscriptionId}/cancel`,
method: 'put',
},
function (error, response, body) {
if (error != null) {
return RecurlyWrapper._parseXml(body, function (_err, parsed) {
if (
__guard__(
parsed != null ? parsed.error : undefined,
x1 => x1.description
) === "A canceled subscription can't transition to canceled"
) {
// subscription already cancelled, not really an error, proceeding
return callback(null)
} else {
return callback(error)
}
})
} else {
return callback(null)
}
}
)
},
reactivateSubscription(subscriptionId, callback) {
logger.log(
{ subscriptionId },
'telling recurly to reactivating subscription'
)
return RecurlyWrapper.apiRequest(
{
url: `subscriptions/${subscriptionId}/reactivate`,
method: 'put',
},
(error, response, body) => callback(error)
)
},
redeemCoupon(account_code, coupon_code, callback) { redeemCoupon(account_code, coupon_code, callback) {
const data = { const data = {
account_code, account_code,

View file

@ -193,7 +193,7 @@ const SubscriptionHandler = {
) )
} }
if (hasSubscription) { if (hasSubscription) {
RecurlyWrapper.cancelSubscription( RecurlyClient.cancelSubscriptionByUuid(
subscription.recurlySubscription_id, subscription.recurlySubscription_id,
function (error) { function (error) {
if (error != null) { if (error != null) {
@ -242,7 +242,7 @@ const SubscriptionHandler = {
) )
} }
if (hasSubscription) { if (hasSubscription) {
RecurlyWrapper.reactivateSubscription( RecurlyClient.reactivateSubscriptionByUuid(
subscription.recurlySubscription_id, subscription.recurlySubscription_id,
function (error) { function (error) {
if (error != null) { if (error != null) {

View file

@ -264,4 +264,36 @@ describe('RecurlyClient', function () {
}) })
}) })
}) })
describe('reactivateSubscriptionByUuid', function () {
it('should attempt to reactivate the subscription', async function () {
this.client.reactivateSubscription = sinon
.stub()
.resolves(this.recurlySubscription)
await expect(
this.RecurlyClient.promises.reactivateSubscriptionByUuid(
this.subscription.uuid
)
).to.eventually.be.an.instanceOf(recurly.Subscription)
expect(this.client.reactivateSubscription).to.be.calledWith(
'uuid-' + this.subscription.uuid
)
})
})
describe('cancelSubscriptionByUuid', function () {
it('should attempt to cancel the subscription', async function () {
this.client.cancelSubscription = sinon
.stub()
.resolves(this.recurlySubscription)
await expect(
this.RecurlyClient.promises.cancelSubscriptionByUuid(
this.subscription.uuid
)
).to.eventually.be.an.instanceOf(recurly.Subscription)
expect(this.client.cancelSubscription).to.be.calledWith(
'uuid-' + this.subscription.uuid
)
})
})
}) })

View file

@ -334,86 +334,6 @@ describe('RecurlyWrapper', function () {
}) })
}) })
describe('cancelSubscription', function () {
beforeEach(function (done) {
this.recurlySubscriptionId = 'subscription-id-123'
this.apiRequest = sinon
.stub(this.RecurlyWrapper, 'apiRequest')
.callsFake((options, callback) => {
options.url.should.equal(
`subscriptions/${this.recurlySubscriptionId}/cancel`
)
options.method.should.equal('put')
return callback()
})
return this.RecurlyWrapper.cancelSubscription(
this.recurlySubscriptionId,
done
)
})
afterEach(function () {
return this.RecurlyWrapper.apiRequest.restore()
})
it('should send a cancel request to the API', function () {
return this.apiRequest.called.should.equal(true)
})
describe('when the subscription is already cancelled', function () {
beforeEach(function () {
this.RecurlyWrapper.apiRequest.restore()
this.recurlySubscriptionId = 'subscription-id-123'
return (this.apiRequest = sinon
.stub(this.RecurlyWrapper, 'apiRequest')
.callsFake((options, callback) => {
return callback(
new Error('woops'),
{},
"<error><description>A canceled subscription can't transition to canceled</description></error>"
)
}))
})
it('should not produce an error', function (done) {
return this.RecurlyWrapper.cancelSubscription(
this.recurlySubscriptionId,
err => {
expect(err).to.equal(null)
return done()
}
)
})
})
})
describe('reactivateSubscription', function () {
beforeEach(function (done) {
this.recurlySubscriptionId = 'subscription-id-123'
this.apiRequest = sinon
.stub(this.RecurlyWrapper, 'apiRequest')
.callsFake((options, callback) => {
options.url.should.equal(
`subscriptions/${this.recurlySubscriptionId}/reactivate`
)
options.method.should.equal('put')
return callback()
})
return this.RecurlyWrapper.reactivateSubscription(
this.recurlySubscriptionId,
done
)
})
afterEach(function () {
return this.RecurlyWrapper.apiRequest.restore()
})
it('should send a cancel request to the API', function () {
return this.apiRequest.called.should.equal(true)
})
})
describe('redeemCoupon', function () { describe('redeemCoupon', function () {
beforeEach(function (done) { beforeEach(function (done) {
this.recurlyAccountId = 'account-id-123' this.recurlyAccountId = 'account-id-123'

View file

@ -78,8 +78,6 @@ describe('SubscriptionHandler', function () {
getSubscription: sinon getSubscription: sinon
.stub() .stub()
.callsArgWith(2, null, this.activeRecurlySubscription), .callsArgWith(2, null, this.activeRecurlySubscription),
cancelSubscription: sinon.stub().callsArgWith(1),
reactivateSubscription: sinon.stub().callsArgWith(1),
redeemCoupon: sinon.stub().callsArgWith(2), redeemCoupon: sinon.stub().callsArgWith(2),
createSubscription: sinon createSubscription: sinon
.stub() .stub()
@ -95,6 +93,10 @@ describe('SubscriptionHandler', function () {
getSubscription: sinon getSubscription: sinon
.stub() .stub()
.yields(null, this.activeRecurlyClientSubscription), .yields(null, this.activeRecurlyClientSubscription),
reactivateSubscriptionByUuid: sinon
.stub()
.yields(null, this.activeRecurlyClientSubscription),
cancelSubscriptionByUuid: sinon.stub().yields(),
} }
this.SubscriptionUpdater = { this.SubscriptionUpdater = {
@ -389,7 +391,7 @@ describe('SubscriptionHandler', function () {
}) })
it('should redirect to the subscription dashboard', function () { it('should redirect to the subscription dashboard', function () {
this.RecurlyWrapper.cancelSubscription.called.should.equal(false) this.RecurlyClient.cancelSubscriptionByUuid.called.should.equal(false)
}) })
}) })
@ -405,8 +407,8 @@ describe('SubscriptionHandler', function () {
}) })
it('should cancel the subscription', function () { it('should cancel the subscription', function () {
this.RecurlyWrapper.cancelSubscription.called.should.equal(true) this.RecurlyClient.cancelSubscriptionByUuid.called.should.equal(true)
this.RecurlyWrapper.cancelSubscription this.RecurlyClient.cancelSubscriptionByUuid
.calledWith(this.subscription.recurlySubscription_id) .calledWith(this.subscription.recurlySubscription_id)
.should.equal(true) .should.equal(true)
}) })
@ -426,7 +428,9 @@ describe('SubscriptionHandler', function () {
}) })
it('should redirect to the subscription dashboard', function () { it('should redirect to the subscription dashboard', function () {
this.RecurlyWrapper.reactivateSubscription.called.should.equal(false) this.RecurlyClient.reactivateSubscriptionByUuid.called.should.equal(
false
)
}) })
it('should not send a notification email', function () { it('should not send a notification email', function () {
@ -446,8 +450,10 @@ describe('SubscriptionHandler', function () {
}) })
it('should reactivate the subscription', function () { it('should reactivate the subscription', function () {
this.RecurlyWrapper.reactivateSubscription.called.should.equal(true) this.RecurlyClient.reactivateSubscriptionByUuid.called.should.equal(
this.RecurlyWrapper.reactivateSubscription true
)
this.RecurlyClient.reactivateSubscriptionByUuid
.calledWith(this.subscription.recurlySubscription_id) .calledWith(this.subscription.recurlySubscription_id)
.should.equal(true) .should.equal(true)
}) })