@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.
- package/LICENSE +98 -0
- package/README.md +1053 -0
- package/bin/omega +2 -0
- package/bin/omega-backend +2 -0
- package/bin/omg +2 -0
- package/cli.js +3 -0
- package/dist/cli/command-table.js +268 -0
- package/dist/cli/commands/auth.js +259 -0
- package/dist/cli/commands/base-command.js +451 -0
- package/dist/cli/commands/build.js +27 -0
- package/dist/cli/commands/clean.js +13 -0
- package/dist/cli/commands/clear.js +11 -0
- package/dist/cli/commands/cwd.js +9 -0
- package/dist/cli/commands/deploy.js +205 -0
- package/dist/cli/commands/emulator-orphans.js +440 -0
- package/dist/cli/commands/emulator.js +1829 -0
- package/dist/cli/commands/firebase-init.js +101 -0
- package/dist/cli/commands/firestore.js +261 -0
- package/dist/cli/commands/indexes.js +51 -0
- package/dist/cli/commands/install.js +151 -0
- package/dist/cli/commands/logs.js +358 -0
- package/dist/cli/commands/mcp.js +41 -0
- package/dist/cli/commands/migrate-markers.js +176 -0
- package/dist/cli/commands/migrate-rules.js +90 -0
- package/dist/cli/commands/migrate.js +59 -0
- package/dist/cli/commands/serve.js +208 -0
- package/dist/cli/commands/setup-tests/base-test.js +174 -0
- package/dist/cli/commands/setup-tests/emulator-config.js +109 -0
- package/dist/cli/commands/setup-tests/env-runtime-config-deprecated.js +47 -0
- package/dist/cli/commands/setup-tests/firebase-admin.js +49 -0
- package/dist/cli/commands/setup-tests/firebase-auth.js +26 -0
- package/dist/cli/commands/setup-tests/firebase-cli.js +26 -0
- package/dist/cli/commands/setup-tests/firebase-functions.js +49 -0
- package/dist/cli/commands/setup-tests/firestore-indexes-file.js +72 -0
- package/dist/cli/commands/setup-tests/firestore-indexes-in-json.js +20 -0
- package/dist/cli/commands/setup-tests/firestore-indexes-required.js +108 -0
- package/dist/cli/commands/setup-tests/firestore-indexes-synced.js +236 -0
- package/dist/cli/commands/setup-tests/firestore-rules-file.js +115 -0
- package/dist/cli/commands/setup-tests/firestore-rules-in-json.js +49 -0
- package/dist/cli/commands/setup-tests/functions-package.js +44 -0
- package/dist/cli/commands/setup-tests/gcloud-cli.js +26 -0
- package/dist/cli/commands/setup-tests/gitignore.js +122 -0
- package/dist/cli/commands/setup-tests/helpers/merge-line-files.js +28 -0
- package/dist/cli/commands/setup-tests/helpers/required-indexes.js +109 -0
- package/dist/cli/commands/setup-tests/helpers/seed-campaigns.js +246 -0
- package/dist/cli/commands/setup-tests/helpers.js +72 -0
- package/dist/cli/commands/setup-tests/hosting-folder.js +23 -0
- package/dist/cli/commands/setup-tests/hosting-rewrites.js +52 -0
- package/dist/cli/commands/setup-tests/index.js +111 -0
- package/dist/cli/commands/setup-tests/is-firebase-project.js +21 -0
- package/dist/cli/commands/setup-tests/java-installed.js +27 -0
- package/dist/cli/commands/setup-tests/legacy-tests-cleanup.js +43 -0
- package/dist/cli/commands/setup-tests/marketing-campaigns-seeded.js +230 -0
- package/dist/cli/commands/setup-tests/node-version.js +60 -0
- package/dist/cli/commands/setup-tests/npm-project-scripts.js +42 -0
- package/dist/cli/commands/setup-tests/nvmrc-version.js +36 -0
- package/dist/cli/commands/setup-tests/omega-backend.js +42 -0
- package/dist/cli/commands/setup-tests/omega-config.js +84 -0
- package/dist/cli/commands/setup-tests/project-directories.js +34 -0
- package/dist/cli/commands/setup-tests/project-id-consistency.js +201 -0
- package/dist/cli/commands/setup-tests/public-html-files.js +22 -0
- package/dist/cli/commands/setup-tests/realtime-rules-file.js +69 -0
- package/dist/cli/commands/setup-tests/realtime-rules-in-json.js +20 -0
- package/dist/cli/commands/setup-tests/remoteconfig-template-file.js +32 -0
- package/dist/cli/commands/setup-tests/remoteconfig-template-in-json.js +31 -0
- package/dist/cli/commands/setup-tests/service-account.js +86 -0
- package/dist/cli/commands/setup-tests/storage-lifecycle-policy.js +81 -0
- package/dist/cli/commands/setup-tests/storage-rules-file.js +32 -0
- package/dist/cli/commands/setup-tests/storage-rules-in-json.js +20 -0
- package/dist/cli/commands/stripe.js +14 -0
- package/dist/cli/commands/test-lanes/stripe-live.js +440 -0
- package/dist/cli/commands/test.js +976 -0
- package/dist/cli/commands/update.js +27 -0
- package/dist/cli/commands/version.js +10 -0
- package/dist/cli/commands/watch.js +196 -0
- package/dist/cli/flags.js +13 -0
- package/dist/cli/index.js +173 -0
- package/dist/cli/run.js +33 -0
- package/dist/cli/utils/attach-log-file.js +4 -0
- package/dist/cli/utils/compile-rules.js +1155 -0
- package/dist/cli/utils/ensure-target.js +314 -0
- package/dist/cli/utils/project-type.js +140 -0
- package/dist/cli/utils/public-files.js +49 -0
- package/dist/cli/utils/safe-install.js +4 -0
- package/dist/cli/utils/spawn-shell.js +94 -0
- package/dist/cli/utils/stage-functions.js +338 -0
- package/dist/cli/utils/stage-local-packages.js +210 -0
- package/dist/cli/utils/target-checks.js +161 -0
- package/dist/cli/utils/target.js +24 -0
- package/dist/cli/utils/ui.js +292 -0
- package/dist/defaults/AGENTS.md +125 -0
- package/dist/defaults/CHANGELOG.md +15 -0
- package/dist/defaults/CLAUDE.md +1 -0
- package/dist/defaults/_.gitignore +65 -0
- package/dist/defaults/docs/README.md +17 -0
- package/dist/defaults/test/README.md +65 -0
- package/dist/defaults/test/_init.js +14 -0
- package/dist/defaults/test/helpers/connect-trap.js +72 -0
- package/dist/defaults/test/unit/registration.test.js +143 -0
- package/dist/defaults/test/unit/rules-posture.test.js +101 -0
- package/dist/defaults/test/unit/socket-free.test.js +43 -0
- package/dist/manager/events/auth/before-create.js +89 -0
- package/dist/manager/events/auth/before-signin.js +76 -0
- package/dist/manager/events/auth/on-create.js +112 -0
- package/dist/manager/events/auth/on-delete.js +141 -0
- package/dist/manager/events/auth/utils.js +93 -0
- package/dist/manager/events/cron/daily/blog-auto-publisher.js +287 -0
- package/dist/manager/events/cron/daily/data-requests.js +57 -0
- package/dist/manager/events/cron/daily/expire-paypal-cancellations.js +191 -0
- package/dist/manager/events/cron/daily/marketing-prune.js +486 -0
- package/dist/manager/events/cron/daily/reset-usage.js +188 -0
- package/dist/manager/events/cron/daily/trial-lapse-sweep.js +355 -0
- package/dist/manager/events/cron/daily.js +3 -0
- package/dist/manager/events/cron/frequent/abandoned-carts.js +204 -0
- package/dist/manager/events/cron/frequent/email-queue.js +71 -0
- package/dist/manager/events/cron/frequent/marketing-campaigns.js +463 -0
- package/dist/manager/events/cron/frequent/retry-failed-webhooks.js +70 -0
- package/dist/manager/events/cron/frequent.js +3 -0
- package/dist/manager/events/cron/runner.js +64 -0
- package/dist/manager/events/firestore/notifications/on-write.js +123 -0
- package/dist/manager/events/firestore/payments-disputes/on-write.js +256 -0
- package/dist/manager/events/firestore/payments-disputes/providers/stripe.js +218 -0
- package/dist/manager/events/firestore/payments-disputes/providers/test.js +231 -0
- package/dist/manager/events/firestore/payments-webhooks/analytics.js +687 -0
- package/dist/manager/events/firestore/payments-webhooks/on-write.js +1224 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/index.js +395 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/one-time/purchase-completed.js +58 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/one-time/purchase-failed.js +28 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/one-time/purchase-refunded.js +30 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/send-email.js +74 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/cancellation-removed.js +14 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/cancellation-requested.js +28 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/checkout-declined.js +18 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/new-subscription.js +67 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/payment-failed.js +25 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/payment-recovered.js +26 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/payment-refunded.js +34 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/plan-changed.js +42 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/subscription-cancelled.js +33 -0
- package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/subscription-winback.js +17 -0
- package/dist/manager/functions/_legacy/actions/create-post-handler.js +188 -0
- package/dist/manager/functions/_legacy/actions/generate-uuid.js +63 -0
- package/dist/manager/functions/_legacy/actions/sign-up-handler.js +205 -0
- package/dist/manager/functions/_legacy/admin/create-post.js +207 -0
- package/dist/manager/functions/_legacy/admin/firestore-write.js +72 -0
- package/dist/manager/functions/_legacy/admin/get-stats.js +218 -0
- package/dist/manager/functions/_legacy/admin/query.js +198 -0
- package/dist/manager/functions/_legacy/admin/send-notification.js +206 -0
- package/dist/manager/functions/_legacy/template.js +33 -0
- package/dist/manager/functions/_legacy/test/authenticate.js +40 -0
- package/dist/manager/functions/_legacy/test/webhook.js +37 -0
- package/dist/manager/functions/wrappers/mailchimp/addToList.js +25 -0
- package/dist/manager/helpers/analytics.js +498 -0
- package/dist/manager/helpers/api-manager.js +313 -0
- package/dist/manager/helpers/backend-router.js +37 -0
- package/dist/manager/helpers/context/authenticate.js +215 -0
- package/dist/manager/helpers/context/client-info.js +91 -0
- package/dist/manager/helpers/context/index.js +228 -0
- package/dist/manager/helpers/context/logging.js +150 -0
- package/dist/manager/helpers/context/parse.js +160 -0
- package/dist/manager/helpers/context/respond.js +247 -0
- package/dist/manager/helpers/event-middleware.js +109 -0
- package/dist/manager/helpers/metadata.js +32 -0
- package/dist/manager/helpers/middleware.js +541 -0
- package/dist/manager/helpers/redact-secret.js +25 -0
- package/dist/manager/helpers/resolved-config.js +34 -0
- package/dist/manager/helpers/roles.js +69 -0
- package/dist/manager/helpers/safe-compare.js +31 -0
- package/dist/manager/helpers/schema-engine.js +314 -0
- package/dist/manager/helpers/schema-zod.js +203 -0
- package/dist/manager/helpers/settings.js +215 -0
- package/dist/manager/helpers/usage.js +541 -0
- package/dist/manager/helpers/user.js +57 -0
- package/dist/manager/helpers/utilities.js +565 -0
- package/dist/manager/index.js +1404 -0
- package/dist/manager/libraries/abandoned-cart-config.js +12 -0
- package/dist/manager/libraries/ai/index.js +321 -0
- package/dist/manager/libraries/ai/prompt.js +85 -0
- package/dist/manager/libraries/ai/providers/anthropic-format.js +297 -0
- package/dist/manager/libraries/ai/providers/anthropic.js +169 -0
- package/dist/manager/libraries/ai/providers/claude-code.js +183 -0
- package/dist/manager/libraries/ai/providers/openai.js +1272 -0
- package/dist/manager/libraries/ai/providers/test.js +257 -0
- package/dist/manager/libraries/ai/tokens.js +54 -0
- package/dist/manager/libraries/analytics/conversions.js +478 -0
- package/dist/manager/libraries/analytics/match-data.js +573 -0
- package/dist/manager/libraries/analytics/signup.js +120 -0
- package/dist/manager/libraries/auth-user.js +46 -0
- package/dist/manager/libraries/content/feed-parser.js +198 -0
- package/dist/manager/libraries/content/ghostii.js +191 -0
- package/dist/manager/libraries/content/source-resolver.js +629 -0
- package/dist/manager/libraries/email/constants.js +532 -0
- package/dist/manager/libraries/email/data/blocked-local-parts.json +55 -0
- package/dist/manager/libraries/email/data/blocked-local-patterns.js +14 -0
- package/dist/manager/libraries/email/data/corporate-domains.json +23 -0
- package/dist/manager/libraries/email/data/custom-disposable-domains.json +58 -0
- package/dist/manager/libraries/email/data/disposable-domains.json +8184 -0
- package/dist/manager/libraries/email/data/typo-domains.js +83 -0
- package/dist/manager/libraries/email/disposable-domains.js +92 -0
- package/dist/manager/libraries/email/generators/lib/filter.js +179 -0
- package/dist/manager/libraries/email/generators/lib/image-host.js +344 -0
- package/dist/manager/libraries/email/generators/lib/image-illustrator.js +154 -0
- package/dist/manager/libraries/email/generators/lib/markdown-renderer.js +294 -0
- package/dist/manager/libraries/email/generators/lib/mjml-template.js +120 -0
- package/dist/manager/libraries/email/generators/lib/structure.js +299 -0
- package/dist/manager/libraries/email/generators/lib/svg-illustrator.js +197 -0
- package/dist/manager/libraries/email/generators/lib/templates/base.js +229 -0
- package/dist/manager/libraries/email/generators/lib/templates/card.js +33 -0
- package/dist/manager/libraries/email/generators/lib/templates/classic-schema.js +59 -0
- package/dist/manager/libraries/email/generators/lib/templates/clean.js +82 -0
- package/dist/manager/libraries/email/generators/lib/templates/editorial/helpers.js +100 -0
- package/dist/manager/libraries/email/generators/lib/templates/editorial/index.js +318 -0
- package/dist/manager/libraries/email/generators/lib/templates/feedback.js +101 -0
- package/dist/manager/libraries/email/generators/lib/templates/field-report/helpers.js +138 -0
- package/dist/manager/libraries/email/generators/lib/templates/field-report/index.js +494 -0
- package/dist/manager/libraries/email/generators/lib/templates/index.js +47 -0
- package/dist/manager/libraries/email/generators/lib/templates/newsletter-shared.js +610 -0
- package/dist/manager/libraries/email/generators/lib/templates/order.js +461 -0
- package/dist/manager/libraries/email/generators/lib/templates/plain.js +83 -0
- package/dist/manager/libraries/email/generators/lib/templates/shared-campaign.js +64 -0
- package/dist/manager/libraries/email/generators/newsletter.js +899 -0
- package/dist/manager/libraries/email/index.js +146 -0
- package/dist/manager/libraries/email/marketing/index.js +754 -0
- package/dist/manager/libraries/email/prepare.js +365 -0
- package/dist/manager/libraries/email/providers/beehiiv.js +589 -0
- package/dist/manager/libraries/email/providers/sendgrid.js +845 -0
- package/dist/manager/libraries/email/transactional/index.js +510 -0
- package/dist/manager/libraries/email/utm.js +86 -0
- package/dist/manager/libraries/email/validation-provider-neverbounce.js +70 -0
- package/dist/manager/libraries/email/validation-provider-zerobounce.js +42 -0
- package/dist/manager/libraries/email/validation.js +255 -0
- package/dist/manager/libraries/env.js +198 -0
- package/dist/manager/libraries/infer-contact.js +101 -0
- package/dist/manager/libraries/load-provider.js +65 -0
- package/dist/manager/libraries/notification.js +256 -0
- package/dist/manager/libraries/openai.js +13 -0
- package/dist/manager/libraries/payment/discount-codes.js +204 -0
- package/dist/manager/libraries/payment/fetch-failure.js +56 -0
- package/dist/manager/libraries/payment/license.js +44 -0
- package/dist/manager/libraries/payment/order-id.js +57 -0
- package/dist/manager/libraries/payment/provider-errors.js +71 -0
- package/dist/manager/libraries/payment/providers/chargebee.js +817 -0
- package/dist/manager/libraries/payment/providers/coinbase.js +330 -0
- package/dist/manager/libraries/payment/providers/paypal.js +987 -0
- package/dist/manager/libraries/payment/providers/stripe.js +755 -0
- package/dist/manager/libraries/payment/providers/test.js +250 -0
- package/dist/manager/libraries/payment/refund-linkage.js +78 -0
- package/dist/manager/libraries/payment/refund-policy.js +113 -0
- package/dist/manager/libraries/payment/winback.js +55 -0
- package/dist/manager/libraries/prompts/infer-contact.md +78 -0
- package/dist/manager/libraries/rate-limits.js +17 -0
- package/dist/manager/libraries/recaptcha.js +58 -0
- package/dist/manager/libraries/user-doc.js +234 -0
- package/dist/manager/routes/admin/backup/post.js +106 -0
- package/dist/manager/routes/admin/cron/post.js +37 -0
- package/dist/manager/routes/admin/database/get.js +35 -0
- package/dist/manager/routes/admin/database/post.js +34 -0
- package/dist/manager/routes/admin/email/post.js +74 -0
- package/dist/manager/routes/admin/firestore/get.js +35 -0
- package/dist/manager/routes/admin/firestore/post.js +56 -0
- package/dist/manager/routes/admin/firestore/query/post.js +128 -0
- package/dist/manager/routes/admin/hook/post.js +104 -0
- package/dist/manager/routes/admin/infer-contact/post.js +35 -0
- package/dist/manager/routes/admin/notification/post.js +37 -0
- package/dist/manager/routes/admin/payment/post.js +56 -0
- package/dist/manager/routes/admin/post/deduplicate-image-alts.js +52 -0
- package/dist/manager/routes/admin/post/dispatch-deploy.js +36 -0
- package/dist/manager/routes/admin/post/post.js +487 -0
- package/dist/manager/routes/admin/post/put.js +146 -0
- package/dist/manager/routes/admin/post/templates/post.html +14 -0
- package/dist/manager/routes/admin/repo/content/post.js +106 -0
- package/dist/manager/routes/admin/stats/get.js +222 -0
- package/dist/manager/routes/admin/users/disable/post.js +50 -0
- package/dist/manager/routes/admin/users/list/get.js +110 -0
- package/dist/manager/routes/admin/users/sync/post.js +119 -0
- package/dist/manager/routes/brand/get.js +26 -0
- package/dist/manager/routes/content/post/get.js +110 -0
- package/dist/manager/routes/general/email/post.js +89 -0
- package/dist/manager/routes/general/email/templates/general/download-app-link.js +42 -0
- package/dist/manager/routes/general/uuid/post.js +31 -0
- package/dist/manager/routes/handler/post/post.js +142 -0
- package/dist/manager/routes/health/get.js +43 -0
- package/dist/manager/routes/index.js +11 -0
- package/dist/manager/routes/marketing/campaign/delete.js +45 -0
- package/dist/manager/routes/marketing/campaign/get.js +69 -0
- package/dist/manager/routes/marketing/campaign/post.js +124 -0
- package/dist/manager/routes/marketing/campaign/put.js +86 -0
- package/dist/manager/routes/marketing/campaign/utils.js +59 -0
- package/dist/manager/routes/marketing/contact/delete.js +100 -0
- package/dist/manager/routes/marketing/contact/post.js +149 -0
- package/dist/manager/routes/marketing/contact/put.js +37 -0
- package/dist/manager/routes/marketing/email-preferences/post.js +275 -0
- package/dist/manager/routes/marketing/webhook/forward/post.js +170 -0
- package/dist/manager/routes/marketing/webhook/post.js +123 -0
- package/dist/manager/routes/marketing/webhook/providers/beehiiv.js +197 -0
- package/dist/manager/routes/marketing/webhook/providers/sendgrid.js +194 -0
- package/dist/manager/routes/payments/cancel/_is-trialing.js +40 -0
- package/dist/manager/routes/payments/cancel/post.js +149 -0
- package/dist/manager/routes/payments/cancel/providers/chargebee.js +41 -0
- package/dist/manager/routes/payments/cancel/providers/paypal.js +56 -0
- package/dist/manager/routes/payments/cancel/providers/stripe.js +33 -0
- package/dist/manager/routes/payments/cancel/providers/test.js +118 -0
- package/dist/manager/routes/payments/discount/get.js +25 -0
- package/dist/manager/routes/payments/dispute-alert/post.js +117 -0
- package/dist/manager/routes/payments/dispute-alert/providers/chargeblast.js +55 -0
- package/dist/manager/routes/payments/intent/post.js +341 -0
- package/dist/manager/routes/payments/intent/providers/chargebee.js +197 -0
- package/dist/manager/routes/payments/intent/providers/coinbase.js +94 -0
- package/dist/manager/routes/payments/intent/providers/paypal.js +244 -0
- package/dist/manager/routes/payments/intent/providers/stripe.js +141 -0
- package/dist/manager/routes/payments/intent/providers/test.js +341 -0
- package/dist/manager/routes/payments/plan/post.js +147 -0
- package/dist/manager/routes/payments/plan/providers/chargebee.js +62 -0
- package/dist/manager/routes/payments/plan/providers/paypal.js +62 -0
- package/dist/manager/routes/payments/plan/providers/stripe.js +59 -0
- package/dist/manager/routes/payments/plan/providers/test.js +113 -0
- package/dist/manager/routes/payments/portal/post.js +113 -0
- package/dist/manager/routes/payments/portal/providers/chargebee.js +62 -0
- package/dist/manager/routes/payments/portal/providers/paypal.js +25 -0
- package/dist/manager/routes/payments/portal/providers/stripe.js +62 -0
- package/dist/manager/routes/payments/portal/providers/test.js +18 -0
- package/dist/manager/routes/payments/refund/post.js +207 -0
- package/dist/manager/routes/payments/refund/providers/chargebee.js +147 -0
- package/dist/manager/routes/payments/refund/providers/coinbase.js +40 -0
- package/dist/manager/routes/payments/refund/providers/paypal.js +239 -0
- package/dist/manager/routes/payments/refund/providers/stripe.js +160 -0
- package/dist/manager/routes/payments/refund/providers/test.js +204 -0
- package/dist/manager/routes/payments/trial-eligibility/get.js +29 -0
- package/dist/manager/routes/payments/uncancel/post.js +106 -0
- package/dist/manager/routes/payments/uncancel/providers/chargebee.js +30 -0
- package/dist/manager/routes/payments/uncancel/providers/paypal.js +25 -0
- package/dist/manager/routes/payments/uncancel/providers/stripe.js +27 -0
- package/dist/manager/routes/payments/uncancel/providers/test.js +114 -0
- package/dist/manager/routes/payments/webhook/post.js +157 -0
- package/dist/manager/routes/payments/webhook/providers/chargebee.js +218 -0
- package/dist/manager/routes/payments/webhook/providers/coinbase.js +91 -0
- package/dist/manager/routes/payments/webhook/providers/paypal.js +214 -0
- package/dist/manager/routes/payments/webhook/providers/stripe.js +182 -0
- package/dist/manager/routes/payments/webhook/providers/test.js +18 -0
- package/dist/manager/routes/payments/winback/post.js +276 -0
- package/dist/manager/routes/payments/winback/providers/chargebee.js +24 -0
- package/dist/manager/routes/payments/winback/providers/paypal.js +22 -0
- package/dist/manager/routes/payments/winback/providers/stripe.js +41 -0
- package/dist/manager/routes/payments/winback/providers/test.js +42 -0
- package/dist/manager/routes/restart/index.js +37 -0
- package/dist/manager/routes/special/electron-client/post.js +59 -0
- package/dist/manager/routes/test/authenticate/get.js +11 -0
- package/dist/manager/routes/test/health/get.js +8 -0
- package/dist/manager/routes/test/index.js +16 -0
- package/dist/manager/routes/test/lab/post.js +17 -0
- package/dist/manager/routes/test/redirect/get.js +27 -0
- package/dist/manager/routes/test/reset-account/post.js +79 -0
- package/dist/manager/routes/test/roster/get.js +40 -0
- package/dist/manager/routes/test/schema/post.js +20 -0
- package/dist/manager/routes/test/usage/post.js +44 -0
- package/dist/manager/routes/test/webhook/post.js +21 -0
- package/dist/manager/routes/user/api-keys/post.js +51 -0
- package/dist/manager/routes/user/connections/_context.js +194 -0
- package/dist/manager/routes/user/connections/_grant.js +247 -0
- package/dist/manager/routes/user/connections/_lease.js +184 -0
- package/dist/manager/routes/user/connections/_providers.js +191 -0
- package/dist/manager/routes/user/connections/_state.js +117 -0
- package/dist/manager/routes/user/connections/delete.js +55 -0
- package/dist/manager/routes/user/connections/get.js +167 -0
- package/dist/manager/routes/user/connections/post.js +370 -0
- package/dist/manager/routes/user/connections/providers/discord.js +41 -0
- package/dist/manager/routes/user/connections/providers/google.js +55 -0
- package/dist/manager/routes/user/connections/providers/kick.js +68 -0
- package/dist/manager/routes/user/connections/providers/spotify.js +40 -0
- package/dist/manager/routes/user/connections/providers/twitch.js +52 -0
- package/dist/manager/routes/user/data-request/delete.js +84 -0
- package/dist/manager/routes/user/data-request/get.js +229 -0
- package/dist/manager/routes/user/data-request/post.js +136 -0
- package/dist/manager/routes/user/delete.js +149 -0
- package/dist/manager/routes/user/feedback/post.js +81 -0
- package/dist/manager/routes/user/get.js +14 -0
- package/dist/manager/routes/user/orders/get.js +95 -0
- package/dist/manager/routes/user/sessions/delete.js +95 -0
- package/dist/manager/routes/user/sessions/get.js +40 -0
- package/dist/manager/routes/user/settings/validate/post.js +86 -0
- package/dist/manager/routes/user/signup/post.js +662 -0
- package/dist/manager/routes/user/subscription/get.js +77 -0
- package/dist/manager/routes/user/token/post.js +30 -0
- package/dist/manager/routes/verts/delete.js +43 -0
- package/dist/manager/routes/verts/get.js +49 -0
- package/dist/manager/routes/verts/post.js +61 -0
- package/dist/manager/routes/verts/put.js +75 -0
- package/dist/manager/routes/verts/redirect/get.js +36 -0
- package/dist/manager/routes/verts/serve/get.js +67 -0
- package/dist/manager/routes/verts/utils.js +436 -0
- package/dist/manager/schemas/admin/backup/post.js +8 -0
- package/dist/manager/schemas/admin/cron/post.js +8 -0
- package/dist/manager/schemas/admin/database/get.js +5 -0
- package/dist/manager/schemas/admin/database/post.js +6 -0
- package/dist/manager/schemas/admin/email/post.js +31 -0
- package/dist/manager/schemas/admin/firestore/get.js +5 -0
- package/dist/manager/schemas/admin/firestore/post.js +8 -0
- package/dist/manager/schemas/admin/firestore/query/post.js +5 -0
- package/dist/manager/schemas/admin/hook/post.js +8 -0
- package/dist/manager/schemas/admin/infer-contact/post.js +6 -0
- package/dist/manager/schemas/admin/notification/post.js +23 -0
- package/dist/manager/schemas/admin/payment/post.js +8 -0
- package/dist/manager/schemas/admin/post/post.js +23 -0
- package/dist/manager/schemas/admin/post/put.js +13 -0
- package/dist/manager/schemas/admin/repo/content/post.js +10 -0
- package/dist/manager/schemas/admin/stats/get.js +5 -0
- package/dist/manager/schemas/admin/users/disable/post.js +9 -0
- package/dist/manager/schemas/admin/users/list/get.js +10 -0
- package/dist/manager/schemas/admin/users/sync/post.js +7 -0
- package/dist/manager/schemas/brand/get.js +3 -0
- package/dist/manager/schemas/content/post/get.js +8 -0
- package/dist/manager/schemas/general/email/post.js +10 -0
- package/dist/manager/schemas/general/uuid/post.js +9 -0
- package/dist/manager/schemas/handler/post/post.js +13 -0
- package/dist/manager/schemas/health/get.js +3 -0
- package/dist/manager/schemas/marketing/campaign/delete.js +8 -0
- package/dist/manager/schemas/marketing/campaign/get.js +13 -0
- package/dist/manager/schemas/marketing/campaign/post.js +43 -0
- package/dist/manager/schemas/marketing/campaign/put.js +37 -0
- package/dist/manager/schemas/marketing/contact/delete.js +8 -0
- package/dist/manager/schemas/marketing/contact/post.js +14 -0
- package/dist/manager/schemas/marketing/contact/put.js +8 -0
- package/dist/manager/schemas/marketing/email-preferences/post.js +15 -0
- package/dist/manager/schemas/marketing/webhook/forward/post.js +8 -0
- package/dist/manager/schemas/marketing/webhook/post.js +7 -0
- package/dist/manager/schemas/payments/cancel/post.js +15 -0
- package/dist/manager/schemas/payments/discount/get.js +8 -0
- package/dist/manager/schemas/payments/dispute-alert/post.js +6 -0
- package/dist/manager/schemas/payments/intent/post.js +28 -0
- package/dist/manager/schemas/payments/plan/post.js +11 -0
- package/dist/manager/schemas/payments/portal/post.js +12 -0
- package/dist/manager/schemas/payments/refund/post.js +14 -0
- package/dist/manager/schemas/payments/trial-eligibility/get.js +7 -0
- package/dist/manager/schemas/payments/uncancel/post.js +9 -0
- package/dist/manager/schemas/payments/webhook/post.js +6 -0
- package/dist/manager/schemas/payments/winback/post.js +12 -0
- package/dist/manager/schemas/restart/index.js +5 -0
- package/dist/manager/schemas/special/electron-client/post.js +11 -0
- package/dist/manager/schemas/test/authenticate/get.js +3 -0
- package/dist/manager/schemas/test/health/get.js +3 -0
- package/dist/manager/schemas/test/index.js +5 -0
- package/dist/manager/schemas/test/lab/post.js +3 -0
- package/dist/manager/schemas/test/redirect/get.js +7 -0
- package/dist/manager/schemas/test/reset-account/post.js +3 -0
- package/dist/manager/schemas/test/roster/get.js +3 -0
- package/dist/manager/schemas/test/schema/post.js +70 -0
- package/dist/manager/schemas/test/usage/post.js +8 -0
- package/dist/manager/schemas/test/webhook/post.js +7 -0
- package/dist/manager/schemas/user/api-keys/post.js +6 -0
- package/dist/manager/schemas/user/connections/delete.js +8 -0
- package/dist/manager/schemas/user/connections/get.js +21 -0
- package/dist/manager/schemas/user/connections/post.js +12 -0
- package/dist/manager/schemas/user/data-request/delete.js +3 -0
- package/dist/manager/schemas/user/data-request/get.js +5 -0
- package/dist/manager/schemas/user/data-request/post.js +6 -0
- package/dist/manager/schemas/user/delete.js +6 -0
- package/dist/manager/schemas/user/feedback/post.js +8 -0
- package/dist/manager/schemas/user/get.js +3 -0
- package/dist/manager/schemas/user/orders/get.js +6 -0
- package/dist/manager/schemas/user/sessions/delete.js +6 -0
- package/dist/manager/schemas/user/sessions/get.js +6 -0
- package/dist/manager/schemas/user/settings/validate/post.js +8 -0
- package/dist/manager/schemas/user/signup/post.js +27 -0
- package/dist/manager/schemas/user/subscription/get.js +5 -0
- package/dist/manager/schemas/user/token/post.js +5 -0
- package/dist/manager/schemas/verts/delete.js +8 -0
- package/dist/manager/schemas/verts/get.js +9 -0
- package/dist/manager/schemas/verts/post.js +23 -0
- package/dist/manager/schemas/verts/put.js +20 -0
- package/dist/manager/schemas/verts/redirect/get.js +10 -0
- package/dist/manager/schemas/verts/serve/get.js +14 -0
- package/dist/manager/server-manager.js +69 -0
- package/dist/mcp/client.js +98 -0
- package/dist/mcp/handler.js +521 -0
- package/dist/mcp/index.js +157 -0
- package/dist/mcp/tools.js +555 -0
- package/dist/mcp/utils.js +108 -0
- package/dist/omega-bin.js +6 -0
- package/dist/require.js +3 -0
- package/dist/test/fixtures/firebase-project/.firebaserc +5 -0
- package/dist/test/fixtures/firebase-project/config/omega.json5 +131 -0
- package/dist/test/fixtures/firebase-project/database.rules.json +6 -0
- package/dist/test/fixtures/firebase-project/firebase.json +49 -0
- package/dist/test/fixtures/firebase-project/firestore.indexes.json +4 -0
- package/dist/test/fixtures/firebase-project/package.json +14 -0
- package/dist/test/fixtures/firebase-project/src/index.js +11 -0
- package/dist/test/fixtures/firebase-project/storage.rules +8 -0
- package/dist/test/parse-audit.js +25 -0
- package/dist/test/run-tests.js +90 -0
- package/dist/test/runner.js +982 -0
- package/dist/test/seed.js +242 -0
- package/dist/test/test-accounts.js +2660 -0
- package/dist/test/utils/assertions.js +189 -0
- package/dist/test/utils/email-capture.js +252 -0
- package/dist/test/utils/extended-mode-warning.js +11 -0
- package/dist/test/utils/firestore-rules-client.js +277 -0
- package/dist/test/utils/http-client.js +228 -0
- package/dist/test/utils/test-mode-file.js +192 -0
- package/dist/utils/merge-line-files.js +6 -0
- package/dist/utils/scaffold-defaults.js +71 -0
- package/dist/utils/test-lanes.js +62 -0
- package/dist/vendor/account/engine.js +182 -0
- package/dist/vendor/account/features.js +220 -0
- package/dist/vendor/account/index.js +53 -0
- package/dist/vendor/account/schema.js +272 -0
- package/dist/vendor/account/subscription.js +38 -0
- package/dist/vendor/analytics/adapters/ga4.js +26 -0
- package/dist/vendor/analytics/adapters/meta.js +26 -0
- package/dist/vendor/analytics/adapters/resolve.js +130 -0
- package/dist/vendor/analytics/adapters/tiktok.js +27 -0
- package/dist/vendor/analytics/catalog.js +908 -0
- package/dist/vendor/analytics/identity.js +136 -0
- package/dist/vendor/config/company.js +31 -0
- package/dist/vendor/config/defaults.js +173 -0
- package/dist/vendor/config/demo.js +18 -0
- package/dist/vendor/config/desktop-artifacts.js +110 -0
- package/dist/vendor/config/edit.js +769 -0
- package/dist/vendor/config/env-delivery.js +145 -0
- package/dist/vendor/config/env-rules.js +93 -0
- package/dist/vendor/config/env-schema.js +1078 -0
- package/dist/vendor/config/env.js +445 -0
- package/dist/vendor/config/hooks.js +97 -0
- package/dist/vendor/config/index.js +237 -0
- package/dist/vendor/config/instances.js +208 -0
- package/dist/vendor/config/load.js +490 -0
- package/dist/vendor/config/merge.js +68 -0
- package/dist/vendor/config/order.js +139 -0
- package/dist/vendor/config/ports.js +374 -0
- package/dist/vendor/config/providers.js +32 -0
- package/dist/vendor/config/repo.js +142 -0
- package/dist/vendor/config/retired-keys.js +430 -0
- package/dist/vendor/config/schema.js +1610 -0
- package/dist/vendor/config/secrets.js +50 -0
- package/dist/vendor/config/seed.js +34 -0
- package/dist/vendor/config/site-global.js +205 -0
- package/dist/vendor/config/validate.js +554 -0
- package/dist/vendor/config/winback.js +61 -0
- package/dist/vendor/devkit/attach-log-file.js +262 -0
- package/dist/vendor/devkit/bare-requires.js +153 -0
- package/dist/vendor/devkit/command-path.js +46 -0
- package/dist/vendor/devkit/defaults-engine.js +372 -0
- package/dist/vendor/devkit/deploy-record.js +180 -0
- package/dist/vendor/devkit/deploy.js +286 -0
- package/dist/vendor/devkit/license.js +155 -0
- package/dist/vendor/devkit/local-https.js +360 -0
- package/dist/vendor/devkit/local.js +1905 -0
- package/dist/vendor/devkit/logger.js +128 -0
- package/dist/vendor/devkit/merge-line-files.js +296 -0
- package/dist/vendor/devkit/npm-registry.js +52 -0
- package/dist/vendor/devkit/omega-bin.js +345 -0
- package/dist/vendor/devkit/parse-audit.js +74 -0
- package/dist/vendor/devkit/safe-install.js +18 -0
- package/dist/vendor/devkit/scaffold-guard.js +96 -0
- package/dist/vendor/devkit/stop-signals.js +28 -0
- package/dist/vendor/devkit/test/assert.js +120 -0
- package/dist/vendor/devkit/test/define-cases.js +104 -0
- package/dist/vendor/devkit/test/runner-core.js +554 -0
- package/dist/vendor/devkit/test/scope.js +162 -0
- package/dist/vendor/devkit/update.js +569 -0
- package/dist/vendor/monitoring/core.js +180 -0
- package/dist/vendor/monitoring/env.js +49 -0
- package/dist/vendor/monitoring/logger.js +39 -0
- package/dist/vendor/monitoring/node.js +72 -0
- package/docs/admin-post-route.md +57 -0
- package/docs/ai-library.md +171 -0
- package/docs/architecture.md +62 -0
- package/docs/audit.md +70 -0
- package/docs/auth-hooks.md +74 -0
- package/docs/build-system.md +65 -0
- package/docs/cdp-debugging.md +29 -0
- package/docs/cli-firestore-auth.md +85 -0
- package/docs/cli-logs.md +67 -0
- package/docs/cli-output.md +146 -0
- package/docs/code-patterns.md +77 -0
- package/docs/common-mistakes.md +12 -0
- package/docs/common-operations.md +60 -0
- package/docs/connections.md +212 -0
- package/docs/consent.md +362 -0
- package/docs/directory-structure.md +150 -0
- package/docs/email-system.md +459 -0
- package/docs/environment-detection.md +93 -0
- package/docs/file-naming.md +10 -0
- package/docs/firestore.md +134 -0
- package/docs/ghostii.md +240 -0
- package/docs/index.md +299 -0
- package/docs/key-files.md +37 -0
- package/docs/logging.md +60 -0
- package/docs/marketing-campaigns.md +407 -0
- package/docs/marketing-fields.md +25 -0
- package/docs/mcp.md +222 -0
- package/docs/migration.md +129 -0
- package/docs/payment-system.md +856 -0
- package/docs/paypal-sandbox-qa.md +105 -0
- package/docs/response-headers.md +9 -0
- package/docs/routes.md +211 -0
- package/docs/sanitization.md +71 -0
- package/docs/schemas.md +163 -0
- package/docs/shared/agent-docs.md +89 -0
- package/docs/shared/analytics.md +612 -0
- package/docs/shared/brands.md +51 -0
- package/docs/shared/breaking-changes.md +497 -0
- package/docs/shared/config.md +1387 -0
- package/docs/shared/deploys.md +215 -0
- package/docs/shared/icons.md +201 -0
- package/docs/shared/local-dev.md +147 -0
- package/docs/shared/logging.md +202 -0
- package/docs/shared/monitoring.md +153 -0
- package/docs/shared/publishing.md +183 -0
- package/docs/shared/rulings.md +34 -0
- package/docs/shared/testing.md +147 -0
- package/docs/shared/theming.md +604 -0
- package/docs/shared/translation.md +291 -0
- package/docs/shared/updates.md +61 -0
- package/docs/stripe-webhook-forwarding.md +20 -0
- package/docs/test-boot-layer.md +67 -0
- package/docs/test-framework.md +583 -0
- package/docs/usage-rate-limiting.md +121 -0
- package/docs/verts.md +29 -0
- package/package.json +143 -0
- package/templates/config/omega.json5 +322 -0
- package/templates/database.rules.json +82 -0
- package/templates/firebase.json +67 -0
- package/templates/firestore.framework.rules +191 -0
- package/templates/firestore.indexes.json +4 -0
- package/templates/firestore.rules +59 -0
- package/templates/index.js +12 -0
- package/templates/public/404.html +26 -0
- package/templates/public/index.html +24 -0
- package/templates/remoteconfig.template.json +1 -0
- package/templates/storage-lifecycle-config-1-day.json +9 -0
- package/templates/storage-lifecycle-config-30-days.json +9 -0
- package/templates/storage.rules +11 -0
- package/test/_init/accounts-validation.js +58 -0
- package/test/ai/tools-live.test.js +171 -0
- package/test/analytics/conversion-delivery.test.js +1013 -0
- package/test/analytics/match-normalization.test.js +238 -0
- package/test/analytics/signup-conversion.test.js +135 -0
- package/test/boot/cli-dispatch.test.js +142 -0
- package/test/boot/defaults-scaffold.test.js +248 -0
- package/test/boot/deploy-staging.test.js +446 -0
- package/test/boot/disposable-domains.test.js +154 -0
- package/test/boot/emulator-boots.test.js +39 -0
- package/test/boot/emulator-port-preflight.test.js +126 -0
- package/test/boot/emulator-ready-timeout.test.js +46 -0
- package/test/boot/project-id-refresh.test.js +73 -0
- package/test/boot/suite-portability.test.js +278 -0
- package/test/boot/update-command.test.js +63 -0
- package/test/cli/cross-platform.test.js +168 -0
- package/test/cli/custom-project-type.test.js +426 -0
- package/test/cli/deploy-license-env.test.js +100 -0
- package/test/cli/emulator-adoption.test.js +254 -0
- package/test/cli/emulator-orphans.test.js +658 -0
- package/test/cli/emulator-ownership.test.js +177 -0
- package/test/cli/emulator-port-retry.test.js +395 -0
- package/test/cli/emulator-shutdown.test.js +788 -0
- package/test/cli/emulator-stale-reap.test.js +574 -0
- package/test/cli/ensure-target.test.js +325 -0
- package/test/cli/flags.test.js +64 -0
- package/test/cli/https-trust.test.js +38 -0
- package/test/cli/install-update-dispatch.test.js +47 -0
- package/test/cli/lane-environments.test.js +159 -0
- package/test/cli/marketing-campaigns-seeded.test.js +194 -0
- package/test/cli/migrate-bare-requires.test.js +128 -0
- package/test/cli/migrate-markers.test.js +530 -0
- package/test/cli/required-indexes.test.js +64 -0
- package/test/cli/rules-compile.test.js +622 -0
- package/test/cli/rules-migration-deferral.test.js +259 -0
- package/test/cli/rules-version.test.js +104 -0
- package/test/cli/setup-load-files.test.js +66 -0
- package/test/cli/setup-offline-mode.test.js +276 -0
- package/test/cli/setup-retired.test.js +64 -0
- package/test/cli/setup-shared-project-indexes.test.js +262 -0
- package/test/cli/setup-tests-requires.test.js +45 -0
- package/test/cli/stage-env-compose.test.js +316 -0
- package/test/cli/stripe-live-lane.test.js +303 -0
- package/test/cli/target.test.js +182 -0
- package/test/cli/templates.test.js +66 -0
- package/test/cli/test-runner-env.test.js +115 -0
- package/test/cli/test-stack-shutdown.test.js +275 -0
- package/test/cli/test-target-no-match.test.js +128 -0
- package/test/cli/verb-logs.test.js +118 -0
- package/test/cli/version-dispatch.test.js +39 -0
- package/test/content/blog-generate.test.js +164 -0
- package/test/email/campaign-config-fault.test.js +376 -0
- package/test/email/campaign-cron-pipeline.test.js +536 -0
- package/test/email/campaign-send.test.js +47 -0
- package/test/email/consent-lifecycle.test.js +258 -0
- package/test/email/content-html-policy.test.js +270 -0
- package/test/email/feedback-and-plain-send.test.js +54 -0
- package/test/email/fixtures/clean.json +30 -0
- package/test/email/fixtures/editorial.json +30 -0
- package/test/email/fixtures/field-report.json +53 -0
- package/test/email/identity.test.js +321 -0
- package/test/email/marketing/consent-gate.test.js +265 -0
- package/test/email/marketing/custom-fields-catalog.test.js +259 -0
- package/test/email/marketing/prune-per-provider.test.js +762 -0
- package/test/email/marketing/remove-log-privacy.test.js +225 -0
- package/test/email/marketing-lifecycle.test.js +139 -0
- package/test/email/newsletter-generate.test.js +853 -0
- package/test/email/newsletter-svg-tokens.test.js +51 -0
- package/test/email/newsletter-templates.test.js +492 -0
- package/test/email/order-one-time-cta.test.js +97 -0
- package/test/email/render-content.test.js +248 -0
- package/test/email/safe-url.test.js +0 -0
- package/test/email/sanitize-images.test.js +66 -0
- package/test/email/send-log-privacy.test.js +107 -0
- package/test/email/templates.test.js +288 -0
- package/test/email/testing-capture.test.js +260 -0
- package/test/email/transactional-send.test.js +36 -0
- package/test/email/transactional.test.js +562 -0
- package/test/email/unsubscribe-groups.test.js +155 -0
- package/test/email/unsubscribe-key.test.js +91 -0
- package/test/email/validation-cases.test.js +152 -0
- package/test/email/validation.test.js +746 -0
- package/test/events/auth-delete-conversion.test.js +138 -0
- package/test/events/auth-delete-race.test.js +211 -0
- package/test/events/auth-on-create-log.test.js +164 -0
- package/test/events/auth-signup-conversion.test.js +141 -0
- package/test/events/auth-signup-limit.test.js +142 -0
- package/test/events/auth-trigger-log-privacy.test.js +256 -0
- package/test/events/cron-job-doc-shape.test.js +101 -0
- package/test/events/cron-reset-usage.test.js +116 -0
- package/test/events/notification-conversion.test.js +182 -0
- package/test/events/payments/_webhook-harness.js +307 -0
- package/test/events/payments/abandoned-cart-activity.test.js +135 -0
- package/test/events/payments/analytics-payment-events.test.js +945 -0
- package/test/events/payments/dispute-email-status.test.js +55 -0
- package/test/events/payments/journey-payments-abandoned.test.js +126 -0
- package/test/events/payments/journey-payments-cancel-endpoint.test.js +102 -0
- package/test/events/payments/journey-payments-cancel-no-order.test.js +76 -0
- package/test/events/payments/journey-payments-cancel.test.js +184 -0
- package/test/events/payments/journey-payments-decline.test.js +225 -0
- package/test/events/payments/journey-payments-discount.test.js +91 -0
- package/test/events/payments/journey-payments-dispute.test.js +190 -0
- package/test/events/payments/journey-payments-failure.test.js +148 -0
- package/test/events/payments/journey-payments-legacy-product.test.js +151 -0
- package/test/events/payments/journey-payments-one-time-decline.test.js +127 -0
- package/test/events/payments/journey-payments-one-time-failure.test.js +113 -0
- package/test/events/payments/journey-payments-one-time-refund.test.js +186 -0
- package/test/events/payments/journey-payments-one-time.test.js +175 -0
- package/test/events/payments/journey-payments-plan-change.test.js +146 -0
- package/test/events/payments/journey-payments-plan-switch-trial.test.js +161 -0
- package/test/events/payments/journey-payments-plan-switch.test.js +182 -0
- package/test/events/payments/journey-payments-refund-no-order.test.js +76 -0
- package/test/events/payments/journey-payments-refund-webhook.test.js +225 -0
- package/test/events/payments/journey-payments-suspend.test.js +183 -0
- package/test/events/payments/journey-payments-trial-cancel.test.js +132 -0
- package/test/events/payments/journey-payments-trial.test.js +173 -0
- package/test/events/payments/journey-payments-uid-resolution.test.js +134 -0
- package/test/events/payments/journey-payments-uncancel.test.js +157 -0
- package/test/events/payments/journey-payments-upgrade.test.js +137 -0
- package/test/events/payments/journey-payments-winback-decline.test.js +171 -0
- package/test/events/payments/journey-payments-winback.test.js +195 -0
- package/test/events/payments/paypal-expiry-cron.test.js +166 -0
- package/test/events/payments/purchase-failed-handler.test.js +80 -0
- package/test/events/payments/purchase-refunded-handler.test.js +108 -0
- package/test/events/payments/test-processor-doc-shape.test.js +162 -0
- package/test/events/payments/transition-order-emails.test.js +185 -0
- package/test/events/payments/transition-promo-lines.test.js +192 -0
- package/test/events/payments/transitions-detect.test.js +767 -0
- package/test/events/payments/trial-lapse-sweep-staleness.test.js +173 -0
- package/test/events/payments/trial-lapse-sweep.test.js +359 -0
- package/test/events/payments/webhook-atomic-writes.test.js +69 -0
- package/test/events/payments/webhook-chargebee-unreachable.test.js +106 -0
- package/test/events/payments/webhook-discount-clear.test.js +189 -0
- package/test/events/payments/webhook-failure-intent.test.js +93 -0
- package/test/events/payments/webhook-hosted-page-uid-trust.test.js +212 -0
- package/test/events/payments/webhook-ordering.test.js +326 -0
- package/test/events/payments/webhook-provider-lookup-trust.test.js +206 -0
- package/test/events/payments/webhook-refund-amount-trust.test.js +223 -0
- package/test/events/payments/webhook-refund-envelope-shape.test.js +148 -0
- package/test/events/payments/webhook-refund-linkage-trust.test.js +404 -0
- package/test/events/payments/webhook-refund-transaction-fallback.test.js +184 -0
- package/test/events/payments/webhook-refund-without-order.test.js +170 -0
- package/test/events/payments/webhook-refusal-reporting.test.js +194 -0
- package/test/events/payments/webhook-refused-intent.test.js +142 -0
- package/test/events/payments/webhook-retry-sweep.test.js +252 -0
- package/test/events/payments/webhook-transition-claim.test.js +389 -0
- package/test/events/payments/webhook-uid-trust.test.js +203 -0
- package/test/events/payments/webhook-user-without-auth.test.js +161 -0
- package/test/fixtures/chargebee/invoice-one-time.json +27 -0
- package/test/fixtures/chargebee/subscription-active.json +44 -0
- package/test/fixtures/chargebee/subscription-cancelled.json +42 -0
- package/test/fixtures/chargebee/subscription-in-trial.json +41 -0
- package/test/fixtures/chargebee/subscription-legacy-plan.json +41 -0
- package/test/fixtures/chargebee/subscription-non-renewing.json +41 -0
- package/test/fixtures/chargebee/subscription-paused.json +42 -0
- package/test/fixtures/chargebee/webhook-payment-failed.json +51 -0
- package/test/fixtures/chargebee/webhook-subscription-created.json +47 -0
- package/test/fixtures/coinbase/charge-confirmed.json +50 -0
- package/test/fixtures/coinbase/charge-failed.json +34 -0
- package/test/fixtures/coinbase/charge-pending.json +34 -0
- package/test/fixtures/migrate/ported-route.js.txt +11 -0
- package/test/fixtures/paypal/capture-completed.json +51 -0
- package/test/fixtures/paypal/capture-refunded.json +43 -0
- package/test/fixtures/paypal/order-approved.json +62 -0
- package/test/fixtures/paypal/order-completed.json +110 -0
- package/test/fixtures/paypal/sale-refunded.json +38 -0
- package/test/fixtures/paypal/subscription-active.json +76 -0
- package/test/fixtures/paypal/subscription-cancelled.json +50 -0
- package/test/fixtures/paypal/subscription-suspended.json +65 -0
- package/test/fixtures/stripe/checkout-session-completed.json +130 -0
- package/test/fixtures/stripe/invoice-payment-failed.json +148 -0
- package/test/fixtures/stripe/invoice-subscription-payment-failed.json +28 -0
- package/test/fixtures/stripe/invoice-subscription-payment-succeeded.json +28 -0
- package/test/fixtures/stripe/subscription-active.json +161 -0
- package/test/fixtures/stripe/subscription-canceled.json +161 -0
- package/test/fixtures/stripe/subscription-trialing.json +161 -0
- package/test/helpers/_shared-config.js +15 -0
- package/test/helpers/ai-request-payload.test.js +619 -0
- package/test/helpers/ai-schema-resolve.test.js +125 -0
- package/test/helpers/ai-test-provider.test.js +227 -0
- package/test/helpers/ai-token-accounting.test.js +150 -0
- package/test/helpers/ai-tools-format.test.js +384 -0
- package/test/helpers/analytics-no-id-notice.test.js +146 -0
- package/test/helpers/analytics-user-data.test.js +194 -0
- package/test/helpers/api-manager.test.js +311 -0
- package/test/helpers/backend-router.test.js +136 -0
- package/test/helpers/content/blog-auto-publisher.test.js +482 -0
- package/test/helpers/content/feed-parser.test.js +529 -0
- package/test/helpers/content/ghostii-blocks.test.js +135 -0
- package/test/helpers/content/ghostii-feed-integration.test.js +405 -0
- package/test/helpers/content/ghostii-write-article.test.js +244 -0
- package/test/helpers/dev-only-routes.test.js +225 -0
- package/test/helpers/env-reader.test.js +362 -0
- package/test/helpers/environment.test.js +263 -0
- package/test/helpers/event-middleware.test.js +407 -0
- package/test/helpers/infer-contact.test.js +157 -0
- package/test/helpers/lane-url.test.js +90 -0
- package/test/helpers/merge-line-files.test.js +272 -0
- package/test/helpers/metadata.test.js +122 -0
- package/test/helpers/middleware-request-log.test.js +134 -0
- package/test/helpers/middleware-user-log.test.js +124 -0
- package/test/helpers/payment/chargebee/parse-webhook.test.js +490 -0
- package/test/helpers/payment/chargebee/refund-details.test.js +198 -0
- package/test/helpers/payment/chargebee/to-unified-one-time.test.js +148 -0
- package/test/helpers/payment/chargebee/to-unified-subscription.test.js +649 -0
- package/test/helpers/payment/coinbase/create-intent.test.js +247 -0
- package/test/helpers/payment/coinbase/parse-webhook.test.js +195 -0
- package/test/helpers/payment/coinbase/refund-unsupported.test.js +141 -0
- package/test/helpers/payment/coinbase/to-unified-one-time.test.js +201 -0
- package/test/helpers/payment/discount-codes.test.js +141 -0
- package/test/helpers/payment/extract-resource.test.js +113 -0
- package/test/helpers/payment/fetch-failure.test.js +215 -0
- package/test/helpers/payment/license-gate.test.js +108 -0
- package/test/helpers/payment/order-id.test.js +99 -0
- package/test/helpers/payment/paypal/create-intent.test.js +382 -0
- package/test/helpers/payment/paypal/fetch-capture.test.js +123 -0
- package/test/helpers/payment/paypal/fetch-sale.test.js +183 -0
- package/test/helpers/payment/paypal/parse-webhook.test.js +678 -0
- package/test/helpers/payment/paypal/refund-details.test.js +202 -0
- package/test/helpers/payment/paypal/resolve-plan-id.test.js +108 -0
- package/test/helpers/payment/paypal/switch-plan.test.js +92 -0
- package/test/helpers/payment/paypal/to-unified-one-time.test.js +383 -0
- package/test/helpers/payment/paypal/to-unified-subscription.test.js +884 -0
- package/test/helpers/payment/stripe/fetch-charge.test.js +147 -0
- package/test/helpers/payment/stripe/parse-webhook.test.js +448 -0
- package/test/helpers/payment/stripe/refund-details.test.js +147 -0
- package/test/helpers/payment/stripe/to-unified-one-time.test.js +307 -0
- package/test/helpers/payment/stripe/to-unified-subscription.test.js +709 -0
- package/test/helpers/persona-domain.test.js +87 -0
- package/test/helpers/recaptcha.test.js +166 -0
- package/test/helpers/resolved-config.test.js +106 -0
- package/test/helpers/response-log-redaction.test.js +262 -0
- package/test/helpers/roles.test.js +155 -0
- package/test/helpers/route-context-debug-gate.test.js +118 -0
- package/test/helpers/route-context-logging.test.js +214 -0
- package/test/helpers/route-context.test.js +129 -0
- package/test/helpers/safe-compare.test.js +114 -0
- package/test/helpers/sanitize.test.js +223 -0
- package/test/helpers/schema-engine.test.js +537 -0
- package/test/helpers/schema-zod.test.js +554 -0
- package/test/helpers/seed-accounts-load.test.js +163 -0
- package/test/helpers/seed-google-personas.test.js +114 -0
- package/test/helpers/seeded-personas.test.js +823 -0
- package/test/helpers/settings.test.js +292 -0
- package/test/helpers/setup-engines-pin.test.js +68 -0
- package/test/helpers/setup-manifest-sync.test.js +116 -0
- package/test/helpers/slugify.test.js +395 -0
- package/test/helpers/storage.test.js +195 -0
- package/test/helpers/test-banner-latch.test.js +139 -0
- package/test/helpers/usage-consume.test.js +460 -0
- package/test/helpers/usage-log-privacy.test.js +121 -0
- package/test/helpers/user-doc-heal.test.js +540 -0
- package/test/helpers/user.test.js +771 -0
- package/test/helpers/webhook-forward.test.js +419 -0
- package/test/helpers/wipe-auth-project.test.js +143 -0
- package/test/mcp/discovery.test.js +52 -0
- package/test/mcp/oauth.test.js +160 -0
- package/test/mcp/protocol.test.js +278 -0
- package/test/mcp/roles.test.js +203 -0
- package/test/mcp/utils.test.js +245 -0
- package/test/notification/identity.test.js +155 -0
- package/test/routes/admin/create-post.test.js +362 -0
- package/test/routes/admin/database.test.js +133 -0
- package/test/routes/admin/deduplicate-image-alts.test.js +191 -0
- package/test/routes/admin/email-content-html.test.js +58 -0
- package/test/routes/admin/email-request-log.test.js +86 -0
- package/test/routes/admin/email.test.js +116 -0
- package/test/routes/admin/firestore-query.test.js +206 -0
- package/test/routes/admin/firestore.test.js +129 -0
- package/test/routes/admin/infer-contact.test.js +220 -0
- package/test/routes/admin/notification.test.js +199 -0
- package/test/routes/admin/post-convert-image.test.js +159 -0
- package/test/routes/admin/post-deploy-flag.test.js +81 -0
- package/test/routes/admin/post-download-error.test.js +90 -0
- package/test/routes/admin/post-resize-image.test.js +185 -0
- package/test/routes/admin/post.test.js +369 -0
- package/test/routes/admin/repo-content.test.js +223 -0
- package/test/routes/admin/stats.test.js +114 -0
- package/test/routes/admin/users-disable.test.js +63 -0
- package/test/routes/admin/users-list.test.js +71 -0
- package/test/routes/content/post.test.js +60 -0
- package/test/routes/general/uuid.test.js +133 -0
- package/test/routes/health.test.js +110 -0
- package/test/routes/marketing/campaign.test.js +184 -0
- package/test/routes/marketing/contact.test.js +416 -0
- package/test/routes/marketing/email-preferences.test.js +293 -0
- package/test/routes/marketing/push-send.test.js +34 -0
- package/test/routes/marketing/webhook-forward.test.js +63 -0
- package/test/routes/marketing/webhook.test.js +641 -0
- package/test/routes/payments/_route-harness.js +145 -0
- package/test/routes/payments/cancel-provider-errors.test.js +227 -0
- package/test/routes/payments/cancel-skip-guards.test.js +126 -0
- package/test/routes/payments/cancel-trialing.test.js +428 -0
- package/test/routes/payments/cancel.test.js +165 -0
- package/test/routes/payments/dedup-race.test.js +186 -0
- package/test/routes/payments/discount.test.js +82 -0
- package/test/routes/payments/dispute-alert.test.js +324 -0
- package/test/routes/payments/intent-discount-amounts.test.js +550 -0
- package/test/routes/payments/intent-discount-coupons.test.js +293 -0
- package/test/routes/payments/intent-one-time-metadata.test.js +122 -0
- package/test/routes/payments/intent-purchaser-guard.test.js +139 -0
- package/test/routes/payments/intent-zero-total.test.js +260 -0
- package/test/routes/payments/intent.test.js +413 -0
- package/test/routes/payments/plan.test.js +367 -0
- package/test/routes/payments/portal-return-url.test.js +107 -0
- package/test/routes/payments/portal.test.js +94 -0
- package/test/routes/payments/refund-one-time.test.js +254 -0
- package/test/routes/payments/refund-paypal-proration.test.js +190 -0
- package/test/routes/payments/refund.test.js +181 -0
- package/test/routes/payments/trial-eligibility.test.js +73 -0
- package/test/routes/payments/uncancel.test.js +270 -0
- package/test/routes/payments/webhook-stripe-invoice.test.js +129 -0
- package/test/routes/payments/webhook-stripe-refund-one-time.test.js +130 -0
- package/test/routes/payments/webhook-test-provider.test.js +156 -0
- package/test/routes/payments/webhook.test.js +115 -0
- package/test/routes/payments/winback-stripe-coupon.test.js +126 -0
- package/test/routes/payments/winback.test.js +597 -0
- package/test/routes/test/authenticate.test.js +79 -0
- package/test/routes/test/redirect.test.js +66 -0
- package/test/routes/test/reset-account.test.js +126 -0
- package/test/routes/test/roster.test.js +101 -0
- package/test/routes/test/schema.test.js +556 -0
- package/test/routes/test/usage.test.js +374 -0
- package/test/routes/user/api-keys.test.js +158 -0
- package/test/routes/user/connections-grant.test.js +759 -0
- package/test/routes/user/connections-identity.test.js +260 -0
- package/test/routes/user/connections-log-privacy.test.js +206 -0
- package/test/routes/user/connections-refresh-lease.test.js +696 -0
- package/test/routes/user/connections-return.test.js +261 -0
- package/test/routes/user/connections-uid.test.js +516 -0
- package/test/routes/user/delete.test.js +138 -0
- package/test/routes/user/feedback.test.js +106 -0
- package/test/routes/user/orders.test.js +233 -0
- package/test/routes/user/sessions.test.js +231 -0
- package/test/routes/user/settings-validate.test.js +84 -0
- package/test/routes/user/signup-emails.test.js +113 -0
- package/test/routes/user/signup-location.test.js +84 -0
- package/test/routes/user/signup-log-privacy.test.js +112 -0
- package/test/routes/user/signup.test.js +579 -0
- package/test/routes/user/subscription.test.js +101 -0
- package/test/routes/user/token.test.js +111 -0
- package/test/routes/user/user.test.js +158 -0
- package/test/routes/verts/cache.test.js +78 -0
- package/test/routes/verts/click-destination.test.js +76 -0
- package/test/routes/verts/crud.test.js +194 -0
- package/test/routes/verts/redirect.test.js +147 -0
- package/test/routes/verts/selection.test.js +237 -0
- package/test/routes/verts/serve.test.js +185 -0
- package/test/routes/verts/unit-document.test.js +117 -0
- package/test/rules/_environment.js +66 -0
- package/test/rules/brand-merge.test.js +195 -0
- package/test/rules/field-helpers.test.js +309 -0
- package/test/rules/notifications.test.js +577 -0
- package/test/rules/payments-carts.test.js +406 -0
- package/test/rules/sessions.test.js +170 -0
- package/test/rules/user.test.js +517 -0
- package/test/rules/verts.test.js +101 -0
- package/test/security/fetch-log-secrets.test.js +124 -0
- package/test/security/primitives.test.js +105 -0
- package/test/security/repo-pinning.test.js +77 -0
- 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
|
+
};
|