RecurlyEventHandler.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. const AnalyticsManager = require('../Analytics/AnalyticsManager')
  2. const SubscriptionEmailHandler = require('./SubscriptionEmailHandler')
  3. const { AI_ADD_ON_CODE } = require('./RecurlyEntities')
  4. const { ObjectId } = require('mongodb-legacy')
  5. const INVOICE_SUBSCRIPTION_LIMIT = 10
  6. async function sendRecurlyAnalyticsEvent(event, eventData) {
  7. const userId = _getUserId(eventData)
  8. if (!ObjectId.isValid(userId)) {
  9. return
  10. }
  11. switch (event) {
  12. case 'new_subscription_notification':
  13. await _sendSubscriptionStartedEvent(userId, eventData)
  14. break
  15. case 'updated_subscription_notification':
  16. await _sendSubscriptionUpdatedEvent(userId, eventData)
  17. break
  18. case 'canceled_subscription_notification':
  19. await _sendSubscriptionCancelledEvent(userId, eventData)
  20. break
  21. case 'expired_subscription_notification':
  22. await _sendSubscriptionExpiredEvent(userId, eventData)
  23. break
  24. case 'renewed_subscription_notification':
  25. await _sendSubscriptionRenewedEvent(userId, eventData)
  26. break
  27. case 'reactivated_account_notification':
  28. await _sendSubscriptionReactivatedEvent(userId, eventData)
  29. break
  30. case 'paid_charge_invoice_notification':
  31. if (
  32. eventData.invoice.state === 'paid' &&
  33. eventData.invoice.total_in_cents > 0
  34. ) {
  35. await _sendInvoicePaidEvent(userId, eventData)
  36. }
  37. break
  38. case 'closed_invoice_notification':
  39. if (
  40. eventData.invoice.state === 'collected' &&
  41. eventData.invoice.total_in_cents > 0
  42. ) {
  43. await _sendInvoicePaidEvent(userId, eventData)
  44. }
  45. break
  46. }
  47. }
  48. async function _sendSubscriptionStartedEvent(userId, eventData) {
  49. const { planCode, quantity, state, isTrial, hasAiAddOn, subscriptionId } =
  50. _getSubscriptionData(eventData)
  51. AnalyticsManager.recordEventForUserInBackground(
  52. userId,
  53. 'subscription-started',
  54. {
  55. plan_code: planCode,
  56. quantity,
  57. is_trial: isTrial,
  58. has_ai_add_on: hasAiAddOn,
  59. subscriptionId,
  60. }
  61. )
  62. AnalyticsManager.setUserPropertyForUserInBackground(
  63. userId,
  64. 'subscription-plan-code',
  65. planCode
  66. )
  67. AnalyticsManager.setUserPropertyForUserInBackground(
  68. userId,
  69. 'subscription-state',
  70. state
  71. )
  72. AnalyticsManager.setUserPropertyForUserInBackground(
  73. userId,
  74. 'subscription-is-trial',
  75. isTrial
  76. )
  77. if (isTrial) {
  78. await SubscriptionEmailHandler.sendTrialOnboardingEmail(userId, planCode)
  79. }
  80. }
  81. async function _sendSubscriptionUpdatedEvent(userId, eventData) {
  82. const { planCode, quantity, state, isTrial, hasAiAddOn, subscriptionId } =
  83. _getSubscriptionData(eventData)
  84. AnalyticsManager.recordEventForUserInBackground(
  85. userId,
  86. 'subscription-updated',
  87. {
  88. plan_code: planCode,
  89. quantity,
  90. is_trial: isTrial,
  91. has_ai_add_on: hasAiAddOn,
  92. subscriptionId,
  93. }
  94. )
  95. AnalyticsManager.setUserPropertyForUserInBackground(
  96. userId,
  97. 'subscription-plan-code',
  98. planCode
  99. )
  100. AnalyticsManager.setUserPropertyForUserInBackground(
  101. userId,
  102. 'subscription-state',
  103. state
  104. )
  105. AnalyticsManager.setUserPropertyForUserInBackground(
  106. userId,
  107. 'subscription-is-trial',
  108. isTrial
  109. )
  110. }
  111. async function _sendSubscriptionCancelledEvent(userId, eventData) {
  112. const { planCode, quantity, state, isTrial, hasAiAddOn, subscriptionId } =
  113. _getSubscriptionData(eventData)
  114. AnalyticsManager.recordEventForUserInBackground(
  115. userId,
  116. 'subscription-cancelled',
  117. {
  118. plan_code: planCode,
  119. quantity,
  120. is_trial: isTrial,
  121. has_ai_add_on: hasAiAddOn,
  122. subscriptionId,
  123. }
  124. )
  125. AnalyticsManager.setUserPropertyForUserInBackground(
  126. userId,
  127. 'subscription-state',
  128. state
  129. )
  130. AnalyticsManager.setUserPropertyForUserInBackground(
  131. userId,
  132. 'subscription-is-trial',
  133. isTrial
  134. )
  135. }
  136. async function _sendSubscriptionExpiredEvent(userId, eventData) {
  137. const { planCode, quantity, state, isTrial, hasAiAddOn, subscriptionId } =
  138. _getSubscriptionData(eventData)
  139. AnalyticsManager.recordEventForUserInBackground(
  140. userId,
  141. 'subscription-expired',
  142. {
  143. plan_code: planCode,
  144. quantity,
  145. is_trial: isTrial,
  146. has_ai_add_on: hasAiAddOn,
  147. subscriptionId,
  148. }
  149. )
  150. AnalyticsManager.setUserPropertyForUserInBackground(
  151. userId,
  152. 'subscription-plan-code',
  153. planCode
  154. )
  155. AnalyticsManager.setUserPropertyForUserInBackground(
  156. userId,
  157. 'subscription-state',
  158. state
  159. )
  160. AnalyticsManager.setUserPropertyForUserInBackground(
  161. userId,
  162. 'subscription-is-trial',
  163. isTrial
  164. )
  165. }
  166. async function _sendSubscriptionRenewedEvent(userId, eventData) {
  167. const { planCode, quantity, state, isTrial, hasAiAddOn, subscriptionId } =
  168. _getSubscriptionData(eventData)
  169. AnalyticsManager.recordEventForUserInBackground(
  170. userId,
  171. 'subscription-renewed',
  172. {
  173. plan_code: planCode,
  174. quantity,
  175. is_trial: isTrial,
  176. has_ai_add_on: hasAiAddOn,
  177. subscriptionId,
  178. }
  179. )
  180. AnalyticsManager.setUserPropertyForUserInBackground(
  181. userId,
  182. 'subscription-plan-code',
  183. planCode
  184. )
  185. AnalyticsManager.setUserPropertyForUserInBackground(
  186. userId,
  187. 'subscription-state',
  188. state
  189. )
  190. AnalyticsManager.setUserPropertyForUserInBackground(
  191. userId,
  192. 'subscription-is-trial',
  193. isTrial
  194. )
  195. }
  196. async function _sendSubscriptionReactivatedEvent(userId, eventData) {
  197. const { planCode, quantity, state, isTrial, hasAiAddOn, subscriptionId } =
  198. _getSubscriptionData(eventData)
  199. AnalyticsManager.recordEventForUserInBackground(
  200. userId,
  201. 'subscription-reactivated',
  202. {
  203. plan_code: planCode,
  204. quantity,
  205. has_ai_add_on: hasAiAddOn,
  206. subscriptionId,
  207. }
  208. )
  209. AnalyticsManager.setUserPropertyForUserInBackground(
  210. userId,
  211. 'subscription-plan-code',
  212. planCode
  213. )
  214. AnalyticsManager.setUserPropertyForUserInBackground(
  215. userId,
  216. 'subscription-state',
  217. state
  218. )
  219. AnalyticsManager.setUserPropertyForUserInBackground(
  220. userId,
  221. 'subscription-is-trial',
  222. isTrial
  223. )
  224. }
  225. async function _sendInvoicePaidEvent(userId, eventData) {
  226. const invoice = eventData.invoice
  227. if (!invoice) {
  228. return
  229. }
  230. const invoiceNumber = invoice.invoice_number
  231. const currency = invoice.currency
  232. const totalInCents = invoice.total_in_cents
  233. const taxInCents = invoice.tax_in_cents
  234. const country = invoice.address?.country
  235. const collectionMethod = invoice.collection_method
  236. const subscriptionIds = {}
  237. invoice.subscription_ids?.forEach((e, idx) => {
  238. if (idx < INVOICE_SUBSCRIPTION_LIMIT) {
  239. subscriptionIds[`subscriptionId${idx + 1}`] = e
  240. }
  241. })
  242. AnalyticsManager.recordEventForUserInBackground(
  243. userId,
  244. 'subscription-invoice-collected',
  245. {
  246. invoiceNumber,
  247. currency,
  248. totalInCents,
  249. taxInCents,
  250. country,
  251. collectionMethod,
  252. ...subscriptionIds,
  253. }
  254. )
  255. AnalyticsManager.setUserPropertyForUserInBackground(
  256. userId,
  257. 'subscription-is-trial',
  258. false
  259. )
  260. }
  261. function _getUserId(eventData) {
  262. let userId
  263. if (eventData && eventData.account && eventData.account.account_code) {
  264. userId = eventData.account.account_code
  265. } else {
  266. throw new Error(
  267. 'account.account_code missing in event data to identity user ID'
  268. )
  269. }
  270. return userId
  271. }
  272. function _getSubscriptionData(eventData) {
  273. const isTrial =
  274. eventData.subscription.trial_started_at &&
  275. eventData.subscription.current_period_started_at &&
  276. eventData.subscription.trial_started_at.getTime() ===
  277. eventData.subscription.current_period_started_at.getTime()
  278. const hasAiAddOn =
  279. eventData.subscription.subscription_add_ons?.some(
  280. addOn => addOn.add_on_code === AI_ADD_ON_CODE
  281. ) ?? false
  282. return {
  283. planCode: eventData.subscription.plan.plan_code,
  284. quantity: eventData.subscription.quantity,
  285. state: eventData.subscription.state,
  286. subscriptionId: eventData.subscription.uuid,
  287. isTrial,
  288. hasAiAddOn,
  289. }
  290. }
  291. module.exports = {
  292. sendRecurlyAnalyticsEvent,
  293. }