@omega.js/backend 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (993) hide show
  1. package/LICENSE +98 -0
  2. package/README.md +1053 -0
  3. package/bin/omega +2 -0
  4. package/bin/omega-backend +2 -0
  5. package/bin/omg +2 -0
  6. package/cli.js +3 -0
  7. package/dist/cli/command-table.js +268 -0
  8. package/dist/cli/commands/auth.js +259 -0
  9. package/dist/cli/commands/base-command.js +451 -0
  10. package/dist/cli/commands/build.js +27 -0
  11. package/dist/cli/commands/clean.js +13 -0
  12. package/dist/cli/commands/clear.js +11 -0
  13. package/dist/cli/commands/cwd.js +9 -0
  14. package/dist/cli/commands/deploy.js +205 -0
  15. package/dist/cli/commands/emulator-orphans.js +440 -0
  16. package/dist/cli/commands/emulator.js +1829 -0
  17. package/dist/cli/commands/firebase-init.js +101 -0
  18. package/dist/cli/commands/firestore.js +261 -0
  19. package/dist/cli/commands/indexes.js +51 -0
  20. package/dist/cli/commands/install.js +151 -0
  21. package/dist/cli/commands/logs.js +358 -0
  22. package/dist/cli/commands/mcp.js +41 -0
  23. package/dist/cli/commands/migrate-markers.js +176 -0
  24. package/dist/cli/commands/migrate-rules.js +90 -0
  25. package/dist/cli/commands/migrate.js +59 -0
  26. package/dist/cli/commands/serve.js +208 -0
  27. package/dist/cli/commands/setup-tests/base-test.js +174 -0
  28. package/dist/cli/commands/setup-tests/emulator-config.js +109 -0
  29. package/dist/cli/commands/setup-tests/env-runtime-config-deprecated.js +47 -0
  30. package/dist/cli/commands/setup-tests/firebase-admin.js +49 -0
  31. package/dist/cli/commands/setup-tests/firebase-auth.js +26 -0
  32. package/dist/cli/commands/setup-tests/firebase-cli.js +26 -0
  33. package/dist/cli/commands/setup-tests/firebase-functions.js +49 -0
  34. package/dist/cli/commands/setup-tests/firestore-indexes-file.js +72 -0
  35. package/dist/cli/commands/setup-tests/firestore-indexes-in-json.js +20 -0
  36. package/dist/cli/commands/setup-tests/firestore-indexes-required.js +108 -0
  37. package/dist/cli/commands/setup-tests/firestore-indexes-synced.js +236 -0
  38. package/dist/cli/commands/setup-tests/firestore-rules-file.js +115 -0
  39. package/dist/cli/commands/setup-tests/firestore-rules-in-json.js +49 -0
  40. package/dist/cli/commands/setup-tests/functions-package.js +44 -0
  41. package/dist/cli/commands/setup-tests/gcloud-cli.js +26 -0
  42. package/dist/cli/commands/setup-tests/gitignore.js +122 -0
  43. package/dist/cli/commands/setup-tests/helpers/merge-line-files.js +28 -0
  44. package/dist/cli/commands/setup-tests/helpers/required-indexes.js +109 -0
  45. package/dist/cli/commands/setup-tests/helpers/seed-campaigns.js +246 -0
  46. package/dist/cli/commands/setup-tests/helpers.js +72 -0
  47. package/dist/cli/commands/setup-tests/hosting-folder.js +23 -0
  48. package/dist/cli/commands/setup-tests/hosting-rewrites.js +52 -0
  49. package/dist/cli/commands/setup-tests/index.js +111 -0
  50. package/dist/cli/commands/setup-tests/is-firebase-project.js +21 -0
  51. package/dist/cli/commands/setup-tests/java-installed.js +27 -0
  52. package/dist/cli/commands/setup-tests/legacy-tests-cleanup.js +43 -0
  53. package/dist/cli/commands/setup-tests/marketing-campaigns-seeded.js +230 -0
  54. package/dist/cli/commands/setup-tests/node-version.js +60 -0
  55. package/dist/cli/commands/setup-tests/npm-project-scripts.js +42 -0
  56. package/dist/cli/commands/setup-tests/nvmrc-version.js +36 -0
  57. package/dist/cli/commands/setup-tests/omega-backend.js +42 -0
  58. package/dist/cli/commands/setup-tests/omega-config.js +84 -0
  59. package/dist/cli/commands/setup-tests/project-directories.js +34 -0
  60. package/dist/cli/commands/setup-tests/project-id-consistency.js +201 -0
  61. package/dist/cli/commands/setup-tests/public-html-files.js +22 -0
  62. package/dist/cli/commands/setup-tests/realtime-rules-file.js +69 -0
  63. package/dist/cli/commands/setup-tests/realtime-rules-in-json.js +20 -0
  64. package/dist/cli/commands/setup-tests/remoteconfig-template-file.js +32 -0
  65. package/dist/cli/commands/setup-tests/remoteconfig-template-in-json.js +31 -0
  66. package/dist/cli/commands/setup-tests/service-account.js +86 -0
  67. package/dist/cli/commands/setup-tests/storage-lifecycle-policy.js +81 -0
  68. package/dist/cli/commands/setup-tests/storage-rules-file.js +32 -0
  69. package/dist/cli/commands/setup-tests/storage-rules-in-json.js +20 -0
  70. package/dist/cli/commands/stripe.js +14 -0
  71. package/dist/cli/commands/test-lanes/stripe-live.js +440 -0
  72. package/dist/cli/commands/test.js +976 -0
  73. package/dist/cli/commands/update.js +27 -0
  74. package/dist/cli/commands/version.js +10 -0
  75. package/dist/cli/commands/watch.js +196 -0
  76. package/dist/cli/flags.js +13 -0
  77. package/dist/cli/index.js +173 -0
  78. package/dist/cli/run.js +33 -0
  79. package/dist/cli/utils/attach-log-file.js +4 -0
  80. package/dist/cli/utils/compile-rules.js +1155 -0
  81. package/dist/cli/utils/ensure-target.js +314 -0
  82. package/dist/cli/utils/project-type.js +140 -0
  83. package/dist/cli/utils/public-files.js +49 -0
  84. package/dist/cli/utils/safe-install.js +4 -0
  85. package/dist/cli/utils/spawn-shell.js +94 -0
  86. package/dist/cli/utils/stage-functions.js +338 -0
  87. package/dist/cli/utils/stage-local-packages.js +210 -0
  88. package/dist/cli/utils/target-checks.js +161 -0
  89. package/dist/cli/utils/target.js +24 -0
  90. package/dist/cli/utils/ui.js +292 -0
  91. package/dist/defaults/AGENTS.md +125 -0
  92. package/dist/defaults/CHANGELOG.md +15 -0
  93. package/dist/defaults/CLAUDE.md +1 -0
  94. package/dist/defaults/_.gitignore +65 -0
  95. package/dist/defaults/docs/README.md +17 -0
  96. package/dist/defaults/test/README.md +65 -0
  97. package/dist/defaults/test/_init.js +14 -0
  98. package/dist/defaults/test/helpers/connect-trap.js +72 -0
  99. package/dist/defaults/test/unit/registration.test.js +143 -0
  100. package/dist/defaults/test/unit/rules-posture.test.js +101 -0
  101. package/dist/defaults/test/unit/socket-free.test.js +43 -0
  102. package/dist/manager/events/auth/before-create.js +89 -0
  103. package/dist/manager/events/auth/before-signin.js +76 -0
  104. package/dist/manager/events/auth/on-create.js +112 -0
  105. package/dist/manager/events/auth/on-delete.js +141 -0
  106. package/dist/manager/events/auth/utils.js +93 -0
  107. package/dist/manager/events/cron/daily/blog-auto-publisher.js +287 -0
  108. package/dist/manager/events/cron/daily/data-requests.js +57 -0
  109. package/dist/manager/events/cron/daily/expire-paypal-cancellations.js +191 -0
  110. package/dist/manager/events/cron/daily/marketing-prune.js +486 -0
  111. package/dist/manager/events/cron/daily/reset-usage.js +188 -0
  112. package/dist/manager/events/cron/daily/trial-lapse-sweep.js +355 -0
  113. package/dist/manager/events/cron/daily.js +3 -0
  114. package/dist/manager/events/cron/frequent/abandoned-carts.js +204 -0
  115. package/dist/manager/events/cron/frequent/email-queue.js +71 -0
  116. package/dist/manager/events/cron/frequent/marketing-campaigns.js +463 -0
  117. package/dist/manager/events/cron/frequent/retry-failed-webhooks.js +70 -0
  118. package/dist/manager/events/cron/frequent.js +3 -0
  119. package/dist/manager/events/cron/runner.js +64 -0
  120. package/dist/manager/events/firestore/notifications/on-write.js +123 -0
  121. package/dist/manager/events/firestore/payments-disputes/on-write.js +256 -0
  122. package/dist/manager/events/firestore/payments-disputes/providers/stripe.js +218 -0
  123. package/dist/manager/events/firestore/payments-disputes/providers/test.js +231 -0
  124. package/dist/manager/events/firestore/payments-webhooks/analytics.js +687 -0
  125. package/dist/manager/events/firestore/payments-webhooks/on-write.js +1224 -0
  126. package/dist/manager/events/firestore/payments-webhooks/transitions/index.js +395 -0
  127. package/dist/manager/events/firestore/payments-webhooks/transitions/one-time/purchase-completed.js +58 -0
  128. package/dist/manager/events/firestore/payments-webhooks/transitions/one-time/purchase-failed.js +28 -0
  129. package/dist/manager/events/firestore/payments-webhooks/transitions/one-time/purchase-refunded.js +30 -0
  130. package/dist/manager/events/firestore/payments-webhooks/transitions/send-email.js +74 -0
  131. package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/cancellation-removed.js +14 -0
  132. package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/cancellation-requested.js +28 -0
  133. package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/checkout-declined.js +18 -0
  134. package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/new-subscription.js +67 -0
  135. package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/payment-failed.js +25 -0
  136. package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/payment-recovered.js +26 -0
  137. package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/payment-refunded.js +34 -0
  138. package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/plan-changed.js +42 -0
  139. package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/subscription-cancelled.js +33 -0
  140. package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/subscription-winback.js +17 -0
  141. package/dist/manager/functions/_legacy/actions/create-post-handler.js +188 -0
  142. package/dist/manager/functions/_legacy/actions/generate-uuid.js +63 -0
  143. package/dist/manager/functions/_legacy/actions/sign-up-handler.js +205 -0
  144. package/dist/manager/functions/_legacy/admin/create-post.js +207 -0
  145. package/dist/manager/functions/_legacy/admin/firestore-write.js +72 -0
  146. package/dist/manager/functions/_legacy/admin/get-stats.js +218 -0
  147. package/dist/manager/functions/_legacy/admin/query.js +198 -0
  148. package/dist/manager/functions/_legacy/admin/send-notification.js +206 -0
  149. package/dist/manager/functions/_legacy/template.js +33 -0
  150. package/dist/manager/functions/_legacy/test/authenticate.js +40 -0
  151. package/dist/manager/functions/_legacy/test/webhook.js +37 -0
  152. package/dist/manager/functions/wrappers/mailchimp/addToList.js +25 -0
  153. package/dist/manager/helpers/analytics.js +498 -0
  154. package/dist/manager/helpers/api-manager.js +313 -0
  155. package/dist/manager/helpers/backend-router.js +37 -0
  156. package/dist/manager/helpers/context/authenticate.js +215 -0
  157. package/dist/manager/helpers/context/client-info.js +91 -0
  158. package/dist/manager/helpers/context/index.js +228 -0
  159. package/dist/manager/helpers/context/logging.js +150 -0
  160. package/dist/manager/helpers/context/parse.js +160 -0
  161. package/dist/manager/helpers/context/respond.js +247 -0
  162. package/dist/manager/helpers/event-middleware.js +109 -0
  163. package/dist/manager/helpers/metadata.js +32 -0
  164. package/dist/manager/helpers/middleware.js +541 -0
  165. package/dist/manager/helpers/redact-secret.js +25 -0
  166. package/dist/manager/helpers/resolved-config.js +34 -0
  167. package/dist/manager/helpers/roles.js +69 -0
  168. package/dist/manager/helpers/safe-compare.js +31 -0
  169. package/dist/manager/helpers/schema-engine.js +314 -0
  170. package/dist/manager/helpers/schema-zod.js +203 -0
  171. package/dist/manager/helpers/settings.js +215 -0
  172. package/dist/manager/helpers/usage.js +541 -0
  173. package/dist/manager/helpers/user.js +57 -0
  174. package/dist/manager/helpers/utilities.js +565 -0
  175. package/dist/manager/index.js +1404 -0
  176. package/dist/manager/libraries/abandoned-cart-config.js +12 -0
  177. package/dist/manager/libraries/ai/index.js +321 -0
  178. package/dist/manager/libraries/ai/prompt.js +85 -0
  179. package/dist/manager/libraries/ai/providers/anthropic-format.js +297 -0
  180. package/dist/manager/libraries/ai/providers/anthropic.js +169 -0
  181. package/dist/manager/libraries/ai/providers/claude-code.js +183 -0
  182. package/dist/manager/libraries/ai/providers/openai.js +1272 -0
  183. package/dist/manager/libraries/ai/providers/test.js +257 -0
  184. package/dist/manager/libraries/ai/tokens.js +54 -0
  185. package/dist/manager/libraries/analytics/conversions.js +478 -0
  186. package/dist/manager/libraries/analytics/match-data.js +573 -0
  187. package/dist/manager/libraries/analytics/signup.js +120 -0
  188. package/dist/manager/libraries/auth-user.js +46 -0
  189. package/dist/manager/libraries/content/feed-parser.js +198 -0
  190. package/dist/manager/libraries/content/ghostii.js +191 -0
  191. package/dist/manager/libraries/content/source-resolver.js +629 -0
  192. package/dist/manager/libraries/email/constants.js +532 -0
  193. package/dist/manager/libraries/email/data/blocked-local-parts.json +55 -0
  194. package/dist/manager/libraries/email/data/blocked-local-patterns.js +14 -0
  195. package/dist/manager/libraries/email/data/corporate-domains.json +23 -0
  196. package/dist/manager/libraries/email/data/custom-disposable-domains.json +58 -0
  197. package/dist/manager/libraries/email/data/disposable-domains.json +8184 -0
  198. package/dist/manager/libraries/email/data/typo-domains.js +83 -0
  199. package/dist/manager/libraries/email/disposable-domains.js +92 -0
  200. package/dist/manager/libraries/email/generators/lib/filter.js +179 -0
  201. package/dist/manager/libraries/email/generators/lib/image-host.js +344 -0
  202. package/dist/manager/libraries/email/generators/lib/image-illustrator.js +154 -0
  203. package/dist/manager/libraries/email/generators/lib/markdown-renderer.js +294 -0
  204. package/dist/manager/libraries/email/generators/lib/mjml-template.js +120 -0
  205. package/dist/manager/libraries/email/generators/lib/structure.js +299 -0
  206. package/dist/manager/libraries/email/generators/lib/svg-illustrator.js +197 -0
  207. package/dist/manager/libraries/email/generators/lib/templates/base.js +229 -0
  208. package/dist/manager/libraries/email/generators/lib/templates/card.js +33 -0
  209. package/dist/manager/libraries/email/generators/lib/templates/classic-schema.js +59 -0
  210. package/dist/manager/libraries/email/generators/lib/templates/clean.js +82 -0
  211. package/dist/manager/libraries/email/generators/lib/templates/editorial/helpers.js +100 -0
  212. package/dist/manager/libraries/email/generators/lib/templates/editorial/index.js +318 -0
  213. package/dist/manager/libraries/email/generators/lib/templates/feedback.js +101 -0
  214. package/dist/manager/libraries/email/generators/lib/templates/field-report/helpers.js +138 -0
  215. package/dist/manager/libraries/email/generators/lib/templates/field-report/index.js +494 -0
  216. package/dist/manager/libraries/email/generators/lib/templates/index.js +47 -0
  217. package/dist/manager/libraries/email/generators/lib/templates/newsletter-shared.js +610 -0
  218. package/dist/manager/libraries/email/generators/lib/templates/order.js +461 -0
  219. package/dist/manager/libraries/email/generators/lib/templates/plain.js +83 -0
  220. package/dist/manager/libraries/email/generators/lib/templates/shared-campaign.js +64 -0
  221. package/dist/manager/libraries/email/generators/newsletter.js +899 -0
  222. package/dist/manager/libraries/email/index.js +146 -0
  223. package/dist/manager/libraries/email/marketing/index.js +754 -0
  224. package/dist/manager/libraries/email/prepare.js +365 -0
  225. package/dist/manager/libraries/email/providers/beehiiv.js +589 -0
  226. package/dist/manager/libraries/email/providers/sendgrid.js +845 -0
  227. package/dist/manager/libraries/email/transactional/index.js +510 -0
  228. package/dist/manager/libraries/email/utm.js +86 -0
  229. package/dist/manager/libraries/email/validation-provider-neverbounce.js +70 -0
  230. package/dist/manager/libraries/email/validation-provider-zerobounce.js +42 -0
  231. package/dist/manager/libraries/email/validation.js +255 -0
  232. package/dist/manager/libraries/env.js +198 -0
  233. package/dist/manager/libraries/infer-contact.js +101 -0
  234. package/dist/manager/libraries/load-provider.js +65 -0
  235. package/dist/manager/libraries/notification.js +256 -0
  236. package/dist/manager/libraries/openai.js +13 -0
  237. package/dist/manager/libraries/payment/discount-codes.js +204 -0
  238. package/dist/manager/libraries/payment/fetch-failure.js +56 -0
  239. package/dist/manager/libraries/payment/license.js +44 -0
  240. package/dist/manager/libraries/payment/order-id.js +57 -0
  241. package/dist/manager/libraries/payment/provider-errors.js +71 -0
  242. package/dist/manager/libraries/payment/providers/chargebee.js +817 -0
  243. package/dist/manager/libraries/payment/providers/coinbase.js +330 -0
  244. package/dist/manager/libraries/payment/providers/paypal.js +987 -0
  245. package/dist/manager/libraries/payment/providers/stripe.js +755 -0
  246. package/dist/manager/libraries/payment/providers/test.js +250 -0
  247. package/dist/manager/libraries/payment/refund-linkage.js +78 -0
  248. package/dist/manager/libraries/payment/refund-policy.js +113 -0
  249. package/dist/manager/libraries/payment/winback.js +55 -0
  250. package/dist/manager/libraries/prompts/infer-contact.md +78 -0
  251. package/dist/manager/libraries/rate-limits.js +17 -0
  252. package/dist/manager/libraries/recaptcha.js +58 -0
  253. package/dist/manager/libraries/user-doc.js +234 -0
  254. package/dist/manager/routes/admin/backup/post.js +106 -0
  255. package/dist/manager/routes/admin/cron/post.js +37 -0
  256. package/dist/manager/routes/admin/database/get.js +35 -0
  257. package/dist/manager/routes/admin/database/post.js +34 -0
  258. package/dist/manager/routes/admin/email/post.js +74 -0
  259. package/dist/manager/routes/admin/firestore/get.js +35 -0
  260. package/dist/manager/routes/admin/firestore/post.js +56 -0
  261. package/dist/manager/routes/admin/firestore/query/post.js +128 -0
  262. package/dist/manager/routes/admin/hook/post.js +104 -0
  263. package/dist/manager/routes/admin/infer-contact/post.js +35 -0
  264. package/dist/manager/routes/admin/notification/post.js +37 -0
  265. package/dist/manager/routes/admin/payment/post.js +56 -0
  266. package/dist/manager/routes/admin/post/deduplicate-image-alts.js +52 -0
  267. package/dist/manager/routes/admin/post/dispatch-deploy.js +36 -0
  268. package/dist/manager/routes/admin/post/post.js +487 -0
  269. package/dist/manager/routes/admin/post/put.js +146 -0
  270. package/dist/manager/routes/admin/post/templates/post.html +14 -0
  271. package/dist/manager/routes/admin/repo/content/post.js +106 -0
  272. package/dist/manager/routes/admin/stats/get.js +222 -0
  273. package/dist/manager/routes/admin/users/disable/post.js +50 -0
  274. package/dist/manager/routes/admin/users/list/get.js +110 -0
  275. package/dist/manager/routes/admin/users/sync/post.js +119 -0
  276. package/dist/manager/routes/brand/get.js +26 -0
  277. package/dist/manager/routes/content/post/get.js +110 -0
  278. package/dist/manager/routes/general/email/post.js +89 -0
  279. package/dist/manager/routes/general/email/templates/general/download-app-link.js +42 -0
  280. package/dist/manager/routes/general/uuid/post.js +31 -0
  281. package/dist/manager/routes/handler/post/post.js +142 -0
  282. package/dist/manager/routes/health/get.js +43 -0
  283. package/dist/manager/routes/index.js +11 -0
  284. package/dist/manager/routes/marketing/campaign/delete.js +45 -0
  285. package/dist/manager/routes/marketing/campaign/get.js +69 -0
  286. package/dist/manager/routes/marketing/campaign/post.js +124 -0
  287. package/dist/manager/routes/marketing/campaign/put.js +86 -0
  288. package/dist/manager/routes/marketing/campaign/utils.js +59 -0
  289. package/dist/manager/routes/marketing/contact/delete.js +100 -0
  290. package/dist/manager/routes/marketing/contact/post.js +149 -0
  291. package/dist/manager/routes/marketing/contact/put.js +37 -0
  292. package/dist/manager/routes/marketing/email-preferences/post.js +275 -0
  293. package/dist/manager/routes/marketing/webhook/forward/post.js +170 -0
  294. package/dist/manager/routes/marketing/webhook/post.js +123 -0
  295. package/dist/manager/routes/marketing/webhook/providers/beehiiv.js +197 -0
  296. package/dist/manager/routes/marketing/webhook/providers/sendgrid.js +194 -0
  297. package/dist/manager/routes/payments/cancel/_is-trialing.js +40 -0
  298. package/dist/manager/routes/payments/cancel/post.js +149 -0
  299. package/dist/manager/routes/payments/cancel/providers/chargebee.js +41 -0
  300. package/dist/manager/routes/payments/cancel/providers/paypal.js +56 -0
  301. package/dist/manager/routes/payments/cancel/providers/stripe.js +33 -0
  302. package/dist/manager/routes/payments/cancel/providers/test.js +118 -0
  303. package/dist/manager/routes/payments/discount/get.js +25 -0
  304. package/dist/manager/routes/payments/dispute-alert/post.js +117 -0
  305. package/dist/manager/routes/payments/dispute-alert/providers/chargeblast.js +55 -0
  306. package/dist/manager/routes/payments/intent/post.js +341 -0
  307. package/dist/manager/routes/payments/intent/providers/chargebee.js +197 -0
  308. package/dist/manager/routes/payments/intent/providers/coinbase.js +94 -0
  309. package/dist/manager/routes/payments/intent/providers/paypal.js +244 -0
  310. package/dist/manager/routes/payments/intent/providers/stripe.js +141 -0
  311. package/dist/manager/routes/payments/intent/providers/test.js +341 -0
  312. package/dist/manager/routes/payments/plan/post.js +147 -0
  313. package/dist/manager/routes/payments/plan/providers/chargebee.js +62 -0
  314. package/dist/manager/routes/payments/plan/providers/paypal.js +62 -0
  315. package/dist/manager/routes/payments/plan/providers/stripe.js +59 -0
  316. package/dist/manager/routes/payments/plan/providers/test.js +113 -0
  317. package/dist/manager/routes/payments/portal/post.js +113 -0
  318. package/dist/manager/routes/payments/portal/providers/chargebee.js +62 -0
  319. package/dist/manager/routes/payments/portal/providers/paypal.js +25 -0
  320. package/dist/manager/routes/payments/portal/providers/stripe.js +62 -0
  321. package/dist/manager/routes/payments/portal/providers/test.js +18 -0
  322. package/dist/manager/routes/payments/refund/post.js +207 -0
  323. package/dist/manager/routes/payments/refund/providers/chargebee.js +147 -0
  324. package/dist/manager/routes/payments/refund/providers/coinbase.js +40 -0
  325. package/dist/manager/routes/payments/refund/providers/paypal.js +239 -0
  326. package/dist/manager/routes/payments/refund/providers/stripe.js +160 -0
  327. package/dist/manager/routes/payments/refund/providers/test.js +204 -0
  328. package/dist/manager/routes/payments/trial-eligibility/get.js +29 -0
  329. package/dist/manager/routes/payments/uncancel/post.js +106 -0
  330. package/dist/manager/routes/payments/uncancel/providers/chargebee.js +30 -0
  331. package/dist/manager/routes/payments/uncancel/providers/paypal.js +25 -0
  332. package/dist/manager/routes/payments/uncancel/providers/stripe.js +27 -0
  333. package/dist/manager/routes/payments/uncancel/providers/test.js +114 -0
  334. package/dist/manager/routes/payments/webhook/post.js +157 -0
  335. package/dist/manager/routes/payments/webhook/providers/chargebee.js +218 -0
  336. package/dist/manager/routes/payments/webhook/providers/coinbase.js +91 -0
  337. package/dist/manager/routes/payments/webhook/providers/paypal.js +214 -0
  338. package/dist/manager/routes/payments/webhook/providers/stripe.js +182 -0
  339. package/dist/manager/routes/payments/webhook/providers/test.js +18 -0
  340. package/dist/manager/routes/payments/winback/post.js +276 -0
  341. package/dist/manager/routes/payments/winback/providers/chargebee.js +24 -0
  342. package/dist/manager/routes/payments/winback/providers/paypal.js +22 -0
  343. package/dist/manager/routes/payments/winback/providers/stripe.js +41 -0
  344. package/dist/manager/routes/payments/winback/providers/test.js +42 -0
  345. package/dist/manager/routes/restart/index.js +37 -0
  346. package/dist/manager/routes/special/electron-client/post.js +59 -0
  347. package/dist/manager/routes/test/authenticate/get.js +11 -0
  348. package/dist/manager/routes/test/health/get.js +8 -0
  349. package/dist/manager/routes/test/index.js +16 -0
  350. package/dist/manager/routes/test/lab/post.js +17 -0
  351. package/dist/manager/routes/test/redirect/get.js +27 -0
  352. package/dist/manager/routes/test/reset-account/post.js +79 -0
  353. package/dist/manager/routes/test/roster/get.js +40 -0
  354. package/dist/manager/routes/test/schema/post.js +20 -0
  355. package/dist/manager/routes/test/usage/post.js +44 -0
  356. package/dist/manager/routes/test/webhook/post.js +21 -0
  357. package/dist/manager/routes/user/api-keys/post.js +51 -0
  358. package/dist/manager/routes/user/connections/_context.js +194 -0
  359. package/dist/manager/routes/user/connections/_grant.js +247 -0
  360. package/dist/manager/routes/user/connections/_lease.js +184 -0
  361. package/dist/manager/routes/user/connections/_providers.js +191 -0
  362. package/dist/manager/routes/user/connections/_state.js +117 -0
  363. package/dist/manager/routes/user/connections/delete.js +55 -0
  364. package/dist/manager/routes/user/connections/get.js +167 -0
  365. package/dist/manager/routes/user/connections/post.js +370 -0
  366. package/dist/manager/routes/user/connections/providers/discord.js +41 -0
  367. package/dist/manager/routes/user/connections/providers/google.js +55 -0
  368. package/dist/manager/routes/user/connections/providers/kick.js +68 -0
  369. package/dist/manager/routes/user/connections/providers/spotify.js +40 -0
  370. package/dist/manager/routes/user/connections/providers/twitch.js +52 -0
  371. package/dist/manager/routes/user/data-request/delete.js +84 -0
  372. package/dist/manager/routes/user/data-request/get.js +229 -0
  373. package/dist/manager/routes/user/data-request/post.js +136 -0
  374. package/dist/manager/routes/user/delete.js +149 -0
  375. package/dist/manager/routes/user/feedback/post.js +81 -0
  376. package/dist/manager/routes/user/get.js +14 -0
  377. package/dist/manager/routes/user/orders/get.js +95 -0
  378. package/dist/manager/routes/user/sessions/delete.js +95 -0
  379. package/dist/manager/routes/user/sessions/get.js +40 -0
  380. package/dist/manager/routes/user/settings/validate/post.js +86 -0
  381. package/dist/manager/routes/user/signup/post.js +662 -0
  382. package/dist/manager/routes/user/subscription/get.js +77 -0
  383. package/dist/manager/routes/user/token/post.js +30 -0
  384. package/dist/manager/routes/verts/delete.js +43 -0
  385. package/dist/manager/routes/verts/get.js +49 -0
  386. package/dist/manager/routes/verts/post.js +61 -0
  387. package/dist/manager/routes/verts/put.js +75 -0
  388. package/dist/manager/routes/verts/redirect/get.js +36 -0
  389. package/dist/manager/routes/verts/serve/get.js +67 -0
  390. package/dist/manager/routes/verts/utils.js +436 -0
  391. package/dist/manager/schemas/admin/backup/post.js +8 -0
  392. package/dist/manager/schemas/admin/cron/post.js +8 -0
  393. package/dist/manager/schemas/admin/database/get.js +5 -0
  394. package/dist/manager/schemas/admin/database/post.js +6 -0
  395. package/dist/manager/schemas/admin/email/post.js +31 -0
  396. package/dist/manager/schemas/admin/firestore/get.js +5 -0
  397. package/dist/manager/schemas/admin/firestore/post.js +8 -0
  398. package/dist/manager/schemas/admin/firestore/query/post.js +5 -0
  399. package/dist/manager/schemas/admin/hook/post.js +8 -0
  400. package/dist/manager/schemas/admin/infer-contact/post.js +6 -0
  401. package/dist/manager/schemas/admin/notification/post.js +23 -0
  402. package/dist/manager/schemas/admin/payment/post.js +8 -0
  403. package/dist/manager/schemas/admin/post/post.js +23 -0
  404. package/dist/manager/schemas/admin/post/put.js +13 -0
  405. package/dist/manager/schemas/admin/repo/content/post.js +10 -0
  406. package/dist/manager/schemas/admin/stats/get.js +5 -0
  407. package/dist/manager/schemas/admin/users/disable/post.js +9 -0
  408. package/dist/manager/schemas/admin/users/list/get.js +10 -0
  409. package/dist/manager/schemas/admin/users/sync/post.js +7 -0
  410. package/dist/manager/schemas/brand/get.js +3 -0
  411. package/dist/manager/schemas/content/post/get.js +8 -0
  412. package/dist/manager/schemas/general/email/post.js +10 -0
  413. package/dist/manager/schemas/general/uuid/post.js +9 -0
  414. package/dist/manager/schemas/handler/post/post.js +13 -0
  415. package/dist/manager/schemas/health/get.js +3 -0
  416. package/dist/manager/schemas/marketing/campaign/delete.js +8 -0
  417. package/dist/manager/schemas/marketing/campaign/get.js +13 -0
  418. package/dist/manager/schemas/marketing/campaign/post.js +43 -0
  419. package/dist/manager/schemas/marketing/campaign/put.js +37 -0
  420. package/dist/manager/schemas/marketing/contact/delete.js +8 -0
  421. package/dist/manager/schemas/marketing/contact/post.js +14 -0
  422. package/dist/manager/schemas/marketing/contact/put.js +8 -0
  423. package/dist/manager/schemas/marketing/email-preferences/post.js +15 -0
  424. package/dist/manager/schemas/marketing/webhook/forward/post.js +8 -0
  425. package/dist/manager/schemas/marketing/webhook/post.js +7 -0
  426. package/dist/manager/schemas/payments/cancel/post.js +15 -0
  427. package/dist/manager/schemas/payments/discount/get.js +8 -0
  428. package/dist/manager/schemas/payments/dispute-alert/post.js +6 -0
  429. package/dist/manager/schemas/payments/intent/post.js +28 -0
  430. package/dist/manager/schemas/payments/plan/post.js +11 -0
  431. package/dist/manager/schemas/payments/portal/post.js +12 -0
  432. package/dist/manager/schemas/payments/refund/post.js +14 -0
  433. package/dist/manager/schemas/payments/trial-eligibility/get.js +7 -0
  434. package/dist/manager/schemas/payments/uncancel/post.js +9 -0
  435. package/dist/manager/schemas/payments/webhook/post.js +6 -0
  436. package/dist/manager/schemas/payments/winback/post.js +12 -0
  437. package/dist/manager/schemas/restart/index.js +5 -0
  438. package/dist/manager/schemas/special/electron-client/post.js +11 -0
  439. package/dist/manager/schemas/test/authenticate/get.js +3 -0
  440. package/dist/manager/schemas/test/health/get.js +3 -0
  441. package/dist/manager/schemas/test/index.js +5 -0
  442. package/dist/manager/schemas/test/lab/post.js +3 -0
  443. package/dist/manager/schemas/test/redirect/get.js +7 -0
  444. package/dist/manager/schemas/test/reset-account/post.js +3 -0
  445. package/dist/manager/schemas/test/roster/get.js +3 -0
  446. package/dist/manager/schemas/test/schema/post.js +70 -0
  447. package/dist/manager/schemas/test/usage/post.js +8 -0
  448. package/dist/manager/schemas/test/webhook/post.js +7 -0
  449. package/dist/manager/schemas/user/api-keys/post.js +6 -0
  450. package/dist/manager/schemas/user/connections/delete.js +8 -0
  451. package/dist/manager/schemas/user/connections/get.js +21 -0
  452. package/dist/manager/schemas/user/connections/post.js +12 -0
  453. package/dist/manager/schemas/user/data-request/delete.js +3 -0
  454. package/dist/manager/schemas/user/data-request/get.js +5 -0
  455. package/dist/manager/schemas/user/data-request/post.js +6 -0
  456. package/dist/manager/schemas/user/delete.js +6 -0
  457. package/dist/manager/schemas/user/feedback/post.js +8 -0
  458. package/dist/manager/schemas/user/get.js +3 -0
  459. package/dist/manager/schemas/user/orders/get.js +6 -0
  460. package/dist/manager/schemas/user/sessions/delete.js +6 -0
  461. package/dist/manager/schemas/user/sessions/get.js +6 -0
  462. package/dist/manager/schemas/user/settings/validate/post.js +8 -0
  463. package/dist/manager/schemas/user/signup/post.js +27 -0
  464. package/dist/manager/schemas/user/subscription/get.js +5 -0
  465. package/dist/manager/schemas/user/token/post.js +5 -0
  466. package/dist/manager/schemas/verts/delete.js +8 -0
  467. package/dist/manager/schemas/verts/get.js +9 -0
  468. package/dist/manager/schemas/verts/post.js +23 -0
  469. package/dist/manager/schemas/verts/put.js +20 -0
  470. package/dist/manager/schemas/verts/redirect/get.js +10 -0
  471. package/dist/manager/schemas/verts/serve/get.js +14 -0
  472. package/dist/manager/server-manager.js +69 -0
  473. package/dist/mcp/client.js +98 -0
  474. package/dist/mcp/handler.js +521 -0
  475. package/dist/mcp/index.js +157 -0
  476. package/dist/mcp/tools.js +555 -0
  477. package/dist/mcp/utils.js +108 -0
  478. package/dist/omega-bin.js +6 -0
  479. package/dist/require.js +3 -0
  480. package/dist/test/fixtures/firebase-project/.firebaserc +5 -0
  481. package/dist/test/fixtures/firebase-project/config/omega.json5 +131 -0
  482. package/dist/test/fixtures/firebase-project/database.rules.json +6 -0
  483. package/dist/test/fixtures/firebase-project/firebase.json +49 -0
  484. package/dist/test/fixtures/firebase-project/firestore.indexes.json +4 -0
  485. package/dist/test/fixtures/firebase-project/package.json +14 -0
  486. package/dist/test/fixtures/firebase-project/src/index.js +11 -0
  487. package/dist/test/fixtures/firebase-project/storage.rules +8 -0
  488. package/dist/test/parse-audit.js +25 -0
  489. package/dist/test/run-tests.js +90 -0
  490. package/dist/test/runner.js +982 -0
  491. package/dist/test/seed.js +242 -0
  492. package/dist/test/test-accounts.js +2660 -0
  493. package/dist/test/utils/assertions.js +189 -0
  494. package/dist/test/utils/email-capture.js +252 -0
  495. package/dist/test/utils/extended-mode-warning.js +11 -0
  496. package/dist/test/utils/firestore-rules-client.js +277 -0
  497. package/dist/test/utils/http-client.js +228 -0
  498. package/dist/test/utils/test-mode-file.js +192 -0
  499. package/dist/utils/merge-line-files.js +6 -0
  500. package/dist/utils/scaffold-defaults.js +71 -0
  501. package/dist/utils/test-lanes.js +62 -0
  502. package/dist/vendor/account/engine.js +182 -0
  503. package/dist/vendor/account/features.js +220 -0
  504. package/dist/vendor/account/index.js +53 -0
  505. package/dist/vendor/account/schema.js +272 -0
  506. package/dist/vendor/account/subscription.js +38 -0
  507. package/dist/vendor/analytics/adapters/ga4.js +26 -0
  508. package/dist/vendor/analytics/adapters/meta.js +26 -0
  509. package/dist/vendor/analytics/adapters/resolve.js +130 -0
  510. package/dist/vendor/analytics/adapters/tiktok.js +27 -0
  511. package/dist/vendor/analytics/catalog.js +908 -0
  512. package/dist/vendor/analytics/identity.js +136 -0
  513. package/dist/vendor/config/company.js +31 -0
  514. package/dist/vendor/config/defaults.js +173 -0
  515. package/dist/vendor/config/demo.js +18 -0
  516. package/dist/vendor/config/desktop-artifacts.js +110 -0
  517. package/dist/vendor/config/edit.js +769 -0
  518. package/dist/vendor/config/env-delivery.js +145 -0
  519. package/dist/vendor/config/env-rules.js +93 -0
  520. package/dist/vendor/config/env-schema.js +1078 -0
  521. package/dist/vendor/config/env.js +445 -0
  522. package/dist/vendor/config/hooks.js +97 -0
  523. package/dist/vendor/config/index.js +237 -0
  524. package/dist/vendor/config/instances.js +208 -0
  525. package/dist/vendor/config/load.js +490 -0
  526. package/dist/vendor/config/merge.js +68 -0
  527. package/dist/vendor/config/order.js +139 -0
  528. package/dist/vendor/config/ports.js +374 -0
  529. package/dist/vendor/config/providers.js +32 -0
  530. package/dist/vendor/config/repo.js +142 -0
  531. package/dist/vendor/config/retired-keys.js +430 -0
  532. package/dist/vendor/config/schema.js +1610 -0
  533. package/dist/vendor/config/secrets.js +50 -0
  534. package/dist/vendor/config/seed.js +34 -0
  535. package/dist/vendor/config/site-global.js +205 -0
  536. package/dist/vendor/config/validate.js +554 -0
  537. package/dist/vendor/config/winback.js +61 -0
  538. package/dist/vendor/devkit/attach-log-file.js +262 -0
  539. package/dist/vendor/devkit/bare-requires.js +153 -0
  540. package/dist/vendor/devkit/command-path.js +46 -0
  541. package/dist/vendor/devkit/defaults-engine.js +372 -0
  542. package/dist/vendor/devkit/deploy-record.js +180 -0
  543. package/dist/vendor/devkit/deploy.js +286 -0
  544. package/dist/vendor/devkit/license.js +155 -0
  545. package/dist/vendor/devkit/local-https.js +360 -0
  546. package/dist/vendor/devkit/local.js +1905 -0
  547. package/dist/vendor/devkit/logger.js +128 -0
  548. package/dist/vendor/devkit/merge-line-files.js +296 -0
  549. package/dist/vendor/devkit/npm-registry.js +52 -0
  550. package/dist/vendor/devkit/omega-bin.js +345 -0
  551. package/dist/vendor/devkit/parse-audit.js +74 -0
  552. package/dist/vendor/devkit/safe-install.js +18 -0
  553. package/dist/vendor/devkit/scaffold-guard.js +96 -0
  554. package/dist/vendor/devkit/stop-signals.js +28 -0
  555. package/dist/vendor/devkit/test/assert.js +120 -0
  556. package/dist/vendor/devkit/test/define-cases.js +104 -0
  557. package/dist/vendor/devkit/test/runner-core.js +554 -0
  558. package/dist/vendor/devkit/test/scope.js +162 -0
  559. package/dist/vendor/devkit/update.js +569 -0
  560. package/dist/vendor/monitoring/core.js +180 -0
  561. package/dist/vendor/monitoring/env.js +49 -0
  562. package/dist/vendor/monitoring/logger.js +39 -0
  563. package/dist/vendor/monitoring/node.js +72 -0
  564. package/docs/admin-post-route.md +57 -0
  565. package/docs/ai-library.md +171 -0
  566. package/docs/architecture.md +62 -0
  567. package/docs/audit.md +70 -0
  568. package/docs/auth-hooks.md +74 -0
  569. package/docs/build-system.md +65 -0
  570. package/docs/cdp-debugging.md +29 -0
  571. package/docs/cli-firestore-auth.md +85 -0
  572. package/docs/cli-logs.md +67 -0
  573. package/docs/cli-output.md +146 -0
  574. package/docs/code-patterns.md +77 -0
  575. package/docs/common-mistakes.md +12 -0
  576. package/docs/common-operations.md +60 -0
  577. package/docs/connections.md +212 -0
  578. package/docs/consent.md +362 -0
  579. package/docs/directory-structure.md +150 -0
  580. package/docs/email-system.md +459 -0
  581. package/docs/environment-detection.md +93 -0
  582. package/docs/file-naming.md +10 -0
  583. package/docs/firestore.md +134 -0
  584. package/docs/ghostii.md +240 -0
  585. package/docs/index.md +299 -0
  586. package/docs/key-files.md +37 -0
  587. package/docs/logging.md +60 -0
  588. package/docs/marketing-campaigns.md +407 -0
  589. package/docs/marketing-fields.md +25 -0
  590. package/docs/mcp.md +222 -0
  591. package/docs/migration.md +129 -0
  592. package/docs/payment-system.md +856 -0
  593. package/docs/paypal-sandbox-qa.md +105 -0
  594. package/docs/response-headers.md +9 -0
  595. package/docs/routes.md +211 -0
  596. package/docs/sanitization.md +71 -0
  597. package/docs/schemas.md +163 -0
  598. package/docs/shared/agent-docs.md +89 -0
  599. package/docs/shared/analytics.md +612 -0
  600. package/docs/shared/brands.md +51 -0
  601. package/docs/shared/breaking-changes.md +497 -0
  602. package/docs/shared/config.md +1387 -0
  603. package/docs/shared/deploys.md +215 -0
  604. package/docs/shared/icons.md +201 -0
  605. package/docs/shared/local-dev.md +147 -0
  606. package/docs/shared/logging.md +202 -0
  607. package/docs/shared/monitoring.md +153 -0
  608. package/docs/shared/publishing.md +183 -0
  609. package/docs/shared/rulings.md +34 -0
  610. package/docs/shared/testing.md +147 -0
  611. package/docs/shared/theming.md +604 -0
  612. package/docs/shared/translation.md +291 -0
  613. package/docs/shared/updates.md +61 -0
  614. package/docs/stripe-webhook-forwarding.md +20 -0
  615. package/docs/test-boot-layer.md +67 -0
  616. package/docs/test-framework.md +583 -0
  617. package/docs/usage-rate-limiting.md +121 -0
  618. package/docs/verts.md +29 -0
  619. package/package.json +143 -0
  620. package/templates/config/omega.json5 +322 -0
  621. package/templates/database.rules.json +82 -0
  622. package/templates/firebase.json +67 -0
  623. package/templates/firestore.framework.rules +191 -0
  624. package/templates/firestore.indexes.json +4 -0
  625. package/templates/firestore.rules +59 -0
  626. package/templates/index.js +12 -0
  627. package/templates/public/404.html +26 -0
  628. package/templates/public/index.html +24 -0
  629. package/templates/remoteconfig.template.json +1 -0
  630. package/templates/storage-lifecycle-config-1-day.json +9 -0
  631. package/templates/storage-lifecycle-config-30-days.json +9 -0
  632. package/templates/storage.rules +11 -0
  633. package/test/_init/accounts-validation.js +58 -0
  634. package/test/ai/tools-live.test.js +171 -0
  635. package/test/analytics/conversion-delivery.test.js +1013 -0
  636. package/test/analytics/match-normalization.test.js +238 -0
  637. package/test/analytics/signup-conversion.test.js +135 -0
  638. package/test/boot/cli-dispatch.test.js +142 -0
  639. package/test/boot/defaults-scaffold.test.js +248 -0
  640. package/test/boot/deploy-staging.test.js +446 -0
  641. package/test/boot/disposable-domains.test.js +154 -0
  642. package/test/boot/emulator-boots.test.js +39 -0
  643. package/test/boot/emulator-port-preflight.test.js +126 -0
  644. package/test/boot/emulator-ready-timeout.test.js +46 -0
  645. package/test/boot/project-id-refresh.test.js +73 -0
  646. package/test/boot/suite-portability.test.js +278 -0
  647. package/test/boot/update-command.test.js +63 -0
  648. package/test/cli/cross-platform.test.js +168 -0
  649. package/test/cli/custom-project-type.test.js +426 -0
  650. package/test/cli/deploy-license-env.test.js +100 -0
  651. package/test/cli/emulator-adoption.test.js +254 -0
  652. package/test/cli/emulator-orphans.test.js +658 -0
  653. package/test/cli/emulator-ownership.test.js +177 -0
  654. package/test/cli/emulator-port-retry.test.js +395 -0
  655. package/test/cli/emulator-shutdown.test.js +788 -0
  656. package/test/cli/emulator-stale-reap.test.js +574 -0
  657. package/test/cli/ensure-target.test.js +325 -0
  658. package/test/cli/flags.test.js +64 -0
  659. package/test/cli/https-trust.test.js +38 -0
  660. package/test/cli/install-update-dispatch.test.js +47 -0
  661. package/test/cli/lane-environments.test.js +159 -0
  662. package/test/cli/marketing-campaigns-seeded.test.js +194 -0
  663. package/test/cli/migrate-bare-requires.test.js +128 -0
  664. package/test/cli/migrate-markers.test.js +530 -0
  665. package/test/cli/required-indexes.test.js +64 -0
  666. package/test/cli/rules-compile.test.js +622 -0
  667. package/test/cli/rules-migration-deferral.test.js +259 -0
  668. package/test/cli/rules-version.test.js +104 -0
  669. package/test/cli/setup-load-files.test.js +66 -0
  670. package/test/cli/setup-offline-mode.test.js +276 -0
  671. package/test/cli/setup-retired.test.js +64 -0
  672. package/test/cli/setup-shared-project-indexes.test.js +262 -0
  673. package/test/cli/setup-tests-requires.test.js +45 -0
  674. package/test/cli/stage-env-compose.test.js +316 -0
  675. package/test/cli/stripe-live-lane.test.js +303 -0
  676. package/test/cli/target.test.js +182 -0
  677. package/test/cli/templates.test.js +66 -0
  678. package/test/cli/test-runner-env.test.js +115 -0
  679. package/test/cli/test-stack-shutdown.test.js +275 -0
  680. package/test/cli/test-target-no-match.test.js +128 -0
  681. package/test/cli/verb-logs.test.js +118 -0
  682. package/test/cli/version-dispatch.test.js +39 -0
  683. package/test/content/blog-generate.test.js +164 -0
  684. package/test/email/campaign-config-fault.test.js +376 -0
  685. package/test/email/campaign-cron-pipeline.test.js +536 -0
  686. package/test/email/campaign-send.test.js +47 -0
  687. package/test/email/consent-lifecycle.test.js +258 -0
  688. package/test/email/content-html-policy.test.js +270 -0
  689. package/test/email/feedback-and-plain-send.test.js +54 -0
  690. package/test/email/fixtures/clean.json +30 -0
  691. package/test/email/fixtures/editorial.json +30 -0
  692. package/test/email/fixtures/field-report.json +53 -0
  693. package/test/email/identity.test.js +321 -0
  694. package/test/email/marketing/consent-gate.test.js +265 -0
  695. package/test/email/marketing/custom-fields-catalog.test.js +259 -0
  696. package/test/email/marketing/prune-per-provider.test.js +762 -0
  697. package/test/email/marketing/remove-log-privacy.test.js +225 -0
  698. package/test/email/marketing-lifecycle.test.js +139 -0
  699. package/test/email/newsletter-generate.test.js +853 -0
  700. package/test/email/newsletter-svg-tokens.test.js +51 -0
  701. package/test/email/newsletter-templates.test.js +492 -0
  702. package/test/email/order-one-time-cta.test.js +97 -0
  703. package/test/email/render-content.test.js +248 -0
  704. package/test/email/safe-url.test.js +0 -0
  705. package/test/email/sanitize-images.test.js +66 -0
  706. package/test/email/send-log-privacy.test.js +107 -0
  707. package/test/email/templates.test.js +288 -0
  708. package/test/email/testing-capture.test.js +260 -0
  709. package/test/email/transactional-send.test.js +36 -0
  710. package/test/email/transactional.test.js +562 -0
  711. package/test/email/unsubscribe-groups.test.js +155 -0
  712. package/test/email/unsubscribe-key.test.js +91 -0
  713. package/test/email/validation-cases.test.js +152 -0
  714. package/test/email/validation.test.js +746 -0
  715. package/test/events/auth-delete-conversion.test.js +138 -0
  716. package/test/events/auth-delete-race.test.js +211 -0
  717. package/test/events/auth-on-create-log.test.js +164 -0
  718. package/test/events/auth-signup-conversion.test.js +141 -0
  719. package/test/events/auth-signup-limit.test.js +142 -0
  720. package/test/events/auth-trigger-log-privacy.test.js +256 -0
  721. package/test/events/cron-job-doc-shape.test.js +101 -0
  722. package/test/events/cron-reset-usage.test.js +116 -0
  723. package/test/events/notification-conversion.test.js +182 -0
  724. package/test/events/payments/_webhook-harness.js +307 -0
  725. package/test/events/payments/abandoned-cart-activity.test.js +135 -0
  726. package/test/events/payments/analytics-payment-events.test.js +945 -0
  727. package/test/events/payments/dispute-email-status.test.js +55 -0
  728. package/test/events/payments/journey-payments-abandoned.test.js +126 -0
  729. package/test/events/payments/journey-payments-cancel-endpoint.test.js +102 -0
  730. package/test/events/payments/journey-payments-cancel-no-order.test.js +76 -0
  731. package/test/events/payments/journey-payments-cancel.test.js +184 -0
  732. package/test/events/payments/journey-payments-decline.test.js +225 -0
  733. package/test/events/payments/journey-payments-discount.test.js +91 -0
  734. package/test/events/payments/journey-payments-dispute.test.js +190 -0
  735. package/test/events/payments/journey-payments-failure.test.js +148 -0
  736. package/test/events/payments/journey-payments-legacy-product.test.js +151 -0
  737. package/test/events/payments/journey-payments-one-time-decline.test.js +127 -0
  738. package/test/events/payments/journey-payments-one-time-failure.test.js +113 -0
  739. package/test/events/payments/journey-payments-one-time-refund.test.js +186 -0
  740. package/test/events/payments/journey-payments-one-time.test.js +175 -0
  741. package/test/events/payments/journey-payments-plan-change.test.js +146 -0
  742. package/test/events/payments/journey-payments-plan-switch-trial.test.js +161 -0
  743. package/test/events/payments/journey-payments-plan-switch.test.js +182 -0
  744. package/test/events/payments/journey-payments-refund-no-order.test.js +76 -0
  745. package/test/events/payments/journey-payments-refund-webhook.test.js +225 -0
  746. package/test/events/payments/journey-payments-suspend.test.js +183 -0
  747. package/test/events/payments/journey-payments-trial-cancel.test.js +132 -0
  748. package/test/events/payments/journey-payments-trial.test.js +173 -0
  749. package/test/events/payments/journey-payments-uid-resolution.test.js +134 -0
  750. package/test/events/payments/journey-payments-uncancel.test.js +157 -0
  751. package/test/events/payments/journey-payments-upgrade.test.js +137 -0
  752. package/test/events/payments/journey-payments-winback-decline.test.js +171 -0
  753. package/test/events/payments/journey-payments-winback.test.js +195 -0
  754. package/test/events/payments/paypal-expiry-cron.test.js +166 -0
  755. package/test/events/payments/purchase-failed-handler.test.js +80 -0
  756. package/test/events/payments/purchase-refunded-handler.test.js +108 -0
  757. package/test/events/payments/test-processor-doc-shape.test.js +162 -0
  758. package/test/events/payments/transition-order-emails.test.js +185 -0
  759. package/test/events/payments/transition-promo-lines.test.js +192 -0
  760. package/test/events/payments/transitions-detect.test.js +767 -0
  761. package/test/events/payments/trial-lapse-sweep-staleness.test.js +173 -0
  762. package/test/events/payments/trial-lapse-sweep.test.js +359 -0
  763. package/test/events/payments/webhook-atomic-writes.test.js +69 -0
  764. package/test/events/payments/webhook-chargebee-unreachable.test.js +106 -0
  765. package/test/events/payments/webhook-discount-clear.test.js +189 -0
  766. package/test/events/payments/webhook-failure-intent.test.js +93 -0
  767. package/test/events/payments/webhook-hosted-page-uid-trust.test.js +212 -0
  768. package/test/events/payments/webhook-ordering.test.js +326 -0
  769. package/test/events/payments/webhook-provider-lookup-trust.test.js +206 -0
  770. package/test/events/payments/webhook-refund-amount-trust.test.js +223 -0
  771. package/test/events/payments/webhook-refund-envelope-shape.test.js +148 -0
  772. package/test/events/payments/webhook-refund-linkage-trust.test.js +404 -0
  773. package/test/events/payments/webhook-refund-transaction-fallback.test.js +184 -0
  774. package/test/events/payments/webhook-refund-without-order.test.js +170 -0
  775. package/test/events/payments/webhook-refusal-reporting.test.js +194 -0
  776. package/test/events/payments/webhook-refused-intent.test.js +142 -0
  777. package/test/events/payments/webhook-retry-sweep.test.js +252 -0
  778. package/test/events/payments/webhook-transition-claim.test.js +389 -0
  779. package/test/events/payments/webhook-uid-trust.test.js +203 -0
  780. package/test/events/payments/webhook-user-without-auth.test.js +161 -0
  781. package/test/fixtures/chargebee/invoice-one-time.json +27 -0
  782. package/test/fixtures/chargebee/subscription-active.json +44 -0
  783. package/test/fixtures/chargebee/subscription-cancelled.json +42 -0
  784. package/test/fixtures/chargebee/subscription-in-trial.json +41 -0
  785. package/test/fixtures/chargebee/subscription-legacy-plan.json +41 -0
  786. package/test/fixtures/chargebee/subscription-non-renewing.json +41 -0
  787. package/test/fixtures/chargebee/subscription-paused.json +42 -0
  788. package/test/fixtures/chargebee/webhook-payment-failed.json +51 -0
  789. package/test/fixtures/chargebee/webhook-subscription-created.json +47 -0
  790. package/test/fixtures/coinbase/charge-confirmed.json +50 -0
  791. package/test/fixtures/coinbase/charge-failed.json +34 -0
  792. package/test/fixtures/coinbase/charge-pending.json +34 -0
  793. package/test/fixtures/migrate/ported-route.js.txt +11 -0
  794. package/test/fixtures/paypal/capture-completed.json +51 -0
  795. package/test/fixtures/paypal/capture-refunded.json +43 -0
  796. package/test/fixtures/paypal/order-approved.json +62 -0
  797. package/test/fixtures/paypal/order-completed.json +110 -0
  798. package/test/fixtures/paypal/sale-refunded.json +38 -0
  799. package/test/fixtures/paypal/subscription-active.json +76 -0
  800. package/test/fixtures/paypal/subscription-cancelled.json +50 -0
  801. package/test/fixtures/paypal/subscription-suspended.json +65 -0
  802. package/test/fixtures/stripe/checkout-session-completed.json +130 -0
  803. package/test/fixtures/stripe/invoice-payment-failed.json +148 -0
  804. package/test/fixtures/stripe/invoice-subscription-payment-failed.json +28 -0
  805. package/test/fixtures/stripe/invoice-subscription-payment-succeeded.json +28 -0
  806. package/test/fixtures/stripe/subscription-active.json +161 -0
  807. package/test/fixtures/stripe/subscription-canceled.json +161 -0
  808. package/test/fixtures/stripe/subscription-trialing.json +161 -0
  809. package/test/helpers/_shared-config.js +15 -0
  810. package/test/helpers/ai-request-payload.test.js +619 -0
  811. package/test/helpers/ai-schema-resolve.test.js +125 -0
  812. package/test/helpers/ai-test-provider.test.js +227 -0
  813. package/test/helpers/ai-token-accounting.test.js +150 -0
  814. package/test/helpers/ai-tools-format.test.js +384 -0
  815. package/test/helpers/analytics-no-id-notice.test.js +146 -0
  816. package/test/helpers/analytics-user-data.test.js +194 -0
  817. package/test/helpers/api-manager.test.js +311 -0
  818. package/test/helpers/backend-router.test.js +136 -0
  819. package/test/helpers/content/blog-auto-publisher.test.js +482 -0
  820. package/test/helpers/content/feed-parser.test.js +529 -0
  821. package/test/helpers/content/ghostii-blocks.test.js +135 -0
  822. package/test/helpers/content/ghostii-feed-integration.test.js +405 -0
  823. package/test/helpers/content/ghostii-write-article.test.js +244 -0
  824. package/test/helpers/dev-only-routes.test.js +225 -0
  825. package/test/helpers/env-reader.test.js +362 -0
  826. package/test/helpers/environment.test.js +263 -0
  827. package/test/helpers/event-middleware.test.js +407 -0
  828. package/test/helpers/infer-contact.test.js +157 -0
  829. package/test/helpers/lane-url.test.js +90 -0
  830. package/test/helpers/merge-line-files.test.js +272 -0
  831. package/test/helpers/metadata.test.js +122 -0
  832. package/test/helpers/middleware-request-log.test.js +134 -0
  833. package/test/helpers/middleware-user-log.test.js +124 -0
  834. package/test/helpers/payment/chargebee/parse-webhook.test.js +490 -0
  835. package/test/helpers/payment/chargebee/refund-details.test.js +198 -0
  836. package/test/helpers/payment/chargebee/to-unified-one-time.test.js +148 -0
  837. package/test/helpers/payment/chargebee/to-unified-subscription.test.js +649 -0
  838. package/test/helpers/payment/coinbase/create-intent.test.js +247 -0
  839. package/test/helpers/payment/coinbase/parse-webhook.test.js +195 -0
  840. package/test/helpers/payment/coinbase/refund-unsupported.test.js +141 -0
  841. package/test/helpers/payment/coinbase/to-unified-one-time.test.js +201 -0
  842. package/test/helpers/payment/discount-codes.test.js +141 -0
  843. package/test/helpers/payment/extract-resource.test.js +113 -0
  844. package/test/helpers/payment/fetch-failure.test.js +215 -0
  845. package/test/helpers/payment/license-gate.test.js +108 -0
  846. package/test/helpers/payment/order-id.test.js +99 -0
  847. package/test/helpers/payment/paypal/create-intent.test.js +382 -0
  848. package/test/helpers/payment/paypal/fetch-capture.test.js +123 -0
  849. package/test/helpers/payment/paypal/fetch-sale.test.js +183 -0
  850. package/test/helpers/payment/paypal/parse-webhook.test.js +678 -0
  851. package/test/helpers/payment/paypal/refund-details.test.js +202 -0
  852. package/test/helpers/payment/paypal/resolve-plan-id.test.js +108 -0
  853. package/test/helpers/payment/paypal/switch-plan.test.js +92 -0
  854. package/test/helpers/payment/paypal/to-unified-one-time.test.js +383 -0
  855. package/test/helpers/payment/paypal/to-unified-subscription.test.js +884 -0
  856. package/test/helpers/payment/stripe/fetch-charge.test.js +147 -0
  857. package/test/helpers/payment/stripe/parse-webhook.test.js +448 -0
  858. package/test/helpers/payment/stripe/refund-details.test.js +147 -0
  859. package/test/helpers/payment/stripe/to-unified-one-time.test.js +307 -0
  860. package/test/helpers/payment/stripe/to-unified-subscription.test.js +709 -0
  861. package/test/helpers/persona-domain.test.js +87 -0
  862. package/test/helpers/recaptcha.test.js +166 -0
  863. package/test/helpers/resolved-config.test.js +106 -0
  864. package/test/helpers/response-log-redaction.test.js +262 -0
  865. package/test/helpers/roles.test.js +155 -0
  866. package/test/helpers/route-context-debug-gate.test.js +118 -0
  867. package/test/helpers/route-context-logging.test.js +214 -0
  868. package/test/helpers/route-context.test.js +129 -0
  869. package/test/helpers/safe-compare.test.js +114 -0
  870. package/test/helpers/sanitize.test.js +223 -0
  871. package/test/helpers/schema-engine.test.js +537 -0
  872. package/test/helpers/schema-zod.test.js +554 -0
  873. package/test/helpers/seed-accounts-load.test.js +163 -0
  874. package/test/helpers/seed-google-personas.test.js +114 -0
  875. package/test/helpers/seeded-personas.test.js +823 -0
  876. package/test/helpers/settings.test.js +292 -0
  877. package/test/helpers/setup-engines-pin.test.js +68 -0
  878. package/test/helpers/setup-manifest-sync.test.js +116 -0
  879. package/test/helpers/slugify.test.js +395 -0
  880. package/test/helpers/storage.test.js +195 -0
  881. package/test/helpers/test-banner-latch.test.js +139 -0
  882. package/test/helpers/usage-consume.test.js +460 -0
  883. package/test/helpers/usage-log-privacy.test.js +121 -0
  884. package/test/helpers/user-doc-heal.test.js +540 -0
  885. package/test/helpers/user.test.js +771 -0
  886. package/test/helpers/webhook-forward.test.js +419 -0
  887. package/test/helpers/wipe-auth-project.test.js +143 -0
  888. package/test/mcp/discovery.test.js +52 -0
  889. package/test/mcp/oauth.test.js +160 -0
  890. package/test/mcp/protocol.test.js +278 -0
  891. package/test/mcp/roles.test.js +203 -0
  892. package/test/mcp/utils.test.js +245 -0
  893. package/test/notification/identity.test.js +155 -0
  894. package/test/routes/admin/create-post.test.js +362 -0
  895. package/test/routes/admin/database.test.js +133 -0
  896. package/test/routes/admin/deduplicate-image-alts.test.js +191 -0
  897. package/test/routes/admin/email-content-html.test.js +58 -0
  898. package/test/routes/admin/email-request-log.test.js +86 -0
  899. package/test/routes/admin/email.test.js +116 -0
  900. package/test/routes/admin/firestore-query.test.js +206 -0
  901. package/test/routes/admin/firestore.test.js +129 -0
  902. package/test/routes/admin/infer-contact.test.js +220 -0
  903. package/test/routes/admin/notification.test.js +199 -0
  904. package/test/routes/admin/post-convert-image.test.js +159 -0
  905. package/test/routes/admin/post-deploy-flag.test.js +81 -0
  906. package/test/routes/admin/post-download-error.test.js +90 -0
  907. package/test/routes/admin/post-resize-image.test.js +185 -0
  908. package/test/routes/admin/post.test.js +369 -0
  909. package/test/routes/admin/repo-content.test.js +223 -0
  910. package/test/routes/admin/stats.test.js +114 -0
  911. package/test/routes/admin/users-disable.test.js +63 -0
  912. package/test/routes/admin/users-list.test.js +71 -0
  913. package/test/routes/content/post.test.js +60 -0
  914. package/test/routes/general/uuid.test.js +133 -0
  915. package/test/routes/health.test.js +110 -0
  916. package/test/routes/marketing/campaign.test.js +184 -0
  917. package/test/routes/marketing/contact.test.js +416 -0
  918. package/test/routes/marketing/email-preferences.test.js +293 -0
  919. package/test/routes/marketing/push-send.test.js +34 -0
  920. package/test/routes/marketing/webhook-forward.test.js +63 -0
  921. package/test/routes/marketing/webhook.test.js +641 -0
  922. package/test/routes/payments/_route-harness.js +145 -0
  923. package/test/routes/payments/cancel-provider-errors.test.js +227 -0
  924. package/test/routes/payments/cancel-skip-guards.test.js +126 -0
  925. package/test/routes/payments/cancel-trialing.test.js +428 -0
  926. package/test/routes/payments/cancel.test.js +165 -0
  927. package/test/routes/payments/dedup-race.test.js +186 -0
  928. package/test/routes/payments/discount.test.js +82 -0
  929. package/test/routes/payments/dispute-alert.test.js +324 -0
  930. package/test/routes/payments/intent-discount-amounts.test.js +550 -0
  931. package/test/routes/payments/intent-discount-coupons.test.js +293 -0
  932. package/test/routes/payments/intent-one-time-metadata.test.js +122 -0
  933. package/test/routes/payments/intent-purchaser-guard.test.js +139 -0
  934. package/test/routes/payments/intent-zero-total.test.js +260 -0
  935. package/test/routes/payments/intent.test.js +413 -0
  936. package/test/routes/payments/plan.test.js +367 -0
  937. package/test/routes/payments/portal-return-url.test.js +107 -0
  938. package/test/routes/payments/portal.test.js +94 -0
  939. package/test/routes/payments/refund-one-time.test.js +254 -0
  940. package/test/routes/payments/refund-paypal-proration.test.js +190 -0
  941. package/test/routes/payments/refund.test.js +181 -0
  942. package/test/routes/payments/trial-eligibility.test.js +73 -0
  943. package/test/routes/payments/uncancel.test.js +270 -0
  944. package/test/routes/payments/webhook-stripe-invoice.test.js +129 -0
  945. package/test/routes/payments/webhook-stripe-refund-one-time.test.js +130 -0
  946. package/test/routes/payments/webhook-test-provider.test.js +156 -0
  947. package/test/routes/payments/webhook.test.js +115 -0
  948. package/test/routes/payments/winback-stripe-coupon.test.js +126 -0
  949. package/test/routes/payments/winback.test.js +597 -0
  950. package/test/routes/test/authenticate.test.js +79 -0
  951. package/test/routes/test/redirect.test.js +66 -0
  952. package/test/routes/test/reset-account.test.js +126 -0
  953. package/test/routes/test/roster.test.js +101 -0
  954. package/test/routes/test/schema.test.js +556 -0
  955. package/test/routes/test/usage.test.js +374 -0
  956. package/test/routes/user/api-keys.test.js +158 -0
  957. package/test/routes/user/connections-grant.test.js +759 -0
  958. package/test/routes/user/connections-identity.test.js +260 -0
  959. package/test/routes/user/connections-log-privacy.test.js +206 -0
  960. package/test/routes/user/connections-refresh-lease.test.js +696 -0
  961. package/test/routes/user/connections-return.test.js +261 -0
  962. package/test/routes/user/connections-uid.test.js +516 -0
  963. package/test/routes/user/delete.test.js +138 -0
  964. package/test/routes/user/feedback.test.js +106 -0
  965. package/test/routes/user/orders.test.js +233 -0
  966. package/test/routes/user/sessions.test.js +231 -0
  967. package/test/routes/user/settings-validate.test.js +84 -0
  968. package/test/routes/user/signup-emails.test.js +113 -0
  969. package/test/routes/user/signup-location.test.js +84 -0
  970. package/test/routes/user/signup-log-privacy.test.js +112 -0
  971. package/test/routes/user/signup.test.js +579 -0
  972. package/test/routes/user/subscription.test.js +101 -0
  973. package/test/routes/user/token.test.js +111 -0
  974. package/test/routes/user/user.test.js +158 -0
  975. package/test/routes/verts/cache.test.js +78 -0
  976. package/test/routes/verts/click-destination.test.js +76 -0
  977. package/test/routes/verts/crud.test.js +194 -0
  978. package/test/routes/verts/redirect.test.js +147 -0
  979. package/test/routes/verts/selection.test.js +237 -0
  980. package/test/routes/verts/serve.test.js +185 -0
  981. package/test/routes/verts/unit-document.test.js +117 -0
  982. package/test/rules/_environment.js +66 -0
  983. package/test/rules/brand-merge.test.js +195 -0
  984. package/test/rules/field-helpers.test.js +309 -0
  985. package/test/rules/notifications.test.js +577 -0
  986. package/test/rules/payments-carts.test.js +406 -0
  987. package/test/rules/sessions.test.js +170 -0
  988. package/test/rules/user.test.js +517 -0
  989. package/test/rules/verts.test.js +101 -0
  990. package/test/security/fetch-log-secrets.test.js +124 -0
  991. package/test/security/primitives.test.js +105 -0
  992. package/test/security/repo-pinning.test.js +77 -0
  993. package/test/stripe-live/subscription-lifecycle.test.js +141 -0
@@ -0,0 +1,687 @@
1
+ const discountCodes = require('../../../libraries/payment/discount-codes.js');
2
+ const isTrialing = require('../../../routes/payments/cancel/_is-trialing.js');
3
+ const { deliverConversion } = require('../../../libraries/analytics/conversions.js');
4
+ const { buildAttributionContext, buildIdentity } = require('../../../libraries/analytics/match-data.js');
5
+
6
+ /**
7
+ * Payment analytics tracking
8
+ *
9
+ * The webhook decides WHAT happened; `@omega.js/analytics`' catalog decides what
10
+ * each platform calls it and what shape it takes
11
+ * ([#385](https://github.com/Omega-JS-Stack/omega/issues/385)). So this file maps
12
+ * a transition to a CANONICAL event name plus canonical params, and hands both to
13
+ * the conversion delivery library — which walks the adapters and the three
14
+ * platform APIs. No provider dialect lives here any more.
15
+ *
16
+ * Two independent concerns:
17
+ * 1. Transition events (mutually exclusive, one per webhook):
18
+ * new-subscription (no trial) → purchase
19
+ * new-subscription (trial) → trial_start
20
+ * subscription-winback → purchase
21
+ * payment-recovered → payment_recovered
22
+ * subscription-cancelled (paid) → subscription_cancel
23
+ * subscription-cancelled (in trial) → trial_lapse
24
+ * subscription-cancelled (after a lapse) → nothing
25
+ * cancellation-removed → subscription_uncancel
26
+ * plan-changed → subscription_plan_change
27
+ * payment-failed (in trial) → trial_lapse
28
+ * payment-refunded → refund
29
+ * purchase-completed → purchase (one-time)
30
+ * purchase-refunded → refund (one-time)
31
+ *
32
+ * 2. Payment events (fire whenever money changes hands, including renewals):
33
+ * first charge after a trial → trial_convert
34
+ * subscription renewal → subscription_renew
35
+ *
36
+ * ONE LIMIT OF THE TRIAL EVENTS, deliberate. This resolver reads the subscription's
37
+ * TERM, never an invoice amount (the same reason PayPal's transform refuses to read
38
+ * the payment record — a day-zero setup fee exists during a trial and is not a
39
+ * billing period, docs/payment-system.md), so a real charge taken INSIDE the trial
40
+ * term books nothing at all.
41
+ *
42
+ * `trial_lapse` covers EVERY way a trial ends without paying, in whatever shape the
43
+ * provider announces it: the failed charge that suspends the subscription, and the
44
+ * cancellation that ends it outright. Chargebee cancels a trial with no card on
45
+ * file, and a subscriber on any provider may simply quit mid-trial. The check reads
46
+ * the unified term, never a provider name, so the two arrive as one story
47
+ * ([#414](https://github.com/Omega-JS-Stack/omega/issues/414)). And a trial that
48
+ * already lapsed is not told twice: the cancellation that ends a dunning suspension
49
+ * whose trial never converted books nothing, because the decline that suspended it
50
+ * already reported the outcome.
51
+ *
52
+ * Which leaves `subscription_cancel` to the cancellations a trial does not
53
+ * explain: a subscriber who left their trial behind, and one who never had a trial
54
+ * at all. It is no longer where a never-paid trialist lands, which is the claim this
55
+ * file can actually stand behind.
56
+ *
57
+ * This file covers the outcomes a PROVIDER announces. The ones it never announces
58
+ * — most of all PayPal's, which fires no trial-end event at all — are reported by
59
+ * the daily trial-lapse sweep (`events/cron/daily/trial-lapse-sweep.js`) at the
60
+ * moment it stamps `trial.outcome`, through the same delivery path and keyed on the
61
+ * same subscription-scoped event id, so the two can never both report one outcome.
62
+ *
63
+ * `cancellation-requested` stays deliberately event-less: a schedule changed,
64
+ * nothing ended and no money moved — the subscription is still active and may
65
+ * never cancel at all. Its WITHDRAWAL is not the mirror image of that, which is
66
+ * why the pair is not symmetric: keeping a subscriber is an outcome
67
+ * ([#407](https://github.com/Omega-JS-Stack/omega/issues/407)).
68
+ *
69
+ * A `payment-failed` outside a trial stays event-less too: the provider is still
70
+ * retrying, and dunning is not yet an outcome. Only `subscription-cancelled` and
71
+ * `payment-recovered` say how it ended.
72
+ */
73
+
74
+ /**
75
+ * Track payment events across analytics platforms (non-blocking)
76
+ *
77
+ * @param {object} options
78
+ * @param {string} options.category - 'subscription' | 'one-time'
79
+ * @param {string|null} options.transitionName - The detected transition
80
+ * @param {string} options.eventType - The provider's webhook event name
81
+ * @param {object} options.unified - The unified subscription/purchase object
82
+ * @param {object} options.order - The order doc about to be written (attribution, request, consent)
83
+ * @param {object} [options.userDoc] - The owner's user doc — the email/phone the match data hashes
84
+ * @param {object|null} [options.refundDetails] - The library's { amount, currency, reason }
85
+ * @param {object|null} [options.before] - The subscription as it stood BEFORE this event
86
+ * @param {string} options.uid - The owner
87
+ * @param {string} options.provider - The provider that sent the webhook
88
+ * @param {string|null} [options.chargeId] - THIS charge's own provider id, where the
89
+ * event names one (Stripe/Chargebee invoice, PayPal sale) — the id one charge is
90
+ * reported under ([#656](https://github.com/Omega-JS-Stack/omega/issues/656))
91
+ * @param {object} options.ctx - The event context
92
+ */
93
+ function trackPayment({ category, transitionName, eventType, unified, order, userDoc, refundDetails, before, uid, provider, chargeId, ctx }) {
94
+ const Manager = ctx.Manager;
95
+ const config = Manager.config;
96
+
97
+ try {
98
+ // Resolve what kind of payment event this is
99
+ const resolved = resolvePaymentEvent(category, transitionName, eventType, unified, order, refundDetails, before);
100
+
101
+ if (!resolved) {
102
+ ctx.log(`trackPayment: skipped — no trackable event (category=${category}, transition=${transitionName || 'null'}, eventType=${eventType})`);
103
+ return;
104
+ }
105
+
106
+ const currency = config.payment?.currency || 'USD';
107
+
108
+ ctx.log(`trackPayment: event=${resolved.event}, reason=${resolved.reason}, value=${resolved.value}, currency=${currency}, product=${resolved.productId}, uid=${uid}, provider=${provider}`);
109
+
110
+ deliverConversion({
111
+ event: resolved.event,
112
+ params: buildParams({ resolved, currency, provider, order, chargeId }),
113
+ attribution: buildAttributionContext(order?.attribution),
114
+ // The doc goes in whole: the ONE reader takes every match parameter off
115
+ // it, so this call site never decides which key an address lives under
116
+ // ([#577](https://github.com/Omega-JS-Stack/omega/issues/577)).
117
+ identity: buildIdentity({
118
+ uid,
119
+ user: userDoc,
120
+ request: order?.request,
121
+ }),
122
+ trackingConsent: order?.trackingConsent,
123
+ eventId: resolveEventId(resolved, order),
124
+ ctx,
125
+ Manager,
126
+ });
127
+ } catch (e) {
128
+ ctx.error(`trackPayment failed: ${e.message}`, e);
129
+ }
130
+ }
131
+
132
+ /**
133
+ * The canonical commerce params every money event carries.
134
+ * GA4's vocabulary IS the canonical one; Meta and TikTok reshape it in the catalog.
135
+ */
136
+ function buildParams({ resolved, currency, provider, order, chargeId }) {
137
+ return {
138
+ transaction_id: resolveTransactionId({ resolved, order, chargeId }),
139
+ value: resolved.value,
140
+ currency: currency,
141
+ items: [{
142
+ item_id: resolved.productId,
143
+ item_name: resolved.productName,
144
+ price: resolved.value,
145
+ quantity: 1,
146
+ }],
147
+ payment_provider: provider,
148
+ payment_frequency: resolved.frequency,
149
+ is_trial: resolved.isTrial,
150
+ is_recurring: resolved.isRecurring,
151
+ // The plan a switch came FROM — only a plan change has one, and `items` above
152
+ // is always the plan they moved TO
153
+ // ([#407](https://github.com/Omega-JS-Stack/omega/issues/407)).
154
+ ...(resolved.previous ? {
155
+ previous_item_id: resolved.previous.productId,
156
+ previous_item_name: resolved.previous.productName,
157
+ previous_value: resolved.previous.value,
158
+ } : {}),
159
+ };
160
+ }
161
+
162
+ // The money events with a BROWSER half. Both halves can only ever agree on the
163
+ // ORDER — the browser holds nothing else — and both are the FIRST charge of that
164
+ // order, so the order id names exactly one charge here.
165
+ const ORDER_KEYED_EVENTS = ['purchase', 'trial_start'];
166
+
167
+ // The events that report a charge the ORDER cannot name: a subscription bills
168
+ // again and again against one order, so each of these carries the provider's own
169
+ // id for the charge it is about.
170
+ const CHARGE_KEYED_EVENTS = ['trial_convert', 'subscription_renew', 'payment_recovered', 'refund'];
171
+
172
+ /**
173
+ * The `transaction_id` this fire carries — ONE id per CHARGE, never the
174
+ * subscription's (Ian 2026-08-27,
175
+ * [#656](https://github.com/Omega-JS-Stack/omega/issues/656)).
176
+ *
177
+ * GA4 deduplicates `purchase` on `transaction_id`
178
+ * (https://support.google.com/analytics/answer/12313109), and `trial_convert`,
179
+ * `subscription_renew` and `payment_recovered` all ride GA4's standard
180
+ * `purchase`. The subscription id this used to send is CONSTANT for the life of
181
+ * the subscription, so GA4 counted the first charge and dropped every renewal
182
+ * after it as a duplicate: recurring revenue read as one charge per subscriber.
183
+ *
184
+ * So the id is the charge's:
185
+ * - first purchase (a paid checkout, a one-time buy, or a trial start) — the
186
+ * ORDER id, which the confirmation page holds too, so the browser half and
187
+ * the webhook half deduplicate into one purchase instead of two;
188
+ * - renewal, recovery and trial conversion — the provider's own id for that
189
+ * charge (Stripe/Chargebee invoice, PayPal sale);
190
+ * - refund — the id of the charge it reverses, so GA4 nets the two.
191
+ *
192
+ * A ONE-TIME refund is the exception to that last line: a one-time order has
193
+ * exactly ONE charge, the purchase it reverses was reported under the order id,
194
+ * and no provider names a charge on its one-time refund at all — so it names the
195
+ * order and GA4 nets the two. Only one-time: a subscription bills again and
196
+ * again against one order, so an order-keyed refund would net a renewal's
197
+ * reversal against the first purchase.
198
+ *
199
+ * A charge event whose payload names no charge id falls back to the WEBHOOK
200
+ * delivery, never to the order: the delivery is unique per charge, and the order
201
+ * is the collapse this fixes. THE ONE KNOWN GAP: a refund of a SUBSCRIPTION's
202
+ * first charge names that first invoice, while the purchase it reverses was
203
+ * recorded under the order id — nothing in the refund payload can tell a first
204
+ * invoice from a renewal's, so GA4 books the refund without netting it.
205
+ *
206
+ * Everything else (a cancellation, an uncancel, a plan change, a lapsed trial)
207
+ * is a GA4 CUSTOM event with no dedupe of its own, and its subject really is the
208
+ * subscription — so those keep naming it.
209
+ *
210
+ * @param {object} options
211
+ * @param {object} options.resolved - The resolved payment event
212
+ * @param {object} [options.order] - The order doc this event is about
213
+ * @param {string|null} [options.chargeId] - The provider's id for this charge
214
+ * @returns {string|undefined}
215
+ */
216
+ function resolveTransactionId({ resolved, order, chargeId }) {
217
+ if (ORDER_KEYED_EVENTS.includes(resolved.event)) {
218
+ return order?.id || resolved.resourceId;
219
+ }
220
+
221
+ if (CHARGE_KEYED_EVENTS.includes(resolved.event)) {
222
+ // The one-time refund: the order names the single charge it reverses, and
223
+ // that purchase was reported under the order id.
224
+ if (resolved.event === 'refund' && resolved.category === 'one-time') {
225
+ return order?.id || resolved.resourceId;
226
+ }
227
+
228
+ return chargeId || order?.metadata?.updatedBy?.event?.id || resolved.resourceId;
229
+ }
230
+
231
+ return resolved.resourceId;
232
+ }
233
+
234
+ /**
235
+ * The platform dedupe id for this fire.
236
+ *
237
+ * `purchase` and `trial_start` are keyed on the ORDER, because they are the
238
+ * payment events with a BROWSER twin: the confirmation page fires its own pixel
239
+ * ([#386]) and the only id it can possibly compute is `<canonical>.<orderId>` —
240
+ * the webhook's event id never reaches a browser. Two different ids for one
241
+ * conversion is a double count, which is the whole thing dedupe exists to
242
+ * prevent. `trial_start` joined this branch with
243
+ * [#654](https://github.com/Omega-JS-Stack/omega/issues/654): the browser used
244
+ * to fire `purchase` for a trial checkout while this fired `trial_start`, and
245
+ * two different event NAMES never deduplicate at all, so a $0 trial booked the
246
+ * plan's price as browser revenue. Both halves now say trial_start, and the
247
+ * webhook-delivery fallback that used to key this is gone — a browser cannot
248
+ * derive it. Reusing an order id is safe: Meta's and TikTok's dedupe windows are
249
+ * ~48h, and the only way one order sees a second purchase is a win-back weeks or
250
+ * months later, long outside any window.
251
+ *
252
+ * Everything else keys on the WEBHOOK delivery. A subscription renews against
253
+ * the same order id month after month, so an order-keyed id would have the
254
+ * platforms discard every renewal after the first as a duplicate — and none of
255
+ * these events has a browser twin to match anyway. A redelivery of the SAME
256
+ * webhook carries the same id, which is exactly what dedupe is for.
257
+ */
258
+ function resolveEventId(resolved, order) {
259
+ if (ORDER_KEYED_EVENTS.includes(resolved.event)) {
260
+ return `${resolved.event}.${order?.id}`;
261
+ }
262
+
263
+ // A trial has exactly ONE outcome, and two different paths can be the one to see
264
+ // it: this webhook when the provider announces it, and the trial-lapse sweep
265
+ // when no webhook ever comes (PayPal fires no trial-end event at all). Keying
266
+ // both on the SUBSCRIPTION means a race between them is one conversion to every
267
+ // platform that deduplicates, never two
268
+ // ([#407](https://github.com/Omega-JS-Stack/omega/issues/407)).
269
+ if (resolved.event === 'trial_convert' || resolved.event === 'trial_lapse') {
270
+ return `${resolved.event}.${resolved.resourceId}`;
271
+ }
272
+
273
+ return `${resolved.event}.${order?.metadata?.updatedBy?.event?.id || order?.id}`;
274
+ }
275
+
276
+ // ---------------------------------------------------------------------------
277
+ // Event resolution
278
+ // ---------------------------------------------------------------------------
279
+
280
+ /**
281
+ * Determine what kind of payment event occurred and extract common fields
282
+ *
283
+ * Returns null if nothing should be tracked. `event` is the canonical catalog
284
+ * name; `reason` is the finer-grained why, which several canonical names share
285
+ * (three different reasons are all a `purchase`) and which the logs read.
286
+ *
287
+ * @param {string} category - 'subscription' | 'one-time'
288
+ * @param {string|null} transitionName - The detected transition
289
+ * @param {string} eventType - The provider's webhook event name
290
+ * @param {object} unified - The unified subscription/purchase object
291
+ * @param {object} order - The order doc
292
+ * @param {object|null} [refundDetails] - The library's { amount, currency, reason }
293
+ * @param {object|null} [before] - The subscription as it stood BEFORE this event
294
+ * @returns {object|null}
295
+ */
296
+ function resolvePaymentEvent(category, transitionName, eventType, unified, order, refundDetails, before) {
297
+ const productId = unified.product?.id;
298
+ const productName = unified.product?.name;
299
+ const frequency = unified.payment?.frequency || null;
300
+ const isTrial = unified.trial?.claimed === true;
301
+ const resourceId = unified.payment?.resourceId;
302
+ const price = parseFloat(unified.payment?.price || 0);
303
+
304
+ // Compute actual amount paid (accounting for trial and discount)
305
+ const value = resolveActualValue(price, isTrial, order?.discount);
306
+
307
+ // `category` rides along: a refund's transaction id turns on it (a one-time
308
+ // order names one charge, a subscription names many —
309
+ // [#656](https://github.com/Omega-JS-Stack/omega/issues/656)).
310
+ const base = { category, productId, productName, frequency, resourceId, isTrial };
311
+
312
+ // --- Refunds (both categories) ---
313
+ // Detected off the TRANSITION rather than the event type, so the transition
314
+ // layer's redelivery guard covers this too: a webhook doc that already
315
+ // completed once detects no transition, and reports no second refund.
316
+ if (transitionName === 'payment-refunded' || transitionName === 'purchase-refunded') {
317
+ return {
318
+ ...base,
319
+ event: 'refund',
320
+ reason: 'refund',
321
+ // What actually went back to the customer — the provider's own number,
322
+ // and only the price as a last resort (a partial refund reported as the
323
+ // full price would overstate the reversal).
324
+ value: resolveRefundValue(refundDetails, unified, price),
325
+ isRecurring: false,
326
+ };
327
+ }
328
+
329
+ // --- Subscription transitions ---
330
+ if (category === 'subscription') {
331
+ if (transitionName === 'new-subscription' && isTrial) {
332
+ return { ...base, event: 'trial_start', reason: 'trial-started', value: 0, isRecurring: false };
333
+ }
334
+
335
+ if (transitionName === 'new-subscription') {
336
+ return { ...base, event: 'purchase', reason: 'first-purchase', value, isRecurring: false };
337
+ }
338
+
339
+ // A win-back is a returning customer buying again — a purchase, at what they
340
+ // actually paid. Its checkout arrives on a payment event, so without this the
341
+ // renewal branch below claimed it and reported recurring revenue ([#218]).
342
+ if (transitionName === 'subscription-winback') {
343
+ return { ...base, event: 'purchase', reason: 'winback-purchase', value, isRecurring: false };
344
+ }
345
+
346
+ if (transitionName === 'payment-recovered') {
347
+ return { ...base, event: 'payment_recovered', reason: 'payment-recovered', value: price, isRecurring: true };
348
+ }
349
+
350
+ // The cancellation that TOOK EFFECT. No money moves, so it is not recurring
351
+ // revenue — the value is the subscription's price, which is what the churn
352
+ // cost, and what an ad platform optimizing away from churn needs to see.
353
+ //
354
+ // Inside the trial term it is no churn at all: nobody who never paid can be lost
355
+ // revenue. It is the trial's own outcome, and it reaches here on every provider.
356
+ // Chargebee ends a no-card trial by cancelling it (booked as paid churn until
357
+ // [#414]), and a Stripe or PayPal subscriber may simply quit mid-trial. The PRIOR
358
+ // state's term is what says which of the two this is, exactly as it does for the
359
+ // payment-failed lapse below.
360
+ //
361
+ // Except the prior state is a stored DELIVERY, and a degraded one carries no term
362
+ // (see `isInsideTrial`), which reads as a trial this subscriber left long ago. So
363
+ // the cancelled payload's own evidence overrules it: a term that outlived the
364
+ // trial's end belongs to somebody who paid, whatever the record on file lost.
365
+ //
366
+ // And a trial that already lapsed says nothing a second time. The provider
367
+ // suspends the subscription at the failed charge (reported there, as the lapse),
368
+ // then exhausts dunning and cancels what it was holding: one outcome, two
369
+ // webhooks, and booking this one too put paid-churn revenue and an ad-platform
370
+ // audience signal behind a customer who never paid.
371
+ if (transitionName === 'subscription-cancelled') {
372
+ const outlived = outlivedItsTrial(unified, before);
373
+
374
+ if (!outlived && isInsideTrial(before)) {
375
+ return { ...base, event: 'trial_lapse', reason: 'trial-cancelled', value: price, isRecurring: false };
376
+ }
377
+
378
+ if (!outlived && isLapsedTrialSuspension(before)) {
379
+ return null;
380
+ }
381
+
382
+ return { ...base, event: 'subscription_cancel', reason: 'subscription-cancelled', value: price, isRecurring: false };
383
+ }
384
+
385
+ // The scheduled cancellation the subscriber took back. No money moves, so the
386
+ // value is the subscription that was KEPT — what the retention win is worth,
387
+ // and the mirror of the churn number the cancellation above reports.
388
+ if (transitionName === 'cancellation-removed') {
389
+ return { ...base, event: 'subscription_uncancel', reason: 'cancellation-removed', value: price, isRecurring: false };
390
+ }
391
+
392
+ // An upgrade or a downgrade. No money moves at the switch itself (the
393
+ // provider prorates on its own schedule), so the value is what the customer
394
+ // now pays — and the plan they came from rides along, which is the only way
395
+ // the direction is readable without a second event.
396
+ if (transitionName === 'plan-changed') {
397
+ return {
398
+ ...base,
399
+ event: 'subscription_plan_change',
400
+ reason: 'plan-changed',
401
+ value: price,
402
+ isRecurring: false,
403
+ previous: {
404
+ productId: before?.product?.id || null,
405
+ productName: before?.product?.name || null,
406
+ value: parseFloat(before?.payment?.price || 0),
407
+ },
408
+ };
409
+ }
410
+
411
+ // The trial that ended without ever paying. It has the same active → suspended
412
+ // shape a routine dunning failure has, so the PRIOR state is what tells them
413
+ // apart: only a subscription still inside its trial had been charged nothing at
414
+ // all. The value is the subscription that never started paying.
415
+ if (transitionName === 'payment-failed' && isInsideTrial(before)) {
416
+ return { ...base, event: 'trial_lapse', reason: 'trial-lapsed', value: price, isRecurring: false };
417
+ }
418
+
419
+ // The first REAL charge after a trial. It arrives exactly like a renewal — no
420
+ // transition, a payment event — so before [#407] it was reported as one, and
421
+ // the funnel's most valuable step was indistinguishable from month two.
422
+ if (isTrialConversion({ transitionName, eventType, unified, before })) {
423
+ return {
424
+ ...base,
425
+ event: 'trial_convert',
426
+ reason: 'trial-converted',
427
+ // A checkout discount lands on exactly this invoice — the trial's own $0
428
+ // is behind us, so this is the one charge it can apply to.
429
+ value: resolveActualValue(price, false, order?.discount),
430
+ isRecurring: false,
431
+ };
432
+ }
433
+
434
+ // No transition but a payment event fired (renewal)
435
+ // Renewals always use full price (discount is one-time only)
436
+ //
437
+ // A subscription still INSIDE its trial is never renewing: the trial-start
438
+ // invoice is a payment event for $0, and this branch reads the PLAN price, so
439
+ // it booked a full month of revenue for an invoice that charged nothing
440
+ // ([#407](https://github.com/Omega-JS-Stack/omega/issues/407)). A real renewal
441
+ // is outside the trial by definition, so the clause cannot touch one. Which is
442
+ // why the payload it reads is weighed against the term the subscription already
443
+ // had ([#414]): a thin delivery must not read as a trial and swallow a renewal.
444
+ if (!transitionName && isPaymentEvent(eventType) && price > 0 && !isInsideTrial(unified, before)) {
445
+ return { ...base, event: 'subscription_renew', reason: 'renewal', value: price, isRecurring: true };
446
+ }
447
+
448
+ return null;
449
+ }
450
+
451
+ // --- One-time transitions ---
452
+ if (category === 'one-time') {
453
+ if (transitionName === 'purchase-completed') {
454
+ return { ...base, event: 'purchase', reason: 'one-time-purchase', value, isRecurring: false, productId: productId || 'unknown', productName: productName || 'Unknown' };
455
+ }
456
+
457
+ return null;
458
+ }
459
+
460
+ return null;
461
+ }
462
+
463
+ /**
464
+ * Is this charge the trial's CONVERSION — the first real money on a subscription
465
+ * that ever claimed one?
466
+ *
467
+ * TWO DURABLE FACTS DECIDE IT, and neither is the order the provider delivered in:
468
+ * the subscription EVER claimed a trial, and its trial has no outcome on record yet.
469
+ * Whichever webhook lands first, both facts read the same.
470
+ *
471
+ * The doc's in-trial-ness at charge time used to be the signal, and it is not one:
472
+ * Stripe delivered `customer.subscription.updated` (trial over, term pushed out) a
473
+ * full hour before `invoice.payment_succeeded`, so by charge time the stored doc had
474
+ * already left the trial and the conversion booked as a month-two renewal
475
+ * ([#697](https://github.com/Omega-JS-Stack/omega/issues/697)). The two webhooks race
476
+ * in real time, so an arbitrary share of conversions mislabeled — revenue stayed
477
+ * right (both ride the same charge-keyed purchase), the funnel's most valuable step
478
+ * did not.
479
+ *
480
+ * `trial.claimed` says only that a trial HAPPENED — it stays true for the life of the
481
+ * subscription — so `trial.outcome` is what says the first paid charge is behind us
482
+ * (the schema's own "one stored conversion signal"). It is written by whichever path
483
+ * sees the trial end: this pipeline stamps `converted` on the very charge this
484
+ * function claims, and the trial-lapse sweep stamps whatever the provider confirms
485
+ * when no webhook ever comes. Month two reads the stamp and books an ordinary
486
+ * renewal. THE LIMIT: a subscription that converted before the stamp existed carries
487
+ * none, so its next renewal books one conversion — once, and only for trials already
488
+ * converted on the day this shipped.
489
+ *
490
+ * The trial still has to be OVER, which is what declines the $0 invoice a trial
491
+ * STARTS with: that one carries no transition either, but the subscription is inside
492
+ * its trial on both sides of it, so nothing converted and nothing was charged.
493
+ *
494
+ * @param {object} options
495
+ * @param {string|null} options.transitionName - The detected transition (a conversion has none)
496
+ * @param {string} options.eventType - The provider's webhook event name
497
+ * @param {object} options.unified - The subscription the provider just delivered
498
+ * @param {object|null} [options.before] - The stored subscription this event arrived at
499
+ * @returns {boolean}
500
+ */
501
+ function isTrialConversion({ transitionName, eventType, unified, before }) {
502
+ if (transitionName || !isPaymentEvent(eventType) || parseFloat(unified?.payment?.price || 0) <= 0) {
503
+ return false;
504
+ }
505
+
506
+ const claimedATrial = unified?.trial?.claimed === true || before?.trial?.claimed === true;
507
+
508
+ return !!(claimedATrial && !before?.trial?.outcome && !isInsideTrial(unified, before));
509
+ }
510
+
511
+ /**
512
+ * Is this subscription still INSIDE its free trial, for reporting purposes?
513
+ *
514
+ * `trial.claimed` only ever means "this subscription HAD a trial" — it stays true
515
+ * for the life of the subscription (the trial-lapse sweep exists for exactly that
516
+ * reason), so on its own it can never tell a trial's own outcome from a renewal or
517
+ * a dunning failure months later. "Still inside the trial" has ONE definition,
518
+ * `routes/payments/cancel/_is-trialing.js`, and this DELEGATES to it rather than
519
+ * keeping a second copy of the comparison.
520
+ *
521
+ * The one widening, and only here: Chargebee's in-trial payload names no
522
+ * `current_term_end` at all, so `expires` folds to the epoch and the timestamp
523
+ * match cannot see the trial — every Chargebee conversion read as a renewal and
524
+ * every Chargebee lapse reported nothing
525
+ * ([#407](https://github.com/Omega-JS-Stack/omega/issues/407)). An epoch expiry is
526
+ * the ABSENCE of a term, never a term that ended in 1970, so a claimed trial with a
527
+ * real trial expiry and no term at all is still a trial.
528
+ *
529
+ * That widening stays OUT of the shared predicate on purpose: it also waives the
530
+ * cancel route's 24-hour guard, and a guard must never be waived by missing data
531
+ * ([#267](https://github.com/Omega-JS-Stack/omega/issues/267)). Reporting has no
532
+ * such stake — the worst case is a funnel number, not an unguarded cancellation.
533
+ *
534
+ * And the widening is BOUNDED by the state the subscription arrived from, when the
535
+ * caller has one. A degraded payload carries no term either: the stale fallback
536
+ * hands over the webhook's own body when the provider API is unreachable
537
+ * ([#222](https://github.com/Omega-JS-Stack/omega/issues/222)), and an active PAID
538
+ * subscription in that body is shaped exactly like a Chargebee trial, with the same
539
+ * claimed trial and the same epoch expiry. Nothing in the payload can tell them
540
+ * apart, so the PRIOR state does: a subscription already paid through a real term
541
+ * did not lose that term by trialing, so a term missing THERE is a hole in the
542
+ * delivery, not the absence of a term. Unbounded, a renewal on that path matched no
543
+ * branch at all and booked nothing: real revenue, silently unreported
544
+ * ([#414](https://github.com/Omega-JS-Stack/omega/issues/414)).
545
+ *
546
+ * The bound needs one HEALTHY delivery behind it, which is its limit: two degraded
547
+ * deliveries in a row leave no term on either side, so that renewal still books
548
+ * nothing. Narrower than the hole it closes, and it fails toward silence rather than
549
+ * toward a fabricated number.
550
+ *
551
+ * @param {object|null} [subscription] - A subscription object
552
+ * @param {object|null} [before] - The state it arrived FROM, when the question is
553
+ * about a payload a provider just delivered. Omitted, the widening is unbounded,
554
+ * which is what the trial-lapse sweep wants: it asks about a STORED subscription,
555
+ * which has no delivery behind it to be degraded.
556
+ * @returns {boolean}
557
+ */
558
+ function isInsideTrial(subscription, before) {
559
+ if (isTrialing(subscription)) {
560
+ return true;
561
+ }
562
+
563
+ if (before?.expires?.timestampUNIX) {
564
+ return false;
565
+ }
566
+
567
+ return !!(subscription?.trial?.claimed
568
+ && subscription?.status === 'active'
569
+ && subscription?.trial?.expires?.timestampUNIX
570
+ && !subscription?.expires?.timestampUNIX);
571
+ }
572
+
573
+ /**
574
+ * Does the payload in hand PROVE this subscription outlived its trial?
575
+ *
576
+ * The trial rules read the prior state, and the prior state is a stored delivery
577
+ * like any other, and a degraded one lost its term, which reads as a trial
578
+ * ([#414](https://github.com/Omega-JS-Stack/omega/issues/414)). This is the evidence
579
+ * that overrules it, taken from the payload the provider just sent: a term reaching
580
+ * PAST the trial's end can only have been paid for. It says nothing when either date
581
+ * is missing, which is the honest answer: an absent term proves nothing either way.
582
+ *
583
+ * A cancelled trial never trips it, on any provider: Stripe cancels one with the
584
+ * period still set to the trial's own end (equal, not past), and Chargebee cancels
585
+ * one with no term at all (`test/fixtures/chargebee/subscription-in-trial.json`).
586
+ *
587
+ * @param {object|null} [subscription] - The payload the provider just delivered
588
+ * @param {object|null} [before] - The state it arrived FROM
589
+ * @returns {boolean}
590
+ */
591
+ function outlivedItsTrial(subscription, before) {
592
+ const trialExpires = before?.trial?.expires?.timestampUNIX;
593
+ const expires = subscription?.expires?.timestampUNIX;
594
+
595
+ return !!(trialExpires && expires && expires > trialExpires);
596
+ }
597
+
598
+ /**
599
+ * Is this the suspension a LAPSED trial leaves behind?
600
+ *
601
+ * A trial that never converted and a subscriber who paid for months land in the same
602
+ * `suspended` state, and the term tells them apart exactly the way it does for a live
603
+ * subscription: a trial that never converted never moved its term past the trial's
604
+ * end, and a Chargebee one never had a term at all.
605
+ *
606
+ * This is not `isInsideTrial` with another status: that one answers "is the trial
607
+ * still running", delegates to the cancel flow's shared predicate for it, and is read
608
+ * by the trial-lapse sweep. This answers "did the trial end unpaid, and has the
609
+ * provider been sitting on it since" — which is the question a cancellation arriving
610
+ * after dunning asks ([#414](https://github.com/Omega-JS-Stack/omega/issues/414)).
611
+ *
612
+ * @param {object|null} [subscription] - A subscription object
613
+ * @returns {boolean}
614
+ */
615
+ function isLapsedTrialSuspension(subscription) {
616
+ const trialExpires = subscription?.trial?.expires?.timestampUNIX;
617
+ const expires = subscription?.expires?.timestampUNIX;
618
+
619
+ return !!(subscription?.status === 'suspended'
620
+ && subscription?.trial?.claimed
621
+ && trialExpires
622
+ && (expires === trialExpires || !expires));
623
+ }
624
+
625
+ /**
626
+ * Compute the actual amount paid, accounting for trial and promo discount
627
+ * Trial = $0, discount = price - savings, otherwise full price
628
+ */
629
+ function resolveActualValue(price, isTrial, discount) {
630
+ if (isTrial) {
631
+ return 0;
632
+ }
633
+
634
+ // Both coupon shapes count — gating on `percent` alone reported the LIST price
635
+ // as revenue for an amount-based code while the customer paid less
636
+ if (discount?.valid === true) {
637
+ return discountCodes.applyToAmount(price, discount);
638
+ }
639
+
640
+ return price;
641
+ }
642
+
643
+ /**
644
+ * What a refund actually reversed: the provider's amount, then the amount the
645
+ * order fold already recorded, then the price as the last resort.
646
+ */
647
+ function resolveRefundValue(refundDetails, unified, price) {
648
+ const amount = refundDetails?.amount ?? unified.payment?.refund?.amount;
649
+
650
+ return amount === null || amount === undefined ? price : parseFloat(amount);
651
+ }
652
+
653
+ /**
654
+ * Check if a webhook event type represents a payment being made
655
+ */
656
+ function isPaymentEvent(eventType) {
657
+ if (!eventType) {
658
+ return false;
659
+ }
660
+
661
+ return [
662
+ // PayPal
663
+ 'PAYMENT.SALE.COMPLETED',
664
+ // Stripe
665
+ 'invoice.payment_succeeded',
666
+ 'invoice.paid',
667
+ // Chargebee — the renewal event its webhook parser reports for a recurring charge
668
+ 'subscription_renewed',
669
+ ].includes(eventType);
670
+ }
671
+
672
+ module.exports = {
673
+ trackPayment,
674
+ // The trial-lapse sweep reports the outcomes no webhook ever announces, and both
675
+ // paths have to agree on what "still inside the trial" means ([#407]).
676
+ isInsideTrial,
677
+ // The pipeline stamps `trial.outcome` on the charge this books as the conversion,
678
+ // and that stamp is what makes the NEXT charge a renewal — so the write and the
679
+ // label read one predicate rather than a copy each ([#697]).
680
+ isTrialConversion,
681
+ // Exported for testing
682
+ resolvePaymentEvent,
683
+ isPaymentEvent,
684
+ buildParams,
685
+ resolveTransactionId,
686
+ resolveEventId,
687
+ };