RecurlyEventHandler.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  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 'subscription_paused_notification':
  31. await _sendSubscriptionPausedEvent(userId, eventData)
  32. break
  33. case 'subscription_resumed_notification':
  34. // 'resumed' here means resumed from pause
  35. await _sendSubscriptionResumedEvent(userId, eventData)
  36. break
  37. case 'paid_charge_invoice_notification':
  38. if (
  39. eventData.invoice.state === 'paid' &&
  40. eventData.invoice.total_in_cents > 0
  41. ) {
  42. await _sendInvoicePaidEvent(userId, eventData)
  43. }
  44. break
  45. case 'closed_invoice_notification':
  46. if (
  47. eventData.invoice.state === 'collected' &&
  48. eventData.invoice.total_in_cents > 0
  49. ) {
  50. await _sendInvoicePaidEvent(userId, eventData)
  51. }
  52. break
  53. }
  54. }
  55. async function _sendSubscriptionResumedEvent(userId, eventData) {
  56. const { planCode, state, subscriptionId } = _getSubscriptionData(eventData)
  57. AnalyticsManager.recordEventForUserInBackground(
  58. userId,
  59. 'subscription-resumed',
  60. {
  61. plan_code: planCode,
  62. subscriptionId,
  63. }
  64. )
  65. AnalyticsManager.setUserPropertyForUserInBackground(
  66. userId,
  67. 'subscription-state',
  68. state
  69. )
  70. }
  71. async function _sendSubscriptionPausedEvent(userId, eventData) {
  72. const { planCode, state, subscriptionId } = _getSubscriptionData(eventData)
  73. const pauseLength = eventData.subscription.remaining_pause_cycles
  74. AnalyticsManager.recordEventForUserInBackground(
  75. userId,
  76. 'subscription-paused',
  77. {
  78. pause_length: pauseLength,
  79. plan_code: planCode,
  80. subscriptionId,
  81. }
  82. )
  83. AnalyticsManager.setUserPropertyForUserInBackground(
  84. userId,
  85. 'subscription-state',
  86. state
  87. )
  88. }
  89. async function _sendSubscriptionStartedEvent(userId, eventData) {
  90. const { planCode, quantity, state, isTrial, hasAiAddOn, subscriptionId } =
  91. _getSubscriptionData(eventData)
  92. AnalyticsManager.recordEventForUserInBackground(
  93. userId,
  94. 'subscription-started',
  95. {
  96. plan_code: planCode,
  97. quantity,
  98. is_trial: isTrial,
  99. has_ai_add_on: hasAiAddOn,
  100. subscriptionId,
  101. }
  102. )
  103. AnalyticsManager.setUserPropertyForUserInBackground(
  104. userId,
  105. 'subscription-plan-code',
  106. planCode
  107. )
  108. AnalyticsManager.setUserPropertyForUserInBackground(
  109. userId,
  110. 'subscription-state',
  111. state
  112. )
  113. AnalyticsManager.setUserPropertyForUserInBackground(
  114. userId,
  115. 'subscription-is-trial',
  116. isTrial
  117. )
  118. if (isTrial) {
  119. await SubscriptionEmailHandler.sendTrialOnboardingEmail(userId, planCode)
  120. }
  121. }
  122. async function _sendSubscriptionUpdatedEvent(userId, eventData) {
  123. const { planCode, quantity, state, isTrial, hasAiAddOn, subscriptionId } =
  124. _getSubscriptionData(eventData)
  125. AnalyticsManager.recordEventForUserInBackground(
  126. userId,
  127. 'subscription-updated',
  128. {
  129. plan_code: planCode,
  130. quantity,
  131. is_trial: isTrial,
  132. has_ai_add_on: hasAiAddOn,
  133. subscriptionId,
  134. }
  135. )
  136. AnalyticsManager.setUserPropertyForUserInBackground(
  137. userId,
  138. 'subscription-plan-code',
  139. planCode
  140. )
  141. AnalyticsManager.setUserPropertyForUserInBackground(
  142. userId,
  143. 'subscription-state',
  144. state
  145. )
  146. AnalyticsManager.setUserPropertyForUserInBackground(
  147. userId,
  148. 'subscription-is-trial',
  149. isTrial
  150. )
  151. }
  152. async function _sendSubscriptionCancelledEvent(userId, eventData) {
  153. const { planCode, quantity, state, isTrial, hasAiAddOn, subscriptionId } =
  154. _getSubscriptionData(eventData)
  155. AnalyticsManager.recordEventForUserInBackground(
  156. userId,
  157. 'subscription-cancelled',
  158. {
  159. plan_code: planCode,
  160. quantity,
  161. is_trial: isTrial,
  162. has_ai_add_on: hasAiAddOn,
  163. subscriptionId,
  164. }
  165. )
  166. AnalyticsManager.setUserPropertyForUserInBackground(
  167. userId,
  168. 'subscription-state',
  169. state
  170. )
  171. AnalyticsManager.setUserPropertyForUserInBackground(
  172. userId,
  173. 'subscription-is-trial',
  174. isTrial
  175. )
  176. }
  177. async function _sendSubscriptionExpiredEvent(userId, eventData) {
  178. const { planCode, quantity, state, isTrial, hasAiAddOn, subscriptionId } =
  179. _getSubscriptionData(eventData)
  180. AnalyticsManager.recordEventForUserInBackground(
  181. userId,
  182. 'subscription-expired',
  183. {
  184. plan_code: planCode,
  185. quantity,
  186. is_trial: isTrial,
  187. has_ai_add_on: hasAiAddOn,
  188. subscriptionId,
  189. }
  190. )
  191. AnalyticsManager.setUserPropertyForUserInBackground(
  192. userId,
  193. 'subscription-plan-code',
  194. planCode
  195. )
  196. AnalyticsManager.setUserPropertyForUserInBackground(
  197. userId,
  198. 'subscription-state',
  199. state
  200. )
  201. AnalyticsManager.setUserPropertyForUserInBackground(
  202. userId,
  203. 'subscription-is-trial',
  204. isTrial
  205. )
  206. }
  207. async function _sendSubscriptionRenewedEvent(userId, eventData) {
  208. const { planCode, quantity, state, isTrial, hasAiAddOn, subscriptionId } =
  209. _getSubscriptionData(eventData)
  210. AnalyticsManager.recordEventForUserInBackground(
  211. userId,
  212. 'subscription-renewed',
  213. {
  214. plan_code: planCode,
  215. quantity,
  216. is_trial: isTrial,
  217. has_ai_add_on: hasAiAddOn,
  218. subscriptionId,
  219. }
  220. )
  221. AnalyticsManager.setUserPropertyForUserInBackground(
  222. userId,
  223. 'subscription-plan-code',
  224. planCode
  225. )
  226. AnalyticsManager.setUserPropertyForUserInBackground(
  227. userId,
  228. 'subscription-state',
  229. state
  230. )
  231. AnalyticsManager.setUserPropertyForUserInBackground(
  232. userId,
  233. 'subscription-is-trial',
  234. isTrial
  235. )
  236. }
  237. async function _sendSubscriptionReactivatedEvent(userId, eventData) {
  238. const { planCode, quantity, state, isTrial, hasAiAddOn, subscriptionId } =
  239. _getSubscriptionData(eventData)
  240. AnalyticsManager.recordEventForUserInBackground(
  241. userId,
  242. 'subscription-reactivated',
  243. {
  244. plan_code: planCode,
  245. quantity,
  246. has_ai_add_on: hasAiAddOn,
  247. subscriptionId,
  248. }
  249. )
  250. AnalyticsManager.setUserPropertyForUserInBackground(
  251. userId,
  252. 'subscription-plan-code',
  253. planCode
  254. )
  255. AnalyticsManager.setUserPropertyForUserInBackground(
  256. userId,
  257. 'subscription-state',
  258. state
  259. )
  260. AnalyticsManager.setUserPropertyForUserInBackground(
  261. userId,
  262. 'subscription-is-trial',
  263. isTrial
  264. )
  265. }
  266. async function _sendInvoicePaidEvent(userId, eventData) {
  267. const invoice = eventData.invoice
  268. if (!invoice) {
  269. return
  270. }
  271. const invoiceNumber = invoice.invoice_number
  272. const currency = invoice.currency
  273. const totalInCents = invoice.total_in_cents
  274. const taxInCents = invoice.tax_in_cents
  275. const country = invoice.address?.country
  276. const collectionMethod = invoice.collection_method
  277. const subscriptionIds = {}
  278. invoice.subscription_ids?.forEach((e, idx) => {
  279. if (idx < INVOICE_SUBSCRIPTION_LIMIT) {
  280. subscriptionIds[`subscriptionId${idx + 1}`] = e
  281. }
  282. })
  283. AnalyticsManager.recordEventForUserInBackground(
  284. userId,
  285. 'subscription-invoice-collected',
  286. {
  287. invoiceNumber,
  288. currency,
  289. totalInCents,
  290. taxInCents,
  291. country,
  292. collectionMethod,
  293. ...subscriptionIds,
  294. }
  295. )
  296. AnalyticsManager.setUserPropertyForUserInBackground(
  297. userId,
  298. 'subscription-is-trial',
  299. false
  300. )
  301. }
  302. function _getUserId(eventData) {
  303. let userId
  304. if (eventData && eventData.account && eventData.account.account_code) {
  305. userId = eventData.account.account_code
  306. } else {
  307. throw new Error(
  308. 'account.account_code missing in event data to identity user ID'
  309. )
  310. }
  311. return userId
  312. }
  313. function _getSubscriptionData(eventData) {
  314. const isTrial =
  315. eventData.subscription.trial_started_at &&
  316. eventData.subscription.current_period_started_at &&
  317. eventData.subscription.trial_started_at.getTime() ===
  318. eventData.subscription.current_period_started_at.getTime()
  319. const hasAiAddOn =
  320. eventData.subscription.subscription_add_ons?.some(
  321. addOn => addOn.add_on_code === AI_ADD_ON_CODE
  322. ) ?? false
  323. return {
  324. planCode: eventData.subscription.plan.plan_code,
  325. quantity: eventData.subscription.quantity,
  326. state: eventData.subscription.state,
  327. subscriptionId: eventData.subscription.uuid,
  328. isTrial,
  329. hasAiAddOn,
  330. }
  331. }
  332. module.exports = {
  333. sendRecurlyAnalyticsEvent,
  334. }