RecurlyClient.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. const recurly = require('recurly')
  2. const Settings = require('@overleaf/settings')
  3. const logger = require('@overleaf/logger')
  4. const { callbackify } = require('util')
  5. const UserGetter = require('../User/UserGetter')
  6. const recurlySettings = Settings.apis.recurly
  7. const recurlyApiKey = recurlySettings ? recurlySettings.apiKey : undefined
  8. const client = new recurly.Client(recurlyApiKey)
  9. async function getAccountForUserId(userId) {
  10. try {
  11. return await client.getAccount(`code-${userId}`)
  12. } catch (err) {
  13. if (err instanceof recurly.errors.NotFoundError) {
  14. // An expected error, we don't need to handle it, just return nothing
  15. logger.debug({ userId }, 'no recurly account found for user')
  16. } else {
  17. throw err
  18. }
  19. }
  20. }
  21. async function createAccountForUserId(userId) {
  22. const user = await UserGetter.promises.getUser(userId, {
  23. _id: 1,
  24. first_name: 1,
  25. last_name: 1,
  26. email: 1,
  27. })
  28. const accountCreate = {
  29. code: user._id.toString(),
  30. email: user.email,
  31. firstName: user.first_name,
  32. lastName: user.last_name,
  33. }
  34. const account = await client.createAccount(accountCreate)
  35. logger.debug({ userId, account }, 'created recurly account')
  36. return account
  37. }
  38. async function getSubscription(subscriptionId) {
  39. return await client.getSubscription(subscriptionId)
  40. }
  41. async function getSubscriptionByUuid(subscriptionUuid) {
  42. return await client.getSubscription('uuid-' + subscriptionUuid)
  43. }
  44. async function changeSubscription(subscriptionId, body) {
  45. const change = await client.createSubscriptionChange(subscriptionId, body)
  46. logger.debug(
  47. { subscriptionId, changeId: change.id },
  48. 'created subscription change'
  49. )
  50. return change
  51. }
  52. async function changeSubscriptionByUuid(subscriptionUuid, ...args) {
  53. return await changeSubscription('uuid-' + subscriptionUuid, ...args)
  54. }
  55. async function removeSubscriptionChange(subscriptionId) {
  56. const removed = await client.removeSubscriptionChange(subscriptionId)
  57. logger.debug({ subscriptionId }, 'removed pending subscription change')
  58. return removed
  59. }
  60. async function removeSubscriptionChangeByUuid(subscriptionUuid) {
  61. return await removeSubscriptionChange('uuid-' + subscriptionUuid)
  62. }
  63. async function reactivateSubscriptionByUuid(subscriptionUuid) {
  64. return await client.reactivateSubscription('uuid-' + subscriptionUuid)
  65. }
  66. async function cancelSubscriptionByUuid(subscriptionUuid) {
  67. try {
  68. return await client.cancelSubscription('uuid-' + subscriptionUuid)
  69. } catch (err) {
  70. if (err instanceof recurly.errors.ValidationError) {
  71. if (
  72. err.message === 'Only active and future subscriptions can be canceled.'
  73. ) {
  74. logger.debug(
  75. { subscriptionUuid },
  76. 'subscription cancellation failed, subscription not active'
  77. )
  78. }
  79. } else {
  80. throw err
  81. }
  82. }
  83. }
  84. function subscriptionIsCanceledOrExpired(subscription) {
  85. const state = subscription?.recurlyStatus?.state
  86. return state === 'canceled' || state === 'expired'
  87. }
  88. module.exports = {
  89. errors: recurly.errors,
  90. getAccountForUserId: callbackify(getAccountForUserId),
  91. createAccountForUserId: callbackify(createAccountForUserId),
  92. getSubscription: callbackify(getSubscription),
  93. getSubscriptionByUuid: callbackify(getSubscriptionByUuid),
  94. changeSubscription: callbackify(changeSubscription),
  95. changeSubscriptionByUuid: callbackify(changeSubscriptionByUuid),
  96. removeSubscriptionChange: callbackify(removeSubscriptionChange),
  97. removeSubscriptionChangeByUuid: callbackify(removeSubscriptionChangeByUuid),
  98. reactivateSubscriptionByUuid: callbackify(reactivateSubscriptionByUuid),
  99. cancelSubscriptionByUuid: callbackify(cancelSubscriptionByUuid),
  100. subscriptionIsCanceledOrExpired,
  101. promises: {
  102. getSubscription,
  103. getSubscriptionByUuid,
  104. getAccountForUserId,
  105. createAccountForUserId,
  106. changeSubscription,
  107. changeSubscriptionByUuid,
  108. removeSubscriptionChange,
  109. removeSubscriptionChangeByUuid,
  110. reactivateSubscriptionByUuid,
  111. cancelSubscriptionByUuid,
  112. },
  113. }